From 8bfa0c652cfb36ca0dfe10a1d35c7b6199e064ba Mon Sep 17 00:00:00 2001 From: 3gg <3gg@shellblade.net> Date: Thu, 29 Aug 2024 20:12:19 -0700 Subject: More consistent changing of coordinates. --- gfx-iso/src/isogfx.c | 103 +++++++++++++++++++++++++++++---------------------- 1 file changed, 58 insertions(+), 45 deletions(-) (limited to 'gfx-iso/src') diff --git a/gfx-iso/src/isogfx.c b/gfx-iso/src/isogfx.c index 5c1c2d1..f7d0fa9 100644 --- a/gfx-iso/src/isogfx.c +++ b/gfx-iso/src/isogfx.c @@ -237,9 +237,9 @@ static const Pixel* tile_xy_const_ref( 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(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); @@ -444,7 +444,7 @@ bool isogfx_load_world(IsoGfx* iso, const char* filepath) { // 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 (!make_relative_path(filepath, ts_path, ts_path_cwd, PATH_MAX)) { + if (!path_make_relative(filepath, ts_path, ts_path_cwd, PATH_MAX)) { goto cleanup; } @@ -724,14 +724,24 @@ typedef struct CoordSystem { /// expressed in the Cartesian system. static CoordSystem make_iso_coord_system(const IsoGfx* iso) { assert(iso); - // const ivec2 o = {(iso->screen_width / 2) - (iso->tile_width / 2), 0}; - const ivec2 o = { - (iso->screen_width / 2) - (iso->tile_width / 2), iso->tile_height}; + 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}; return (CoordSystem){o, x, y}; } +/// Get the screen position of the top diamond-corner of the tile at world +/// (x,y). +static ivec2 GetTileScreenOrigin( + const CoordSystem iso_space, int world_x, int world_y) { + const ivec2 vx_offset = ivec2_scale(iso_space.x, world_x); + const ivec2 vy_offset = ivec2_scale(iso_space.y, world_y); + const ivec2 screen_origin = + ivec2_add(iso_space.o, ivec2_add(vx_offset, vy_offset)); + + return screen_origin; +} + static Pixel alpha_blend(Pixel src, Pixel dst) { if ((src.a == 255) || (dst.a == 0)) { return src; @@ -761,7 +771,7 @@ static Pixel alpha_blend(Pixel src, Pixel dst) { /// 'pixels' is the palette and 'indices' the pixel indices. Otherwise, the /// image is assumed to be in plain RGBA format. static void draw_rect( - IsoGfx* iso, ivec2 origin, int rect_width, int rect_height, + IsoGfx* iso, ivec2 top_left, int rect_width, int rect_height, const Pixel* pixels, const uint8_t* indices) { assert(iso); @@ -769,20 +779,24 @@ static void draw_rect( (indices ? pixels[indices[py * rect_width + px]] \ : pixels[py * rect_width + px]) - // Rect can exceed screen bounds, so we must clip it. + // Rect origin can be outside screen bounds, so we must offset accordingly to + // draw only the visible portion. #define max(a, b) (a > b ? a : b) - const int py_offset = max(0, rect_height - origin.y); - origin.y = max(0, origin.y - rect_height); + const int px_offset = max(0, -top_left.x); + const int py_offset = max(0, -top_left.y); + // Adjust top-left to be inside bounds. + top_left.x = max(0, top_left.x); + top_left.y = max(0, top_left.y); - // Clip along Y and X as we draw. + // Rect can exceed screen bounds, so clip along Y and X as we draw. for (int py = py_offset; - (py < rect_height) && (origin.y + py < iso->screen_height); ++py) { - const int sy = origin.y + py - py_offset; - for (int px = 0; (px < rect_width) && (origin.x + px < iso->screen_width); - ++px) { + (py < rect_height) && (top_left.y + py < iso->screen_height); ++py) { + const int sy = top_left.y + py - py_offset; + for (int px = px_offset; + (px < rect_width) && (top_left.x + px < iso->screen_width); ++px) { const Pixel colour = rect_pixel(px, py); if (colour.a > 0) { - const int sx = origin.x + px; + const int sx = top_left.x + px; const Pixel dst = screen_xy(iso, sx, sy); const Pixel final = alpha_blend(colour, dst); *screen_xy_mut(iso, sx, sy) = final; @@ -791,18 +805,29 @@ static void draw_rect( } } -static void draw_tile(IsoGfx* iso, ivec2 origin, Tile tile) { +/// Draw a tile. +/// +/// 'screen_origin' is the screen coordinates of the top diamond-corner of the +/// tile (the base tile for super tiles). +/// World (0, 0) -> (screen_width / 2, 0). +static void draw_tile(IsoGfx* iso, ivec2 screen_origin, Tile tile) { assert(iso); 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); - draw_rect(iso, origin, tile_data->width, tile_data->height, pixels, 0); + // 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}; + const ivec2 top_left = ivec2_add(screen_origin, offset); + + draw_rect(iso, top_left, tile_data->width, tile_data->height, pixels, 0); } -static void draw(IsoGfx* iso) { +static void draw_world(IsoGfx* iso) { assert(iso); const int W = iso->screen_width; @@ -817,14 +842,11 @@ static void draw(IsoGfx* iso) { // 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 ty = 0; ty < iso->world_height; ++ty) { - for (int tx = 0; tx < iso->world_width; ++tx) { - const Tile tile = world_xy(iso, tx, ty); - const ivec2 so = ivec2_add( - iso_space.o, - ivec2_add( - ivec2_scale(iso_space.x, tx), ivec2_scale(iso_space.y, ty))); - draw_tile(iso, so, tile); + 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); + const ivec2 screen_origin = GetTileScreenOrigin(iso_space, wx, wy); + draw_tile(iso, screen_origin, tile); } } } @@ -855,18 +877,15 @@ static void draw_sprites(IsoGfx* iso) { const SpriteSheetData* sheet = mem_get_chunk(&iso->sheets, sprite->sheet); assert(sheet); - const ivec2 so = ivec2_add( - iso_space.o, ivec2_add( - ivec2_scale(iso_space.x, sprite->position.x), - ivec2_scale(iso_space.y, sprite->position.y))); - - draw_sprite(iso, so, sprite, 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) { assert(iso); - draw(iso); + draw_world(iso); draw_sprites(iso); } @@ -877,15 +896,9 @@ void isogfx_draw_tile(IsoGfx* iso, int x, int y, Tile tile) { assert(x < iso->world_width); assert(y < iso->world_height); - // const ivec2 o = {(iso->screen_width / 2) - (iso->tile_width / 2), 0}; - const ivec2 o = { - (iso->screen_width / 2) - (iso->tile_width / 2), iso->tile_height}; - const ivec2 vx = {.x = iso->tile_width / 2, .y = iso->tile_height / 2}; - const ivec2 vy = {.x = -iso->tile_width / 2, .y = iso->tile_height / 2}; - const ivec2 so = - ivec2_add(o, ivec2_add(ivec2_scale(vx, x), ivec2_scale(vy, y))); - - draw_tile(iso, so, tile); + 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) { -- cgit v1.2.3