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 /gfx-iso/src | |
| parent | 7a59d85dfead20dc34081badad6c8940e7190001 (diff) | |
More consistent changing of coordinates.
Diffstat (limited to 'gfx-iso/src')
| -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) { |
