From eca9315556ab4e9c813180de5f33c1e0b8bbb9c3 Mon Sep 17 00:00:00 2001 From: 3gg <3gg@shellblade.net> Date: Tue, 2 Sep 2025 19:53:28 -0700 Subject: Offset sprites properly --- src/gfx2d.c | 94 +++++++++++++++++++++++++++++++++---------------------------- 1 file changed, 51 insertions(+), 43 deletions(-) (limited to 'src') diff --git a/src/gfx2d.c b/src/gfx2d.c index da265b0..9767308 100644 --- a/src/gfx2d.c +++ b/src/gfx2d.c @@ -79,17 +79,49 @@ static inline ivec2 ivec2_scale(ivec2 a, int s) { static inline ivec2 ivec2_neg(ivec2 a) { return (ivec2){.x = -a.x, .y = -a.y}; } -static inline ivec2 iso2cart(ivec2 iso, int s, int t, int w) { - return (ivec2){.x = (iso.x - iso.y) * (s / 2) + (w / 2), - .y = (iso.x + iso.y) * (t / 2)}; -} - static inline vec2 vec2_add(vec2 a, vec2 b) { return (vec2){.x = a.x + b.x, .y = a.y + b.y}; } static inline vec2 ivec2_to_vec2(ivec2 a) { return (vec2){a.x, a.y}; } +// Not actually used because we pre-compute the two axis vectors instead. +// See make_iso_coord_system() and the other definition of iso2cart() below. +// static inline ivec2 iso2cart(ivec2 iso, int s, int t, int w) { +// return (ivec2){.x = (iso.x - iso.y) * (s / 2) + (w / 2), +// .y = (iso.x + iso.y) * (t / 2)}; +// } + +/// Create the basis for the isometric coordinate system with origin and vectors +/// expressed in the Cartesian system. +static CoordSystem make_iso_coord_system( + const Tm_Map* const map, const Screen* const screen) { + assert(map); + assert(screen); + const ivec2 o = {screen->width / 2, 0}; + const ivec2 x = { + .x = map->base_tile_width / 2, .y = map->base_tile_height / 2}; + const ivec2 y = { + .x = -map->base_tile_width / 2, .y = map->base_tile_height / 2}; + return (CoordSystem){o, x, y}; +} + +/// Map isometric coordinates to Cartesian coordinates. +/// +/// For a tile, this gets the screen position of the top diamond-corner of the +/// tile. +/// +/// Takes the camera displacement into account. +static ivec2 iso2cart( + const CoordSystem iso_space, ivec2 camera, int iso_x, int iso_y) { + const ivec2 vx_offset = ivec2_scale(iso_space.x, iso_x); + const ivec2 vy_offset = ivec2_scale(iso_space.y, iso_y); + const ivec2 screen_origin = + ivec2_add(iso_space.o, ivec2_add(vx_offset, vy_offset)); + const ivec2 origin_view_space = ivec2_add(screen_origin, ivec2_neg(camera)); + return origin_view_space; +} + // Method 1. // static inline vec2 cart2iso(vec2 cart, int s, int t, int w) { // const double x = cart.x - (double)(w / 2); @@ -125,20 +157,6 @@ static inline Pixel* screen_xy_mut(Screen* screen, int x, int y) { return (Pixel*)screen_xy_const_ref(screen, x, y); } -/// Create the basis for the isometric coordinate system with origin and vectors -/// expressed in the Cartesian system. -static CoordSystem make_iso_coord_system( - const Tm_Map* const map, const Screen* const screen) { - assert(map); - assert(screen); - const ivec2 o = {screen->width / 2, 0}; - const ivec2 x = { - .x = map->base_tile_width / 2, .y = map->base_tile_height / 2}; - const ivec2 y = { - .x = -map->base_tile_width / 2, .y = map->base_tile_height / 2}; - return (CoordSystem){o, x, y}; -} - // ----------------------------------------------------------------------------- // Renderer, world and tile management. // ----------------------------------------------------------------------------- @@ -498,18 +516,6 @@ void isogfx_update(IsoGfx* iso, double t) { // Rendering and picking. // ----------------------------------------------------------------------------- -/// Get the screen position of the top diamond-corner of the tile at world -/// (x,y). -static ivec2 GetTileScreenOrigin( - const CoordSystem iso_space, ivec2 camera, 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)); - const ivec2 origin_view_space = ivec2_add(screen_origin, ivec2_neg(camera)); - return origin_view_space; -} - static Pixel alpha_blend(Pixel src, Pixel dst) { if ((src.a == 255) || (dst.a == 0)) { return src; @@ -609,17 +615,15 @@ static void draw_map(IsoGfx* iso) { // screen-centric approach would juggle multiple tiles throughout the scan. 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->iso_space, iso->camera, wx, wy); + const Tile tile = tm_layer_get_tile(iso->map, layer, wx, wy); + const ivec2 screen_origin = iso2cart(iso->iso_space, iso->camera, wx, wy); draw_tile(iso, screen_origin, tile); } } } static void draw_sprite( - IsoGfx* iso, ivec2 origin, const SpriteInstance* sprite, - const Ss_SpriteSheet* sheet) { + IsoGfx* iso, const SpriteInstance* sprite, const Ss_SpriteSheet* sheet) { assert(iso); assert(sprite); assert(sheet); @@ -627,10 +631,18 @@ static void draw_sprite( assert(sprite->animation < sheet->num_rows); assert(sprite->frame >= 0); + // Apply an offset similarly to how we offset tiles. The sprite is offset by + // -base_tile_width/2 along the x-axis to align the sprite with the leftmost + // edge of the tile it is on. + const ivec2 screen_origin = iso2cart( + iso->iso_space, iso->camera, sprite->position.x, sprite->position.y); + const ivec2 offset = {-(iso->map->base_tile_width / 2), 0}; + const ivec2 top_left = ivec2_add(screen_origin, offset); + 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->screen, origin, sheet->sprite_width, sheet->sprite_height, + &iso->screen, top_left, sheet->sprite_width, sheet->sprite_height, sheet->palette.colours, frame); } @@ -641,10 +653,7 @@ static void draw_sprites(IsoGfx* iso) { sprite = sprite->next) { const Ss_SpriteSheet* sheet = sprite->sheet; assert(sheet); - - const ivec2 screen_origin = GetTileScreenOrigin( - iso->iso_space, iso->camera, sprite->position.x, sprite->position.y); - draw_sprite(iso, screen_origin, sprite, sheet); + draw_sprite(iso, sprite, sheet); } } @@ -666,8 +675,7 @@ void isogfx_draw_tile(IsoGfx* iso, int x, int y, Tile tile) { assert(x < iso->map->world_width); assert(y < iso->map->world_height); - const ivec2 screen_origin = - GetTileScreenOrigin(iso->iso_space, iso->camera, x, y); + const ivec2 screen_origin = iso2cart(iso->iso_space, iso->camera, x, y); draw_tile(iso, screen_origin, tile); } -- cgit v1.2.3