From d1370b955f9a86c82f92d7368237ed96318de330 Mon Sep 17 00:00:00 2001 From: 3gg <3gg@shellblade.net> Date: Sat, 19 Jul 2025 09:29:12 -0700 Subject: Allocate data from a stack allocator --- CMakeLists.txt | 4 +- demos/checkerboard/checkerboard.c | 29 +- demos/isomap/isomap.c | 24 +- include/isogfx/asset.h | 149 ++++++++-- include/isogfx/isogfx.h | 57 ++-- include/isogfx/types.h | 14 + src/asset.c | 34 +++ src/isogfx.c | 605 ++++++++++++++------------------------ tools/mkasset.py | 53 ++-- 9 files changed, 487 insertions(+), 482 deletions(-) create mode 100644 include/isogfx/types.h create mode 100644 src/asset.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 42d2502..498adc8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,6 +9,7 @@ set(CMAKE_C_EXTENSIONS Off) # isogfx add_library(isogfx + src/asset.c src/isogfx.c) target_include_directories(isogfx PUBLIC @@ -16,8 +17,7 @@ target_include_directories(isogfx PUBLIC target_link_libraries(isogfx PUBLIC filesystem - mem - mempool) + memstack) target_compile_options(isogfx PRIVATE -Wall -Wextra -Wpedantic) diff --git a/demos/checkerboard/checkerboard.c b/demos/checkerboard/checkerboard.c index 9d1791e..b408fc2 100644 --- a/demos/checkerboard/checkerboard.c +++ b/demos/checkerboard/checkerboard.c @@ -20,6 +20,9 @@ static const int TILE_HEIGHT = TILE_WIDTH / 2; static const int WORLD_WIDTH = 20; static const int WORLD_HEIGHT = 20; +#define MEMORY_SIZE (2 * 1024 * 1024) +uint8_t MEMORY[MEMORY_SIZE]; + static const TileDesc tile_set[] = { {.type = TileFromColour, .width = TILE_WIDTH, @@ -35,6 +38,8 @@ static const TileDesc tile_set[] = { .colour = (Pixel){.r = 0xdc, .g = 0x76, .b = 0x84, .a = 0xff}}, }; +#define NUM_TILES (sizeof(tile_set) / sizeof(tile_set[0])) + typedef enum Colour { Black, White, @@ -67,28 +72,28 @@ static bool init(GfxAppState* state, int argc, const char** argv) { (void)argc; (void)argv; - if (!(state->iso = isogfx_new(&(IsoGfxDesc){ - .screen_width = SCREEN_WIDTH, .screen_height = SCREEN_HEIGHT}))) { + if (!((state->iso = + isogfx_new(&(IsoGfxDesc){.memory = MEMORY, + .memory_size = MEMORY_SIZE, + .screen_width = SCREEN_WIDTH, + .screen_height = SCREEN_HEIGHT})))) { return false; } IsoGfx* iso = state->iso; - isogfx_resize(iso, SCREEN_WIDTH, SCREEN_HEIGHT); - - if (!isogfx_make_world( - iso, &(WorldDesc){.tile_width = TILE_WIDTH, - .tile_height = TILE_HEIGHT, - .world_width = WORLD_WIDTH, - .world_height = WORLD_HEIGHT})) { - return false; - } + isogfx_make_world( + iso, &(WorldDesc){.tile_width = TILE_WIDTH, + .tile_height = TILE_HEIGHT, + .world_width = WORLD_WIDTH, + .world_height = WORLD_HEIGHT, + .num_tiles = NUM_TILES}); const Tile black = isogfx_make_tile(iso, &tile_set[Black]); const Tile white = isogfx_make_tile(iso, &tile_set[White]); state->red = isogfx_make_tile(iso, &tile_set[Red]); make_checkerboard(iso, black, white); - if (!(state->backend = iso_backend_init(iso))) { + if (!((state->backend = iso_backend_init(iso)))) { return false; } diff --git a/demos/isomap/isomap.c b/demos/isomap/isomap.c index b328bfa..efae7fd 100644 --- a/demos/isomap/isomap.c +++ b/demos/isomap/isomap.c @@ -5,6 +5,7 @@ #include #include +#include static const int WINDOW_WIDTH = 1408; static const int WINDOW_HEIGHT = 960; @@ -14,6 +15,9 @@ static const int MAX_FPS = 60; static const int SCREEN_WIDTH = 704; static const int SCREEN_HEIGHT = 480; +#define MEMORY_SIZE (2 * 1024 * 1024) +uint8_t MEMORY[MEMORY_SIZE]; + typedef struct GfxAppState { IsoBackend* backend; IsoGfx* iso; @@ -28,30 +32,30 @@ static bool init(GfxAppState* state, int argc, const char** argv) { (void)argc; (void)argv; - if (!(state->iso = isogfx_new(&(IsoGfxDesc){ - .screen_width = SCREEN_WIDTH, .screen_height = SCREEN_HEIGHT}))) { + if (!((state->iso = + isogfx_new(&(IsoGfxDesc){.memory = MEMORY, + .memory_size = MEMORY_SIZE, + .screen_width = SCREEN_WIDTH, + .screen_height = SCREEN_HEIGHT})))) { return false; } IsoGfx* iso = state->iso; - isogfx_resize(iso, SCREEN_WIDTH, SCREEN_HEIGHT); - if (!isogfx_load_world(iso, "/home/jeanne/Nextcloud/assets/maps/demo-1.tm")) { return false; } - if (!isogfx_load_sprite_sheet( - iso, - "/home/jeanne/Nextcloud/assets/tilesets/scrabling/critters/stag/" - "stag.ss", - &state->stag_sheet)) { + if (!((state->stag_sheet = isogfx_load_sprite_sheet( + iso, + "/home/jeanne/Nextcloud/assets/tilesets/scrabling/critters/stag/" + "stag.ss")))) { return false; } state->stag = isogfx_make_sprite(iso, state->stag_sheet); isogfx_set_sprite_position(iso, state->stag, 5, 4); - if (!(state->backend = iso_backend_init(iso))) { + if (!((state->backend = iso_backend_init(iso)))) { return false; } diff --git a/include/isogfx/asset.h b/include/isogfx/asset.h index 298c469..9aeb55d 100644 --- a/include/isogfx/asset.h +++ b/include/isogfx/asset.h @@ -7,6 +7,8 @@ */ #pragma once +#include + #include #include @@ -18,16 +20,16 @@ // ----------------------------------------------------------------------------- typedef struct Ts_Tile { - uint16_t width; // Tile width in pixels. - uint16_t height; // Tile height in pixels. - Pixel pixels[1]; // Count: width * height. + uint16_t width; // Tile width in pixels. + uint16_t height; // Tile height in pixels. + uint32_t pixels; // Byte offset into the Ts_TileSet's 'pixels'. } Ts_Tile; typedef struct Ts_TileSet { uint16_t num_tiles; - uint16_t max_tile_width; // Maximum tile width in pixels. - uint16_t max_tile_height; // Maximum tile height in pixels. - Ts_Tile tiles[1]; // Count: num_tiles. + uint16_t _pad; + Ts_Tile tiles[1]; // Count: num_tiles. + Pixel pixels[]; // Count: sum_i(tile[i].width * tile[i].height). } Ts_TileSet; // ----------------------------------------------------------------------------- @@ -35,19 +37,17 @@ typedef struct Ts_TileSet { // ----------------------------------------------------------------------------- typedef struct Tm_Layer { - union { - char tileset_path[ISOGFX_MAX_PATH_LENGTH]; // Relative to the Tm_Map file. - }; Tile tiles[1]; // Count: world_width * world_height. } Tm_Layer; typedef struct Tm_Map { + char tileset_path[ISOGFX_MAX_PATH_LENGTH]; // Relative to the Tm_Map file. uint16_t world_width; // World width in number of tiles. uint16_t world_height; // World height in number of tiles. uint16_t base_tile_width; uint16_t base_tile_height; uint16_t num_layers; - Tm_Layer layers[1]; // Count: num_layers. + Tm_Layer layers[]; // Count: num_layers. } Tm_Map; // ----------------------------------------------------------------------------- @@ -88,28 +88,117 @@ typedef struct Ss_SpriteSheet { // Data accessors. // ----------------------------------------------------------------------------- -/// Return the next tile in the tile set. -static inline const Ts_Tile* ts_tileset_get_next_tile( - const Ts_TileSet* tileset, const Ts_Tile* tile) { +/// Return the tile set's pixels. +static inline const Pixel* ts_tileset_get_pixels(const Ts_TileSet* tileset) { + assert(tileset); + return (const Pixel*)((const uint8_t*)&tileset->tiles[0] + + (tileset->num_tiles * sizeof(Ts_Tile))); +} + +/// Return the ith tile in the tile set. +static inline const Ts_Tile* ts_tileset_get_tile( + const Ts_TileSet* tileset, const int tile) { + assert(tileset); + assert(tile >= 0); + assert(tile < tileset->num_tiles); + return &tileset->tiles[tile]; +} + +/// Return the ith tile in the tile set. +static inline Ts_Tile* ts_tileset_get_tile_mut( + Ts_TileSet* tileset, const int tile) { + return (Ts_Tile*)ts_tileset_get_tile(tileset, tile); +} + +/// Return the ith tile's pixels. +static inline const Pixel* ts_tileset_get_tile_pixels( + const Ts_TileSet* tileset, const int tile) { assert(tileset); + assert(tile >= 0); + assert(tile < tileset->num_tiles); + const Pixel* pixels = ts_tileset_get_pixels(tileset); + const Ts_Tile* pTile = ts_tileset_get_tile(tileset, tile); + return (const Pixel*)((const uint8_t*)pixels + pTile->pixels); +} + +/// Return the ith tile's pixels. +static inline Pixel* ts_tileset_get_tile_pixels_mut( + Ts_TileSet* tileset, const int tile) { + return (Pixel*)ts_tileset_get_tile_pixels(tileset, tile); +} + +/// Return the ith tile's pixels. +static inline const Pixel* ts_tile_get_pixels( + const Pixel* pixels, const Ts_Tile* tile) { + assert(pixels); assert(tile); - return (const Ts_Tile*)((const uint8_t*)tile + sizeof(Ts_Tile) + - ((tile->width * tile->height - 1) * sizeof(Pixel))); + return (const Pixel*)((const uint8_t*)pixels + tile->pixels); +} + +/// Return the tile's pixel at (x,y). +static const Pixel* ts_tile_xy( + const Pixel* tile_pixels, const Ts_Tile* tile, int x, int y) { + assert(tile_pixels); + assert(tile); + assert(x >= 0); + assert(y >= 0); + assert(x < tile->width); + assert(y < tile->height); + return &tile_pixels[y * tile->width + x]; +} + +/// Return the tile's pixel at (x,y). +static Pixel* ts_tile_xy_mut( + const Pixel* pixels, const Ts_Tile* tile, int x, int y) { + return (Pixel*)ts_tile_xy(pixels, tile, x, y); } -/// Return the next layer in the tile map. -static inline const Tm_Layer* tm_map_get_next_layer( - const Tm_Map* map, const Tm_Layer* layer) { +/// Return the ith layer in the tile map. +static inline const Tm_Layer* tm_map_get_layer( + const Tm_Map* map, const int layer) { assert(map); - assert(layer); - return (const Tm_Layer*)((const uint8_t*)layer + sizeof(Tm_Layer) + - ((map->world_width * map->world_height - 1) * + assert(layer >= 0); + assert(layer < map->num_layers); + return (const Tm_Layer*)((const uint8_t*)map->layers + + (layer * map->world_width * map->world_height * sizeof(Tile))); } +/// Return the ith layer in the tile map. +static inline Tm_Layer* tm_map_get_layer_mut(Tm_Map* map, const int layer) { + assert(map); + assert(layer >= 0); + assert(layer < map->num_layers); + return (Tm_Layer*)tm_map_get_layer(map, layer); +} + +/// Return the tile in the layer. +static inline const Tile* tm_layer_get_tile_const_ref( + const Tm_Map* map, const Tm_Layer* layer, const int x, const int y) { + assert(map); + assert(layer); + assert(x >= 0); + assert(y >= 0); + assert(x < map->world_width); + assert(y < map->world_height); + return &layer->tiles[y * map->world_width + x]; +} + +/// Return the tile in the layer. +static inline Tile tm_layer_get_tile( + const Tm_Map* map, const Tm_Layer* layer, const int x, const int y) { + return *tm_layer_get_tile_const_ref(map, layer, x, y); +} + +/// Return the tile in the layer. +static inline Tile* tm_layer_get_tile_mut( + Tm_Map* map, Tm_Layer* layer, const int x, const int y) { + return (Tile*)tm_layer_get_tile_const_ref(map, layer, x, y); +} + /// Return the ith row in the sprite sheet. -static inline const Ss_Row* get_sprite_sheet_row( - const Ss_SpriteSheet* sheet, int row) { +static inline const Ss_Row* ss_get_sprite_sheet_row( + const Ss_SpriteSheet* sheet, const int row) { assert(sheet); assert(row >= 0); assert(row < sheet->num_rows); @@ -120,8 +209,8 @@ static inline const Ss_Row* get_sprite_sheet_row( } /// Return the ith sprite in the row. -static inline const uint8_t* get_sprite_sheet_sprite( - const Ss_SpriteSheet* sheet, const Ss_Row* row, int col) { +static inline const uint8_t* ss_get_sprite_sheet_sprite( + const Ss_SpriteSheet* sheet, const Ss_Row* row, const int col) { assert(sheet); assert(row); assert(col >= 0); @@ -130,3 +219,13 @@ static inline const uint8_t* get_sprite_sheet_sprite( const uint8_t* sprite = &row->pixels[sprite_offset]; return sprite; } + +// ----------------------------------------------------------------------------- +// Validation. +// ----------------------------------------------------------------------------- + +/// Validate the tile set. +bool ts_validate_tileset(const Ts_TileSet* tileset); + +/// Validate the map. +bool tm_validate_map(const Tm_Map* map, const Ts_TileSet* tileset); diff --git a/include/isogfx/isogfx.h b/include/isogfx/isogfx.h index 3421a7b..93c6d4e 100644 --- a/include/isogfx/isogfx.h +++ b/include/isogfx/isogfx.h @@ -3,26 +3,18 @@ */ #pragma once -#include +#include + +#include #include typedef struct IsoGfx IsoGfx; /// Sprite sheet handle. -typedef uint16_t SpriteSheet; +typedef uintptr_t SpriteSheet; /// Sprite handle. -typedef uint16_t Sprite; - -/// Tile handle. -typedef uint16_t Tile; - -/// Colour channel. -typedef uint8_t Channel; - -typedef struct Pixel { - Channel r, g, b, a; -} Pixel; +typedef uintptr_t Sprite; typedef enum TileDescType { TileFromColour, @@ -32,32 +24,32 @@ typedef enum TileDescType { typedef struct TileDesc { TileDescType type; - int width; /// Tile width in pixels. - int height; /// Tile height in pixels. + int width; // Tile width in pixels. + int height; // Tile height in pixels. union { - Pixel colour; /// Constant colour tile. + Pixel colour; // Constant colour tile. struct { const char* path; } file; struct { - const uint8_t* data; /// sizeof(Pixel) * width * height + const uint8_t* data; // sizeof(Pixel) * width * height } mem; }; } TileDesc; typedef struct WorldDesc { - int tile_width; /// Base tile width in pixels. - int tile_height; /// Base tile height in pixels. - int world_width; /// World width in tiles. - int world_height; /// World height in tiles. - int max_num_tiles; /// 0 for an implementation-defined default. + int tile_width; // Base tile width in pixels. + int tile_height; // Base tile height in pixels. + int world_width; // World width in tiles. + int world_height; // World height in tiles. + int num_tiles; // Number of tiles to allocate memory for. } WorldDesc; typedef struct IsoGfxDesc { - int screen_width; /// Screen width in pixels. - int screen_height; /// Screen height in pixels. - int max_num_sprites; /// 0 for an implementation-defined default. - int sprite_sheet_pool_size_bytes; /// 0 for an implementation-defined default. + void* memory; // Block of memory for the engine to use. + size_t memory_size; // Size of memory block in bytes. + int screen_width; // Screen width in pixels. + int screen_height; // Screen height in pixels. } IsoGfxDesc; /// Create a new isometric graphics engine. @@ -66,8 +58,11 @@ IsoGfx* isogfx_new(const IsoGfxDesc*); /// Destroy the isometric graphics engine. void isogfx_del(IsoGfx**); +/// Clear all loaded worlds and sprites. +void isogfx_clear(IsoGfx*); + /// Create an empty world. -bool isogfx_make_world(IsoGfx*, const WorldDesc*); +void isogfx_make_world(IsoGfx*, const WorldDesc*); /// Load a world from a tile map (.TM) file. bool isogfx_load_world(IsoGfx*, const char* filepath); @@ -88,14 +83,11 @@ void isogfx_set_tile(IsoGfx*, int x, int y, Tile); void isogfx_set_tiles(IsoGfx*, int x0, int y0, int x1, int y1, Tile); /// Load a sprite sheet (.SS) file. -bool isogfx_load_sprite_sheet(IsoGfx*, const char* filepath, SpriteSheet*); +SpriteSheet isogfx_load_sprite_sheet(IsoGfx*, const char* filepath); /// Create an animated sprite. Sprite isogfx_make_sprite(IsoGfx*, SpriteSheet); -/// Destroy the sprite. -void isogfx_del_sprite(IsoGfx*, Sprite); - /// Destroy all the sprites. void isogfx_del_sprites(IsoGfx*); @@ -120,9 +112,6 @@ void isogfx_render(IsoGfx*); /// position (x,y) instead, use isogfx_set_tile(). void isogfx_draw_tile(IsoGfx*, int x, int y, Tile); -/// Resize the virtual screen's dimensions. -bool isogfx_resize(IsoGfx*, int screen_width, int screen_height); - /// Get the virtual screen's dimensions. void isogfx_get_screen_size(const IsoGfx*, int* width, int* height); diff --git a/include/isogfx/types.h b/include/isogfx/types.h new file mode 100644 index 0000000..ce275dc --- /dev/null +++ b/include/isogfx/types.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +/// Colour channel. +typedef uint8_t Channel; + +/// Pixel. +typedef struct Pixel { + Channel r, g, b, a; +} Pixel; + +/// Tile handle/index. +typedef uint16_t Tile; diff --git a/src/asset.c b/src/asset.c new file mode 100644 index 0000000..98ca083 --- /dev/null +++ b/src/asset.c @@ -0,0 +1,34 @@ +#include + +bool ts_validate_tileset(const Ts_TileSet* tileset) { + assert(tileset); + + for (uint16_t i = 0; i < tileset->num_tiles; ++i) { + const Ts_Tile* tile = ts_tileset_get_tile(tileset, i); + // Tile should be non-empty. + if (tile->width == 0) { + return false; + } + if (tile->height == 0) { + return false; + } + } + return true; +} + +bool tm_validate_map(const Tm_Map* map, const Ts_TileSet* tileset) { + assert(map); + assert(tileset); + + for (uint16_t t = 0; t < tileset->num_tiles; ++t) { + const Ts_Tile* tile = ts_tileset_get_tile(tileset, t); + // Tile dimensions should be a multiple of the base tile size. + if ((tile->width % map->base_tile_width) != 0) { + return false; + } + if ((tile->height % map->base_tile_height) != 0) { + return false; + } + } + return true; +} diff --git a/src/isogfx.c b/src/isogfx.c index 4dff67b..16760ac 100644 --- a/src/isogfx.c +++ b/src/isogfx.c @@ -3,27 +3,16 @@ #include #include -#include -#include +#include #include -#include - #include -#include #include #include -#include #include -/// Maximum number of tiles unless the user specifies a value. -#define DEFAULT_MAX_NUM_TILES 1024 - -/// Maximum number of sprites unless the user specifies a value. -#define DEFAULT_MAX_NUM_SPRITES 128 - -/// Size of sprite sheet pool in bytes unless the user specifies a value. -#define DEFAULT_SPRITE_SHEET_POOL_SIZE_BYTES (8 * 1024 * 1024) +/// Maximum path length. +#define MAX_PATH 256 /// Default animation speed. #define ANIMATION_FPS 10 @@ -43,41 +32,27 @@ typedef struct vec2 { // Renderer state. // ----------------------------------------------------------------------------- -typedef struct TileData { - uint16_t width; - uint16_t height; - uint16_t pixels_handle; // Handle to the tile's pixels in the pixel pool. -} TileData; - -typedef struct SpriteData { - SpriteSheet sheet; // Handle to the sprite's sheet. - ivec2 position; - int animation; // Current animation. - int frame; // Current frame of animation. -} SpriteData; +// TODO: Define a struct Screen with width, height and pixels. -DEF_MEMPOOL_DYN(TilePool, TileData) -DEF_MEM_DYN(PixelPool, Pixel) - -DEF_MEMPOOL_DYN(SpritePool, SpriteData) -DEF_MEM_DYN(SpriteSheetPool, Ss_SpriteSheet) +typedef struct SpriteInstance { + struct SpriteInstance* next; + const Ss_SpriteSheet* sheet; + ivec2 position; + int animation; // Current animation. + int frame; // Current frame of animation. +} SpriteInstance; typedef struct IsoGfx { int screen_width; int screen_height; - int tile_width; - int tile_height; - int world_width; - int world_height; - int max_num_sprites; - int sprite_sheet_pool_size_bytes; double last_animation_time; - Tile* world; + Tile next_tile; // For procedurally-generated tiles. Pixel* screen; - TilePool tiles; - PixelPool pixels; - SpritePool sprites; - SpriteSheetPool sheets; + Tm_Map* map; + Ts_TileSet* tileset; + SpriteInstance* head_sprite; // Head of sprites list. + memstack stack; + size_t watermark; } IsoGfx; // ----------------------------------------------------------------------------- @@ -114,42 +89,6 @@ static inline vec2 cart2iso(vec2 cart, int s, int t, int w) { .y = (-one_over_s * x + one_over_t * cart.y)}; } -static const Pixel* tile_xy_const_ref( - const IsoGfx* iso, const TileData* tile, int x, int y) { - assert(iso); - assert(tile); - assert(x >= 0); - assert(y >= 0); - assert(x < tile->width); - assert(y < tile->height); - return &mem_get_chunk(&iso->pixels, tile->pixels_handle)[y * tile->width + x]; -} - -// static Pixel tile_xy(const IsoGfx* iso, const TileData* tile, int x, int y) { -// return *tile_xy_const_ref(iso, tile, x, y); -// } - -static Pixel* tile_xy_mut(const IsoGfx* iso, TileData* tile, int x, int y) { - return (Pixel*)tile_xy_const_ref(iso, tile, x, y); -} - -static inline const Tile* world_xy_const_ref(const IsoGfx* iso, int x, int y) { - assert(iso); - assert(x >= 0); - assert(y >= 0); - assert(x < iso->world_width); - assert(y < iso->world_height); - return &iso->world[y * iso->world_width + x]; -} - -static inline Tile world_xy(const IsoGfx* iso, int x, int y) { - return *world_xy_const_ref(iso, x, y); -} - -static inline Tile* world_xy_mut(IsoGfx* iso, int x, int y) { - return (Tile*)world_xy_const_ref(iso, x, y); -} - static inline const Pixel* screen_xy_const_ref( const IsoGfx* iso, int x, int y) { assert(iso); @@ -168,15 +107,6 @@ static inline Pixel* screen_xy_mut(IsoGfx* iso, int x, int y) { return (Pixel*)screen_xy_const_ref(iso, x, y); } -static int calc_num_tile_blocks( - int base_tile_width, int base_tile_height, int tile_width, - int tile_height) { - const int base_tile_size = base_tile_width * base_tile_height; - const int tile_size = tile_width * tile_height; - const int num_blocks = tile_size / base_tile_size; - return num_blocks; -} - // ----------------------------------------------------------------------------- // Renderer, world and tile management. // ----------------------------------------------------------------------------- @@ -188,68 +118,54 @@ IsoGfx* isogfx_new(const IsoGfxDesc* desc) { assert((desc->screen_width & 1) == 0); assert((desc->screen_height & 1) == 0); - IsoGfx* iso = calloc(1, sizeof(IsoGfx)); - if (!iso) { - return 0; + IsoGfx tmp = {0}; + if (!memstack_make(&tmp.stack, desc->memory_size, desc->memory)) { + goto cleanup; } + IsoGfx* iso = + memstack_alloc_aligned(&tmp.stack, sizeof(IsoGfx), alignof(IsoGfx)); + *iso = tmp; - iso->screen_width = desc->screen_width; - iso->screen_height = desc->screen_height; - + iso->screen_width = desc->screen_width; + iso->screen_height = desc->screen_height; iso->last_animation_time = 0.0; - iso->max_num_sprites = desc->max_num_sprites == 0 ? DEFAULT_MAX_NUM_SPRITES - : desc->max_num_sprites; - iso->sprite_sheet_pool_size_bytes = desc->sprite_sheet_pool_size_bytes == 0 - ? DEFAULT_SPRITE_SHEET_POOL_SIZE_BYTES - : desc->sprite_sheet_pool_size_bytes; + const size_t screen_size_bytes = + desc->screen_width * desc->screen_height * sizeof(Pixel); + iso->screen = + memstack_alloc_aligned(&iso->stack, screen_size_bytes, alignof(Pixel)); - const int screen_size = desc->screen_width * desc->screen_height; - if (!(iso->screen = calloc(screen_size, sizeof(Pixel)))) { - goto cleanup; - } + iso->watermark = memstack_get_watermark(&iso->stack); return iso; cleanup: isogfx_del(&iso); - return 0; + return nullptr; } -/// Destroy the world, its tile set, and the underlying pools. -static void destroy_world(IsoGfx* iso) { +void isogfx_clear(IsoGfx* iso) { assert(iso); - if (iso->world) { - free(iso->world); - iso->world = 0; - } - mempool_del(&iso->tiles); - mem_del(&iso->pixels); -} - -/// Destroy all loaded sprites and the underlying pools. -static void destroy_sprites(IsoGfx* iso) { - assert(iso); - mempool_del(&iso->sprites); - mem_del(&iso->sheets); -} - -void isogfx_del(IsoGfx** pIso) { - assert(pIso); - IsoGfx* iso = *pIso; + iso->last_animation_time = 0.0; + iso->next_tile = 0; + iso->map = nullptr; + iso->tileset = nullptr; + iso->head_sprite = nullptr; + // The base of the stack contains the IsoGfx and the screen buffer. Make sure + // we don't clear them. + memstack_set_watermark(&iso->stack, iso->watermark); +} + +void isogfx_del(IsoGfx** ppIso) { + assert(ppIso); + IsoGfx* iso = *ppIso; if (iso) { - destroy_world(iso); - destroy_sprites(iso); - if (iso->screen) { - free(iso->screen); - iso->screen = 0; - } - free(iso); - *pIso = 0; + memstack_del(&iso->stack); + *ppIso = nullptr; } } -bool isogfx_make_world(IsoGfx* iso, const WorldDesc* desc) { +void isogfx_make_world(IsoGfx* iso, const WorldDesc* desc) { assert(iso); assert(desc); assert(desc->tile_width > 0); @@ -258,36 +174,41 @@ bool isogfx_make_world(IsoGfx* iso, const WorldDesc* desc) { // precision. assert((desc->tile_width & 1) == 0); assert((desc->tile_height & 1) == 0); - - // Handle recreation by destroying the previous world. - destroy_world(iso); - - iso->tile_width = desc->tile_width; - iso->tile_height = desc->tile_height; - iso->world_width = desc->world_width; - iso->world_height = desc->world_height; - - const int world_size = desc->world_width * desc->world_height; - const int tile_size = desc->tile_width * desc->tile_height; - const int tile_size_bytes = tile_size * (int)sizeof(Pixel); - const int tile_pool_size = - desc->max_num_tiles > 0 ? desc->max_num_tiles : DEFAULT_MAX_NUM_TILES; - - if (!(iso->world = calloc(world_size, sizeof(Tile)))) { - goto cleanup; - } - if (!mempool_make_dyn(&iso->tiles, world_size, sizeof(TileData))) { - goto cleanup; - } - if (!mem_make_dyn(&iso->pixels, tile_pool_size, tile_size_bytes)) { - goto cleanup; - } - - return true; - -cleanup: - destroy_world(iso); - return false; + // World must be non-empty. + assert(desc->world_width > 0); + assert(desc->world_height > 0); + // Must have >0 tiles. + assert(desc->num_tiles > 0); + + // Handle recreation by destroying the previous world and sprites. + isogfx_clear(iso); + + const int world_size = desc->world_width * desc->world_height; + const size_t map_size_bytes = sizeof(Tm_Map) + (world_size * sizeof(Tile)); + + // This implies that all tiles are of the base tile dimensions. + // We could enhance the API to allow for supertiles as well. Take in max tile + // width and height and allocate enough space using those values. + const size_t tile_size = desc->tile_width * desc->tile_height; + const size_t tile_size_bytes = tile_size * sizeof(Pixel); + const size_t tile_data_size_bytes = desc->num_tiles * tile_size_bytes; + const size_t tileset_size_bytes = sizeof(Ts_TileSet) + + (desc->num_tiles * sizeof(Ts_Tile)) + + tile_data_size_bytes; + + iso->map = memstack_alloc_aligned(&iso->stack, map_size_bytes, 4); + *iso->map = (Tm_Map){ + .world_width = desc->world_width, + .world_height = desc->world_height, + .base_tile_width = desc->tile_width, + .base_tile_height = desc->tile_height, + .num_layers = 1, + }; + + iso->tileset = memstack_alloc_aligned(&iso->stack, tileset_size_bytes, 4); + *iso->tileset = (Ts_TileSet){ + .num_tiles = desc->num_tiles, + }; } bool isogfx_load_world(IsoGfx* iso, const char* filepath) { @@ -296,135 +217,74 @@ bool isogfx_load_world(IsoGfx* iso, const char* filepath) { bool success = false; - // Handle recreation by destroying the previous world. - destroy_world(iso); + // Handle recreation by destroying the previous world and sprites. + isogfx_clear(iso); // Load the map. printf("Load tile map: %s\n", filepath); - Tm_Map* map = read_file(filepath); - if (!map) { + WITH_FILE(filepath, { + const size_t map_size = get_file_size_f(file); + iso->map = memstack_alloc_aligned(&iso->stack, map_size, 4); + success = read_file_f(file, iso->map); + }); + if (!success) { goto cleanup; } + Tm_Map* const map = iso->map; - // Allocate memory for the map and tile sets. - const int world_size = map->world_width * map->world_height; - const int base_tile_size = map->base_tile_width * map->base_tile_height; - const int base_tile_size_bytes = base_tile_size * (int)sizeof(Pixel); - // TODO: Need to get the total number of tiles from the map. - const int tile_pool_size = DEFAULT_MAX_NUM_TILES; - - if (!(iso->world = calloc(world_size, sizeof(Tile)))) { - goto cleanup; - } - if (!mempool_make_dyn(&iso->tiles, tile_pool_size, sizeof(TileData))) { + // Load the tile set. + // + // Tile set path is relative to the tile map file. Make it relative to the + // current working directory before loading. + const char* ts_path = map->tileset_path; + char ts_path_cwd[MAX_PATH] = {0}; + if (!path_make_relative(filepath, ts_path, ts_path_cwd, MAX_PATH)) { goto cleanup; } - if (!mem_make_dyn(&iso->pixels, tile_pool_size, base_tile_size_bytes)) { + printf("Load tile set: %s\n", ts_path_cwd); + WITH_FILE(ts_path_cwd, { + const size_t file_size = get_file_size_f(file); + iso->tileset = memstack_alloc_aligned(&iso->stack, file_size, 4); + success = read_file_f(file, iso->tileset); + }); + if (!success) { + // TODO: Log errors using the log library. goto cleanup; } + const Ts_TileSet* const tileset = iso->tileset; + printf("Loaded tile set (%u tiles): %s\n", tileset->num_tiles, ts_path_cwd); - // Load the tile sets. - const Tm_Layer* layer = &map->layers[0]; - // TODO: Handle num_layers layers. - for (int i = 0; i < 1; ++i) { - const char* ts_path = layer->tileset_path; - - // Tile set path is relative to the tile map file. Make it relative to the - // current working directory before loading. - char ts_path_cwd[PATH_MAX] = {0}; - if (!path_make_relative(filepath, ts_path, ts_path_cwd, PATH_MAX)) { - goto cleanup; - } - - Ts_TileSet* tileset = read_file(ts_path_cwd); - if (!tileset) { - // TODO: Log errors using the log library. - goto cleanup; - }; - - // Load tile data. - const Ts_Tile* tile = &tileset->tiles[0]; - for (uint16_t j = 0; j < tileset->num_tiles; ++j) { - // TODO: These checks should be library runtime errors. - // Tile dimensions should be a multiple of the base tile size. - assert((tile->width % map->base_tile_width) == 0); - assert((tile->height % map->base_tile_height) == 0); - - // Allocate N base tile size blocks for the tile. - const uint16_t tile_size = tile->width * tile->height; - const int num_blocks = tile_size / base_tile_size; - Pixel* pixels = mem_alloc(&iso->pixels, num_blocks); - assert(pixels); - memcpy(pixels, tile->pixels, tile_size * sizeof(Pixel)); - - // Allocate the tile data. - TileData* tile_data = mempool_alloc(&iso->tiles); - assert(tile_data); - tile_data->width = tile->width; - tile_data->height = tile->height; - tile_data->pixels_handle = - (uint16_t)mem_get_chunk_handle(&iso->pixels, pixels); - - tile = ts_tileset_get_next_tile(tileset, tile); - } - - printf("Loaded tile set (%u tiles): %s\n", tileset->num_tiles, ts_path_cwd); - - free(tileset); - layer = tm_map_get_next_layer(map, layer); - } - - // Load the map into the world. - layer = &map->layers[0]; - // TODO: Handle num_layers layers. - for (int i = 0; i < 1; ++i) { - memcpy(iso->world, layer->tiles, world_size * sizeof(Tile)); - - // TODO: We need to handle 'firsgid' in TMX files. - for (int j = 0; j < world_size; ++j) { - iso->world[j] -= 1; - } - - layer = tm_map_get_next_layer(map, layer); - } - - iso->world_width = map->world_width; - iso->world_height = map->world_height; - iso->tile_width = map->base_tile_width; - iso->tile_height = map->base_tile_height; + // TODO: These assertions on input data should be library runtime errors. + assert(ts_validate_tileset(tileset)); + assert(tm_validate_map(map, tileset)); success = true; cleanup: - if (map) { - free(map); - } if (!success) { - destroy_world(iso); + isogfx_clear(iso); } return success; } int isogfx_world_width(const IsoGfx* iso) { assert(iso); - return iso->world_width; + return iso->map->world_width; } int isogfx_world_height(const IsoGfx* iso) { assert(iso); - return iso->world_height; + return iso->map->world_height; } -/// Create a tile mask procedurally. static void make_tile_from_colour( - const IsoGfx* iso, Pixel colour, TileData* tile) { - assert(iso); + Pixel colour, const Ts_Tile* tile, Pixel* tile_pixels) { assert(tile); + assert(tile_pixels); const int width = tile->width; const int height = tile->height; const int r = width / height; - for (int y = 0; y < height / 2; ++y) { const int mask_start = width / 2 - r * y - 1; const int mask_end = width / 2 + r * y + 1; @@ -433,11 +293,11 @@ static void make_tile_from_colour( const Pixel val = mask ? colour : (Pixel){.r = 0, .g = 0, .b = 0, .a = 0}; // Top half. - *tile_xy_mut(iso, tile, x, y) = val; + *ts_tile_xy_mut(tile_pixels, tile, x, y) = val; // Bottom half reflects the top half. - const int y_reflected = height - y - 1; - *tile_xy_mut(iso, tile, x, y_reflected) = val; + const int y_reflected = height - y - 1; + *ts_tile_xy_mut(tile_pixels, tile, x, y_reflected) = val; } } } @@ -445,41 +305,58 @@ static void make_tile_from_colour( Tile isogfx_make_tile(IsoGfx* iso, const TileDesc* desc) { assert(iso); assert(desc); - // Client must create world before creating tiles. - assert(iso->tile_width > 0); - assert(iso->tile_height > 0); + // Client must create a world first. + assert(iso->map); + assert(iso->tileset); + // Currently, procedural tiles must match the base tile size. + assert(desc->width == iso->map->base_tile_width); + assert(desc->height == iso->map->base_tile_height); + // Cannot exceed max tiles. + assert(iso->next_tile < iso->tileset->num_tiles); - TileData* tile = mempool_alloc(&iso->tiles); - assert(tile); // TODO: Make this a hard assert. + const Tile tile = iso->next_tile++; - const int num_blocks = calc_num_tile_blocks( - iso->tile_width, iso->tile_height, desc->width, desc->height); + const size_t tile_size_bytes = desc->width * desc->height * sizeof(Pixel); - Pixel* pixels = mem_alloc(&iso->pixels, num_blocks); - assert(pixels); // TODO: Make this a hard assert. + switch (desc->type) { + case TileFromColour: { + assert(desc->width > 0); + assert(desc->height > 0); - tile->width = desc->width; - tile->height = desc->height; - tile->pixels_handle = mem_get_chunk_handle(&iso->pixels, pixels); + Ts_Tile* const ts_tile = ts_tileset_get_tile_mut(iso->tileset, tile); - switch (desc->type) { - case TileFromColour: - make_tile_from_colour(iso, desc->colour, tile); + *ts_tile = (Ts_Tile){ + .width = iso->map->base_tile_width, + .height = iso->map->base_tile_height, + .pixels = tile * tile_size_bytes, + }; + + Pixel* const tile_pixels = + ts_tileset_get_tile_pixels_mut(iso->tileset, tile); + make_tile_from_colour(desc->colour, ts_tile, tile_pixels); break; + } case TileFromFile: assert(false); // TODO break; - case TileFromMemory: + case TileFromMemory: { + assert(desc->width > 0); + assert(desc->height > 0); assert(false); // TODO break; } + } - return (Tile)mempool_get_block_index(&iso->tiles, tile); + return tile; } void isogfx_set_tile(IsoGfx* iso, int x, int y, Tile tile) { assert(iso); - *world_xy_mut(iso, x, y) = tile; + + Tm_Layer* const layer = tm_map_get_layer_mut(iso->map, 0); + Tile* map_tile = tm_layer_get_tile_mut(iso->map, layer, x, y); + + *map_tile = tile; } void isogfx_set_tiles(IsoGfx* iso, int x0, int y0, int x1, int y1, Tile tile) { @@ -491,88 +368,67 @@ void isogfx_set_tiles(IsoGfx* iso, int x0, int y0, int x1, int y1, Tile tile) { } } -bool isogfx_load_sprite_sheet( - IsoGfx* iso, const char* filepath, SpriteSheet* p_sheet) { +SpriteSheet isogfx_load_sprite_sheet(IsoGfx* iso, const char* filepath) { assert(iso); assert(filepath); - assert(p_sheet); - bool success = false; - - // Lazy initialization of sprite pools. - if (mempool_capacity(&iso->sprites) == 0) { - if (!mempool_make_dyn( - &iso->sprites, iso->max_num_sprites, sizeof(SpriteData))) { - return false; - } - } - if (mem_capacity(&iso->sheets) == 0) { - // Using a block size of 1 byte for sprite sheet data. - if (!mem_make_dyn(&iso->sheets, iso->sprite_sheet_pool_size_bytes, 1)) { - return false; - } - } + bool success = false; + SpriteSheet spriteSheet = 0; + const size_t watermark = memstack_get_watermark(&iso->stack); // Load sprite sheet file. printf("Load sprite sheet: %s\n", filepath); - FILE* file = fopen(filepath, "rb"); - if (file == NULL) { - goto cleanup; - } - const size_t sheet_size = get_file_size(file); - Ss_SpriteSheet* ss_sheet = mem_alloc(&iso->sheets, sheet_size); - if (!ss_sheet) { - goto cleanup; - } - if (fread(ss_sheet, sheet_size, 1, file) != 1) { + Ss_SpriteSheet* ss_sheet = nullptr; + WITH_FILE(filepath, { + const size_t file_size = get_file_size_f(file); + ss_sheet = + memstack_alloc_aligned(&iso->stack, file_size, alignof(Ss_SpriteSheet)); + success = read_file_f(file, ss_sheet); + }); + if (!success) { goto cleanup; } + assert(ss_sheet); - *p_sheet = mem_get_chunk_handle(&iso->sheets, ss_sheet); - success = true; + spriteSheet = (SpriteSheet)ss_sheet; cleanup: - // Pools remain initialized since client may attempt to load other sprites. - if (file != NULL) { - fclose(file); - } if (!success) { if (ss_sheet) { - mem_free(&iso->sheets, &ss_sheet); + memstack_set_watermark(&iso->stack, watermark); } } - return success; + return spriteSheet; } Sprite isogfx_make_sprite(IsoGfx* iso, SpriteSheet sheet) { assert(iso); + assert(sheet); - SpriteData* sprite = mempool_alloc(&iso->sprites); - assert(sprite); + // TODO: Remove memstack_alloc() and replace it with a same-name macro that + // calls memstack_alloc_aligned() with sizeof/alignof. No real point in + // having unaligned allocations. + SpriteInstance* sprite = memstack_alloc_aligned( + &iso->stack, sizeof(SpriteInstance), alignof(SpriteInstance)); - sprite->sheet = sheet; + sprite->sheet = (const Ss_SpriteSheet*)sheet; + sprite->next = iso->head_sprite; + iso->head_sprite = sprite; - return mempool_get_block_index(&iso->sprites, sprite); + return (Sprite)sprite; } -#define with_sprite(SPRITE, BODY) \ - { \ - SpriteData* data = mempool_get_block(&iso->sprites, sprite); \ - assert(data); \ - BODY; \ - } - -void isogfx_set_sprite_position(IsoGfx* iso, Sprite sprite, int x, int y) { +void isogfx_set_sprite_position(IsoGfx* iso, Sprite hSprite, int x, int y) { assert(iso); - with_sprite(sprite, { - data->position.x = x; - data->position.y = y; - }); + SpriteInstance* sprite = (SpriteInstance*)hSprite; + sprite->position.x = x; + sprite->position.y = y; } -void isogfx_set_sprite_animation(IsoGfx* iso, Sprite sprite, int animation) { +void isogfx_set_sprite_animation(IsoGfx* iso, Sprite hSprite, int animation) { assert(iso); - with_sprite(sprite, { data->animation = animation; }); + SpriteInstance* sprite = (SpriteInstance*)hSprite; + sprite->animation = animation; } void isogfx_update(IsoGfx* iso, double t) { @@ -586,14 +442,14 @@ void isogfx_update(IsoGfx* iso, double t) { } if ((t - iso->last_animation_time) >= ANIMATION_UPDATE_DELTA) { - // TODO: Consider linking animated sprites in a list so that we only walk - // over those here and not also the static sprites. - mempool_foreach(&iso->sprites, sprite, { - const Ss_SpriteSheet* sheet = mem_get_chunk(&iso->sheets, sprite->sheet); - assert(sheet); // TODO: Make this a hard assert inside the mem/pool. - const Ss_Row* row = get_sprite_sheet_row(sheet, sprite->animation); + // TODO: Consider linking animated sprites in a separate list so that we + // only walk over those here and not also the static sprites. + for (SpriteInstance* sprite = iso->head_sprite; sprite; + sprite = sprite->next) { + const Ss_SpriteSheet* sheet = sprite->sheet; + const Ss_Row* row = ss_get_sprite_sheet_row(sheet, sprite->animation); sprite->frame = (sprite->frame + 1) % row->num_cols; - }); + } iso->last_animation_time = t; } @@ -614,8 +470,10 @@ typedef struct CoordSystem { static CoordSystem make_iso_coord_system(const IsoGfx* iso) { assert(iso); const ivec2 o = {iso->screen_width / 2, 0}; - const ivec2 x = {.x = iso->tile_width / 2, .y = iso->tile_height / 2}; - const ivec2 y = {.x = -iso->tile_width / 2, .y = iso->tile_height / 2}; + const ivec2 x = { + .x = iso->map->base_tile_width / 2, .y = iso->map->base_tile_height / 2}; + const ivec2 y = { + .x = -iso->map->base_tile_width / 2, .y = iso->map->base_tile_height / 2}; return (CoordSystem){o, x, y}; } @@ -696,19 +554,20 @@ static void draw_rect( /// World (0, 0) -> (screen_width / 2, 0). static void draw_tile(IsoGfx* iso, ivec2 screen_origin, Tile tile) { assert(iso); + assert(iso->tileset); - const TileData* tile_data = mempool_get_block(&iso->tiles, tile); - assert(tile_data); - const Pixel* pixels = tile_xy_const_ref(iso, tile_data, 0, 0); + const Ts_Tile* pTile = ts_tileset_get_tile(iso->tileset, tile); + const Pixel* pixels = ts_tileset_get_tile_pixels(iso->tileset, tile); // Move from the top diamond-corner to the top-left corner of the tile image. // For regular tiles, tile height == base tile height, so the y offset is 0. // For super tiles, move as high up as the height of the tile. const ivec2 offset = { - -(iso->tile_width / 2), tile_data->height - iso->tile_height}; + -(iso->map->base_tile_width / 2), + pTile->height - iso->map->base_tile_height}; const ivec2 top_left = ivec2_add(screen_origin, offset); - draw_rect(iso, top_left, tile_data->width, tile_data->height, pixels, 0); + draw_rect(iso, top_left, pTile->width, pTile->height, pixels, 0); } static void draw_world(IsoGfx* iso) { @@ -721,14 +580,16 @@ static void draw_world(IsoGfx* iso) { const CoordSystem iso_space = make_iso_coord_system(iso); + const Tm_Layer* layer = tm_map_get_layer(iso->map, 0); + // TODO: Culling. // Ex: map the screen corners to tile space to cull. // Ex: walk in screen space and fetch the tile. // The tile-centric approach might be more cache-friendly since the // screen-centric approach would juggle multiple tiles throughout the scan. - for (int wy = 0; wy < iso->world_height; ++wy) { - for (int wx = 0; wx < iso->world_width; ++wx) { - const Tile tile = world_xy(iso, wx, wy); + for (int wy = 0; wy < iso->map->world_height; ++wy) { + for (int wx = 0; wx < iso->map->world_width; ++wx) { + const Tile tile = tm_layer_get_tile(iso->map, layer, wx, wy); const ivec2 screen_origin = GetTileScreenOrigin(iso_space, wx, wy); draw_tile(iso, screen_origin, tile); } @@ -736,7 +597,7 @@ static void draw_world(IsoGfx* iso) { } static void draw_sprite( - IsoGfx* iso, ivec2 origin, const SpriteData* sprite, + IsoGfx* iso, ivec2 origin, const SpriteInstance* sprite, const Ss_SpriteSheet* sheet) { assert(iso); assert(sprite); @@ -745,8 +606,8 @@ static void draw_sprite( assert(sprite->animation < sheet->num_rows); assert(sprite->frame >= 0); - const Ss_Row* row = get_sprite_sheet_row(sheet, sprite->animation); - const uint8_t* frame = get_sprite_sheet_sprite(sheet, row, sprite->frame); + const Ss_Row* row = ss_get_sprite_sheet_row(sheet, sprite->animation); + const uint8_t* frame = ss_get_sprite_sheet_sprite(sheet, row, sprite->frame); draw_rect( iso, origin, sheet->sprite_width, sheet->sprite_height, sheet->palette.colours, frame); @@ -757,14 +618,15 @@ static void draw_sprites(IsoGfx* iso) { const CoordSystem iso_space = make_iso_coord_system(iso); - mempool_foreach(&iso->sprites, sprite, { - const Ss_SpriteSheet* sheet = mem_get_chunk(&iso->sheets, sprite->sheet); + for (const SpriteInstance* sprite = iso->head_sprite; sprite; + sprite = sprite->next) { + const Ss_SpriteSheet* sheet = sprite->sheet; assert(sheet); const ivec2 screen_origin = GetTileScreenOrigin(iso_space, sprite->position.x, sprite->position.y); draw_sprite(iso, screen_origin, sprite, sheet); - }); + } } void isogfx_render(IsoGfx* iso) { @@ -777,35 +639,14 @@ void isogfx_draw_tile(IsoGfx* iso, int x, int y, Tile tile) { assert(iso); assert(x >= 0); assert(y >= 0); - assert(x < iso->world_width); - assert(y < iso->world_height); + assert(x < iso->map->world_width); + assert(y < iso->map->world_height); const CoordSystem iso_space = make_iso_coord_system(iso); const ivec2 screen_origin = GetTileScreenOrigin(iso_space, x, y); draw_tile(iso, screen_origin, tile); } -bool isogfx_resize(IsoGfx* iso, int screen_width, int screen_height) { - assert(iso); - assert(iso->screen); - - const int current_size = iso->screen_width * iso->screen_height; - const int new_size = screen_width * screen_height; - - if (new_size > current_size) { - Pixel* new_screen = calloc(new_size, sizeof(Pixel)); - if (new_screen) { - free(iso->screen); - iso->screen = new_screen; - } else { - return false; - } - } - iso->screen_width = screen_width; - iso->screen_height = screen_height; - return true; -} - void isogfx_get_screen_size(const IsoGfx* iso, int* width, int* height) { assert(iso); assert(width); @@ -826,11 +667,11 @@ void isogfx_pick_tile( assert(yiso); const vec2 xy_iso = cart2iso( - (vec2){.x = xcart, .y = ycart}, iso->tile_width, iso->tile_height, - iso->screen_width); + (vec2){.x = xcart, .y = ycart}, iso->map->base_tile_width, + iso->map->base_tile_height, iso->screen_width); - if ((0 <= xy_iso.x) && (xy_iso.x < iso->world_width) && (0 <= xy_iso.y) && - (xy_iso.y < iso->world_height)) { + if ((0 <= xy_iso.x) && (xy_iso.x < iso->map->world_width) && + (0 <= xy_iso.y) && (xy_iso.y < iso->map->world_height)) { *xiso = (int)xy_iso.x; *yiso = (int)xy_iso.y; } else { diff --git a/tools/mkasset.py b/tools/mkasset.py index fd2ead7..ae27ead 100644 --- a/tools/mkasset.py +++ b/tools/mkasset.py @@ -51,10 +51,15 @@ def convert_tsx(input_filepath, output_filepath): print(f"Max width: {max_tile_width}") print(f"Max height: {max_tile_height}") + pixels = [] # List of byte arrays + pixels_offset = 0 + with open(output_filepath, 'bw') as output: + # Write the header. output.write(ctypes.c_uint16(tile_count)) - output.write(ctypes.c_uint16(max_tile_width)) - output.write(ctypes.c_uint16(max_tile_height)) + # output.write(ctypes.c_uint16(max_tile_width)) + # output.write(ctypes.c_uint16(max_tile_height)) + output.write(ctypes.c_uint16(0)) # Pad. num_tile = 0 for tile in root: @@ -72,12 +77,23 @@ def convert_tsx(input_filepath, output_filepath): tile_height = int(image.attrib["height"]) tile_path = image.attrib["source"] - output.write(ctypes.c_uint16(tile_width)) - output.write(ctypes.c_uint16(tile_height)) + assert (tile_width > 0) + assert (tile_height > 0) + tile_pixels_offset = pixels_offset with Image.open(tile_path) as im: bytes = im.convert('RGBA').tobytes() - output.write(bytes) + pixels.append(bytes) + pixels_offset += len(bytes) + + # Write the tile. + output.write(ctypes.c_uint16(tile_width)) + output.write(ctypes.c_uint16(tile_height)) + output.write(ctypes.c_uint32(tile_pixels_offset)) + + # Write the pixel data. + for bytes in pixels: + output.write(bytes) def convert_tmx(input_filepath, output_filepath): @@ -96,23 +112,27 @@ def convert_tmx(input_filepath, output_filepath): print(f"Tile width: {base_tile_width}") print(f"Tile height: {base_tile_height}") - with open(output_filepath, 'bw') as output: - output.write(ctypes.c_uint16(map_width)) - output.write(ctypes.c_uint16(map_height)) - output.write(ctypes.c_uint16(base_tile_width)) - output.write(ctypes.c_uint16(base_tile_height)) - output.write(ctypes.c_uint16(num_layers)) - - tileset_path = None + tileset_path = None + with open(output_filepath, 'bw') as output: for child in root: if child.tag == "tileset": + assert (not tileset_path) # Only supporting one tile set per map right now. + tileset = child tileset_path = tileset.attrib["source"] print(f"Tile set: {tileset_path}") tileset_path = tileset_path.replace("tsx", "ts") + + # Write the header. + output.write(to_char_array(tileset_path, MAX_PATH_LENGTH)) + output.write(ctypes.c_uint16(map_width)) + output.write(ctypes.c_uint16(map_height)) + output.write(ctypes.c_uint16(base_tile_width)) + output.write(ctypes.c_uint16(base_tile_height)) + output.write(ctypes.c_uint16(num_layers)) elif child.tag == "layer": layer = child layer_id = int(layer.attrib["id"]) @@ -123,9 +143,6 @@ def convert_tmx(input_filepath, output_filepath): print(f"Width: {layer_width}") print(f"Height: {layer_height}") - assert (tileset_path) - output.write(to_char_array(tileset_path, MAX_PATH_LENGTH)) - # Assume the layer's dimensions matches the map's. assert (layer_width == map_width) assert (layer_height == map_height) @@ -139,7 +156,9 @@ def convert_tmx(input_filepath, output_filepath): for row in rows: tile_ids = [x.strip() for x in row.split(',') if x] for tile_id in tile_ids: - output.write(ctypes.c_uint16(int(tile_id))) + # TODO: We need to handle 'firsgid' in TMX files. + # For now, assume it's 1 and do -1 to make the tiles 0-based. + output.write(ctypes.c_uint16(int(tile_id) - 1)) def get_num_cols(image, sprite_width): -- cgit v1.2.3