diff options
author | 3gg <3gg@shellblade.net> | 2023-07-19 08:35:00 -0700 |
---|---|---|
committer | 3gg <3gg@shellblade.net> | 2023-07-19 08:35:00 -0700 |
commit | 48cef82988d6209987ae27fe29b72d7d5e402b3c (patch) | |
tree | fe5df57729a61839322ae8c1226d134e317b049f /gfx-iso/src/isogfx.c | |
parent | 2c668763a1d6e645dcfaa713b924de26260542d0 (diff) |
Add sprites.
Diffstat (limited to 'gfx-iso/src/isogfx.c')
-rw-r--r-- | gfx-iso/src/isogfx.c | 362 |
1 files changed, 321 insertions, 41 deletions
diff --git a/gfx-iso/src/isogfx.c b/gfx-iso/src/isogfx.c index 3ed0fde..9ba1bec 100644 --- a/gfx-iso/src/isogfx.c +++ b/gfx-iso/src/isogfx.c | |||
@@ -13,9 +13,29 @@ | |||
13 | #include <stdlib.h> | 13 | #include <stdlib.h> |
14 | #include <string.h> | 14 | #include <string.h> |
15 | 15 | ||
16 | /// Maximum number of tiles unless the user chooses a non-zero value. | 16 | /// Maximum number of tiles unless the user specifies a value. |
17 | #define DEFAULT_MAX_NUM_TILES 1024 | 17 | #define DEFAULT_MAX_NUM_TILES 1024 |
18 | 18 | ||
19 | /// Maximum number of sprites unless the user specifies a value. | ||
20 | #define DEFAULT_MAX_NUM_SPRITES 128 | ||
21 | |||
22 | /// Size of sprite sheet pool in bytes unless the user specifies a value. | ||
23 | #define DEFAULT_SPRITE_SHEET_POOL_SIZE_BYTES (8 * 1024 * 1024) | ||
24 | |||
25 | /// Default animation speed. | ||
26 | #define ANIMATION_FPS 10 | ||
27 | |||
28 | /// Time between animation updates. | ||
29 | #define ANIMATION_UPDATE_DELTA (1.0 / ANIMATION_FPS) | ||
30 | |||
31 | typedef struct ivec2 { | ||
32 | int x, y; | ||
33 | } ivec2; | ||
34 | |||
35 | typedef struct vec2 { | ||
36 | double x, y; | ||
37 | } vec2; | ||
38 | |||
19 | // ----------------------------------------------------------------------------- | 39 | // ----------------------------------------------------------------------------- |
20 | // Tile set (TS) and tile map (TM) file formats. | 40 | // Tile set (TS) and tile map (TM) file formats. |
21 | // ----------------------------------------------------------------------------- | 41 | // ----------------------------------------------------------------------------- |
@@ -70,6 +90,39 @@ static inline const Ts_Tile* ts_tileset_get_next_tile( | |||
70 | } | 90 | } |
71 | 91 | ||
72 | // ----------------------------------------------------------------------------- | 92 | // ----------------------------------------------------------------------------- |
93 | // Sprite sheet file format. | ||
94 | // ----------------------------------------------------------------------------- | ||
95 | |||
96 | /// A row of sprites in a sprite sheet. | ||
97 | /// | ||
98 | /// Each row in a sprite sheet can have a different number of columns. | ||
99 | /// | ||
100 | /// The pixels of the row follow a "sprite-major" order. It contains the | ||
101 | /// 'sprite_width * sprite_height' pixels for the first column/sprite, then the | ||
102 | /// second column/sprite, etc. | ||
103 | typedef struct Ss_Row { | ||
104 | uint16_t num_cols; /// Number of columns in this row. | ||
105 | Pixel pixels[1]; /// Count: num_cols * sprite_width * sprite_height. | ||
106 | } Ss_Row; | ||
107 | |||
108 | /// Sprite sheet top-level data definition. | ||
109 | /// | ||
110 | /// Sprite width and height are assumed constant throughout the sprite sheet. | ||
111 | typedef struct Ss_SpriteSheet { | ||
112 | uint16_t sprite_width; /// Sprite width in pixels. | ||
113 | uint16_t sprite_height; /// Sprite height in pixels. | ||
114 | uint16_t num_rows; | ||
115 | Ss_Row rows[1]; /// Count: num_rows. | ||
116 | } Ss_SpriteSheet; | ||
117 | |||
118 | const Ss_Row* get_sprite_sheet_row(const Ss_SpriteSheet* sheet, int row) { | ||
119 | assert(sheet); | ||
120 | assert(row >= 0); | ||
121 | assert(row < sheet->num_rows); | ||
122 | return &sheet->rows[row]; | ||
123 | } | ||
124 | |||
125 | // ----------------------------------------------------------------------------- | ||
73 | // Renderer state. | 126 | // Renderer state. |
74 | // ----------------------------------------------------------------------------- | 127 | // ----------------------------------------------------------------------------- |
75 | 128 | ||
@@ -79,34 +132,45 @@ typedef struct TileData { | |||
79 | uint16_t pixels_handle; // Handle to the tile's pixels in the pixel pool. | 132 | uint16_t pixels_handle; // Handle to the tile's pixels in the pixel pool. |
80 | } TileData; | 133 | } TileData; |
81 | 134 | ||
135 | // File format is already convenient for working in memory. | ||
136 | typedef Ss_Row SpriteSheetRow; | ||
137 | typedef Ss_SpriteSheet SpriteSheetData; | ||
138 | |||
139 | typedef struct SpriteData { | ||
140 | SpriteSheet sheet; // Handle to the sprite's sheet. | ||
141 | ivec2 position; | ||
142 | int animation; // Current animation. | ||
143 | int frame; // Current frame of animation. | ||
144 | } SpriteData; | ||
145 | |||
82 | DEF_MEMPOOL_DYN(TilePool, TileData) | 146 | DEF_MEMPOOL_DYN(TilePool, TileData) |
83 | DEF_MEM_DYN(PixelPool, Pixel) | 147 | DEF_MEM_DYN(PixelPool, Pixel) |
84 | 148 | ||
149 | DEF_MEMPOOL_DYN(SpritePool, SpriteData) | ||
150 | DEF_MEM_DYN(SpriteSheetPool, SpriteSheetData) | ||
151 | |||
85 | typedef struct IsoGfx { | 152 | typedef struct IsoGfx { |
86 | int screen_width; | 153 | int screen_width; |
87 | int screen_height; | 154 | int screen_height; |
88 | int tile_width; | 155 | int tile_width; |
89 | int tile_height; | 156 | int tile_height; |
90 | int world_width; | 157 | int world_width; |
91 | int world_height; | 158 | int world_height; |
92 | Tile* world; | 159 | int max_num_sprites; |
93 | Pixel* screen; | 160 | int sprite_sheet_pool_size_bytes; |
94 | TilePool tiles; | 161 | double last_animation_time; |
95 | PixelPool pixels; | 162 | Tile* world; |
163 | Pixel* screen; | ||
164 | TilePool tiles; | ||
165 | PixelPool pixels; | ||
166 | SpritePool sprites; | ||
167 | SpriteSheetPool sheets; | ||
96 | } IsoGfx; | 168 | } IsoGfx; |
97 | 169 | ||
98 | // ----------------------------------------------------------------------------- | 170 | // ----------------------------------------------------------------------------- |
99 | // Math and world / tile / screen access. | 171 | // Math and world / tile / screen access. |
100 | // ----------------------------------------------------------------------------- | 172 | // ----------------------------------------------------------------------------- |
101 | 173 | ||
102 | typedef struct ivec2 { | ||
103 | int x, y; | ||
104 | } ivec2; | ||
105 | |||
106 | typedef struct vec2 { | ||
107 | double x, y; | ||
108 | } vec2; | ||
109 | |||
110 | static inline ivec2 ivec2_add(ivec2 a, ivec2 b) { | 174 | static inline ivec2 ivec2_add(ivec2 a, ivec2 b) { |
111 | return (ivec2){.x = a.x + b.x, .y = a.y + b.y}; | 175 | return (ivec2){.x = a.x + b.x, .y = a.y + b.y}; |
112 | } | 176 | } |
@@ -220,8 +284,15 @@ IsoGfx* isogfx_new(const IsoGfxDesc* desc) { | |||
220 | iso->screen_width = desc->screen_width; | 284 | iso->screen_width = desc->screen_width; |
221 | iso->screen_height = desc->screen_height; | 285 | iso->screen_height = desc->screen_height; |
222 | 286 | ||
223 | const int screen_size = desc->screen_width * desc->screen_height; | 287 | iso->last_animation_time = 0.0; |
288 | |||
289 | iso->max_num_sprites = desc->max_num_sprites == 0 ? DEFAULT_MAX_NUM_SPRITES | ||
290 | : desc->max_num_sprites; | ||
291 | iso->sprite_sheet_pool_size_bytes = desc->sprite_sheet_pool_size_bytes == 0 | ||
292 | ? DEFAULT_SPRITE_SHEET_POOL_SIZE_BYTES | ||
293 | : desc->sprite_sheet_pool_size_bytes; | ||
224 | 294 | ||
295 | const int screen_size = desc->screen_width * desc->screen_height; | ||
225 | if (!(iso->screen = calloc(screen_size, sizeof(Pixel)))) { | 296 | if (!(iso->screen = calloc(screen_size, sizeof(Pixel)))) { |
226 | goto cleanup; | 297 | goto cleanup; |
227 | } | 298 | } |
@@ -233,7 +304,7 @@ cleanup: | |||
233 | return 0; | 304 | return 0; |
234 | } | 305 | } |
235 | 306 | ||
236 | /// Destroy the world and its tile set. | 307 | /// Destroy the world, its tile set, and the underlying pools. |
237 | static void destroy_world(IsoGfx* iso) { | 308 | static void destroy_world(IsoGfx* iso) { |
238 | assert(iso); | 309 | assert(iso); |
239 | if (iso->world) { | 310 | if (iso->world) { |
@@ -244,11 +315,19 @@ static void destroy_world(IsoGfx* iso) { | |||
244 | mem_del(&iso->pixels); | 315 | mem_del(&iso->pixels); |
245 | } | 316 | } |
246 | 317 | ||
318 | /// Destroy all loaded sprites and the underlying pools. | ||
319 | static void destroy_sprites(IsoGfx* iso) { | ||
320 | assert(iso); | ||
321 | mempool_del(&iso->sprites); | ||
322 | mem_del(&iso->sheets); | ||
323 | } | ||
324 | |||
247 | void isogfx_del(IsoGfx** pIso) { | 325 | void isogfx_del(IsoGfx** pIso) { |
248 | assert(pIso); | 326 | assert(pIso); |
249 | IsoGfx* iso = *pIso; | 327 | IsoGfx* iso = *pIso; |
250 | if (iso) { | 328 | if (iso) { |
251 | destroy_world(iso); | 329 | destroy_world(iso); |
330 | destroy_sprites(iso); | ||
252 | if (iso->screen) { | 331 | if (iso->screen) { |
253 | free(iso->screen); | 332 | free(iso->screen); |
254 | iso->screen = 0; | 333 | iso->screen = 0; |
@@ -341,7 +420,7 @@ bool isogfx_load_world(IsoGfx* iso, const char* filepath) { | |||
341 | // Tile set path is relative to the tile map file. Make it relative to the | 420 | // Tile set path is relative to the tile map file. Make it relative to the |
342 | // current working directory before loading. | 421 | // current working directory before loading. |
343 | char ts_path_cwd[PATH_MAX] = {0}; | 422 | char ts_path_cwd[PATH_MAX] = {0}; |
344 | if (!make_relative_path(MAX_PATH_LENGTH, filepath, ts_path, ts_path_cwd)) { | 423 | if (!make_relative_path(filepath, ts_path, ts_path_cwd, PATH_MAX)) { |
345 | goto cleanup; | 424 | goto cleanup; |
346 | } | 425 | } |
347 | 426 | ||
@@ -498,36 +577,199 @@ void isogfx_set_tiles(IsoGfx* iso, int x0, int y0, int x1, int y1, Tile tile) { | |||
498 | } | 577 | } |
499 | } | 578 | } |
500 | 579 | ||
580 | bool isogfx_load_sprite_sheet( | ||
581 | IsoGfx* iso, const char* filepath, SpriteSheet* p_sheet) { | ||
582 | assert(iso); | ||
583 | assert(filepath); | ||
584 | assert(p_sheet); | ||
585 | |||
586 | bool success = false; | ||
587 | |||
588 | // Lazy initialization of sprite pools. | ||
589 | if (mempool_capacity(&iso->sprites) == 0) { | ||
590 | if (!mempool_make_dyn( | ||
591 | &iso->sprites, iso->max_num_sprites, sizeof(SpriteData))) { | ||
592 | return false; | ||
593 | } | ||
594 | } | ||
595 | if (mem_capacity(&iso->sheets) == 0) { | ||
596 | // Using a block size of 1 byte for sprite sheet data. | ||
597 | if (!mem_make_dyn(&iso->sheets, iso->sprite_sheet_pool_size_bytes, 1)) { | ||
598 | return false; | ||
599 | } | ||
600 | } | ||
601 | |||
602 | // Load sprite sheet file. | ||
603 | printf("Load sprite sheet: %s\n", filepath); | ||
604 | FILE* file = fopen(filepath, "rb"); | ||
605 | if (file == NULL) { | ||
606 | goto cleanup; | ||
607 | } | ||
608 | const size_t sheet_size = get_file_size(file); | ||
609 | SpriteSheetData* ss_sheet = mem_alloc(&iso->sheets, sheet_size); | ||
610 | if (!ss_sheet) { | ||
611 | goto cleanup; | ||
612 | } | ||
613 | if (fread(ss_sheet, sheet_size, 1, file) != 1) { | ||
614 | goto cleanup; | ||
615 | } | ||
616 | |||
617 | *p_sheet = mem_get_chunk_handle(&iso->sheets, ss_sheet); | ||
618 | success = true; | ||
619 | |||
620 | cleanup: | ||
621 | // Pools remain initialized since client may attempt to load other sprites. | ||
622 | if (file != NULL) { | ||
623 | fclose(file); | ||
624 | } | ||
625 | if (!success) { | ||
626 | if (ss_sheet) { | ||
627 | mem_free(&iso->sheets, &ss_sheet); | ||
628 | } | ||
629 | } | ||
630 | return success; | ||
631 | } | ||
632 | |||
633 | Sprite isogfx_make_sprite(IsoGfx* iso, SpriteSheet sheet) { | ||
634 | assert(iso); | ||
635 | |||
636 | SpriteData* sprite = mempool_alloc(&iso->sprites); | ||
637 | assert(sprite); | ||
638 | |||
639 | sprite->sheet = sheet; | ||
640 | |||
641 | return mempool_get_block_index(&iso->sprites, sprite); | ||
642 | } | ||
643 | |||
644 | #define with_sprite(SPRITE, BODY) \ | ||
645 | { \ | ||
646 | SpriteData* data = mempool_get_block(&iso->sprites, sprite); \ | ||
647 | assert(data); \ | ||
648 | BODY; \ | ||
649 | } | ||
650 | |||
651 | void isogfx_set_sprite_position(IsoGfx* iso, Sprite sprite, int x, int y) { | ||
652 | assert(iso); | ||
653 | with_sprite(sprite, { | ||
654 | data->position.x = x; | ||
655 | data->position.y = y; | ||
656 | }); | ||
657 | } | ||
658 | |||
659 | void isogfx_set_sprite_animation(IsoGfx* iso, Sprite sprite, int animation) { | ||
660 | assert(iso); | ||
661 | with_sprite(sprite, { data->animation = animation; }); | ||
662 | } | ||
663 | |||
664 | void isogfx_update(IsoGfx* iso, double t) { | ||
665 | assert(iso); | ||
666 | |||
667 | // If this is the first time update() is called after initialization, just | ||
668 | // record the starting animation time. | ||
669 | if (iso->last_animation_time == 0.0) { | ||
670 | iso->last_animation_time = t; | ||
671 | return; | ||
672 | } | ||
673 | |||
674 | if ((t - iso->last_animation_time) >= ANIMATION_UPDATE_DELTA) { | ||
675 | // TODO: Consider linking animated sprites in a list so that we only walk | ||
676 | // over those here and not also the static sprites. | ||
677 | mempool_foreach(&iso->sprites, sprite, { | ||
678 | const SpriteSheetData* sheet = mem_get_chunk(&iso->sheets, sprite->sheet); | ||
679 | assert(sheet); // TODO: Make this a hard assert inside the mem/pool. | ||
680 | const SpriteSheetRow* row = | ||
681 | get_sprite_sheet_row(sheet, sprite->animation); | ||
682 | sprite->frame = (sprite->frame + 1) % row->num_cols; | ||
683 | }); | ||
684 | |||
685 | iso->last_animation_time = t; | ||
686 | } | ||
687 | } | ||
688 | |||
501 | // ----------------------------------------------------------------------------- | 689 | // ----------------------------------------------------------------------------- |
502 | // Rendering and picking. | 690 | // Rendering and picking. |
503 | // ----------------------------------------------------------------------------- | 691 | // ----------------------------------------------------------------------------- |
504 | 692 | ||
505 | static void draw_tile(IsoGfx* iso, ivec2 origin, Tile tile) { | 693 | typedef struct CoordSystem { |
694 | ivec2 o; /// Origin. | ||
695 | ivec2 x; | ||
696 | ivec2 y; | ||
697 | } CoordSystem; | ||
698 | |||
699 | /// Create the basis for the isometric coordinate system with origin and vectors | ||
700 | /// expressed in the Cartesian system. | ||
701 | static CoordSystem make_iso_coord_system(const IsoGfx* iso) { | ||
506 | assert(iso); | 702 | assert(iso); |
703 | // const ivec2 o = {(iso->screen_width / 2) - (iso->tile_width / 2), 0}; | ||
704 | const ivec2 o = { | ||
705 | (iso->screen_width / 2) - (iso->tile_width / 2), iso->tile_height}; | ||
706 | const ivec2 x = {.x = iso->tile_width / 2, .y = iso->tile_height / 2}; | ||
707 | const ivec2 y = {.x = -iso->tile_width / 2, .y = iso->tile_height / 2}; | ||
708 | return (CoordSystem){o, x, y}; | ||
709 | } | ||
507 | 710 | ||
508 | const TileData* tile_data = mempool_get_block(&iso->tiles, tile); | 711 | static Pixel alpha_blend(Pixel src, Pixel dst) { |
509 | assert(tile_data); | 712 | if ((src.a == 255) || (dst.a == 0)) { |
713 | return src; | ||
714 | } | ||
715 | const uint16_t one_minus_alpha = 255 - src.a; | ||
716 | #define blend(s, d) \ | ||
717 | (Channel)( \ | ||
718 | (double)((uint16_t)s * (uint16_t)src.a + \ | ||
719 | (uint16_t)d * one_minus_alpha) / \ | ||
720 | 255.0) | ||
721 | return (Pixel){ | ||
722 | .r = blend(src.r, dst.r), | ||
723 | .g = blend(src.g, dst.g), | ||
724 | .b = blend(src.b, dst.b), | ||
725 | .a = src.a}; | ||
726 | } | ||
727 | |||
728 | /// Draw a rectangle (tile or sprite). | ||
729 | /// | ||
730 | /// The rectangle's bottom-left corner is mapped to the given origin. The | ||
731 | /// rectangle then extends to the right and top of the origin. | ||
732 | /// | ||
733 | /// The rectangle's pixels are assumed to be arranged in a linear, row-major | ||
734 | /// fashion. | ||
735 | static void draw_rect( | ||
736 | IsoGfx* iso, ivec2 origin, int rect_width, int rect_height, | ||
737 | const Pixel* pixels) { | ||
738 | assert(iso); | ||
510 | 739 | ||
511 | // Tile can exceed screen bounds, so we must clip it. | 740 | // Rect can exceed screen bounds, so we must clip it. |
512 | #define max(a, b) (a > b ? a : b) | 741 | #define max(a, b) (a > b ? a : b) |
513 | const int py_offset = max(0, (int)tile_data->height - origin.y); | 742 | const int py_offset = max(0, rect_height - origin.y); |
514 | origin.y = max(0, origin.y - (int)tile_data->height); | 743 | origin.y = max(0, origin.y - rect_height); |
515 | 744 | ||
516 | // Clip along Y and X as we draw. | 745 | // Clip along Y and X as we draw. |
517 | for (int py = py_offset; | 746 | for (int py = py_offset; |
518 | (py < tile_data->height) && (origin.y + py < iso->screen_height); ++py) { | 747 | (py < rect_height) && (origin.y + py < iso->screen_height); ++py) { |
519 | const int sy = origin.y + py - py_offset; | 748 | const int sy = origin.y + py - py_offset; |
520 | for (int px = 0; | 749 | for (int px = 0; (px < rect_width) && (origin.x + px < iso->screen_width); |
521 | (px < tile_data->width) && (origin.x + px < iso->screen_width); ++px) { | 750 | ++px) { |
522 | const Pixel colour = tile_xy(iso, tile_data, px, py); | 751 | const Pixel colour = pixels[py * rect_width + px]; |
523 | if (colour.a > 0) { | 752 | if (colour.a > 0) { |
524 | const int sx = origin.x + px; | 753 | const int sx = origin.x + px; |
525 | *screen_xy_mut(iso, sx, sy) = colour; | 754 | const Pixel dst = screen_xy(iso, sx, sy); |
755 | const Pixel final = alpha_blend(colour, dst); | ||
756 | *screen_xy_mut(iso, sx, sy) = final; | ||
526 | } | 757 | } |
527 | } | 758 | } |
528 | } | 759 | } |
529 | } | 760 | } |
530 | 761 | ||
762 | static void draw_tile(IsoGfx* iso, ivec2 origin, Tile tile) { | ||
763 | assert(iso); | ||
764 | |||
765 | const TileData* tile_data = mempool_get_block(&iso->tiles, tile); | ||
766 | assert(tile_data); | ||
767 | |||
768 | const Pixel* pixels = tile_xy_const_ref(iso, tile_data, 0, 0); | ||
769 | |||
770 | draw_rect(iso, origin, tile_data->width, tile_data->height, pixels); | ||
771 | } | ||
772 | |||
531 | static void draw(IsoGfx* iso) { | 773 | static void draw(IsoGfx* iso) { |
532 | assert(iso); | 774 | assert(iso); |
533 | 775 | ||
@@ -536,11 +778,7 @@ static void draw(IsoGfx* iso) { | |||
536 | 778 | ||
537 | memset(iso->screen, 0, W * H * sizeof(Pixel)); | 779 | memset(iso->screen, 0, W * H * sizeof(Pixel)); |
538 | 780 | ||
539 | // const ivec2 o = {(iso->screen_width / 2) - (iso->tile_width / 2), 0}; | 781 | const CoordSystem iso_space = make_iso_coord_system(iso); |
540 | const ivec2 o = { | ||
541 | (iso->screen_width / 2) - (iso->tile_width / 2), iso->tile_height}; | ||
542 | const ivec2 x = {.x = iso->tile_width / 2, .y = iso->tile_height / 2}; | ||
543 | const ivec2 y = {.x = -iso->tile_width / 2, .y = iso->tile_height / 2}; | ||
544 | 782 | ||
545 | // TODO: Culling. | 783 | // TODO: Culling. |
546 | // Ex: map the screen corners to tile space to cull. | 784 | // Ex: map the screen corners to tile space to cull. |
@@ -550,16 +788,58 @@ static void draw(IsoGfx* iso) { | |||
550 | for (int ty = 0; ty < iso->world_height; ++ty) { | 788 | for (int ty = 0; ty < iso->world_height; ++ty) { |
551 | for (int tx = 0; tx < iso->world_width; ++tx) { | 789 | for (int tx = 0; tx < iso->world_width; ++tx) { |
552 | const Tile tile = world_xy(iso, tx, ty); | 790 | const Tile tile = world_xy(iso, tx, ty); |
553 | const ivec2 so = | 791 | const ivec2 so = ivec2_add( |
554 | ivec2_add(o, ivec2_add(ivec2_scale(x, tx), ivec2_scale(y, ty))); | 792 | iso_space.o, |
793 | ivec2_add( | ||
794 | ivec2_scale(iso_space.x, tx), ivec2_scale(iso_space.y, ty))); | ||
555 | draw_tile(iso, so, tile); | 795 | draw_tile(iso, so, tile); |
556 | } | 796 | } |
557 | } | 797 | } |
558 | } | 798 | } |
559 | 799 | ||
800 | static void draw_sprite( | ||
801 | IsoGfx* iso, ivec2 origin, const SpriteData* sprite, | ||
802 | const SpriteSheetData* sheet) { | ||
803 | assert(iso); | ||
804 | assert(sprite); | ||
805 | assert(sheet); | ||
806 | assert(sprite->animation >= 0); | ||
807 | assert(sprite->animation < sheet->num_rows); | ||
808 | assert(sprite->frame >= 0); | ||
809 | |||
810 | const SpriteSheetRow* ss_row = &sheet->rows[sprite->animation]; | ||
811 | assert(sprite->frame < ss_row->num_cols); | ||
812 | |||
813 | const int sprite_offset = | ||
814 | sprite->frame * sheet->sprite_width * sheet->sprite_height; | ||
815 | |||
816 | const Pixel* frame = &ss_row->pixels[sprite_offset]; | ||
817 | |||
818 | draw_rect(iso, origin, sheet->sprite_width, sheet->sprite_height, frame); | ||
819 | } | ||
820 | |||
821 | static void draw_sprites(IsoGfx* iso) { | ||
822 | assert(iso); | ||
823 | |||
824 | const CoordSystem iso_space = make_iso_coord_system(iso); | ||
825 | |||
826 | mempool_foreach(&iso->sprites, sprite, { | ||
827 | const SpriteSheetData* sheet = mem_get_chunk(&iso->sheets, sprite->sheet); | ||
828 | assert(sheet); | ||
829 | |||
830 | const ivec2 so = ivec2_add( | ||
831 | iso_space.o, ivec2_add( | ||
832 | ivec2_scale(iso_space.x, sprite->position.x), | ||
833 | ivec2_scale(iso_space.y, sprite->position.y))); | ||
834 | |||
835 | draw_sprite(iso, so, sprite, sheet); | ||
836 | }); | ||
837 | } | ||
838 | |||
560 | void isogfx_render(IsoGfx* iso) { | 839 | void isogfx_render(IsoGfx* iso) { |
561 | assert(iso); | 840 | assert(iso); |
562 | draw(iso); | 841 | draw(iso); |
842 | draw_sprites(iso); | ||
563 | } | 843 | } |
564 | 844 | ||
565 | void isogfx_draw_tile(IsoGfx* iso, int x, int y, Tile tile) { | 845 | void isogfx_draw_tile(IsoGfx* iso, int x, int y, Tile tile) { |