summaryrefslogtreecommitdiff
path: root/src/gfx2d.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gfx2d.c')
-rw-r--r--src/gfx2d.c109
1 files changed, 82 insertions, 27 deletions
diff --git a/src/gfx2d.c b/src/gfx2d.c
index 266a5f7..1c8b06f 100644
--- a/src/gfx2d.c
+++ b/src/gfx2d.c
@@ -20,6 +20,12 @@
20/// Time between animation updates. 20/// Time between animation updates.
21#define ANIMATION_UPDATE_DELTA (1.0 / ANIMATION_FPS) 21#define ANIMATION_UPDATE_DELTA (1.0 / ANIMATION_FPS)
22 22
23/// Take the maximum of two values.
24#define max(a, b) ((a) > (b) ? (a) : (b))
25
26/// Take the minimum of two values.
27#define min(a, b) ((a) < (b) ? (a) : (b))
28
23typedef struct ivec2 { 29typedef struct ivec2 {
24 int x, y; 30 int x, y;
25} ivec2; 31} ivec2;
@@ -73,6 +79,10 @@ static inline ivec2 ivec2_add(ivec2 a, ivec2 b) {
73 return (ivec2){.x = a.x + b.x, .y = a.y + b.y}; 79 return (ivec2){.x = a.x + b.x, .y = a.y + b.y};
74} 80}
75 81
82static inline ivec2 ivec2_mul(ivec2 a, ivec2 b) {
83 return (ivec2){.x = a.x * b.x, .y = a.y * b.y};
84}
85
76static inline ivec2 ivec2_scale(ivec2 a, int s) { 86static inline ivec2 ivec2_scale(ivec2 a, int s) {
77 return (ivec2){.x = a.x * s, .y = a.y * s}; 87 return (ivec2){.x = a.x * s, .y = a.y * s};
78} 88}
@@ -85,12 +95,14 @@ static inline vec2 vec2_add(vec2 a, vec2 b) {
85 95
86static inline vec2 ivec2_to_vec2(ivec2 a) { return (vec2){a.x, a.y}; } 96static inline vec2 ivec2_to_vec2(ivec2 a) { return (vec2){a.x, a.y}; }
87 97
88/// Map map coordinates to screen coordinates, both Cartesian. 98/// Map ortho coordinates to screen coordinates.
89static ivec2 map2screen( 99///
100/// Camera coordinates are in pixels. Map coordinates are in tiles.
101static ivec2 ortho2screen(
90 ivec2 camera, int tile_width, int tile_height, int map_x, int map_y) { 102 ivec2 camera, int tile_width, int tile_height, int map_x, int map_y) {
91 return ivec2_add( 103 return ivec2_add(
92 ivec2_neg(camera), 104 ivec2_neg(camera),
93 (ivec2){.x = map_x * tile_width, .y = map_y * tile_height}); 105 ivec2_mul((ivec2){map_x, map_y}, (ivec2){tile_width, tile_height}));
94} 106}
95 107
96// Not actually used because we pre-compute the two axis vectors instead. 108// Not actually used because we pre-compute the two axis vectors instead.
@@ -144,8 +156,8 @@ static inline vec2 cart2iso(vec2 cart, int s, int t, int w) {
144 const double one_over_s = 1. / (double)s; 156 const double one_over_s = 1. / (double)s;
145 const double one_over_t = 1. / (double)t; 157 const double one_over_t = 1. / (double)t;
146 const double x = cart.x - (double)(w / 2); 158 const double x = cart.x - (double)(w / 2);
147 return (vec2){.x = (one_over_s * x + one_over_t * cart.y), 159 return (vec2){.x = ((one_over_s * x) + (one_over_t * cart.y)),
148 .y = (-one_over_s * x + one_over_t * cart.y)}; 160 .y = ((-one_over_s * x) + (one_over_t * cart.y))};
149} 161}
150 162
151static inline const Pixel* screen_xy_const_ref( 163static inline const Pixel* screen_xy_const_ref(
@@ -155,10 +167,10 @@ static inline const Pixel* screen_xy_const_ref(
155 assert(y >= 0); 167 assert(y >= 0);
156 assert(x < screen->width); 168 assert(x < screen->width);
157 assert(y < screen->height); 169 assert(y < screen->height);
158 return &screen->pixels[y * screen->width + x]; 170 return &screen->pixels[(y * screen->width) + x];
159} 171}
160 172
161static inline Pixel screen_xy(Screen* screen, int x, int y) { 173static inline Pixel screen_xy(const Screen* screen, int x, int y) {
162 return *screen_xy_const_ref(screen, x, y); 174 return *screen_xy_const_ref(screen, x, y);
163} 175}
164 176
@@ -263,6 +275,8 @@ void gfx2d_make_map(Gfx2d* gfx, const MapDesc* desc) {
263 .base_tile_width = desc->tile_width, 275 .base_tile_width = desc->tile_width,
264 .base_tile_height = desc->tile_height, 276 .base_tile_height = desc->tile_height,
265 .num_layers = 1, 277 .num_layers = 1,
278 .flags =
279 (desc->orientation == MapOrthogonal) ? Tm_Orthogonal : Tm_Isometric,
266 }; 280 };
267 281
268 gfx->tileset = memstack_alloc_aligned(&gfx->stack, tileset_size_bytes, 4); 282 gfx->tileset = memstack_alloc_aligned(&gfx->stack, tileset_size_bytes, 4);
@@ -352,10 +366,10 @@ static void make_tile_from_colour(
352 const int height = tile->height; 366 const int height = tile->height;
353 const int r = width / height; 367 const int r = width / height;
354 for (int y = 0; y < height / 2; ++y) { 368 for (int y = 0; y < height / 2; ++y) {
355 const int mask_start = width / 2 - r * y - 1; 369 const int mask_start = (width / 2) - (r * y) - 1;
356 const int mask_end = width / 2 + r * y + 1; 370 const int mask_end = (width / 2) + (r * y) + 1;
357 for (int x = 0; x < width; ++x) { 371 for (int x = 0; x < width; ++x) {
358 const bool mask = (mask_start <= x) && (x <= mask_end); 372 const bool mask = ((mask_start <= x) && (x <= mask_end)) != 0;
359 const Pixel val = mask ? colour : (Pixel){.r = 0, .g = 0, .b = 0, .a = 0}; 373 const Pixel val = mask ? colour : (Pixel){.r = 0, .g = 0, .b = 0, .a = 0};
360 374
361 // Top half. 375 // Top half.
@@ -563,7 +577,6 @@ static void draw_rect(
563 577
564 // Rect origin can be outside screen bounds, so we must offset accordingly to 578 // Rect origin can be outside screen bounds, so we must offset accordingly to
565 // draw only the visible portion. 579 // draw only the visible portion.
566#define max(a, b) (a > b ? a : b)
567 const int px_offset = max(0, -top_left.x); 580 const int px_offset = max(0, -top_left.x);
568 const int py_offset = max(0, -top_left.y); 581 const int py_offset = max(0, -top_left.y);
569 582
@@ -596,7 +609,7 @@ static void draw_tile_ortho(Gfx2d* gfx, Tile tile, int x, int y) {
596 const Ts_Tile* pTile = ts_tileset_get_tile(gfx->tileset, tile); 609 const Ts_Tile* pTile = ts_tileset_get_tile(gfx->tileset, tile);
597 const Pixel* pixels = ts_tileset_get_tile_pixels(gfx->tileset, tile); 610 const Pixel* pixels = ts_tileset_get_tile_pixels(gfx->tileset, tile);
598 611
599 const ivec2 screen_origin = map2screen( 612 const ivec2 screen_origin = ortho2screen(
600 gfx->camera, gfx->map->base_tile_width, gfx->map->base_tile_height, x, y); 613 gfx->camera, gfx->map->base_tile_width, gfx->map->base_tile_height, x, y);
601 614
602 draw_rect( 615 draw_rect(
@@ -638,14 +651,24 @@ static void draw_map_ortho(Gfx2d* gfx) {
638 assert(gfx); 651 assert(gfx);
639 assert(gfx->map); 652 assert(gfx->map);
640 653
641 // TODO: Same TODOs as in draw_map_iso(). 654 // Render the tiles that the camera view rectangle intersects.
642 655 // +1 when computing x1,y1 because the screen dimensions need not be a
643 const Tm_Layer* layer = tm_map_get_layer(gfx->map, 0); 656 // multiple of the base tile dimensions.
644 657 const int x_tiles = gfx->screen.width / gfx->map->base_tile_width;
645 for (int wy = 0; wy < gfx->map->world_height; ++wy) { 658 const int y_tiles = gfx->screen.height / gfx->map->base_tile_height;
646 for (int wx = 0; wx < gfx->map->world_width; ++wx) { 659 const int x0 = gfx->camera.x / gfx->map->base_tile_width;
647 const Tile tile = tm_layer_get_tile(gfx->map, layer, wx, wy); 660 const int y0 = gfx->camera.y / gfx->map->base_tile_height;
648 draw_tile_ortho(gfx, tile, wx, wy); 661 const int x1 = min(gfx->map->world_width, x0 + x_tiles + 1);
662 const int y1 = min(gfx->map->world_height, y0 + y_tiles + 1);
663
664 for (uint16_t l = 0; l < gfx->map->num_layers; ++l) {
665 const Tm_Layer* layer = tm_map_get_layer(gfx->map, l);
666
667 for (int wy = y0; wy < y1; ++wy) {
668 for (int wx = x0; wx < x1; ++wx) {
669 const Tile tile = tm_layer_get_tile(gfx->map, layer, wx, wy);
670 draw_tile_ortho(gfx, tile, wx, wy);
671 }
649 } 672 }
650 } 673 }
651} 674}
@@ -704,7 +727,7 @@ static void draw_sprite_ortho(
704 // Apply an offset similarly to how we offset tiles. The sprite is offset by 727 // Apply an offset similarly to how we offset tiles. The sprite is offset by
705 // -base_tile_width/2 along the x-axis to align the sprite with the leftmost 728 // -base_tile_width/2 along the x-axis to align the sprite with the leftmost
706 // edge of the tile it is on. 729 // edge of the tile it is on.
707 const ivec2 screen_origin = map2screen( 730 const ivec2 screen_origin = ortho2screen(
708 gfx->camera, gfx->map->base_tile_width, gfx->map->base_tile_height, 731 gfx->camera, gfx->map->base_tile_width, gfx->map->base_tile_height,
709 sprite->position.x, sprite->position.y); 732 sprite->position.x, sprite->position.y);
710 733
@@ -761,13 +784,11 @@ static void draw_sprites(Gfx2d* gfx) {
761 } 784 }
762} 785}
763 786
764void gfx2d_set_camera(Gfx2d* gfx, int x, int y) { 787void gfx2d_render(Gfx2d* gfx, int camera_x, int camera_y) {
765 assert(gfx);
766 gfx->camera = (ivec2){x, y};
767}
768
769void gfx2d_render(Gfx2d* gfx) {
770 assert(gfx); 788 assert(gfx);
789 // Storing the camera mostly for debugging convenience. It could otherwise be
790 // passed around.
791 gfx->camera = (ivec2){camera_x, camera_y};
771 draw_map(gfx); 792 draw_map(gfx);
772 draw_sprites(gfx); 793 draw_sprites(gfx);
773} 794}
@@ -787,6 +808,40 @@ void gfx2d_draw_tile(Gfx2d* gfx, int x, int y, Tile tile) {
787 } 808 }
788} 809}
789 810
811static void clip_camera_ortho(const Gfx2d* gfx, float* x, float* y) {
812 assert(gfx);
813 assert(gfx->map);
814
815 if (x != nullptr) {
816 const int width_pixels = gfx->map->world_width * gfx->map->base_tile_width;
817 const int x_max = width_pixels - gfx->screen.width;
818 *x = min((float)x_max, max(0.F, *x));
819 }
820 if (y != nullptr) {
821 const int height_pixels =
822 gfx->map->world_height * gfx->map->base_tile_height;
823 const int y_max = height_pixels - gfx->screen.height;
824 *y = min((float)y_max, max(0.F, *y));
825 }
826}
827
828void gfx2d_clip_camera(const Gfx2d* gfx, float* x, float* y) {
829 assert(gfx);
830 assert(gfx->map);
831 assert(x);
832 assert(y);
833
834 const Tm_Flags* flags = (const Tm_Flags*)&gfx->map->flags;
835 switch (flags->orientation) {
836 case Tm_Orthogonal:
837 clip_camera_ortho(gfx, x, y);
838 break;
839 case Tm_Isometric:
840 // TODO: Clip camera in isometric maps.
841 break;
842 }
843}
844
790void gfx2d_get_screen_size(const Gfx2d* gfx, int* width, int* height) { 845void gfx2d_get_screen_size(const Gfx2d* gfx, int* width, int* height) {
791 assert(gfx); 846 assert(gfx);
792 assert(width); 847 assert(width);