summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend.c30
-rw-r--r--src/gfx2d.c (renamed from src/isogfx.c)524
2 files changed, 337 insertions, 217 deletions
diff --git a/src/backend.c b/src/backend.c
index 94f1728..4bb3592 100644
--- a/src/backend.c
+++ b/src/backend.c
@@ -1,5 +1,5 @@
1#include <isogfx/backend.h> 1#include <isogfx/backend.h>
2#include <isogfx/isogfx.h> 2#include <isogfx/gfx2d.h>
3 3
4#include <gfx/core.h> 4#include <gfx/core.h>
5#include <gfx/gfx.h> 5#include <gfx/gfx.h>
@@ -13,7 +13,7 @@
13#include <assert.h> 13#include <assert.h>
14#include <stdlib.h> 14#include <stdlib.h>
15 15
16typedef struct IsoBackend { 16typedef struct Gfx2dBackend {
17 Gfx* gfx; 17 Gfx* gfx;
18 Mesh* quad_mesh; 18 Mesh* quad_mesh;
19 /// The screen or "iso screen" refers to the colour buffer of the iso graphics 19 /// The screen or "iso screen" refers to the colour buffer of the iso graphics
@@ -29,12 +29,12 @@ typedef struct IsoBackend {
29 int viewport_x, viewport_y, viewport_width, viewport_height; 29 int viewport_x, viewport_y, viewport_width, viewport_height;
30 double stretch; // Stretch factor from iso screen dimensions to viewport 30 double stretch; // Stretch factor from iso screen dimensions to viewport
31 // dimensions. 31 // dimensions.
32} IsoBackend; 32} Gfx2dBackend;
33 33
34IsoBackend* iso_backend_init(const IsoGfx* iso) { 34Gfx2dBackend* gfx2d_backend_init(const Gfx2d* iso) {
35 assert(iso); 35 assert(iso);
36 36
37 IsoBackend* backend = calloc(1, sizeof(IsoBackend)); 37 Gfx2dBackend* backend = calloc(1, sizeof(Gfx2dBackend));
38 if (!backend) { 38 if (!backend) {
39 return nullptr; 39 return nullptr;
40 } 40 }
@@ -45,7 +45,7 @@ IsoBackend* iso_backend_init(const IsoGfx* iso) {
45 GfxCore* gfxcore = gfx_get_core(backend->gfx); 45 GfxCore* gfxcore = gfx_get_core(backend->gfx);
46 46
47 int screen_width, screen_height; 47 int screen_width, screen_height;
48 isogfx_get_screen_size(iso, &screen_width, &screen_height); 48 gfx2d_get_screen_size(iso, &screen_width, &screen_height);
49 49
50 if (!((backend->screen_texture = gfx_make_texture( 50 if (!((backend->screen_texture = gfx_make_texture(
51 gfxcore, &(TextureDesc){.width = screen_width, 51 gfxcore, &(TextureDesc){.width = screen_width,
@@ -95,10 +95,10 @@ cleanup:
95 return nullptr; 95 return nullptr;
96} 96}
97 97
98void iso_backend_shutdown(IsoBackend** ppApp) { 98void gfx2d_backend_shutdown(Gfx2dBackend** ppApp) {
99 assert(ppApp); 99 assert(ppApp);
100 100
101 IsoBackend* app = *ppApp; 101 Gfx2dBackend* app = *ppApp;
102 if (!app) { 102 if (!app) {
103 return; 103 return;
104 } 104 }
@@ -106,8 +106,8 @@ void iso_backend_shutdown(IsoBackend** ppApp) {
106 gfx_destroy(&app->gfx); 106 gfx_destroy(&app->gfx);
107} 107}
108 108
109void iso_backend_resize_window( 109void gfx2d_backend_resize_window(
110 IsoBackend* app, const IsoGfx* iso, int width, int height) { 110 Gfx2dBackend* app, const Gfx2d* iso, int width, int height) {
111 assert(app); 111 assert(app);
112 assert(iso); 112 assert(iso);
113 113
@@ -116,7 +116,7 @@ void iso_backend_resize_window(
116 116
117 // Virtual screen dimensions. 117 // Virtual screen dimensions.
118 int screen_width, screen_height; 118 int screen_width, screen_height;
119 isogfx_get_screen_size(iso, &screen_width, &screen_height); 119 gfx2d_get_screen_size(iso, &screen_width, &screen_height);
120 120
121 // Stretch the virtual screen onto the viewport while respecting the screen's 121 // Stretch the virtual screen onto the viewport while respecting the screen's
122 // aspect ratio to prevent distortion. 122 // aspect ratio to prevent distortion.
@@ -135,11 +135,11 @@ void iso_backend_resize_window(
135 } 135 }
136} 136}
137 137
138void iso_backend_render(const IsoBackend* app, const IsoGfx* iso) { 138void gfx2d_backend_render(const Gfx2dBackend* app, const Gfx2d* iso) {
139 assert(app); 139 assert(app);
140 assert(iso); 140 assert(iso);
141 141
142 const Pixel* screen = isogfx_get_screen_buffer(iso); 142 const Pixel* screen = gfx2d_get_screen_buffer(iso);
143 assert(screen); 143 assert(screen);
144 gfx_update_texture(app->screen_texture, &(TextureDataDesc){.pixels = screen}); 144 gfx_update_texture(app->screen_texture, &(TextureDataDesc){.pixels = screen});
145 145
@@ -162,8 +162,8 @@ void iso_backend_render(const IsoBackend* app, const IsoGfx* iso) {
162 gfx_end_frame(gfxcore); 162 gfx_end_frame(gfxcore);
163} 163}
164 164
165bool iso_backend_get_mouse_position( 165bool gfx2d_backend_get_mouse_position(
166 const IsoBackend* app, double window_x, double window_y, double* x, 166 const Gfx2dBackend* app, double window_x, double window_y, double* x,
167 double* y) { 167 double* y) {
168 assert(app); 168 assert(app);
169 169
diff --git a/src/isogfx.c b/src/gfx2d.c
index c3a87bf..f609c98 100644
--- a/src/isogfx.c
+++ b/src/gfx2d.c
@@ -1,4 +1,4 @@
1#include <isogfx/isogfx.h> 1#include <isogfx/gfx2d.h>
2 2
3#include <isogfx/asset.h> 3#include <isogfx/asset.h>
4 4
@@ -32,11 +32,11 @@ typedef struct vec2 {
32// Renderer state. 32// Renderer state.
33// ----------------------------------------------------------------------------- 33// -----------------------------------------------------------------------------
34 34
35typedef struct CoordSystem { 35typedef struct IsoCoordSystem {
36 ivec2 o; // Origin. 36 ivec2 o; // Origin.
37 ivec2 x; 37 ivec2 x;
38 ivec2 y; 38 ivec2 y;
39} CoordSystem; 39} IsoCoordSystem;
40 40
41typedef struct Screen { 41typedef struct Screen {
42 int width; 42 int width;
@@ -52,9 +52,9 @@ typedef struct SpriteInstance {
52 int frame; // Current frame of animation. 52 int frame; // Current frame of animation.
53} SpriteInstance; 53} SpriteInstance;
54 54
55typedef struct IsoGfx { 55typedef struct Gfx2d {
56 Screen screen; 56 Screen screen;
57 CoordSystem iso_space; 57 IsoCoordSystem iso_space;
58 ivec2 camera; 58 ivec2 camera;
59 double last_animation_time; 59 double last_animation_time;
60 Tile next_tile; // For procedurally-generated tiles. 60 Tile next_tile; // For procedurally-generated tiles.
@@ -63,7 +63,7 @@ typedef struct IsoGfx {
63 SpriteInstance* head_sprite; // Head of sprites list. 63 SpriteInstance* head_sprite; // Head of sprites list.
64 memstack stack; 64 memstack stack;
65 size_t watermark; 65 size_t watermark;
66} IsoGfx; 66} Gfx2d;
67 67
68// ----------------------------------------------------------------------------- 68// -----------------------------------------------------------------------------
69// Math and world / tile / screen access. 69// Math and world / tile / screen access.
@@ -79,17 +79,58 @@ static inline ivec2 ivec2_scale(ivec2 a, int s) {
79 79
80static inline ivec2 ivec2_neg(ivec2 a) { return (ivec2){.x = -a.x, .y = -a.y}; } 80static inline ivec2 ivec2_neg(ivec2 a) { return (ivec2){.x = -a.x, .y = -a.y}; }
81 81
82static inline ivec2 iso2cart(ivec2 iso, int s, int t, int w) {
83 return (ivec2){.x = (iso.x - iso.y) * (s / 2) + (w / 2),
84 .y = (iso.x + iso.y) * (t / 2)};
85}
86
87static inline vec2 vec2_add(vec2 a, vec2 b) { 82static inline vec2 vec2_add(vec2 a, vec2 b) {
88 return (vec2){.x = a.x + b.x, .y = a.y + b.y}; 83 return (vec2){.x = a.x + b.x, .y = a.y + b.y};
89} 84}
90 85
91static inline vec2 ivec2_to_vec2(ivec2 a) { return (vec2){a.x, a.y}; } 86static inline vec2 ivec2_to_vec2(ivec2 a) { return (vec2){a.x, a.y}; }
92 87
88/// Map map coordinates to screen coordinates, both Cartesian.
89static ivec2 map2screen(
90 ivec2 camera, int tile_width, int tile_height, int map_x, int map_y) {
91 return ivec2_add(
92 ivec2_neg(camera),
93 (ivec2){.x = map_x * tile_width, .y = map_y * tile_height});
94}
95
96// Not actually used because we pre-compute the two axis vectors instead.
97// See make_iso_coord_system() and the other definition of iso2cart() below.
98// static inline ivec2 iso2cart(ivec2 iso, int s, int t, int w) {
99// return (ivec2){.x = (iso.x - iso.y) * (s / 2) + (w / 2),
100// .y = (iso.x + iso.y) * (t / 2)};
101// }
102
103/// Create the basis for the isometric coordinate system with origin and vectors
104/// expressed in the Cartesian system.
105static IsoCoordSystem make_iso_coord_system(
106 const Tm_Map* const map, const Screen* const screen) {
107 assert(map);
108 assert(screen);
109 const ivec2 o = {screen->width / 2, 0};
110 const ivec2 x = {
111 .x = map->base_tile_width / 2, .y = map->base_tile_height / 2};
112 const ivec2 y = {
113 .x = -map->base_tile_width / 2, .y = map->base_tile_height / 2};
114 return (IsoCoordSystem){o, x, y};
115}
116
117/// Map isometric coordinates to Cartesian coordinates.
118///
119/// For a tile, this gets the screen position of the top diamond-corner of the
120/// tile.
121///
122/// Takes the camera displacement into account.
123static ivec2 iso2cart(
124 const IsoCoordSystem iso_space, ivec2 camera, int iso_x, int iso_y) {
125 const ivec2 vx_offset = ivec2_scale(iso_space.x, iso_x);
126 const ivec2 vy_offset = ivec2_scale(iso_space.y, iso_y);
127 const ivec2 origin_world_space =
128 ivec2_add(iso_space.o, ivec2_add(vx_offset, vy_offset));
129 const ivec2 origin_view_space =
130 ivec2_add(origin_world_space, ivec2_neg(camera));
131 return origin_view_space;
132}
133
93// Method 1. 134// Method 1.
94// static inline vec2 cart2iso(vec2 cart, int s, int t, int w) { 135// static inline vec2 cart2iso(vec2 cart, int s, int t, int w) {
95// const double x = cart.x - (double)(w / 2); 136// const double x = cart.x - (double)(w / 2);
@@ -125,81 +166,67 @@ static inline Pixel* screen_xy_mut(Screen* screen, int x, int y) {
125 return (Pixel*)screen_xy_const_ref(screen, x, y); 166 return (Pixel*)screen_xy_const_ref(screen, x, y);
126} 167}
127 168
128/// Create the basis for the isometric coordinate system with origin and vectors
129/// expressed in the Cartesian system.
130static CoordSystem make_iso_coord_system(
131 const Tm_Map* const map, const Screen* const screen) {
132 assert(map);
133 assert(screen);
134 const ivec2 o = {screen->width / 2, 0};
135 const ivec2 x = {
136 .x = map->base_tile_width / 2, .y = map->base_tile_height / 2};
137 const ivec2 y = {
138 .x = -map->base_tile_width / 2, .y = map->base_tile_height / 2};
139 return (CoordSystem){o, x, y};
140}
141
142// ----------------------------------------------------------------------------- 169// -----------------------------------------------------------------------------
143// Renderer, world and tile management. 170// Renderer, world and tile management.
144// ----------------------------------------------------------------------------- 171// -----------------------------------------------------------------------------
145 172
146IsoGfx* isogfx_new(const IsoGfxDesc* desc) { 173Gfx2d* gfx2d_new(const Gfx2dDesc* desc) {
147 assert(desc->screen_width > 0); 174 assert(desc->screen_width > 0);
148 assert(desc->screen_height > 0); 175 assert(desc->screen_height > 0);
149 // Part of our implementation assumes even widths and heights for precision. 176 // Part of our implementation assumes even widths and heights for precision.
150 assert((desc->screen_width & 1) == 0); 177 assert((desc->screen_width & 1) == 0);
151 assert((desc->screen_height & 1) == 0); 178 assert((desc->screen_height & 1) == 0);
152 179
153 IsoGfx tmp = {0}; 180 Gfx2d tmp = {0};
154 if (!memstack_make(&tmp.stack, desc->memory_size, desc->memory)) { 181 if (!memstack_make(&tmp.stack, desc->memory_size, desc->memory)) {
155 goto cleanup; 182 goto cleanup;
156 } 183 }
157 IsoGfx* iso = 184 Gfx2d* gfx =
158 memstack_alloc_aligned(&tmp.stack, sizeof(IsoGfx), alignof(IsoGfx)); 185 memstack_alloc_aligned(&tmp.stack, sizeof(Gfx2d), alignof(Gfx2d));
159 *iso = tmp; 186 *gfx = tmp;
160 187
161 const size_t screen_size_bytes = 188 const size_t screen_size_bytes =
162 desc->screen_width * desc->screen_height * sizeof(Pixel); 189 desc->screen_width * desc->screen_height * sizeof(Pixel);
163 Pixel* screen = 190 Pixel* screen =
164 memstack_alloc_aligned(&iso->stack, screen_size_bytes, alignof(Pixel)); 191 memstack_alloc_aligned(&gfx->stack, screen_size_bytes, alignof(Pixel));
165 192
166 iso->screen = (Screen){.width = desc->screen_width, 193 gfx->screen = (Screen){.width = desc->screen_width,
167 .height = desc->screen_height, 194 .height = desc->screen_height,
168 .pixels = screen}; 195 .pixels = screen};
169 196
170 iso->last_animation_time = 0.0; 197 gfx->last_animation_time = 0.0;
171 iso->watermark = memstack_watermark(&iso->stack); 198 gfx->watermark = memstack_watermark(&gfx->stack);
172 199
173 return iso; 200 return gfx;
174 201
175cleanup: 202cleanup:
176 isogfx_del(&iso); 203 gfx2d_del(&gfx);
177 return nullptr; 204 return nullptr;
178} 205}
179 206
180void isogfx_clear(IsoGfx* iso) { 207void gfx2d_clear(Gfx2d* gfx) {
181 assert(iso); 208 assert(gfx);
182 iso->last_animation_time = 0.0; 209 gfx->last_animation_time = 0.0;
183 iso->next_tile = 0; 210 gfx->next_tile = 0;
184 iso->map = nullptr; 211 gfx->map = nullptr;
185 iso->tileset = nullptr; 212 gfx->tileset = nullptr;
186 iso->head_sprite = nullptr; 213 gfx->head_sprite = nullptr;
187 // The base of the stack contains the IsoGfx and the screen buffer. Make sure 214 // The base of the stack contains the Gfx2d and the screen buffer. Make sure
188 // we don't clear them. 215 // we don't clear them.
189 memstack_set_watermark(&iso->stack, iso->watermark); 216 memstack_set_watermark(&gfx->stack, gfx->watermark);
190} 217}
191 218
192void isogfx_del(IsoGfx** ppIso) { 219void gfx2d_del(Gfx2d** ppGfx) {
193 assert(ppIso); 220 assert(ppGfx);
194 IsoGfx* iso = *ppIso; 221 Gfx2d* gfx = *ppGfx;
195 if (iso) { 222 if (gfx) {
196 memstack_del(&iso->stack); 223 memstack_del(&gfx->stack);
197 *ppIso = nullptr; 224 *ppGfx = nullptr;
198 } 225 }
199} 226}
200 227
201void isogfx_make_world(IsoGfx* iso, const WorldDesc* desc) { 228void gfx2d_make_map(Gfx2d* gfx, const MapDesc* desc) {
202 assert(iso); 229 assert(gfx);
203 assert(desc); 230 assert(desc);
204 assert(desc->tile_width > 0); 231 assert(desc->tile_width > 0);
205 assert(desc->tile_height > 0); 232 assert(desc->tile_height > 0);
@@ -214,7 +241,7 @@ void isogfx_make_world(IsoGfx* iso, const WorldDesc* desc) {
214 assert(desc->num_tiles > 0); 241 assert(desc->num_tiles > 0);
215 242
216 // Handle recreation by destroying the previous world and sprites. 243 // Handle recreation by destroying the previous world and sprites.
217 isogfx_clear(iso); 244 gfx2d_clear(gfx);
218 245
219 const int world_size = desc->world_width * desc->world_height; 246 const int world_size = desc->world_width * desc->world_height;
220 const size_t map_size_bytes = sizeof(Tm_Map) + (world_size * sizeof(Tile)); 247 const size_t map_size_bytes = sizeof(Tm_Map) + (world_size * sizeof(Tile));
@@ -229,43 +256,47 @@ void isogfx_make_world(IsoGfx* iso, const WorldDesc* desc) {
229 (desc->num_tiles * sizeof(Ts_Tile)) + 256 (desc->num_tiles * sizeof(Ts_Tile)) +
230 tile_data_size_bytes; 257 tile_data_size_bytes;
231 258
232 iso->map = memstack_alloc_aligned(&iso->stack, map_size_bytes, 4); 259 gfx->map = memstack_alloc_aligned(&gfx->stack, map_size_bytes, 4);
233 *iso->map = (Tm_Map){ 260 *gfx->map = (Tm_Map){
234 .world_width = desc->world_width, 261 .world_width = desc->world_width,
235 .world_height = desc->world_height, 262 .world_height = desc->world_height,
236 .base_tile_width = desc->tile_width, 263 .base_tile_width = desc->tile_width,
237 .base_tile_height = desc->tile_height, 264 .base_tile_height = desc->tile_height,
238 .num_layers = 1, 265 .num_layers = 1,
266 .flags =
267 (desc->orientation == MapOrthogonal) ? Tm_Orthogonal : Tm_Isometric,
239 }; 268 };
240 269
241 iso->tileset = memstack_alloc_aligned(&iso->stack, tileset_size_bytes, 4); 270 gfx->tileset = memstack_alloc_aligned(&gfx->stack, tileset_size_bytes, 4);
242 *iso->tileset = (Ts_TileSet){ 271 *gfx->tileset = (Ts_TileSet){
243 .num_tiles = desc->num_tiles, 272 .num_tiles = desc->num_tiles,
244 }; 273 };
245 274
246 iso->iso_space = make_iso_coord_system(iso->map, &iso->screen); 275 gfx->iso_space = make_iso_coord_system(gfx->map, &gfx->screen);
247} 276}
248 277
249bool isogfx_load_world(IsoGfx* iso, const char* filepath) { 278bool gfx2d_load_map(Gfx2d* gfx, const char* filepath) {
250 assert(iso); 279 assert(gfx);
251 assert(filepath); 280 assert(filepath);
252 281
253 bool success = false; 282 bool success = false;
254 283
255 // Handle recreation by destroying the previous world and sprites. 284 // Handle recreation by destroying the previous world and sprites.
256 isogfx_clear(iso); 285 gfx2d_clear(gfx);
257 286
258 // Load the map. 287 // Load the map.
259 printf("Load tile map: %s\n", filepath); 288 printf("Load tile map: %s\n", filepath);
260 WITH_FILE(filepath, { 289 WITH_FILE(filepath, {
261 const size_t map_size = get_file_size_f(file); 290 const size_t map_size = get_file_size_f(file);
262 iso->map = memstack_alloc_aligned(&iso->stack, map_size, 4); 291 gfx->map = memstack_alloc_aligned(&gfx->stack, map_size, 4);
263 success = read_file_f(file, iso->map); 292 success = read_file_f(file, gfx->map);
264 }); 293 });
265 if (!success) { 294 if (!success) {
266 goto cleanup; 295 goto cleanup;
267 } 296 }
268 Tm_Map* const map = iso->map; 297 Tm_Map* const map = gfx->map;
298
299 printf("Map orientation: %d\n", ((Tm_Flags*)&map->flags)->orientation);
269 300
270 // Load the tile set. 301 // Load the tile set.
271 // 302 //
@@ -279,39 +310,39 @@ bool isogfx_load_world(IsoGfx* iso, const char* filepath) {
279 printf("Load tile set: %s\n", ts_path_cwd); 310 printf("Load tile set: %s\n", ts_path_cwd);
280 WITH_FILE(ts_path_cwd, { 311 WITH_FILE(ts_path_cwd, {
281 const size_t file_size = get_file_size_f(file); 312 const size_t file_size = get_file_size_f(file);
282 iso->tileset = memstack_alloc_aligned(&iso->stack, file_size, 4); 313 gfx->tileset = memstack_alloc_aligned(&gfx->stack, file_size, 4);
283 success = read_file_f(file, iso->tileset); 314 success = read_file_f(file, gfx->tileset);
284 }); 315 });
285 if (!success) { 316 if (!success) {
286 // TODO: Log errors using the log library. 317 // TODO: Log errors using the log library.
287 goto cleanup; 318 goto cleanup;
288 } 319 }
289 const Ts_TileSet* const tileset = iso->tileset; 320 const Ts_TileSet* const tileset = gfx->tileset;
290 printf("Loaded tile set (%u tiles): %s\n", tileset->num_tiles, ts_path_cwd); 321 printf("Loaded tile set (%u tiles): %s\n", tileset->num_tiles, ts_path_cwd);
291 322
292 // TODO: These assertions on input data should be library runtime errors. 323 // TODO: These assertions on input data should be library runtime errors.
293 assert(ts_validate_tileset(tileset)); 324 assert(ts_validate_tileset(tileset));
294 assert(tm_validate_map(map, tileset)); 325 assert(tm_validate_map(map, tileset));
295 326
296 iso->iso_space = make_iso_coord_system(iso->map, &iso->screen); 327 gfx->iso_space = make_iso_coord_system(gfx->map, &gfx->screen);
297 328
298 success = true; 329 success = true;
299 330
300cleanup: 331cleanup:
301 if (!success) { 332 if (!success) {
302 isogfx_clear(iso); 333 gfx2d_clear(gfx);
303 } 334 }
304 return success; 335 return success;
305} 336}
306 337
307int isogfx_world_width(const IsoGfx* iso) { 338int gfx2d_world_width(const Gfx2d* gfx) {
308 assert(iso); 339 assert(gfx);
309 return iso->map->world_width; 340 return gfx->map->world_width;
310} 341}
311 342
312int isogfx_world_height(const IsoGfx* iso) { 343int gfx2d_world_height(const Gfx2d* gfx) {
313 assert(iso); 344 assert(gfx);
314 return iso->map->world_height; 345 return gfx->map->world_height;
315} 346}
316 347
317static void make_tile_from_colour( 348static void make_tile_from_colour(
@@ -339,19 +370,19 @@ static void make_tile_from_colour(
339 } 370 }
340} 371}
341 372
342Tile isogfx_make_tile(IsoGfx* iso, const TileDesc* desc) { 373Tile gfx2d_make_tile(Gfx2d* gfx, const TileDesc* desc) {
343 assert(iso); 374 assert(gfx);
344 assert(desc); 375 assert(desc);
345 // Client must create a world first. 376 // Client must create a world first.
346 assert(iso->map); 377 assert(gfx->map);
347 assert(iso->tileset); 378 assert(gfx->tileset);
348 // Currently, procedural tiles must match the base tile size. 379 // Currently, procedural tiles must match the base tile size.
349 assert(desc->width == iso->map->base_tile_width); 380 assert(desc->width == gfx->map->base_tile_width);
350 assert(desc->height == iso->map->base_tile_height); 381 assert(desc->height == gfx->map->base_tile_height);
351 // Cannot exceed max tiles. 382 // Cannot exceed max tiles.
352 assert(iso->next_tile < iso->tileset->num_tiles); 383 assert(gfx->next_tile < gfx->tileset->num_tiles);
353 384
354 const Tile tile = iso->next_tile++; 385 const Tile tile = gfx->next_tile++;
355 386
356 const size_t tile_size_bytes = desc->width * desc->height * sizeof(Pixel); 387 const size_t tile_size_bytes = desc->width * desc->height * sizeof(Pixel);
357 388
@@ -360,16 +391,16 @@ Tile isogfx_make_tile(IsoGfx* iso, const TileDesc* desc) {
360 assert(desc->width > 0); 391 assert(desc->width > 0);
361 assert(desc->height > 0); 392 assert(desc->height > 0);
362 393
363 Ts_Tile* const ts_tile = ts_tileset_get_tile_mut(iso->tileset, tile); 394 Ts_Tile* const ts_tile = ts_tileset_get_tile_mut(gfx->tileset, tile);
364 395
365 *ts_tile = (Ts_Tile){ 396 *ts_tile = (Ts_Tile){
366 .width = iso->map->base_tile_width, 397 .width = gfx->map->base_tile_width,
367 .height = iso->map->base_tile_height, 398 .height = gfx->map->base_tile_height,
368 .pixels = tile * tile_size_bytes, 399 .pixels = tile * tile_size_bytes,
369 }; 400 };
370 401
371 Pixel* const tile_pixels = 402 Pixel* const tile_pixels =
372 ts_tileset_get_tile_pixels_mut(iso->tileset, tile); 403 ts_tileset_get_tile_pixels_mut(gfx->tileset, tile);
373 make_tile_from_colour(desc->colour, ts_tile, tile_pixels); 404 make_tile_from_colour(desc->colour, ts_tile, tile_pixels);
374 break; 405 break;
375 } 406 }
@@ -387,31 +418,31 @@ Tile isogfx_make_tile(IsoGfx* iso, const TileDesc* desc) {
387 return tile; 418 return tile;
388} 419}
389 420
390void isogfx_set_tile(IsoGfx* iso, int x, int y, Tile tile) { 421void gfx2d_set_tile(Gfx2d* gfx, int x, int y, Tile tile) {
391 assert(iso); 422 assert(gfx);
392 423
393 Tm_Layer* const layer = tm_map_get_layer_mut(iso->map, 0); 424 Tm_Layer* const layer = tm_map_get_layer_mut(gfx->map, 0);
394 Tile* map_tile = tm_layer_get_tile_mut(iso->map, layer, x, y); 425 Tile* map_tile = tm_layer_get_tile_mut(gfx->map, layer, x, y);
395 426
396 *map_tile = tile; 427 *map_tile = tile;
397} 428}
398 429
399void isogfx_set_tiles(IsoGfx* iso, int x0, int y0, int x1, int y1, Tile tile) { 430void gfx2d_set_tiles(Gfx2d* gfx, int x0, int y0, int x1, int y1, Tile tile) {
400 assert(iso); 431 assert(gfx);
401 for (int y = y0; y < y1; ++y) { 432 for (int y = y0; y < y1; ++y) {
402 for (int x = x0; x < x1; ++x) { 433 for (int x = x0; x < x1; ++x) {
403 isogfx_set_tile(iso, x, y, tile); 434 gfx2d_set_tile(gfx, x, y, tile);
404 } 435 }
405 } 436 }
406} 437}
407 438
408SpriteSheet isogfx_load_sprite_sheet(IsoGfx* iso, const char* filepath) { 439SpriteSheet gfx2d_load_sprite_sheet(Gfx2d* gfx, const char* filepath) {
409 assert(iso); 440 assert(gfx);
410 assert(filepath); 441 assert(filepath);
411 442
412 bool success = false; 443 bool success = false;
413 SpriteSheet spriteSheet = 0; 444 SpriteSheet spriteSheet = 0;
414 const size_t watermark = memstack_watermark(&iso->stack); 445 const size_t watermark = memstack_watermark(&gfx->stack);
415 446
416 // Load sprite sheet file. 447 // Load sprite sheet file.
417 printf("Load sprite sheet: %s\n", filepath); 448 printf("Load sprite sheet: %s\n", filepath);
@@ -419,7 +450,7 @@ SpriteSheet isogfx_load_sprite_sheet(IsoGfx* iso, const char* filepath) {
419 WITH_FILE(filepath, { 450 WITH_FILE(filepath, {
420 const size_t file_size = get_file_size_f(file); 451 const size_t file_size = get_file_size_f(file);
421 ss_sheet = 452 ss_sheet =
422 memstack_alloc_aligned(&iso->stack, file_size, alignof(Ss_SpriteSheet)); 453 memstack_alloc_aligned(&gfx->stack, file_size, alignof(Ss_SpriteSheet));
423 success = read_file_f(file, ss_sheet); 454 success = read_file_f(file, ss_sheet);
424 }); 455 });
425 if (!success) { 456 if (!success) {
@@ -432,63 +463,63 @@ SpriteSheet isogfx_load_sprite_sheet(IsoGfx* iso, const char* filepath) {
432cleanup: 463cleanup:
433 if (!success) { 464 if (!success) {
434 if (ss_sheet) { 465 if (ss_sheet) {
435 memstack_set_watermark(&iso->stack, watermark); 466 memstack_set_watermark(&gfx->stack, watermark);
436 } 467 }
437 } 468 }
438 return spriteSheet; 469 return spriteSheet;
439} 470}
440 471
441Sprite isogfx_make_sprite(IsoGfx* iso, SpriteSheet sheet) { 472Sprite gfx2d_make_sprite(Gfx2d* gfx, SpriteSheet sheet) {
442 assert(iso); 473 assert(gfx);
443 assert(sheet); 474 assert(sheet);
444 475
445 // TODO: Remove memstack_alloc() and replace it with a same-name macro that 476 // TODO: Remove memstack_alloc() and replace it with a same-name macro that
446 // calls memstack_alloc_aligned() with sizeof/alignof. No real point in 477 // calls memstack_alloc_aligned() with sizeof/alignof. No real point in
447 // having unaligned allocations. 478 // having unaligned allocations.
448 SpriteInstance* sprite = memstack_alloc_aligned( 479 SpriteInstance* sprite = memstack_alloc_aligned(
449 &iso->stack, sizeof(SpriteInstance), alignof(SpriteInstance)); 480 &gfx->stack, sizeof(SpriteInstance), alignof(SpriteInstance));
450 481
451 sprite->sheet = (const Ss_SpriteSheet*)sheet; 482 sprite->sheet = (const Ss_SpriteSheet*)sheet;
452 sprite->next = iso->head_sprite; 483 sprite->next = gfx->head_sprite;
453 iso->head_sprite = sprite; 484 gfx->head_sprite = sprite;
454 485
455 return (Sprite)sprite; 486 return (Sprite)sprite;
456} 487}
457 488
458void isogfx_set_sprite_position(IsoGfx* iso, Sprite hSprite, int x, int y) { 489void gfx2d_set_sprite_position(Gfx2d* gfx, Sprite hSprite, int x, int y) {
459 assert(iso); 490 assert(gfx);
460 SpriteInstance* sprite = (SpriteInstance*)hSprite; 491 SpriteInstance* sprite = (SpriteInstance*)hSprite;
461 sprite->position.x = x; 492 sprite->position.x = x;
462 sprite->position.y = y; 493 sprite->position.y = y;
463} 494}
464 495
465void isogfx_set_sprite_animation(IsoGfx* iso, Sprite hSprite, int animation) { 496void gfx2d_set_sprite_animation(Gfx2d* gfx, Sprite hSprite, int animation) {
466 assert(iso); 497 assert(gfx);
467 SpriteInstance* sprite = (SpriteInstance*)hSprite; 498 SpriteInstance* sprite = (SpriteInstance*)hSprite;
468 sprite->animation = animation; 499 sprite->animation = animation;
469} 500}
470 501
471void isogfx_update(IsoGfx* iso, double t) { 502void gfx2d_update(Gfx2d* gfx, double t) {
472 assert(iso); 503 assert(gfx);
473 504
474 // If this is the first time update() is called after initialization, just 505 // If this is the first time update() is called after initialization, just
475 // record the starting animation time. 506 // record the starting animation time.
476 if (iso->last_animation_time == 0.0) { 507 if (gfx->last_animation_time == 0.0) {
477 iso->last_animation_time = t; 508 gfx->last_animation_time = t;
478 return; 509 return;
479 } 510 }
480 511
481 if ((t - iso->last_animation_time) >= ANIMATION_UPDATE_DELTA) { 512 if ((t - gfx->last_animation_time) >= ANIMATION_UPDATE_DELTA) {
482 // TODO: Consider linking animated sprites in a separate list so that we 513 // TODO: Consider linking animated sprites in a separate list so that we
483 // only walk over those here and not also the static sprites. 514 // only walk over those here and not also the static sprites.
484 for (SpriteInstance* sprite = iso->head_sprite; sprite; 515 for (SpriteInstance* sprite = gfx->head_sprite; sprite;
485 sprite = sprite->next) { 516 sprite = sprite->next) {
486 const Ss_SpriteSheet* sheet = sprite->sheet; 517 const Ss_SpriteSheet* sheet = sprite->sheet;
487 const Ss_Row* row = ss_get_sprite_sheet_row(sheet, sprite->animation); 518 const Ss_Row* row = ss_get_sprite_sheet_row(sheet, sprite->animation);
488 sprite->frame = (sprite->frame + 1) % row->num_cols; 519 sprite->frame = (sprite->frame + 1) % row->num_cols;
489 } 520 }
490 521
491 iso->last_animation_time = t; 522 gfx->last_animation_time = t;
492 } 523 }
493} 524}
494 525
@@ -496,18 +527,6 @@ void isogfx_update(IsoGfx* iso, double t) {
496// Rendering and picking. 527// Rendering and picking.
497// ----------------------------------------------------------------------------- 528// -----------------------------------------------------------------------------
498 529
499/// Get the screen position of the top diamond-corner of the tile at world
500/// (x,y).
501static ivec2 GetTileScreenOrigin(
502 const CoordSystem iso_space, ivec2 camera, int world_x, int world_y) {
503 const ivec2 vx_offset = ivec2_scale(iso_space.x, world_x);
504 const ivec2 vy_offset = ivec2_scale(iso_space.y, world_y);
505 const ivec2 screen_origin =
506 ivec2_add(iso_space.o, ivec2_add(vx_offset, vy_offset));
507 const ivec2 origin_view_space = ivec2_add(screen_origin, ivec2_neg(camera));
508 return origin_view_space;
509}
510
511static Pixel alpha_blend(Pixel src, Pixel dst) { 530static Pixel alpha_blend(Pixel src, Pixel dst) {
512 if ((src.a == 255) || (dst.a == 0)) { 531 if ((src.a == 255) || (dst.a == 0)) {
513 return src; 532 return src;
@@ -539,6 +558,7 @@ static void draw_rect(
539 Screen* screen, ivec2 top_left, int rect_width, int rect_height, 558 Screen* screen, ivec2 top_left, int rect_width, int rect_height,
540 const Pixel* pixels, const uint8_t* indices) { 559 const Pixel* pixels, const uint8_t* indices) {
541 assert(screen); 560 assert(screen);
561 assert(pixels);
542 562
543#define rect_pixel(X, Y) \ 563#define rect_pixel(X, Y) \
544 (indices ? pixels[indices[Y * rect_width + X]] : pixels[Y * rect_width + X]) 564 (indices ? pixels[indices[Y * rect_width + X]] : pixels[Y * rect_width + X])
@@ -566,137 +586,237 @@ static void draw_rect(
566 } 586 }
567} 587}
568 588
569/// Draw a tile. 589/// Draw a tile in an orthogonal map.
570/// 590static void draw_tile_ortho(Gfx2d* gfx, Tile tile, int x, int y) {
571/// 'screen_origin' is the screen coordinates of the top diamond-corner of the 591 assert(gfx);
572/// tile (the base tile for super tiles). 592 assert(gfx->tileset);
573/// World (0, 0) -> (screen_width / 2, 0). 593 assert(x >= 0);
574static void draw_tile(IsoGfx* iso, ivec2 screen_origin, Tile tile) { 594 assert(y >= 0);
575 assert(iso); 595 assert(x < gfx->map->world_width);
576 assert(iso->tileset); 596 assert(y < gfx->map->world_height);
597
598 const Ts_Tile* pTile = ts_tileset_get_tile(gfx->tileset, tile);
599 const Pixel* pixels = ts_tileset_get_tile_pixels(gfx->tileset, tile);
577 600
578 const Ts_Tile* pTile = ts_tileset_get_tile(iso->tileset, tile); 601 const ivec2 screen_origin = map2screen(
579 const Pixel* pixels = ts_tileset_get_tile_pixels(iso->tileset, tile); 602 gfx->camera, gfx->map->base_tile_width, gfx->map->base_tile_height, x, y);
603
604 draw_rect(
605 &gfx->screen, screen_origin, pTile->width, pTile->height, pixels,
606 nullptr);
607}
608
609/// Draw a tile in an isometric map.
610static void draw_tile_iso(Gfx2d* gfx, Tile tile, int iso_x, int iso_y) {
611 assert(gfx);
612 assert(gfx->tileset);
613 assert(iso_x >= 0);
614 assert(iso_y >= 0);
615 assert(iso_x < gfx->map->world_width);
616 assert(iso_y < gfx->map->world_height);
617
618 const Ts_Tile* pTile = ts_tileset_get_tile(gfx->tileset, tile);
619 const Pixel* pixels = ts_tileset_get_tile_pixels(gfx->tileset, tile);
620
621 // Compute the screen coordinates of the top diamond-corner of the tile (the
622 // base tile for super tiles).
623 // World (0, 0) -> (screen_width / 2, 0).
624 const ivec2 screen_origin =
625 iso2cart(gfx->iso_space, gfx->camera, iso_x, iso_y);
580 626
581 // Move from the top diamond-corner to the top-left corner of the tile image. 627 // Move from the top diamond-corner to the top-left corner of the tile image.
582 // For regular tiles, tile height == base tile height, so the y offset is 0. 628 // For regular tiles, tile height == base tile height, so the y offset is 0.
583 // For super tiles, move as high up as the height of the tile. 629 // For super tiles, move as high up as the height of the tile.
584 const ivec2 offset = { 630 const ivec2 offset = {
585 -(iso->map->base_tile_width / 2), 631 -(gfx->map->base_tile_width / 2),
586 pTile->height - iso->map->base_tile_height}; 632 pTile->height - gfx->map->base_tile_height};
587 const ivec2 top_left = ivec2_add(screen_origin, offset); 633 const ivec2 top_left = ivec2_add(screen_origin, offset);
588 634
589 draw_rect( 635 draw_rect(
590 &iso->screen, top_left, pTile->width, pTile->height, pixels, nullptr); 636 &gfx->screen, top_left, pTile->width, pTile->height, pixels, nullptr);
591} 637}
592 638
593static void draw_world(IsoGfx* iso) { 639static void draw_map_ortho(Gfx2d* gfx) {
594 assert(iso); 640 assert(gfx);
641 assert(gfx->map);
642
643 // TODO: Same TODOs as in draw_map_iso().
644
645 const Tm_Layer* layer = tm_map_get_layer(gfx->map, 0);
595 646
596 const int W = iso->screen.width; 647 for (int wy = 0; wy < gfx->map->world_height; ++wy) {
597 const int H = iso->screen.height; 648 for (int wx = 0; wx < gfx->map->world_width; ++wx) {
649 const Tile tile = tm_layer_get_tile(gfx->map, layer, wx, wy);
650 draw_tile_ortho(gfx, tile, wx, wy);
651 }
652 }
653}
598 654
599 memset(iso->screen.pixels, 0, W * H * sizeof(Pixel)); 655static void draw_map_iso(Gfx2d* gfx) {
656 assert(gfx);
657 assert(gfx->map);
600 658
601 const Tm_Layer* layer = tm_map_get_layer(iso->map, 0); 659 // TODO: Support for multiple layers.
660 const Tm_Layer* layer = tm_map_get_layer(gfx->map, 0);
602 661
603 // TODO: Culling. 662 // TODO: Culling.
604 // Ex: map the screen corners to tile space to cull. 663 // Ex: map the screen corners to tile space to cull.
605 // Ex: walk in screen space and fetch the tile. 664 // Ex: walk in screen space and fetch the tile.
606 // The tile-centric approach might be more cache-friendly since the 665 // The tile-centric approach might be more cache-friendly since the
607 // screen-centric approach would juggle multiple tiles throughout the scan. 666 // screen-centric approach would juggle multiple tiles throughout the scan.
608 for (int wy = 0; wy < iso->map->world_height; ++wy) { 667 for (int wy = 0; wy < gfx->map->world_height; ++wy) {
609 for (int wx = 0; wx < iso->map->world_width; ++wx) { 668 for (int wx = 0; wx < gfx->map->world_width; ++wx) {
610 const Tile tile = tm_layer_get_tile(iso->map, layer, wx, wy); 669 const Tile tile = tm_layer_get_tile(gfx->map, layer, wx, wy);
611 const ivec2 screen_origin = 670 draw_tile_iso(gfx, tile, wx, wy);
612 GetTileScreenOrigin(iso->iso_space, iso->camera, wx, wy);
613 draw_tile(iso, screen_origin, tile);
614 } 671 }
615 } 672 }
616} 673}
617 674
618static void draw_sprite( 675static void draw_map(Gfx2d* gfx) {
619 IsoGfx* iso, ivec2 origin, const SpriteInstance* sprite, 676 assert(gfx);
620 const Ss_SpriteSheet* sheet) { 677 assert(gfx->map);
621 assert(iso); 678 assert(gfx->screen.pixels);
679
680 const int W = gfx->screen.width;
681 const int H = gfx->screen.height;
682
683 memset(gfx->screen.pixels, 0, W * H * sizeof(Pixel));
684
685 const Tm_Flags* flags = (const Tm_Flags*)&gfx->map->flags;
686 switch (flags->orientation) {
687 case Tm_Orthogonal:
688 draw_map_ortho(gfx);
689 break;
690 case Tm_Isometric:
691 draw_map_iso(gfx);
692 break;
693 }
694}
695
696/// Draw a sprite in an orthogonal/Cartesian coordinate system.
697static void draw_sprite_ortho(
698 Gfx2d* gfx, const SpriteInstance* sprite, const Ss_SpriteSheet* sheet) {
699 assert(gfx);
622 assert(sprite); 700 assert(sprite);
623 assert(sheet); 701 assert(sheet);
624 assert(sprite->animation >= 0); 702 assert(sprite->animation >= 0);
625 assert(sprite->animation < sheet->num_rows); 703 assert(sprite->animation < sheet->num_rows);
626 assert(sprite->frame >= 0); 704 assert(sprite->frame >= 0);
627 705
706 // Apply an offset similarly to how we offset tiles. The sprite is offset by
707 // -base_tile_width/2 along the x-axis to align the sprite with the leftmost
708 // edge of the tile it is on.
709 const ivec2 screen_origin = map2screen(
710 gfx->camera, gfx->map->base_tile_width, gfx->map->base_tile_height,
711 sprite->position.x, sprite->position.y);
712
628 const Ss_Row* row = ss_get_sprite_sheet_row(sheet, sprite->animation); 713 const Ss_Row* row = ss_get_sprite_sheet_row(sheet, sprite->animation);
629 const uint8_t* frame = ss_get_sprite_sheet_sprite(sheet, row, sprite->frame); 714 const uint8_t* frame = ss_get_sprite_sheet_sprite(sheet, row, sprite->frame);
630 draw_rect( 715 draw_rect(
631 &iso->screen, origin, sheet->sprite_width, sheet->sprite_height, 716 &gfx->screen, screen_origin, sheet->sprite_width, sheet->sprite_height,
632 sheet->palette.colours, frame); 717 sheet->palette.colours, frame);
633} 718}
634 719
635static void draw_sprites(IsoGfx* iso) { 720/// Draw a sprite in an isometric coordinate system.
636 assert(iso); 721static void draw_sprite_iso(
722 Gfx2d* gfx, const SpriteInstance* sprite, const Ss_SpriteSheet* sheet) {
723 assert(gfx);
724 assert(sprite);
725 assert(sheet);
726 assert(sprite->animation >= 0);
727 assert(sprite->animation < sheet->num_rows);
728 assert(sprite->frame >= 0);
729
730 // Apply an offset similarly to how we offset tiles. The sprite is offset by
731 // -base_tile_width/2 along the x-axis to align the sprite with the leftmost
732 // edge of the tile it is on.
733 const ivec2 screen_origin = iso2cart(
734 gfx->iso_space, gfx->camera, sprite->position.x, sprite->position.y);
735 const ivec2 offset = {-(gfx->map->base_tile_width / 2), 0};
736 const ivec2 top_left = ivec2_add(screen_origin, offset);
737
738 const Ss_Row* row = ss_get_sprite_sheet_row(sheet, sprite->animation);
739 const uint8_t* frame = ss_get_sprite_sheet_sprite(sheet, row, sprite->frame);
740 draw_rect(
741 &gfx->screen, top_left, sheet->sprite_width, sheet->sprite_height,
742 sheet->palette.colours, frame);
743}
637 744
638 for (const SpriteInstance* sprite = iso->head_sprite; sprite; 745static void draw_sprites(Gfx2d* gfx) {
639 sprite = sprite->next) { 746 assert(gfx);
640 const Ss_SpriteSheet* sheet = sprite->sheet; 747 assert(gfx->map);
641 assert(sheet);
642 748
643 const ivec2 screen_origin = GetTileScreenOrigin( 749 const Tm_Flags* flags = (const Tm_Flags*)&gfx->map->flags;
644 iso->iso_space, iso->camera, sprite->position.x, sprite->position.y); 750 switch (flags->orientation) {
645 draw_sprite(iso, screen_origin, sprite, sheet); 751 case Tm_Orthogonal:
752 for (const SpriteInstance* sprite = gfx->head_sprite; sprite;
753 sprite = sprite->next) {
754 draw_sprite_ortho(gfx, sprite, sprite->sheet);
755 }
756 break;
757 case Tm_Isometric:
758 for (const SpriteInstance* sprite = gfx->head_sprite; sprite;
759 sprite = sprite->next) {
760 draw_sprite_iso(gfx, sprite, sprite->sheet);
761 }
762 break;
646 } 763 }
647} 764}
648 765
649void isogfx_set_camera(IsoGfx* iso, int x, int y) { 766void gfx2d_set_camera(Gfx2d* gfx, int x, int y) {
650 assert(iso); 767 assert(gfx);
651 iso->camera = (ivec2){x, y}; 768 gfx->camera = (ivec2){x, y};
652} 769}
653 770
654void isogfx_render(IsoGfx* iso) { 771void gfx2d_render(Gfx2d* gfx) {
655 assert(iso); 772 assert(gfx);
656 draw_world(iso); 773 draw_map(gfx);
657 draw_sprites(iso); 774 draw_sprites(gfx);
658} 775}
659 776
660void isogfx_draw_tile(IsoGfx* iso, int x, int y, Tile tile) { 777void gfx2d_draw_tile(Gfx2d* gfx, int x, int y, Tile tile) {
661 assert(iso); 778 assert(gfx);
662 assert(x >= 0); 779 assert(gfx->map);
663 assert(y >= 0);
664 assert(x < iso->map->world_width);
665 assert(y < iso->map->world_height);
666 780
667 const ivec2 screen_origin = 781 const Tm_Flags* flags = (const Tm_Flags*)&gfx->map->flags;
668 GetTileScreenOrigin(iso->iso_space, iso->camera, x, y); 782 switch (flags->orientation) {
669 draw_tile(iso, screen_origin, tile); 783 case Tm_Orthogonal:
784 draw_tile_ortho(gfx, tile, x, y);
785 break;
786 case Tm_Isometric:
787 draw_tile_iso(gfx, tile, x, y);
788 break;
789 }
670} 790}
671 791
672void isogfx_get_screen_size(const IsoGfx* iso, int* width, int* height) { 792void gfx2d_get_screen_size(const Gfx2d* gfx, int* width, int* height) {
673 assert(iso); 793 assert(gfx);
674 assert(width); 794 assert(width);
675 assert(height); 795 assert(height);
676 *width = iso->screen.width; 796 *width = gfx->screen.width;
677 *height = iso->screen.height; 797 *height = gfx->screen.height;
678} 798}
679 799
680const Pixel* isogfx_get_screen_buffer(const IsoGfx* iso) { 800const Pixel* gfx2d_get_screen_buffer(const Gfx2d* gfx) {
681 assert(iso); 801 assert(gfx);
682 return iso->screen.pixels; 802 return gfx->screen.pixels;
683} 803}
684 804
685void isogfx_pick_tile( 805void gfx2d_pick_tile(
686 const IsoGfx* iso, double xcart, double ycart, int* xiso, int* yiso) { 806 const Gfx2d* gfx, double xcart, double ycart, int* xiso, int* yiso) {
687 assert(iso); 807 assert(gfx);
688 assert(xiso); 808 assert(xiso);
689 assert(yiso); 809 assert(yiso);
690 810
691 const vec2 camera = ivec2_to_vec2(iso->camera); 811 const vec2 camera = ivec2_to_vec2(gfx->camera);
692 const vec2 xy_cart = vec2_add(camera, (vec2){xcart, ycart}); 812 const vec2 xy_cart = vec2_add(camera, (vec2){xcart, ycart});
693 813
694 const vec2 xy_iso = cart2iso( 814 const vec2 xy_iso = cart2iso(
695 xy_cart, iso->map->base_tile_width, iso->map->base_tile_height, 815 xy_cart, gfx->map->base_tile_width, gfx->map->base_tile_height,
696 iso->screen.width); 816 gfx->screen.width);
697 817
698 if ((0 <= xy_iso.x) && (xy_iso.x < iso->map->world_width) && 818 if ((0 <= xy_iso.x) && (xy_iso.x < gfx->map->world_width) &&
699 (0 <= xy_iso.y) && (xy_iso.y < iso->map->world_height)) { 819 (0 <= xy_iso.y) && (xy_iso.y < gfx->map->world_height)) {
700 *xiso = (int)xy_iso.x; 820 *xiso = (int)xy_iso.x;
701 *yiso = (int)xy_iso.y; 821 *yiso = (int)xy_iso.y;
702 } else { 822 } else {