From 4d1e3ec455d9ae2593bac57e33e40e4312ec1178 Mon Sep 17 00:00:00 2001 From: Reuh Date: Fri, 22 Apr 2016 16:45:56 +0200 Subject: [PATCH] Improved map:draw a lot, fixed GC issues with ctr.map, changed some things internally map:draw should be faster and more flexible. --- source/gfx.c | 28 ++++++++++++++------ source/gfx.h | 12 +++++++++ source/map.c | 73 ++++++++++++++++++++++++++++++++++++++++------------ 3 files changed, 88 insertions(+), 25 deletions(-) create mode 100644 source/gfx.h diff --git a/source/gfx.c b/source/gfx.c index 447f8df..f464c7e 100644 --- a/source/gfx.c +++ b/source/gfx.c @@ -17,6 +17,7 @@ The `gfx` module. #include #include +#include "gfx.h" #include "font.h" #include "texture.h" @@ -27,6 +28,13 @@ typedef struct { bool isGfxInitialized = false; bool is3DEnabled = false; //TODO: add a function for this in the ctrulib/sf2dlib. +// The scissor-test state, as defined in Lua code. When you apply a new scissor in C, remember to get back to this state to avoid unexpected behaviour. +scissor_state lua_scissor = { + GPU_SCISSOR_DISABLE, + 0, 0, + 0, 0 +}; + /*** The `ctr.gfx.color` module. @table color @@ -385,17 +393,21 @@ Calls this function without argument to disable the scissor test. */ static int gfx_scissor(lua_State *L) { if (lua_gettop(L) == 0) { - sf2d_set_scissor_test(GPU_SCISSOR_DISABLE, 0, 0, 0, 0); + lua_scissor.mode = GPU_SCISSOR_DISABLE; + lua_scissor.x = 0; + lua_scissor.y = 0; + lua_scissor.width = 0; + lua_scissor.height = 0; } else { - int x = luaL_checkinteger(L, 1); - int y = luaL_checkinteger(L, 2); - int width = luaL_checkinteger(L, 3); - int height = luaL_checkinteger(L, 4); - bool invert = lua_toboolean(L, 5); - - sf2d_set_scissor_test(invert ? GPU_SCISSOR_INVERT : GPU_SCISSOR_NORMAL, x, y, width, height); + lua_scissor.x = luaL_checkinteger(L, 1); + lua_scissor.y = luaL_checkinteger(L, 2); + lua_scissor.width = luaL_checkinteger(L, 3); + lua_scissor.height = luaL_checkinteger(L, 4); + lua_scissor.mode = lua_toboolean(L, 5) ? GPU_SCISSOR_INVERT : GPU_SCISSOR_NORMAL; } + sf2d_set_scissor_test(lua_scissor.mode, lua_scissor.x, lua_scissor.y, lua_scissor.width, lua_scissor.height); + return 0; } diff --git a/source/gfx.h b/source/gfx.h new file mode 100644 index 0000000..7a7afbe --- /dev/null +++ b/source/gfx.h @@ -0,0 +1,12 @@ +#ifndef GFX_H +#define GFX_H + +typedef struct { + GPU_SCISSORMODE mode; + u32 x; u32 y; + u32 width; u32 height; +} scissor_state; + +extern scissor_state lua_scissor; + +#endif diff --git a/source/map.c b/source/map.c index c92242d..193347d 100644 --- a/source/map.c +++ b/source/map.c @@ -13,7 +13,9 @@ Tile coordinates start at x=0,y=0. #include #include #include +#include +#include "gfx.h" #include "texture.h" typedef struct { @@ -59,7 +61,16 @@ static int map_load(lua_State *L) { map_userdata *map = lua_newuserdata(L, sizeof(map_userdata)); luaL_getmetatable(L, "LMap"); lua_setmetatable(L, -2); - + + // Block GC of the texture by keeping a reference to it in the registry + // registry[map_userdata] = texture_userdata + lua_pushnil(L); + lua_copy(L, -2, -1); // map_userdata + lua_pushnil(L); + lua_copy(L, 2, -1); // texture_userdata + lua_settable(L, LUA_REGISTRYINDEX); + + // Init userdata fields map->texture = texture; map->tileSizeX = tileSizeX; map->tileSizeY = tileSizeY; @@ -158,50 +169,81 @@ Map object */ /*** -Draw a map. +Draw (a part of) the map on the screen. @function :draw -@tparam number x X position -@tparam number y Y position -@within Methods +@tparam integer x X top-left coordinate to draw the map on the screen (pixels) +@tparam integer y Y top-left coordinate to draw the map on the screen (pixels) +@tparam[opt=0] integer offsetX drawn area X start coordinate on the map (pixels) (x=0,y=0 correspond to the first tile top-left corner) +@tparam[opt=0] integer offsetY drawn area Y start coordinate on the map (pixels) +@tparam[opt=400] integer width width of the drawn area on the map (pixels) +@tparam[opt=240] integer height height of the drawn area on the map (pixels) +@usage +-- This will draw on the screen at x=5,y=5 a part of the map. The part is the rectangle on the map starting at x=16,y=16 and width=32,height=48. +-- For example, if you use 16x16 pixel tiles, this will draw the tiles from 1,1 (top-left corner of the rectangle) to 2,3 (bottom-right corner). +map:draw(5, 5, 16, 16, 32, 48) */ static int map_draw(lua_State *L) { map_userdata *map = luaL_checkudata(L, 1, "LMap"); int x = luaL_checkinteger(L, 2); int y = luaL_checkinteger(L, 3); + int offsetX = luaL_optinteger(L, 4, 0); + int offsetY = luaL_optinteger(L, 5, 0); + int width = luaL_optinteger(L, 6, 400); + int height = luaL_optinteger(L, 7, 240); + + int xI = fmax(floor((double)offsetX / map->tileSizeX), 0); // initial tile X + int xF = fmin(ceil((double)(offsetX + width) / map->tileSizeX), map->width); // final tile X + + int yI = fmax(floor((double)offsetY / map->tileSizeY), 0); // initial tile Y + int yF = fmin(ceil((double)(offsetY + height) / map->tileSizeY), map->height); // final tile Y + + if (sf2d_get_current_screen() == GFX_TOP) + sf2d_set_scissor_test(GPU_SCISSOR_NORMAL, x, y, fmin(width, 400 - x), fmin(height, 240 - y)); // Scissor test doesn't work when x/y + width > screenWidth/Height + else + sf2d_set_scissor_test(GPU_SCISSOR_NORMAL, x, y, fmin(width, 320 - x), fmin(height, 240 - y)); + int texX = 0; int texY = 0; - + if (map->texture->blendColor == 0xffffffff) { - for (int xp=0; xpwidth; xp++) { - for (int yp=0; ypheight; yp++) { + for (int xp = xI; xp < xF; xp++) { + for (int yp = yI; yp < yF; yp++) { u16 tile = getTile(map, xp, yp); getTilePos(map, tile, &texX, &texY); - sf2d_draw_texture_part(map->texture->texture, (x+(map->tileSizeX*xp)+(xp*map->spaceX)), (y+(map->tileSizeY*yp)+(yp*map->spaceY)), texX, texY, map->tileSizeX, map->tileSizeY); + sf2d_draw_texture_part(map->texture->texture, x+(map->tileSizeX*xp)+(xp*map->spaceX)-offsetX, y+(map->tileSizeY*yp)+(yp*map->spaceY)-offsetY, texX, texY, map->tileSizeX, map->tileSizeY); } } } else { - for (int xp=0; xpwidth; xp++) { - for (int yp=0; ypheight; yp++) { + for (int xp = xI; xp < xF; xp++) { + for (int yp = yI; yp < yF; yp++) { u16 tile = getTile(map, xp, yp); getTilePos(map, tile, &texX, &texY); - sf2d_draw_texture_part_blend(map->texture->texture, (x+(map->tileSizeX*xp)+(xp*map->spaceX)), (y+(map->tileSizeY*yp)+(yp*map->spaceY)), texX, texY, map->tileSizeX, map->tileSizeY, map->texture->blendColor); + sf2d_draw_texture_part_blend(map->texture->texture, x+(map->tileSizeX*xp)+(xp*map->spaceX)-offsetX, y+(map->tileSizeY*yp)+(yp*map->spaceY)-offsetY, texX, texY, map->tileSizeX, map->tileSizeY, map->texture->blendColor); } } } - + + sf2d_set_scissor_test(lua_scissor.mode, lua_scissor.x, lua_scissor.y, lua_scissor.width, lua_scissor.height); + return 0; } /*** Unload a map. @function :unload -@within Methods */ static int map_unload(lua_State *L) { map_userdata *map = luaL_checkudata(L, 1, "LMap"); free(map->data); + // Remove the reference to the texture in the registry + // registry[map_userdata] = nil + lua_pushnil(L); + lua_copy(L, 1, -1); // map_userdata + lua_pushnil(L); + lua_settable(L, LUA_REGISTRYINDEX); + return 0; } @@ -210,7 +252,6 @@ Return the size of a map. @function :getSize @treturn number width of the map, in tiles @treturn number height of the map, in tiles -@within Methods */ static int map_getSize(lua_State *L) { map_userdata *map = luaL_checkudata(L, 1, "LMap"); @@ -227,7 +268,6 @@ Return the value of a tile. @tparam number x X position of the tile (in tiles) @tparam number y Y position of the tile (in tiles) @treturn number value of the tile -@within Methods */ static int map_getTile(lua_State *L) { map_userdata *map = luaL_checkudata(L, 1, "LMap"); @@ -245,7 +285,6 @@ Set the value of a tile. @tparam number x X position of the tile (in tiles) @tparam number y Y position of the tile (in tiles) @tparam number value new value for the tile -@within Methods */ static int map_setTile(lua_State *L) { map_userdata *map = luaL_checkudata(L, 1, "LMap");