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')

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