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 | |
| parent | 2c668763a1d6e645dcfaa713b924de26260542d0 (diff) | |
Add sprites.
Diffstat (limited to 'gfx-iso/src')
| -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) { |
