summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2025-09-02 19:53:28 -0700
committer3gg <3gg@shellblade.net>2025-09-02 19:53:28 -0700
commiteca9315556ab4e9c813180de5f33c1e0b8bbb9c3 (patch)
treeef5a1ad9102571ca08a1b4c6424b016d3c54fb00
parentbab4a8169135dfecb2e434cca6825dcb96e1b9ec (diff)
Offset sprites properlyHEADmain
-rw-r--r--demos/isomap/isomap.c2
-rw-r--r--src/gfx2d.c94
2 files changed, 52 insertions, 44 deletions
diff --git a/demos/isomap/isomap.c b/demos/isomap/isomap.c
index b9b6568..bca27f6 100644
--- a/demos/isomap/isomap.c
+++ b/demos/isomap/isomap.c
@@ -59,7 +59,7 @@ static bool init(GfxApp* app, GfxAppState* state, int argc, const char** argv) {
59 } 59 }
60 60
61 state->stag = isogfx_make_sprite(iso, state->stag_sheet); 61 state->stag = isogfx_make_sprite(iso, state->stag_sheet);
62 isogfx_set_sprite_position(iso, state->stag, 5, 4); 62 isogfx_set_sprite_position(iso, state->stag, 0, 0);
63 63
64 if (!((state->backend = iso_backend_init(iso)))) { 64 if (!((state->backend = iso_backend_init(iso)))) {
65 return false; 65 return false;
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) {
79 79
80static inline ivec2 ivec2_neg(ivec2 a) { return (ivec2){.x = -a.x, .y = -a.y}; } 80static inline ivec2 ivec2_neg(ivec2 a) { return (ivec2){.x = -a.x, .y = -a.y}; }
81 81
82static inline ivec2 iso2cart(ivec2 iso, int s, int t, int w) {
83 return (ivec2){.x = (iso.x - iso.y) * (s / 2) + (w / 2),
84 .y = (iso.x + iso.y) * (t / 2)};
85}
86
87static inline vec2 vec2_add(vec2 a, vec2 b) { 82static inline vec2 vec2_add(vec2 a, vec2 b) {
88 return (vec2){.x = a.x + b.x, .y = a.y + b.y}; 83 return (vec2){.x = a.x + b.x, .y = a.y + b.y};
89} 84}
90 85
91static inline vec2 ivec2_to_vec2(ivec2 a) { return (vec2){a.x, a.y}; } 86static inline vec2 ivec2_to_vec2(ivec2 a) { return (vec2){a.x, a.y}; }
92 87
88// Not actually used because we pre-compute the two axis vectors instead.
89// See make_iso_coord_system() and the other definition of iso2cart() below.
90// static inline ivec2 iso2cart(ivec2 iso, int s, int t, int w) {
91// return (ivec2){.x = (iso.x - iso.y) * (s / 2) + (w / 2),
92// .y = (iso.x + iso.y) * (t / 2)};
93// }
94
95/// Create the basis for the isometric coordinate system with origin and vectors
96/// expressed in the Cartesian system.
97static CoordSystem make_iso_coord_system(
98 const Tm_Map* const map, const Screen* const screen) {
99 assert(map);
100 assert(screen);
101 const ivec2 o = {screen->width / 2, 0};
102 const ivec2 x = {
103 .x = map->base_tile_width / 2, .y = map->base_tile_height / 2};
104 const ivec2 y = {
105 .x = -map->base_tile_width / 2, .y = map->base_tile_height / 2};
106 return (CoordSystem){o, x, y};
107}
108
109/// Map isometric coordinates to Cartesian coordinates.
110///
111/// For a tile, this gets the screen position of the top diamond-corner of the
112/// tile.
113///
114/// Takes the camera displacement into account.
115static ivec2 iso2cart(
116 const CoordSystem iso_space, ivec2 camera, int iso_x, int iso_y) {
117 const ivec2 vx_offset = ivec2_scale(iso_space.x, iso_x);
118 const ivec2 vy_offset = ivec2_scale(iso_space.y, iso_y);
119 const ivec2 screen_origin =
120 ivec2_add(iso_space.o, ivec2_add(vx_offset, vy_offset));
121 const ivec2 origin_view_space = ivec2_add(screen_origin, ivec2_neg(camera));
122 return origin_view_space;
123}
124
93// Method 1. 125// Method 1.
94// static inline vec2 cart2iso(vec2 cart, int s, int t, int w) { 126// static inline vec2 cart2iso(vec2 cart, int s, int t, int w) {
95// const double x = cart.x - (double)(w / 2); 127// const double x = cart.x - (double)(w / 2);
@@ -125,20 +157,6 @@ static inline Pixel* screen_xy_mut(Screen* screen, int x, int y) {
125 return (Pixel*)screen_xy_const_ref(screen, x, y); 157 return (Pixel*)screen_xy_const_ref(screen, x, y);
126} 158}
127 159
128/// Create the basis for the isometric coordinate system with origin and vectors
129/// expressed in the Cartesian system.
130static CoordSystem make_iso_coord_system(
131 const Tm_Map* const map, const Screen* const screen) {
132 assert(map);
133 assert(screen);
134 const ivec2 o = {screen->width / 2, 0};
135 const ivec2 x = {
136 .x = map->base_tile_width / 2, .y = map->base_tile_height / 2};
137 const ivec2 y = {
138 .x = -map->base_tile_width / 2, .y = map->base_tile_height / 2};
139 return (CoordSystem){o, x, y};
140}
141
142// ----------------------------------------------------------------------------- 160// -----------------------------------------------------------------------------
143// Renderer, world and tile management. 161// Renderer, world and tile management.
144// ----------------------------------------------------------------------------- 162// -----------------------------------------------------------------------------
@@ -498,18 +516,6 @@ void isogfx_update(IsoGfx* iso, double t) {
498// Rendering and picking. 516// Rendering and picking.
499// ----------------------------------------------------------------------------- 517// -----------------------------------------------------------------------------
500 518
501/// Get the screen position of the top diamond-corner of the tile at world
502/// (x,y).
503static ivec2 GetTileScreenOrigin(
504 const CoordSystem iso_space, ivec2 camera, int world_x, int world_y) {
505 const ivec2 vx_offset = ivec2_scale(iso_space.x, world_x);
506 const ivec2 vy_offset = ivec2_scale(iso_space.y, world_y);
507 const ivec2 screen_origin =
508 ivec2_add(iso_space.o, ivec2_add(vx_offset, vy_offset));
509 const ivec2 origin_view_space = ivec2_add(screen_origin, ivec2_neg(camera));
510 return origin_view_space;
511}
512
513static Pixel alpha_blend(Pixel src, Pixel dst) { 519static Pixel alpha_blend(Pixel src, Pixel dst) {
514 if ((src.a == 255) || (dst.a == 0)) { 520 if ((src.a == 255) || (dst.a == 0)) {
515 return src; 521 return src;
@@ -609,17 +615,15 @@ static void draw_map(IsoGfx* iso) {
609 // screen-centric approach would juggle multiple tiles throughout the scan. 615 // screen-centric approach would juggle multiple tiles throughout the scan.
610 for (int wy = 0; wy < iso->map->world_height; ++wy) { 616 for (int wy = 0; wy < iso->map->world_height; ++wy) {
611 for (int wx = 0; wx < iso->map->world_width; ++wx) { 617 for (int wx = 0; wx < iso->map->world_width; ++wx) {
612 const Tile tile = tm_layer_get_tile(iso->map, layer, wx, wy); 618 const Tile tile = tm_layer_get_tile(iso->map, layer, wx, wy);
613 const ivec2 screen_origin = 619 const ivec2 screen_origin = iso2cart(iso->iso_space, iso->camera, wx, wy);
614 GetTileScreenOrigin(iso->iso_space, iso->camera, wx, wy);
615 draw_tile(iso, screen_origin, tile); 620 draw_tile(iso, screen_origin, tile);
616 } 621 }
617 } 622 }
618} 623}
619 624
620static void draw_sprite( 625static void draw_sprite(
621 IsoGfx* iso, ivec2 origin, const SpriteInstance* sprite, 626 IsoGfx* iso, const SpriteInstance* sprite, const Ss_SpriteSheet* sheet) {
622 const Ss_SpriteSheet* sheet) {
623 assert(iso); 627 assert(iso);
624 assert(sprite); 628 assert(sprite);
625 assert(sheet); 629 assert(sheet);
@@ -627,10 +631,18 @@ static void draw_sprite(
627 assert(sprite->animation < sheet->num_rows); 631 assert(sprite->animation < sheet->num_rows);
628 assert(sprite->frame >= 0); 632 assert(sprite->frame >= 0);
629 633
634 // Apply an offset similarly to how we offset tiles. The sprite is offset by
635 // -base_tile_width/2 along the x-axis to align the sprite with the leftmost
636 // edge of the tile it is on.
637 const ivec2 screen_origin = iso2cart(
638 iso->iso_space, iso->camera, sprite->position.x, sprite->position.y);
639 const ivec2 offset = {-(iso->map->base_tile_width / 2), 0};
640 const ivec2 top_left = ivec2_add(screen_origin, offset);
641
630 const Ss_Row* row = ss_get_sprite_sheet_row(sheet, sprite->animation); 642 const Ss_Row* row = ss_get_sprite_sheet_row(sheet, sprite->animation);
631 const uint8_t* frame = ss_get_sprite_sheet_sprite(sheet, row, sprite->frame); 643 const uint8_t* frame = ss_get_sprite_sheet_sprite(sheet, row, sprite->frame);
632 draw_rect( 644 draw_rect(
633 &iso->screen, origin, sheet->sprite_width, sheet->sprite_height, 645 &iso->screen, top_left, sheet->sprite_width, sheet->sprite_height,
634 sheet->palette.colours, frame); 646 sheet->palette.colours, frame);
635} 647}
636 648
@@ -641,10 +653,7 @@ static void draw_sprites(IsoGfx* iso) {
641 sprite = sprite->next) { 653 sprite = sprite->next) {
642 const Ss_SpriteSheet* sheet = sprite->sheet; 654 const Ss_SpriteSheet* sheet = sprite->sheet;
643 assert(sheet); 655 assert(sheet);
644 656 draw_sprite(iso, sprite, sheet);
645 const ivec2 screen_origin = GetTileScreenOrigin(
646 iso->iso_space, iso->camera, sprite->position.x, sprite->position.y);
647 draw_sprite(iso, screen_origin, sprite, sheet);
648 } 657 }
649} 658}
650 659
@@ -666,8 +675,7 @@ void isogfx_draw_tile(IsoGfx* iso, int x, int y, Tile tile) {
666 assert(x < iso->map->world_width); 675 assert(x < iso->map->world_width);
667 assert(y < iso->map->world_height); 676 assert(y < iso->map->world_height);
668 677
669 const ivec2 screen_origin = 678 const ivec2 screen_origin = iso2cart(iso->iso_space, iso->camera, x, y);
670 GetTileScreenOrigin(iso->iso_space, iso->camera, x, y);
671 draw_tile(iso, screen_origin, tile); 679 draw_tile(iso, screen_origin, tile);
672} 680}
673 681