diff --git a/LuaEngine.cpp b/LuaEngine.cpp index d55edbf..2efcee2 100644 --- a/LuaEngine.cpp +++ b/LuaEngine.cpp @@ -15,9 +15,7 @@ void check(lua_State* L, const vector& typearr) { if (lua_type(L, i + 1) != typearr[i]) { - char tmp[128] = { 0 }; - sprintf(tmp, "Bad argument #%d. %s expected, got %s", i + 1, lua_typename(L, typearr[i]), lua_typename(L, lua_type(L, i + 1))); - luaL_error(L, tmp); + luaL_error(L, "Bad argument #%d. %s expected, got %s", i + 1, lua_typename(L, typearr[i]), lua_typename(L, lua_type(L, i + 1))); } } } @@ -48,6 +46,7 @@ struct Window static int close(lua_State* L) { check(L, { LUA_TUSERDATA }); + cout << "In Window::close" << endl; Window* p = (Window*)lua_touserdata(L, 1); if (p->wnd) { @@ -165,7 +164,7 @@ struct Window static int create(lua_State* L) { check(L, { LUA_TSTRING, LUA_TNUMBER, LUA_TNUMBER }); - cout << "In SDLWindow::Create" << endl; + cout << "In Window::create" << endl; Window* p = (Window*) lua_newuserdata(L, sizeof(Window)); // Data table (for storing lua values) lua_pushlightuserdata(L, p); @@ -203,6 +202,161 @@ struct Window } }; + +struct Texture +{ + SDL_Texture* text; + int w; + int h; + + static int close(lua_State* L) + { + check(L, { LUA_TUSERDATA }); + cout << "In Texture::close" << endl; + Texture* t = (Texture*)lua_touserdata(L, 1); + if (t->text) + { + SDL_DestroyTexture(t->text); + t->text = nullptr; + } + return 0; + } + + static void create(lua_State* L) // -2, +1 + { + // 此函数由Renderer在C层直接调用. 调用前需要先放一个renderer userdata和lightuserdata在栈上, 值就是当前的SDL_Texture. + // 调用后栈上会留下Texture的userdata. + // 因此在Lua层没有直接创建Texture的方法. + cout << "In Texture::create" << endl; + Texture* p = (Texture*)lua_newuserdata(L, sizeof(Texture)); + // Metatable + lua_getfield(L, LUA_REGISTRYINDEX, "__texture_mt"); + if (lua_type(L, -1) == LUA_TNIL) + { + lua_pop(L, 1); + lua_newtable(L); + // GC + lua_pushcfunction(L, close); + lua_setfield(L, -2, "__gc"); + + // Fields + lua_newtable(L); + lua_pushstring(L, "texture"); lua_setfield(L, -2, "type"); + setfn(L, close, "close"); + + // Binding + lua_setfield(L, -2, "__index"); + + lua_setfield(L, LUA_REGISTRYINDEX, "__texture_mt"); + lua_getfield(L, LUA_REGISTRYINDEX, "__texture_mt"); + } + lua_setmetatable(L, -2); + p->text = (SDL_Texture * )lua_touserdata(L, -2); + SDL_QueryTexture(p->text, NULL, NULL, &p->w, &p->h); + lua_pushvalue(L, -3); + lua_setuservalue(L, -2); // 绑定这个Rendere userdata + + // stack balance + lua_remove(L, lua_gettop(L) - 1); // 删除lightuserdata + lua_remove(L, lua_gettop(L) - 1); // 删除rendere userdata + } +}; + +int LuaRectPointToRect(lua_State* L, int index, SDL_Rect& rect) +{ + if (lua_getfield(L, index, "type") != LUA_TSTRING) + { + lua_pop(L, 1); + return -1; + } + + const char* type = lua_tostring(L, -1); + if (strcmp(type, "point") == 0) + { + lua_pop(L, 1); + lua_getfield(L, index, "x"); + rect.x = lua_tointeger(L, -1); + lua_pop(L, 1); + lua_getfield(L, index, "y"); + rect.y = lua_tointeger(L, -1); + lua_pop(L, 1); + + return 1; + } + else if (strcmp(type, "rect") == 0) + { + lua_pop(L, 1); + lua_getfield(L, index, "x"); + rect.x = lua_tointeger(L, -1); + lua_pop(L, 1); + lua_getfield(L, index, "y"); + rect.y = lua_tointeger(L, -1); + lua_pop(L, 1); + lua_getfield(L, index, "w"); + rect.w = lua_tointeger(L, -1); + lua_pop(L, 1); + lua_getfield(L, index, "h"); + rect.h = lua_tointeger(L, -1); + lua_pop(L, 1); + + return 0; + } + else + { + return -1; + } +} + +struct Surface +{ + SDL_Surface* surf; + + static int close(lua_State* L) + { + check(L, { LUA_TUSERDATA }); + cout << "In Surface::close" << endl; + Surface* p = (Surface*)lua_touserdata(L, 1); + if (p->surf) + { + SDL_FreeSurface(p->surf); + p->surf = nullptr; + } + return 0; + } + + static void create(lua_State* L) // -1, +1 + { + cout << "In Surface::create" << endl; + Surface* p = (Surface*)lua_newuserdata(L, sizeof(Surface)); + // Metatable + lua_getfield(L, LUA_REGISTRYINDEX, "__surface_mt"); + if (lua_type(L, -1) == LUA_TNIL) + { + lua_pop(L, 1); + lua_newtable(L); + // GC + lua_pushcfunction(L, close); + lua_setfield(L, -2, "__gc"); + + // Fields + lua_newtable(L); + lua_pushstring(L, "surface"); lua_setfield(L, -2, "type"); + setfn(L, close, "close"); + + // Binding + lua_setfield(L, -2, "__index"); + + lua_setfield(L, LUA_REGISTRYINDEX, "__surface_mt"); + lua_getfield(L, LUA_REGISTRYINDEX, "__surface_mt"); + } + lua_setmetatable(L, -2); + p->surf = (SDL_Surface*)lua_touserdata(L, -2); + + // stack balance + lua_remove(L, lua_gettop(L) - 1); // 删除lightuserdata + } +}; + struct Renderer { SDL_Renderer* rnd; @@ -210,6 +364,7 @@ struct Renderer static int close(lua_State* L) { check(L, { LUA_TUSERDATA }); + cout << "In Renderer::close" << endl; Renderer* p = (Renderer*)lua_touserdata(L, 1); if (p->rnd) { @@ -235,10 +390,50 @@ struct Renderer return 0; } + static int loadTexture(lua_State* L) + { + check(L, { LUA_TUSERDATA, LUA_TSTRING }); + Renderer* p = (Renderer*)lua_touserdata(L, 1); + const char* filepath = lua_tostring(L, 2); + SDL_Texture* text = IMG_LoadTexture(p->rnd, filepath); + lua_pushvalue(L, 1); + lua_pushlightuserdata(L, text); + Texture::create(L); + return 1; + } + + static int copy(lua_State* L) + { + check(L, { LUA_TUSERDATA, LUA_TUSERDATA, LUA_TTABLE, LUA_TTABLE }); + Renderer* p = (Renderer*)lua_touserdata(L, 1); + + return 0; + } + + static int copyTo(lua_State* L) + { + check(L, { LUA_TUSERDATA, LUA_TUSERDATA, LUA_TTABLE }); + Renderer* r = (Renderer*)lua_touserdata(L, 1); + Texture* t = (Texture*)lua_touserdata(L, 2); + SDL_Rect rect; + int ret = LuaRectPointToRect(L, 3, rect); + if (ret < 0) + { + luaL_error(L, "Bad argument #%d. Point or Rect expected, got %s", 3, lua_typename(L, lua_type(L, 3))); + } + if (ret == 1) + { + rect.w = t->w; + rect.h = t->h; + } + SDL_RenderCopy(r->rnd, t->text, NULL, &rect); + return 0; + } + static int create(lua_State* L) { check(L, { LUA_TUSERDATA }); - cout << "In SDLWindow::Create" << endl; + cout << "In Renderer::create" << endl; Renderer* p = (Renderer*)lua_newuserdata(L, sizeof(Renderer)); lua_getfield(L, LUA_REGISTRYINDEX, "__renderer_mt"); if (lua_type(L, -1) == LUA_TNIL) @@ -254,7 +449,10 @@ struct Renderer lua_pushstring(L, "renderer"); lua_setfield(L, -2, "type"); setfn(L, close, "close"); setfn(L, update, "update"); - setfn(L, clear, "clear"); + setfn(L, clear, "clear"); + setfn(L, loadTexture, "loadTexture"); + setfn(L, copy, "copy"); + setfn(L, copyTo, "copyTo"); // Set __index of metatable. lua_setfield(L, -2, "__index"); @@ -263,25 +461,283 @@ struct Renderer lua_getfield(L, LUA_REGISTRYINDEX, "__renderer_mt"); } lua_setmetatable(L, -2); - + // 绑定当前Renderer的对象到自身, 防止SDL_Window被提前销毁. + lua_pushvalue(L, 1); + lua_setuservalue(L, -2); // Internal logic p->rnd = SDL_CreateRenderer(static_cast(lua_touserdata(L, 1))->wnd, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_TARGETTEXTURE); return 1; } }; +int LuaColorToColor(lua_State* L, int index, SDL_Color& c) +{ + if (lua_getfield(L, index, "type") != LUA_TSTRING) + { + lua_pop(L, 1); + return -1; + } + + const char* type = lua_tostring(L, -1); + if (strcmp(type, "color") == 0) + { + lua_pop(L, 1); + lua_getfield(L, index, "r"); + c.r = lua_tointeger(L, -1); + lua_pop(L, 1); + lua_getfield(L, index, "g"); + c.g = lua_tointeger(L, -1); + lua_pop(L, 1); + lua_getfield(L, index, "b"); + c.b = lua_tointeger(L, -1); + lua_pop(L, 1); + lua_getfield(L, index, "a"); + c.a = lua_tointeger(L, -1); + lua_pop(L, 1); + + return 0; + } + else + { + return -1; + } +} + +struct Font +{ + TTF_Font* font; + + static int close(lua_State* L) + { + check(L, { LUA_TUSERDATA }); + cout << "In Font::close" << endl; + Font* f = (Font*)lua_touserdata(L, 1); + if (f->font) + { + TTF_CloseFont(f->font); + f->font = nullptr; + } + return 0; + } + + static int renderText(lua_State* L) + { + Renderer* r = nullptr; + Font* f = nullptr; + const char* text = nullptr; + SDL_Color c; + if (lua_gettop(L) >= 4) + { + check(L, { LUA_TUSERDATA, LUA_TUSERDATA, LUA_TSTRING, LUA_TTABLE }); + f = (Font*)lua_touserdata(L, 1); + r = (Renderer*)lua_touserdata(L, 2); + text = lua_tostring(L, 3); + if (LuaColorToColor(L, 4, c) < 0) + { + luaL_error(L, "Bad argument #%d. Color expected, got %s", 4, lua_typename(L, lua_type(L, 4))); + } + } + else + { + check(L, { LUA_TUSERDATA, LUA_TSTRING, LUA_TTABLE }); + f = (Font*)lua_touserdata(L, 1); + text = lua_tostring(L, 3); + if (LuaColorToColor(L, 4, c) < 0) + { + luaL_error(L, "Bad argument #%d. Color expected, got %s", 4, lua_typename(L, lua_type(L, 4))); + } + } + + SDL_Surface* surf = TTF_RenderText_Blended(f->font, text, c); + + if (r) + { + SDL_Texture* text = SDL_CreateTextureFromSurface(r->rnd, surf); + SDL_FreeSurface(surf); + lua_pushvalue(L, 2); + lua_pushlightuserdata(L, text); + Texture::create(L); + } + else + { + lua_pushlightuserdata(L, surf); + Surface::create(L); + } + return 1; + } + + static int create(lua_State* L) + { + check(L, { LUA_TSTRING, LUA_TNUMBER }); + cout << "In Font::create" << endl; + Font* f = (Font*)lua_newuserdata(L, sizeof(Font)); + lua_getfield(L, LUA_REGISTRYINDEX, "__font_mt"); + if (lua_type(L, -1) == LUA_TNIL) + { + lua_pop(L, 1); + lua_newtable(L); + // GC + lua_pushcfunction(L, close); + lua_setfield(L, -2, "__gc"); + + // Fields + lua_newtable(L); + lua_pushstring(L, "font"); lua_setfield(L, -2, "type"); + setfn(L, close, "close"); + setfn(L, renderText, "renderText"); + + // Set __index of metatable. + lua_setfield(L, -2, "__index"); + + lua_setfield(L, LUA_REGISTRYINDEX, "__font_mt"); + lua_getfield(L, LUA_REGISTRYINDEX, "__font_mt"); + } + lua_setmetatable(L, -2); + const char* filename = lua_tostring(L, 1); + int fontsz = lua_tointeger(L, 2); + f->font = TTF_OpenFont(filename, fontsz); + + return 1; + } +}; + +struct Music +{ + Mix_Music* music; + + static int close(lua_State* L) + { + check(L, { LUA_TUSERDATA }); + cout << "In Music::close" << endl; + + Music* m = (Music*)lua_touserdata(L, 1); + if (m->music) + { + Mix_FreeMusic(m->music); + m->music = nullptr; + } + return 0; + } + + static int create(lua_State* L) + { + check(L, { LUA_TSTRING }); + cout << "In Music::create" << endl; + + Music* m = (Music*)lua_newuserdata(L, sizeof(Music)); + lua_getfield(L, LUA_REGISTRYINDEX, "__music_mt"); + if (lua_type(L, -1) == LUA_TNIL) + { + lua_pop(L, 1); + lua_newtable(L); + // GC + lua_pushcfunction(L, close); + lua_setfield(L, -2, "__gc"); + + // Fields + lua_newtable(L); + lua_pushstring(L, "music"); lua_setfield(L, -2, "type"); + setfn(L, close, "close"); + + // Set __index of metatable. + lua_setfield(L, -2, "__index"); + + lua_setfield(L, LUA_REGISTRYINDEX, "__music_mt"); + lua_getfield(L, LUA_REGISTRYINDEX, "__music_mt"); + } + lua_setmetatable(L, -2); + + const char* filename = lua_tostring(L, 1); + m->music = Mix_LoadMUS(filename); + cout << "Load music error: " << SDL_GetError() << endl; + + return 1; + } +}; + +struct MusicPlayer +{ + static int close(lua_State* L) + { + check(L, { LUA_TUSERDATA }); + cout << "In MusicPlayer::close" << endl; + + return 0; + } + + static int play(lua_State* L) + { + check(L, { LUA_TUSERDATA, LUA_TUSERDATA }); + MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1); + Music* m = (Music*)lua_touserdata(L, 2); + cout << "Play music ret: " << Mix_PlayMusic(m->music, 1) << " " << SDL_GetError() << endl; + return 0; + } + + static int create(lua_State* L) + { + cout << "In MusicPlayer::create" << endl; + + MusicPlayer* m = (MusicPlayer*)lua_newuserdata(L, sizeof(MusicPlayer)); + lua_getfield(L, LUA_REGISTRYINDEX, "__musicplayer_mt"); + if (lua_type(L, -1) == LUA_TNIL) + { + lua_pop(L, 1); + lua_newtable(L); + // GC + lua_pushcfunction(L, close); + lua_setfield(L, -2, "__gc"); + + // Fields + lua_newtable(L); + lua_pushstring(L, "musicplayer"); lua_setfield(L, -2, "type"); + setfn(L, close, "close"); + setfn(L, play, "play"); + + // Set __index of metatable. + lua_setfield(L, -2, "__index"); + + lua_setfield(L, LUA_REGISTRYINDEX, "__musicplayer_mt"); + lua_getfield(L, LUA_REGISTRYINDEX, "__musicplayer_mt"); + } + lua_setmetatable(L, -2); + + return 1; + } +}; + #define reg_in_lua(L, cfunc, name) lua_pushcfunction(L, cfunc); lua_setglobal(L, name) +void check_init(lua_State* L, const char* name, int ret, int expected = 0) +{ + if (ret != expected) + { + luaL_error(L, "Failed to initialize %s. ret: %d. expected: %d. SDLError: %s", name, ret, expected, SDL_GetError()); + } +} + int InitEngine(lua_State* L) { - SDL_Init(SDL_INIT_EVERYTHING); - IMG_Init(IMG_INIT_JPG | IMG_INIT_PNG); - TTF_Init(); - Mix_Init(MIX_INIT_MP3); + int ret; + ret = SDL_Init(SDL_INIT_EVERYTHING); + check_init(L, "SDL", ret); + ret = IMG_Init(IMG_INIT_JPG | IMG_INIT_PNG); + check_init(L, "IMG", ret, IMG_INIT_JPG | IMG_INIT_PNG); + ret = TTF_Init(); + check_init(L, "TTF", ret); + ret = Mix_Init(MIX_INIT_MP3 | MIX_INIT_MOD | MIX_INIT_OGG ); + check_init(L, "MIX", ret, MIX_INIT_MP3 | MIX_INIT_MOD | MIX_INIT_OGG); + + ret = Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 512); + check_init(L, "AUDIO", ret); + Mix_AllocateChannels(16); lua_newtable(L); reg_in_lua(L, Window::create, "Window"); reg_in_lua(L, Renderer::create, "Renderer"); + reg_in_lua(L, Font::create, "Font"); + reg_in_lua(L, Music::create, "Music"); + reg_in_lua(L, MusicPlayer::create, "MusicPlayer"); return 0; }