diff options
author | 3gg <3gg@shellblade.net> | 2024-08-29 20:12:19 -0700 |
---|---|---|
committer | 3gg <3gg@shellblade.net> | 2024-08-29 20:12:19 -0700 |
commit | 8bfa0c652cfb36ca0dfe10a1d35c7b6199e064ba (patch) | |
tree | c2e22565590aabb123eb43d87e69aa9758a0c4dc | |
parent | 7a59d85dfead20dc34081badad6c8940e7190001 (diff) |
More consistent changing of coordinates.
-rw-r--r-- | gfx-iso/src/isogfx.c | 103 |
1 files changed, 58 insertions, 45 deletions
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( | |||
237 | return &mem_get_chunk(&iso->pixels, tile->pixels_handle)[y * tile->width + x]; | 237 | return &mem_get_chunk(&iso->pixels, tile->pixels_handle)[y * tile->width + x]; |
238 | } | 238 | } |
239 | 239 | ||
240 | static Pixel tile_xy(const IsoGfx* iso, const TileData* tile, int x, int y) { | 240 | // static Pixel tile_xy(const IsoGfx* iso, const TileData* tile, int x, int y) { |
241 | return *tile_xy_const_ref(iso, tile, x, y); | 241 | // return *tile_xy_const_ref(iso, tile, x, y); |
242 | } | 242 | // } |
243 | 243 | ||
244 | static Pixel* tile_xy_mut(const IsoGfx* iso, TileData* tile, int x, int y) { | 244 | static Pixel* tile_xy_mut(const IsoGfx* iso, TileData* tile, int x, int y) { |
245 | return (Pixel*)tile_xy_const_ref(iso, tile, x, y); | 245 | return (Pixel*)tile_xy_const_ref(iso, tile, x, y); |
@@ -444,7 +444,7 @@ bool isogfx_load_world(IsoGfx* iso, const char* filepath) { | |||
444 | // Tile set path is relative to the tile map file. Make it relative to the | 444 | // Tile set path is relative to the tile map file. Make it relative to the |
445 | // current working directory before loading. | 445 | // current working directory before loading. |
446 | char ts_path_cwd[PATH_MAX] = {0}; | 446 | char ts_path_cwd[PATH_MAX] = {0}; |
447 | if (!make_relative_path(filepath, ts_path, ts_path_cwd, PATH_MAX)) { | 447 | if (!path_make_relative(filepath, ts_path, ts_path_cwd, PATH_MAX)) { |
448 | goto cleanup; | 448 | goto cleanup; |
449 | } | 449 | } |
450 | 450 | ||
@@ -724,14 +724,24 @@ typedef struct CoordSystem { | |||
724 | /// expressed in the Cartesian system. | 724 | /// expressed in the Cartesian system. |
725 | static CoordSystem make_iso_coord_system(const IsoGfx* iso) { | 725 | static CoordSystem make_iso_coord_system(const IsoGfx* iso) { |
726 | assert(iso); | 726 | assert(iso); |
727 | // const ivec2 o = {(iso->screen_width / 2) - (iso->tile_width / 2), 0}; | 727 | const ivec2 o = {iso->screen_width / 2, 0}; |
728 | const ivec2 o = { | ||
729 | (iso->screen_width / 2) - (iso->tile_width / 2), iso->tile_height}; | ||
730 | const ivec2 x = {.x = iso->tile_width / 2, .y = iso->tile_height / 2}; | 728 | const ivec2 x = {.x = iso->tile_width / 2, .y = iso->tile_height / 2}; |
731 | const ivec2 y = {.x = -iso->tile_width / 2, .y = iso->tile_height / 2}; | 729 | const ivec2 y = {.x = -iso->tile_width / 2, .y = iso->tile_height / 2}; |
732 | return (CoordSystem){o, x, y}; | 730 | return (CoordSystem){o, x, y}; |
733 | } | 731 | } |
734 | 732 | ||
733 | /// Get the screen position of the top diamond-corner of the tile at world | ||
734 | /// (x,y). | ||
735 | static ivec2 GetTileScreenOrigin( | ||
736 | const CoordSystem iso_space, int world_x, int world_y) { | ||
737 | const ivec2 vx_offset = ivec2_scale(iso_space.x, world_x); | ||
738 | const ivec2 vy_offset = ivec2_scale(iso_space.y, world_y); | ||
739 | const ivec2 screen_origin = | ||
740 | ivec2_add(iso_space.o, ivec2_add(vx_offset, vy_offset)); | ||
741 | |||
742 | return screen_origin; | ||
743 | } | ||
744 | |||
735 | static Pixel alpha_blend(Pixel src, Pixel dst) { | 745 | static Pixel alpha_blend(Pixel src, Pixel dst) { |
736 | if ((src.a == 255) || (dst.a == 0)) { | 746 | if ((src.a == 255) || (dst.a == 0)) { |
737 | return src; | 747 | return src; |
@@ -761,7 +771,7 @@ static Pixel alpha_blend(Pixel src, Pixel dst) { | |||
761 | /// 'pixels' is the palette and 'indices' the pixel indices. Otherwise, the | 771 | /// 'pixels' is the palette and 'indices' the pixel indices. Otherwise, the |
762 | /// image is assumed to be in plain RGBA format. | 772 | /// image is assumed to be in plain RGBA format. |
763 | static void draw_rect( | 773 | static void draw_rect( |
764 | IsoGfx* iso, ivec2 origin, int rect_width, int rect_height, | 774 | IsoGfx* iso, ivec2 top_left, int rect_width, int rect_height, |
765 | const Pixel* pixels, const uint8_t* indices) { | 775 | const Pixel* pixels, const uint8_t* indices) { |
766 | assert(iso); | 776 | assert(iso); |
767 | 777 | ||
@@ -769,20 +779,24 @@ static void draw_rect( | |||
769 | (indices ? pixels[indices[py * rect_width + px]] \ | 779 | (indices ? pixels[indices[py * rect_width + px]] \ |
770 | : pixels[py * rect_width + px]) | 780 | : pixels[py * rect_width + px]) |
771 | 781 | ||
772 | // Rect can exceed screen bounds, so we must clip it. | 782 | // Rect origin can be outside screen bounds, so we must offset accordingly to |
783 | // draw only the visible portion. | ||
773 | #define max(a, b) (a > b ? a : b) | 784 | #define max(a, b) (a > b ? a : b) |
774 | const int py_offset = max(0, rect_height - origin.y); | 785 | const int px_offset = max(0, -top_left.x); |
775 | origin.y = max(0, origin.y - rect_height); | 786 | const int py_offset = max(0, -top_left.y); |
787 | // Adjust top-left to be inside bounds. | ||
788 | top_left.x = max(0, top_left.x); | ||
789 | top_left.y = max(0, top_left.y); | ||
776 | 790 | ||
777 | // Clip along Y and X as we draw. | 791 | // Rect can exceed screen bounds, so clip along Y and X as we draw. |
778 | for (int py = py_offset; | 792 | for (int py = py_offset; |
779 | (py < rect_height) && (origin.y + py < iso->screen_height); ++py) { | 793 | (py < rect_height) && (top_left.y + py < iso->screen_height); ++py) { |
780 | const int sy = origin.y + py - py_offset; | 794 | const int sy = top_left.y + py - py_offset; |
781 | for (int px = 0; (px < rect_width) && (origin.x + px < iso->screen_width); | 795 | for (int px = px_offset; |
782 | ++px) { | 796 | (px < rect_width) && (top_left.x + px < iso->screen_width); ++px) { |
783 | const Pixel colour = rect_pixel(px, py); | 797 | const Pixel colour = rect_pixel(px, py); |
784 | if (colour.a > 0) { | 798 | if (colour.a > 0) { |
785 | const int sx = origin.x + px; | 799 | const int sx = top_left.x + px; |
786 | const Pixel dst = screen_xy(iso, sx, sy); | 800 | const Pixel dst = screen_xy(iso, sx, sy); |
787 | const Pixel final = alpha_blend(colour, dst); | 801 | const Pixel final = alpha_blend(colour, dst); |
788 | *screen_xy_mut(iso, sx, sy) = final; | 802 | *screen_xy_mut(iso, sx, sy) = final; |
@@ -791,18 +805,29 @@ static void draw_rect( | |||
791 | } | 805 | } |
792 | } | 806 | } |
793 | 807 | ||
794 | static void draw_tile(IsoGfx* iso, ivec2 origin, Tile tile) { | 808 | /// Draw a tile. |
809 | /// | ||
810 | /// 'screen_origin' is the screen coordinates of the top diamond-corner of the | ||
811 | /// tile (the base tile for super tiles). | ||
812 | /// World (0, 0) -> (screen_width / 2, 0). | ||
813 | static void draw_tile(IsoGfx* iso, ivec2 screen_origin, Tile tile) { | ||
795 | assert(iso); | 814 | assert(iso); |
796 | 815 | ||
797 | const TileData* tile_data = mempool_get_block(&iso->tiles, tile); | 816 | const TileData* tile_data = mempool_get_block(&iso->tiles, tile); |
798 | assert(tile_data); | 817 | assert(tile_data); |
799 | |||
800 | const Pixel* pixels = tile_xy_const_ref(iso, tile_data, 0, 0); | 818 | const Pixel* pixels = tile_xy_const_ref(iso, tile_data, 0, 0); |
801 | 819 | ||
802 | draw_rect(iso, origin, tile_data->width, tile_data->height, pixels, 0); | 820 | // Move from the top diamond-corner to the top-left corner of the tile image. |
821 | // For regular tiles, tile height == base tile height, so the y offset is 0. | ||
822 | // For super tiles, move as high up as the height of the tile. | ||
823 | const ivec2 offset = { | ||
824 | -(iso->tile_width / 2), tile_data->height - iso->tile_height}; | ||
825 | const ivec2 top_left = ivec2_add(screen_origin, offset); | ||
826 | |||
827 | draw_rect(iso, top_left, tile_data->width, tile_data->height, pixels, 0); | ||
803 | } | 828 | } |
804 | 829 | ||
805 | static void draw(IsoGfx* iso) { | 830 | static void draw_world(IsoGfx* iso) { |
806 | assert(iso); | 831 | assert(iso); |
807 | 832 | ||
808 | const int W = iso->screen_width; | 833 | const int W = iso->screen_width; |
@@ -817,14 +842,11 @@ static void draw(IsoGfx* iso) { | |||
817 | // Ex: walk in screen space and fetch the tile. | 842 | // Ex: walk in screen space and fetch the tile. |
818 | // The tile-centric approach might be more cache-friendly since the | 843 | // The tile-centric approach might be more cache-friendly since the |
819 | // screen-centric approach would juggle multiple tiles throughout the scan. | 844 | // screen-centric approach would juggle multiple tiles throughout the scan. |
820 | for (int ty = 0; ty < iso->world_height; ++ty) { | 845 | for (int wy = 0; wy < iso->world_height; ++wy) { |
821 | for (int tx = 0; tx < iso->world_width; ++tx) { | 846 | for (int wx = 0; wx < iso->world_width; ++wx) { |
822 | const Tile tile = world_xy(iso, tx, ty); | 847 | const Tile tile = world_xy(iso, wx, wy); |
823 | const ivec2 so = ivec2_add( | 848 | const ivec2 screen_origin = GetTileScreenOrigin(iso_space, wx, wy); |
824 | iso_space.o, | 849 | draw_tile(iso, screen_origin, tile); |
825 | ivec2_add( | ||
826 | ivec2_scale(iso_space.x, tx), ivec2_scale(iso_space.y, ty))); | ||
827 | draw_tile(iso, so, tile); | ||
828 | } | 850 | } |
829 | } | 851 | } |
830 | } | 852 | } |
@@ -855,18 +877,15 @@ static void draw_sprites(IsoGfx* iso) { | |||
855 | const SpriteSheetData* sheet = mem_get_chunk(&iso->sheets, sprite->sheet); | 877 | const SpriteSheetData* sheet = mem_get_chunk(&iso->sheets, sprite->sheet); |
856 | assert(sheet); | 878 | assert(sheet); |
857 | 879 | ||
858 | const ivec2 so = ivec2_add( | 880 | const ivec2 screen_origin = |
859 | iso_space.o, ivec2_add( | 881 | GetTileScreenOrigin(iso_space, sprite->position.x, sprite->position.y); |
860 | ivec2_scale(iso_space.x, sprite->position.x), | 882 | draw_sprite(iso, screen_origin, sprite, sheet); |
861 | ivec2_scale(iso_space.y, sprite->position.y))); | ||
862 | |||
863 | draw_sprite(iso, so, sprite, sheet); | ||
864 | }); | 883 | }); |
865 | } | 884 | } |
866 | 885 | ||
867 | void isogfx_render(IsoGfx* iso) { | 886 | void isogfx_render(IsoGfx* iso) { |
868 | assert(iso); | 887 | assert(iso); |
869 | draw(iso); | 888 | draw_world(iso); |
870 | draw_sprites(iso); | 889 | draw_sprites(iso); |
871 | } | 890 | } |
872 | 891 | ||
@@ -877,15 +896,9 @@ void isogfx_draw_tile(IsoGfx* iso, int x, int y, Tile tile) { | |||
877 | assert(x < iso->world_width); | 896 | assert(x < iso->world_width); |
878 | assert(y < iso->world_height); | 897 | assert(y < iso->world_height); |
879 | 898 | ||
880 | // const ivec2 o = {(iso->screen_width / 2) - (iso->tile_width / 2), 0}; | 899 | const CoordSystem iso_space = make_iso_coord_system(iso); |
881 | const ivec2 o = { | 900 | const ivec2 screen_origin = GetTileScreenOrigin(iso_space, x, y); |
882 | (iso->screen_width / 2) - (iso->tile_width / 2), iso->tile_height}; | 901 | draw_tile(iso, screen_origin, tile); |
883 | const ivec2 vx = {.x = iso->tile_width / 2, .y = iso->tile_height / 2}; | ||
884 | const ivec2 vy = {.x = -iso->tile_width / 2, .y = iso->tile_height / 2}; | ||
885 | const ivec2 so = | ||
886 | ivec2_add(o, ivec2_add(ivec2_scale(vx, x), ivec2_scale(vy, y))); | ||
887 | |||
888 | draw_tile(iso, so, tile); | ||
889 | } | 902 | } |
890 | 903 | ||
891 | bool isogfx_resize(IsoGfx* iso, int screen_width, int screen_height) { | 904 | bool isogfx_resize(IsoGfx* iso, int screen_width, int screen_height) { |