summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--demos/checkerboard/checkerboard.c65
-rw-r--r--demos/isomap/isomap.c50
-rw-r--r--include/isogfx/backend.h29
-rw-r--r--include/isogfx/gfx2d.h70
-rw-r--r--src/backend.c28
-rw-r--r--src/gfx2d.c520
-rw-r--r--tools/mkasset.py7
7 files changed, 449 insertions, 320 deletions
diff --git a/demos/checkerboard/checkerboard.c b/demos/checkerboard/checkerboard.c
index 4f43526..243f7a8 100644
--- a/demos/checkerboard/checkerboard.c
+++ b/demos/checkerboard/checkerboard.c
@@ -47,21 +47,21 @@ typedef enum Colour {
47} Colour; 47} Colour;
48 48
49typedef struct GfxAppState { 49typedef struct GfxAppState {
50 IsoBackend* backend; 50 Gfx2dBackend* backend;
51 IsoGfx* iso; 51 Gfx2d* gfx;
52 Tile red; 52 Tile red;
53 int xpick; 53 int xpick;
54 int ypick; 54 int ypick;
55} GfxAppState; 55} GfxAppState;
56 56
57static void make_checkerboard(IsoGfx* iso, Tile black, Tile white) { 57static void make_checkerboard(Gfx2d* iso, Tile black, Tile white) {
58 assert(iso); 58 assert(iso);
59 for (int y = 0; y < isogfx_world_height(iso); ++y) { 59 for (int y = 0; y < gfx2d_world_height(iso); ++y) {
60 for (int x = 0; x < isogfx_world_width(iso); ++x) { 60 for (int x = 0; x < gfx2d_world_width(iso); ++x) {
61 const int odd_col = x & 1; 61 const int odd_col = x & 1;
62 const int odd_row = y & 1; 62 const int odd_row = y & 1;
63 const Tile value = (odd_row ^ odd_col) == 0 ? black : white; 63 const Tile value = (odd_row ^ odd_col) == 0 ? black : white;
64 isogfx_set_tile(iso, x, y, value); 64 gfx2d_set_tile(iso, x, y, value);
65 } 65 }
66 } 66 }
67} 67}
@@ -72,28 +72,29 @@ static bool init(GfxApp* app, GfxAppState* state, int argc, const char** argv) {
72 (void)argc; 72 (void)argc;
73 (void)argv; 73 (void)argv;
74 74
75 if (!((state->iso = 75 if (!((state->gfx =
76 isogfx_new(&(IsoGfxDesc){.memory = MEMORY, 76 gfx2d_new(&(Gfx2dDesc){.memory = MEMORY,
77 .memory_size = MEMORY_SIZE, 77 .memory_size = MEMORY_SIZE,
78 .screen_width = SCREEN_WIDTH, 78 .screen_width = SCREEN_WIDTH,
79 .screen_height = SCREEN_HEIGHT})))) { 79 .screen_height = SCREEN_HEIGHT})))) {
80 return false; 80 return false;
81 } 81 }
82 IsoGfx* iso = state->iso; 82 Gfx2d* iso = state->gfx;
83 83
84 isogfx_make_map( 84 gfx2d_make_map(
85 iso, &(MapDesc){.tile_width = TILE_WIDTH, 85 iso, &(MapDesc){.tile_width = TILE_WIDTH,
86 .tile_height = TILE_HEIGHT, 86 .tile_height = TILE_HEIGHT,
87 .world_width = WORLD_WIDTH, 87 .world_width = WORLD_WIDTH,
88 .world_height = WORLD_HEIGHT, 88 .world_height = WORLD_HEIGHT,
89 .num_tiles = NUM_TILES}); 89 .num_tiles = NUM_TILES,
90 .orientation = MapIsometric});
90 91
91 const Tile black = isogfx_make_tile(iso, &tile_set[Black]); 92 const Tile black = gfx2d_make_tile(iso, &tile_set[Black]);
92 const Tile white = isogfx_make_tile(iso, &tile_set[White]); 93 const Tile white = gfx2d_make_tile(iso, &tile_set[White]);
93 state->red = isogfx_make_tile(iso, &tile_set[Red]); 94 state->red = gfx2d_make_tile(iso, &tile_set[Red]);
94 make_checkerboard(iso, black, white); 95 make_checkerboard(iso, black, white);
95 96
96 if (!((state->backend = iso_backend_init(iso)))) { 97 if (!((state->backend = gfx2d_backend_init(iso)))) {
97 return false; 98 return false;
98 } 99 }
99 100
@@ -104,8 +105,8 @@ static void shutdown(GfxApp* app, GfxAppState* state) {
104 assert(app); 105 assert(app);
105 assert(state); 106 assert(state);
106 107
107 iso_backend_shutdown(&state->backend); 108 gfx2d_backend_shutdown(&state->backend);
108 isogfx_del(&state->iso); 109 gfx2d_del(&state->gfx);
109} 110}
110 111
111static void update(GfxApp* app, GfxAppState* state, double t, double dt) { 112static void update(GfxApp* app, GfxAppState* state, double t, double dt) {
@@ -113,19 +114,19 @@ static void update(GfxApp* app, GfxAppState* state, double t, double dt) {
113 assert(state); 114 assert(state);
114 (void)dt; 115 (void)dt;
115 116
116 IsoGfx* iso = state->iso; 117 Gfx2d* iso = state->gfx;
117 118
118 isogfx_update(iso, t); 119 gfx2d_update(iso, t);
119 120
120 // Get mouse position in window coordinates. 121 // Get mouse position in window coordinates.
121 double mouse_x, mouse_y; 122 double mouse_x, mouse_y;
122 gfx_app_get_mouse_position(app, &mouse_x, &mouse_y); 123 gfx_app_get_mouse_position(app, &mouse_x, &mouse_y);
123 124
124 // Map from window coordinates to virtual screen coordinates. 125 // Map from window coordinates to virtual screen coordinates.
125 iso_backend_get_mouse_position( 126 gfx2d_backend_get_mouse_position(
126 state->backend, mouse_x, mouse_y, &mouse_x, &mouse_y); 127 state->backend, mouse_x, mouse_y, &mouse_x, &mouse_y);
127 128
128 isogfx_pick_tile(iso, mouse_x, mouse_y, &state->xpick, &state->ypick); 129 gfx2d_pick_tile(iso, mouse_x, mouse_y, &state->xpick, &state->ypick);
129 130
130 printf("Picked tile: (%d, %d)\n", state->xpick, state->ypick); 131 printf("Picked tile: (%d, %d)\n", state->xpick, state->ypick);
131} 132}
@@ -134,22 +135,22 @@ static void render(const GfxApp* app, GfxAppState* state) {
134 assert(app); 135 assert(app);
135 assert(state); 136 assert(state);
136 137
137 IsoGfx* iso = state->iso; 138 Gfx2d* iso = state->gfx;
138 139
139 isogfx_render(iso); 140 gfx2d_render(iso);
140 141
141 if ((state->xpick != -1) && (state->ypick != -1)) { 142 if ((state->xpick != -1) && (state->ypick != -1)) {
142 isogfx_draw_tile(iso, state->xpick, state->ypick, state->red); 143 gfx2d_draw_tile(iso, state->xpick, state->ypick, state->red);
143 } 144 }
144 145
145 iso_backend_render(state->backend, iso); 146 gfx2d_backend_render(state->backend, iso);
146} 147}
147 148
148static void resize(GfxApp* app, GfxAppState* state, int width, int height) { 149static void resize(GfxApp* app, GfxAppState* state, int width, int height) {
149 assert(app); 150 assert(app);
150 assert(state); 151 assert(state);
151 152
152 iso_backend_resize_window(state->backend, state->iso, width, height); 153 gfx2d_backend_resize_window(state->backend, state->gfx, width, height);
153} 154}
154 155
155int main(int argc, const char** argv) { 156int main(int argc, const char** argv) {
diff --git a/demos/isomap/isomap.c b/demos/isomap/isomap.c
index b9b6568..471ef57 100644
--- a/demos/isomap/isomap.c
+++ b/demos/isomap/isomap.c
@@ -22,13 +22,13 @@ static const R CAMERA_SPEED = 800;
22uint8_t MEMORY[MEMORY_SIZE]; 22uint8_t MEMORY[MEMORY_SIZE];
23 23
24typedef struct GfxAppState { 24typedef struct GfxAppState {
25 IsoBackend* backend; 25 Gfx2dBackend* backend;
26 IsoGfx* iso; 26 Gfx2d* gfx;
27 int xpick; 27 int xpick;
28 int ypick; 28 int ypick;
29 vec2 camera; 29 vec2 camera;
30 SpriteSheet stag_sheet; 30 SpriteSheet stag_sheet;
31 Sprite stag; 31 Sprite stag;
32} GfxAppState; 32} GfxAppState;
33 33
34static bool init(GfxApp* app, GfxAppState* state, int argc, const char** argv) { 34static bool init(GfxApp* app, GfxAppState* state, int argc, const char** argv) {
@@ -37,31 +37,31 @@ static bool init(GfxApp* app, GfxAppState* state, int argc, const char** argv) {
37 (void)argc; 37 (void)argc;
38 (void)argv; 38 (void)argv;
39 39
40 if (!((state->iso = 40 if (!((state->gfx =
41 isogfx_new(&(IsoGfxDesc){.memory = MEMORY, 41 gfx2d_new(&(Gfx2dDesc){.memory = MEMORY,
42 .memory_size = MEMORY_SIZE, 42 .memory_size = MEMORY_SIZE,
43 .screen_width = SCREEN_WIDTH, 43 .screen_width = SCREEN_WIDTH,
44 .screen_height = SCREEN_HEIGHT})))) { 44 .screen_height = SCREEN_HEIGHT})))) {
45 return false; 45 return false;
46 } 46 }
47 IsoGfx* iso = state->iso; 47 Gfx2d* iso = state->gfx;
48 48
49 if (!isogfx_load_map( 49 if (!gfx2d_load_map(
50 iso, "/home/jeanne/Nextcloud/assets/tilemaps/scrabling1.tm")) { 50 iso, "/home/jeanne/Nextcloud/assets/tilemaps/scrabling1.tm")) {
51 return false; 51 return false;
52 } 52 }
53 53
54 if (!((state->stag_sheet = isogfx_load_sprite_sheet( 54 if (!((state->stag_sheet = gfx2d_load_sprite_sheet(
55 iso, 55 iso,
56 "/home/jeanne/Nextcloud/assets/tilesets/scrabling/critters/stag/" 56 "/home/jeanne/Nextcloud/assets/tilesets/scrabling/critters/stag/"
57 "stag.ss")))) { 57 "stag.ss")))) {
58 return false; 58 return false;
59 } 59 }
60 60
61 state->stag = isogfx_make_sprite(iso, state->stag_sheet); 61 state->stag = gfx2d_make_sprite(iso, state->stag_sheet);
62 isogfx_set_sprite_position(iso, state->stag, 5, 4); 62 gfx2d_set_sprite_position(iso, state->stag, 0, 0);
63 63
64 if (!((state->backend = iso_backend_init(iso)))) { 64 if (!((state->backend = gfx2d_backend_init(iso)))) {
65 return false; 65 return false;
66 } 66 }
67 67
@@ -101,25 +101,25 @@ static void update(GfxApp* app, GfxAppState* state, double t, double dt) {
101 101
102 state->camera = vec2_add(state->camera, get_camera_movement(app, (R)dt)); 102 state->camera = vec2_add(state->camera, get_camera_movement(app, (R)dt));
103 103
104 IsoGfx* iso = state->iso; 104 Gfx2d* iso = state->gfx;
105 isogfx_set_camera(iso, (int)state->camera.x, (int)state->camera.y); 105 gfx2d_set_camera(iso, (int)state->camera.x, (int)state->camera.y);
106 isogfx_update(iso, t); 106 gfx2d_update(iso, t);
107} 107}
108 108
109static void render(const GfxApp* app, GfxAppState* state) { 109static void render(const GfxApp* app, GfxAppState* state) {
110 assert(app); 110 assert(app);
111 assert(state); 111 assert(state);
112 112
113 IsoGfx* iso = state->iso; 113 Gfx2d* iso = state->gfx;
114 isogfx_render(iso); 114 gfx2d_render(iso);
115 iso_backend_render(state->backend, iso); 115 gfx2d_backend_render(state->backend, iso);
116} 116}
117 117
118static void resize(GfxApp* app, GfxAppState* state, int width, int height) { 118static void resize(GfxApp* app, GfxAppState* state, int width, int height) {
119 assert(app); 119 assert(app);
120 assert(state); 120 assert(state);
121 121
122 iso_backend_resize_window(state->backend, state->iso, width, height); 122 gfx2d_backend_resize_window(state->backend, state->gfx, width, height);
123} 123}
124 124
125int main(int argc, const char** argv) { 125int main(int argc, const char** argv) {
diff --git a/include/isogfx/backend.h b/include/isogfx/backend.h
index 76ee13d..86afed5 100644
--- a/include/isogfx/backend.h
+++ b/include/isogfx/backend.h
@@ -2,28 +2,29 @@
2 2
3#include <stdbool.h> 3#include <stdbool.h>
4 4
5typedef struct Gfx Gfx; 5typedef struct Gfx Gfx;
6typedef struct IsoGfx IsoGfx; 6typedef struct Gfx2d Gfx2d;
7 7
8typedef struct IsoBackend IsoBackend; 8typedef struct Gfx2dBackend Gfx2dBackend;
9 9
10/// Initialize the backend. 10/// Initialize the backend.
11IsoBackend* iso_backend_init(const IsoGfx*); 11Gfx2dBackend* gfx2d_backend_init(const Gfx2d*);
12 12
13/// Shut down the backend. 13/// Shut down the backend.
14void iso_backend_shutdown(IsoBackend**); 14void gfx2d_backend_shutdown(Gfx2dBackend**);
15 15
16/// Notify the backend of a window resize event. 16/// Notify the backend of a window resize event.
17/// This allows the backend to determine how to position and scale the iso 17/// This allows the backend to determine how to position and scale the gfx
18/// screen buffer on the graphics window. 18/// screen buffer on the graphics window.
19void iso_backend_resize_window( 19void gfx2d_backend_resize_window(
20 IsoBackend*, const IsoGfx*, int width, int height); 20 Gfx2dBackend*, const Gfx2d*, int width, int height);
21 21
22/// Render the iso screen to the graphics window. 22/// Render the gfx screen to the graphics window.
23void iso_backend_render(const IsoBackend*, const IsoGfx*); 23void gfx2d_backend_render(const Gfx2dBackend*, const Gfx2d*);
24 24
25/// Map window coordinates to iso space coordinates. 25/// Map window coordinates to gfx space coordinates.
26/// This takes into account any possible resizing done by the backend in 26/// This takes into account any possible resizing done by the backend in
27/// response to calls to iso_backend_resize_window(). 27/// response to calls to gfx2d_backend_resize_window().
28bool iso_backend_get_mouse_position( 28bool gfx2d_backend_get_mouse_position(
29 const IsoBackend*, double window_x, double window_y, double* x, double* y); 29 const Gfx2dBackend*, double window_x, double window_y, double* x,
30 double* y);
diff --git a/include/isogfx/gfx2d.h b/include/isogfx/gfx2d.h
index 323b389..a3ddbb6 100644
--- a/include/isogfx/gfx2d.h
+++ b/include/isogfx/gfx2d.h
@@ -8,7 +8,7 @@
8#include <stddef.h> 8#include <stddef.h>
9#include <stdint.h> 9#include <stdint.h>
10 10
11typedef struct IsoGfx IsoGfx; 11typedef struct Gfx2d Gfx2d;
12 12
13/// Sprite sheet handle. 13/// Sprite sheet handle.
14typedef uintptr_t SpriteSheet; 14typedef uintptr_t SpriteSheet;
@@ -37,12 +37,18 @@ typedef struct TileDesc {
37 }; 37 };
38} TileDesc; 38} TileDesc;
39 39
40typedef enum MapOrientation {
41 MapOrthogonal,
42 MapIsometric,
43} MapOrientation;
44
40typedef struct MapDesc { 45typedef struct MapDesc {
41 int tile_width; // Base tile width in pixels. 46 int tile_width; // Base tile width in pixels.
42 int tile_height; // Base tile height in pixels. 47 int tile_height; // Base tile height in pixels.
43 int world_width; // World width in tiles. 48 int world_width; // World width in tiles.
44 int world_height; // World height in tiles. 49 int world_height; // World height in tiles.
45 int num_tiles; // Number of tiles to allocate memory for. 50 int num_tiles; // Number of tiles to allocate memory for.
51 MapOrientation orientation; // Map orientation.
46} MapDesc; 52} MapDesc;
47 53
48typedef struct IsoGfxDesc { 54typedef struct IsoGfxDesc {
@@ -50,43 +56,43 @@ typedef struct IsoGfxDesc {
50 size_t memory_size; // Size of memory block in bytes. 56 size_t memory_size; // Size of memory block in bytes.
51 int screen_width; // Screen width in pixels. 57 int screen_width; // Screen width in pixels.
52 int screen_height; // Screen height in pixels. 58 int screen_height; // Screen height in pixels.
53} IsoGfxDesc; 59} Gfx2dDesc;
54 60
55/// Create a new isometric graphics engine. 61/// Create a new isometric graphics engine.
56IsoGfx* isogfx_new(const IsoGfxDesc*); 62Gfx2d* gfx2d_new(const Gfx2dDesc*);
57 63
58/// Destroy the isometric graphics engine. 64/// Destroy the isometric graphics engine.
59void isogfx_del(IsoGfx**); 65void gfx2d_del(Gfx2d**);
60 66
61/// Clear all loaded worlds and sprites. 67/// Clear all loaded worlds and sprites.
62void isogfx_clear(IsoGfx*); 68void gfx2d_clear(Gfx2d*);
63 69
64/// Create an empty map. 70/// Create an empty map.
65void isogfx_make_map(IsoGfx*, const MapDesc*); 71void gfx2d_make_map(Gfx2d*, const MapDesc*);
66 72
67/// Load a tile map (.TM) file. 73/// Load a tile map (.TM) file.
68bool isogfx_load_map(IsoGfx*, const char* filepath); 74bool gfx2d_load_map(Gfx2d*, const char* filepath);
69 75
70/// Return the world's width. 76/// Return the world's width.
71int isogfx_world_width(const IsoGfx*); 77int gfx2d_world_width(const Gfx2d*);
72 78
73/// Return the world's height. 79/// Return the world's height.
74int isogfx_world_height(const IsoGfx*); 80int gfx2d_world_height(const Gfx2d*);
75 81
76/// Create a new tile. 82/// Create a new tile.
77Tile isogfx_make_tile(IsoGfx*, const TileDesc*); 83Tile gfx2d_make_tile(Gfx2d*, const TileDesc*);
78 84
79/// Set the tile at position (x,y). 85/// Set the tile at position (x,y).
80void isogfx_set_tile(IsoGfx*, int x, int y, Tile); 86void gfx2d_set_tile(Gfx2d*, int x, int y, Tile);
81 87
82/// Set the tiles in positions in the range (x0,y0) - (x1,y1). 88/// Set the tiles in positions in the range (x0,y0) - (x1,y1).
83void isogfx_set_tiles(IsoGfx*, int x0, int y0, int x1, int y1, Tile); 89void gfx2d_set_tiles(Gfx2d*, int x0, int y0, int x1, int y1, Tile);
84 90
85/// Load a sprite sheet (.SS) file. 91/// Load a sprite sheet (.SS) file.
86SpriteSheet isogfx_load_sprite_sheet(IsoGfx*, const char* filepath); 92SpriteSheet gfx2d_load_sprite_sheet(Gfx2d*, const char* filepath);
87 93
88/// Create an animated sprite. 94/// Create an animated sprite.
89Sprite isogfx_make_sprite(IsoGfx*, SpriteSheet); 95Sprite gfx2d_make_sprite(Gfx2d*, SpriteSheet);
90 96
91// TODO: Add a function to delete a sprite. Making the caller manage and re-use 97// TODO: Add a function to delete a sprite. Making the caller manage and re-use
92// sprites is a shitty API. 98// sprites is a shitty API.
@@ -94,41 +100,41 @@ Sprite isogfx_make_sprite(IsoGfx*, SpriteSheet);
94// list of sprites so that we can re-use the ones that have been "freed". 100// list of sprites so that we can re-use the ones that have been "freed".
95 101
96/// Set the sprite's position. 102/// Set the sprite's position.
97void isogfx_set_sprite_position(IsoGfx*, Sprite, int x, int y); 103void gfx2d_set_sprite_position(Gfx2d*, Sprite, int x, int y);
98 104
99/// Set the sprite's current animation. 105/// Set the sprite's current animation.
100void isogfx_set_sprite_animation(IsoGfx*, Sprite, int animation); 106void gfx2d_set_sprite_animation(Gfx2d*, Sprite, int animation);
101 107
102/// Update the renderer. 108/// Update the renderer.
103/// 109///
104/// Currently, this updates the sprite animations. 110/// Currently, this updates the sprite animations.
105void isogfx_update(IsoGfx*, double t); 111void gfx2d_update(Gfx2d*, double t);
106 112
107// TODO: Do we really need to store the camera in the library? It's not used 113// TODO: Do we really need to store the camera in the library? It's not used
108// for anything other than to render, so we could remove library state and 114// for anything other than to render, so we could remove library state and
109// take a camera argument in render() instead. 115// take a camera argument in render() instead.
110 116
111/// Set the camera. 117/// Set the camera.
112void isogfx_set_camera(IsoGfx*, int x, int y); 118void gfx2d_set_camera(Gfx2d*, int x, int y);
113 119
114/// Render the world. 120/// Render the world.
115void isogfx_render(IsoGfx*); 121void gfx2d_render(Gfx2d*);
116 122
117/// Draw/overlay a tile at position (x,y). 123/// Draw/overlay a tile at position (x,y).
118/// 124///
119/// This function just renders a tile at position (x,y) and should be called 125/// This function just renders a tile at position (x,y) and should be called
120/// after isogfx_render() to obtain the correct result. To set the tile at 126/// after gfx2d_render() to obtain the correct result. To set the tile at
121/// position (x,y) instead, use isogfx_set_tile(). 127/// position (x,y) instead, use gfx2d_set_tile().
122void isogfx_draw_tile(IsoGfx*, int x, int y, Tile); 128void gfx2d_draw_tile(Gfx2d*, int x, int y, Tile);
123 129
124/// Get the virtual screen's dimensions. 130/// Get the virtual screen's dimensions.
125void isogfx_get_screen_size(const IsoGfx*, int* width, int* height); 131void gfx2d_get_screen_size(const Gfx2d*, int* width, int* height);
126 132
127/// Return a pointer to the virtual screen's colour buffer. 133/// Return a pointer to the virtual screen's colour buffer.
128/// 134///
129/// Call after each call to isogfx_render() to retrieve the render output. 135/// Call after each call to gfx2d_render() to retrieve the render output.
130const Pixel* isogfx_get_screen_buffer(const IsoGfx*); 136const Pixel* gfx2d_get_screen_buffer(const Gfx2d*);
131 137
132/// Translate Cartesian to isometric coordinates. 138/// Translate Cartesian to isometric coordinates.
133void isogfx_pick_tile( 139void gfx2d_pick_tile(
134 const IsoGfx*, double xcart, double ycart, int* xiso, int* yiso); 140 const Gfx2d*, double xcart, double ycart, int* xiso, int* yiso);
diff --git a/src/backend.c b/src/backend.c
index 80c5974..4bb3592 100644
--- a/src/backend.c
+++ b/src/backend.c
@@ -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/gfx2d.c b/src/gfx2d.c
index da265b0..f609c98 100644
--- a/src/gfx2d.c
+++ b/src/gfx2d.c
@@ -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_map(IsoGfx* iso, const MapDesc* 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_map(IsoGfx* iso, const MapDesc* 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,45 @@ void isogfx_make_map(IsoGfx* iso, const MapDesc* 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_map(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;
269 298
270 printf("Map orientation: %d\n", ((Tm_Flags*)&map->flags)->orientation); 299 printf("Map orientation: %d\n", ((Tm_Flags*)&map->flags)->orientation);
271 300
@@ -281,39 +310,39 @@ bool isogfx_load_map(IsoGfx* iso, const char* filepath) {
281 printf("Load tile set: %s\n", ts_path_cwd); 310 printf("Load tile set: %s\n", ts_path_cwd);
282 WITH_FILE(ts_path_cwd, { 311 WITH_FILE(ts_path_cwd, {
283 const size_t file_size = get_file_size_f(file); 312 const size_t file_size = get_file_size_f(file);
284 iso->tileset = memstack_alloc_aligned(&iso->stack, file_size, 4); 313 gfx->tileset = memstack_alloc_aligned(&gfx->stack, file_size, 4);
285 success = read_file_f(file, iso->tileset); 314 success = read_file_f(file, gfx->tileset);
286 }); 315 });
287 if (!success) { 316 if (!success) {
288 // TODO: Log errors using the log library. 317 // TODO: Log errors using the log library.
289 goto cleanup; 318 goto cleanup;
290 } 319 }
291 const Ts_TileSet* const tileset = iso->tileset; 320 const Ts_TileSet* const tileset = gfx->tileset;
292 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);
293 322
294 // TODO: These assertions on input data should be library runtime errors. 323 // TODO: These assertions on input data should be library runtime errors.
295 assert(ts_validate_tileset(tileset)); 324 assert(ts_validate_tileset(tileset));
296 assert(tm_validate_map(map, tileset)); 325 assert(tm_validate_map(map, tileset));
297 326
298 iso->iso_space = make_iso_coord_system(iso->map, &iso->screen); 327 gfx->iso_space = make_iso_coord_system(gfx->map, &gfx->screen);
299 328
300 success = true; 329 success = true;
301 330
302cleanup: 331cleanup:
303 if (!success) { 332 if (!success) {
304 isogfx_clear(iso); 333 gfx2d_clear(gfx);
305 } 334 }
306 return success; 335 return success;
307} 336}
308 337
309int isogfx_world_width(const IsoGfx* iso) { 338int gfx2d_world_width(const Gfx2d* gfx) {
310 assert(iso); 339 assert(gfx);
311 return iso->map->world_width; 340 return gfx->map->world_width;
312} 341}
313 342
314int isogfx_world_height(const IsoGfx* iso) { 343int gfx2d_world_height(const Gfx2d* gfx) {
315 assert(iso); 344 assert(gfx);
316 return iso->map->world_height; 345 return gfx->map->world_height;
317} 346}
318 347
319static void make_tile_from_colour( 348static void make_tile_from_colour(
@@ -341,19 +370,19 @@ static void make_tile_from_colour(
341 } 370 }
342} 371}
343 372
344Tile isogfx_make_tile(IsoGfx* iso, const TileDesc* desc) { 373Tile gfx2d_make_tile(Gfx2d* gfx, const TileDesc* desc) {
345 assert(iso); 374 assert(gfx);
346 assert(desc); 375 assert(desc);
347 // Client must create a world first. 376 // Client must create a world first.
348 assert(iso->map); 377 assert(gfx->map);
349 assert(iso->tileset); 378 assert(gfx->tileset);
350 // Currently, procedural tiles must match the base tile size. 379 // Currently, procedural tiles must match the base tile size.
351 assert(desc->width == iso->map->base_tile_width); 380 assert(desc->width == gfx->map->base_tile_width);
352 assert(desc->height == iso->map->base_tile_height); 381 assert(desc->height == gfx->map->base_tile_height);
353 // Cannot exceed max tiles. 382 // Cannot exceed max tiles.
354 assert(iso->next_tile < iso->tileset->num_tiles); 383 assert(gfx->next_tile < gfx->tileset->num_tiles);
355 384
356 const Tile tile = iso->next_tile++; 385 const Tile tile = gfx->next_tile++;
357 386
358 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);
359 388
@@ -362,16 +391,16 @@ Tile isogfx_make_tile(IsoGfx* iso, const TileDesc* desc) {
362 assert(desc->width > 0); 391 assert(desc->width > 0);
363 assert(desc->height > 0); 392 assert(desc->height > 0);
364 393
365 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);
366 395
367 *ts_tile = (Ts_Tile){ 396 *ts_tile = (Ts_Tile){
368 .width = iso->map->base_tile_width, 397 .width = gfx->map->base_tile_width,
369 .height = iso->map->base_tile_height, 398 .height = gfx->map->base_tile_height,
370 .pixels = tile * tile_size_bytes, 399 .pixels = tile * tile_size_bytes,
371 }; 400 };
372 401
373 Pixel* const tile_pixels = 402 Pixel* const tile_pixels =
374 ts_tileset_get_tile_pixels_mut(iso->tileset, tile); 403 ts_tileset_get_tile_pixels_mut(gfx->tileset, tile);
375 make_tile_from_colour(desc->colour, ts_tile, tile_pixels); 404 make_tile_from_colour(desc->colour, ts_tile, tile_pixels);
376 break; 405 break;
377 } 406 }
@@ -389,31 +418,31 @@ Tile isogfx_make_tile(IsoGfx* iso, const TileDesc* desc) {
389 return tile; 418 return tile;
390} 419}
391 420
392void isogfx_set_tile(IsoGfx* iso, int x, int y, Tile tile) { 421void gfx2d_set_tile(Gfx2d* gfx, int x, int y, Tile tile) {
393 assert(iso); 422 assert(gfx);
394 423
395 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);
396 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);
397 426
398 *map_tile = tile; 427 *map_tile = tile;
399} 428}
400 429
401void 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) {
402 assert(iso); 431 assert(gfx);
403 for (int y = y0; y < y1; ++y) { 432 for (int y = y0; y < y1; ++y) {
404 for (int x = x0; x < x1; ++x) { 433 for (int x = x0; x < x1; ++x) {
405 isogfx_set_tile(iso, x, y, tile); 434 gfx2d_set_tile(gfx, x, y, tile);
406 } 435 }
407 } 436 }
408} 437}
409 438
410SpriteSheet isogfx_load_sprite_sheet(IsoGfx* iso, const char* filepath) { 439SpriteSheet gfx2d_load_sprite_sheet(Gfx2d* gfx, const char* filepath) {
411 assert(iso); 440 assert(gfx);
412 assert(filepath); 441 assert(filepath);
413 442
414 bool success = false; 443 bool success = false;
415 SpriteSheet spriteSheet = 0; 444 SpriteSheet spriteSheet = 0;
416 const size_t watermark = memstack_watermark(&iso->stack); 445 const size_t watermark = memstack_watermark(&gfx->stack);
417 446
418 // Load sprite sheet file. 447 // Load sprite sheet file.
419 printf("Load sprite sheet: %s\n", filepath); 448 printf("Load sprite sheet: %s\n", filepath);
@@ -421,7 +450,7 @@ SpriteSheet isogfx_load_sprite_sheet(IsoGfx* iso, const char* filepath) {
421 WITH_FILE(filepath, { 450 WITH_FILE(filepath, {
422 const size_t file_size = get_file_size_f(file); 451 const size_t file_size = get_file_size_f(file);
423 ss_sheet = 452 ss_sheet =
424 memstack_alloc_aligned(&iso->stack, file_size, alignof(Ss_SpriteSheet)); 453 memstack_alloc_aligned(&gfx->stack, file_size, alignof(Ss_SpriteSheet));
425 success = read_file_f(file, ss_sheet); 454 success = read_file_f(file, ss_sheet);
426 }); 455 });
427 if (!success) { 456 if (!success) {
@@ -434,63 +463,63 @@ SpriteSheet isogfx_load_sprite_sheet(IsoGfx* iso, const char* filepath) {
434cleanup: 463cleanup:
435 if (!success) { 464 if (!success) {
436 if (ss_sheet) { 465 if (ss_sheet) {
437 memstack_set_watermark(&iso->stack, watermark); 466 memstack_set_watermark(&gfx->stack, watermark);
438 } 467 }
439 } 468 }
440 return spriteSheet; 469 return spriteSheet;
441} 470}
442 471
443Sprite isogfx_make_sprite(IsoGfx* iso, SpriteSheet sheet) { 472Sprite gfx2d_make_sprite(Gfx2d* gfx, SpriteSheet sheet) {
444 assert(iso); 473 assert(gfx);
445 assert(sheet); 474 assert(sheet);
446 475
447 // 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
448 // calls memstack_alloc_aligned() with sizeof/alignof. No real point in 477 // calls memstack_alloc_aligned() with sizeof/alignof. No real point in
449 // having unaligned allocations. 478 // having unaligned allocations.
450 SpriteInstance* sprite = memstack_alloc_aligned( 479 SpriteInstance* sprite = memstack_alloc_aligned(
451 &iso->stack, sizeof(SpriteInstance), alignof(SpriteInstance)); 480 &gfx->stack, sizeof(SpriteInstance), alignof(SpriteInstance));
452 481
453 sprite->sheet = (const Ss_SpriteSheet*)sheet; 482 sprite->sheet = (const Ss_SpriteSheet*)sheet;
454 sprite->next = iso->head_sprite; 483 sprite->next = gfx->head_sprite;
455 iso->head_sprite = sprite; 484 gfx->head_sprite = sprite;
456 485
457 return (Sprite)sprite; 486 return (Sprite)sprite;
458} 487}
459 488
460void 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) {
461 assert(iso); 490 assert(gfx);
462 SpriteInstance* sprite = (SpriteInstance*)hSprite; 491 SpriteInstance* sprite = (SpriteInstance*)hSprite;
463 sprite->position.x = x; 492 sprite->position.x = x;
464 sprite->position.y = y; 493 sprite->position.y = y;
465} 494}
466 495
467void isogfx_set_sprite_animation(IsoGfx* iso, Sprite hSprite, int animation) { 496void gfx2d_set_sprite_animation(Gfx2d* gfx, Sprite hSprite, int animation) {
468 assert(iso); 497 assert(gfx);
469 SpriteInstance* sprite = (SpriteInstance*)hSprite; 498 SpriteInstance* sprite = (SpriteInstance*)hSprite;
470 sprite->animation = animation; 499 sprite->animation = animation;
471} 500}
472 501
473void isogfx_update(IsoGfx* iso, double t) { 502void gfx2d_update(Gfx2d* gfx, double t) {
474 assert(iso); 503 assert(gfx);
475 504
476 // 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
477 // record the starting animation time. 506 // record the starting animation time.
478 if (iso->last_animation_time == 0.0) { 507 if (gfx->last_animation_time == 0.0) {
479 iso->last_animation_time = t; 508 gfx->last_animation_time = t;
480 return; 509 return;
481 } 510 }
482 511
483 if ((t - iso->last_animation_time) >= ANIMATION_UPDATE_DELTA) { 512 if ((t - gfx->last_animation_time) >= ANIMATION_UPDATE_DELTA) {
484 // 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
485 // only walk over those here and not also the static sprites. 514 // only walk over those here and not also the static sprites.
486 for (SpriteInstance* sprite = iso->head_sprite; sprite; 515 for (SpriteInstance* sprite = gfx->head_sprite; sprite;
487 sprite = sprite->next) { 516 sprite = sprite->next) {
488 const Ss_SpriteSheet* sheet = sprite->sheet; 517 const Ss_SpriteSheet* sheet = sprite->sheet;
489 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);
490 sprite->frame = (sprite->frame + 1) % row->num_cols; 519 sprite->frame = (sprite->frame + 1) % row->num_cols;
491 } 520 }
492 521
493 iso->last_animation_time = t; 522 gfx->last_animation_time = t;
494 } 523 }
495} 524}
496 525
@@ -498,18 +527,6 @@ void isogfx_update(IsoGfx* iso, double t) {
498// Rendering and picking. 527// Rendering and picking.
499// ----------------------------------------------------------------------------- 528// -----------------------------------------------------------------------------
500 529
501/// Get the screen position of the top diamond-corner of the tile at world
502/// (x,y).
503static ivec2 GetTileScreenOrigin(
504 const CoordSystem iso_space, ivec2 camera, int world_x, int world_y) {
505 const ivec2 vx_offset = ivec2_scale(iso_space.x, world_x);
506 const ivec2 vy_offset = ivec2_scale(iso_space.y, world_y);
507 const ivec2 screen_origin =
508 ivec2_add(iso_space.o, ivec2_add(vx_offset, vy_offset));
509 const ivec2 origin_view_space = ivec2_add(screen_origin, ivec2_neg(camera));
510 return origin_view_space;
511}
512
513static Pixel alpha_blend(Pixel src, Pixel dst) { 530static Pixel alpha_blend(Pixel src, Pixel dst) {
514 if ((src.a == 255) || (dst.a == 0)) { 531 if ((src.a == 255) || (dst.a == 0)) {
515 return src; 532 return src;
@@ -541,6 +558,7 @@ static void draw_rect(
541 Screen* screen, ivec2 top_left, int rect_width, int rect_height, 558 Screen* screen, ivec2 top_left, int rect_width, int rect_height,
542 const Pixel* pixels, const uint8_t* indices) { 559 const Pixel* pixels, const uint8_t* indices) {
543 assert(screen); 560 assert(screen);
561 assert(pixels);
544 562
545#define rect_pixel(X, Y) \ 563#define rect_pixel(X, Y) \
546 (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])
@@ -568,137 +586,237 @@ static void draw_rect(
568 } 586 }
569} 587}
570 588
571/// Draw a tile. 589/// Draw a tile in an orthogonal map.
572/// 590static void draw_tile_ortho(Gfx2d* gfx, Tile tile, int x, int y) {
573/// 'screen_origin' is the screen coordinates of the top diamond-corner of the 591 assert(gfx);
574/// tile (the base tile for super tiles). 592 assert(gfx->tileset);
575/// World (0, 0) -> (screen_width / 2, 0). 593 assert(x >= 0);
576static void draw_tile(IsoGfx* iso, ivec2 screen_origin, Tile tile) { 594 assert(y >= 0);
577 assert(iso); 595 assert(x < gfx->map->world_width);
578 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);
579 600
580 const Ts_Tile* pTile = ts_tileset_get_tile(iso->tileset, tile); 601 const ivec2 screen_origin = map2screen(
581 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);
582 626
583 // 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.
584 // 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.
585 // 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.
586 const ivec2 offset = { 630 const ivec2 offset = {
587 -(iso->map->base_tile_width / 2), 631 -(gfx->map->base_tile_width / 2),
588 pTile->height - iso->map->base_tile_height}; 632 pTile->height - gfx->map->base_tile_height};
589 const ivec2 top_left = ivec2_add(screen_origin, offset); 633 const ivec2 top_left = ivec2_add(screen_origin, offset);
590 634
591 draw_rect( 635 draw_rect(
592 &iso->screen, top_left, pTile->width, pTile->height, pixels, nullptr); 636 &gfx->screen, top_left, pTile->width, pTile->height, pixels, nullptr);
593} 637}
594 638
595static void draw_map(IsoGfx* iso) { 639static void draw_map_ortho(Gfx2d* gfx) {
596 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);
597 646
598 const int W = iso->screen.width; 647 for (int wy = 0; wy < gfx->map->world_height; ++wy) {
599 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}
600 654
601 memset(iso->screen.pixels, 0, W * H * sizeof(Pixel)); 655static void draw_map_iso(Gfx2d* gfx) {
656 assert(gfx);
657 assert(gfx->map);
602 658
603 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);
604 661
605 // TODO: Culling. 662 // TODO: Culling.
606 // Ex: map the screen corners to tile space to cull. 663 // Ex: map the screen corners to tile space to cull.
607 // Ex: walk in screen space and fetch the tile. 664 // Ex: walk in screen space and fetch the tile.
608 // The tile-centric approach might be more cache-friendly since the 665 // The tile-centric approach might be more cache-friendly since the
609 // screen-centric approach would juggle multiple tiles throughout the scan. 666 // screen-centric approach would juggle multiple tiles throughout the scan.
610 for (int wy = 0; wy < iso->map->world_height; ++wy) { 667 for (int wy = 0; wy < gfx->map->world_height; ++wy) {
611 for (int wx = 0; wx < iso->map->world_width; ++wx) { 668 for (int wx = 0; wx < gfx->map->world_width; ++wx) {
612 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);
613 const ivec2 screen_origin = 670 draw_tile_iso(gfx, tile, wx, wy);
614 GetTileScreenOrigin(iso->iso_space, iso->camera, wx, wy);
615 draw_tile(iso, screen_origin, tile);
616 } 671 }
617 } 672 }
618} 673}
619 674
620static void draw_sprite( 675static void draw_map(Gfx2d* gfx) {
621 IsoGfx* iso, ivec2 origin, const SpriteInstance* sprite, 676 assert(gfx);
622 const Ss_SpriteSheet* sheet) { 677 assert(gfx->map);
623 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);
624 assert(sprite); 700 assert(sprite);
625 assert(sheet); 701 assert(sheet);
626 assert(sprite->animation >= 0); 702 assert(sprite->animation >= 0);
627 assert(sprite->animation < sheet->num_rows); 703 assert(sprite->animation < sheet->num_rows);
628 assert(sprite->frame >= 0); 704 assert(sprite->frame >= 0);
629 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
630 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);
631 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);
632 draw_rect( 715 draw_rect(
633 &iso->screen, origin, sheet->sprite_width, sheet->sprite_height, 716 &gfx->screen, screen_origin, sheet->sprite_width, sheet->sprite_height,
634 sheet->palette.colours, frame); 717 sheet->palette.colours, frame);
635} 718}
636 719
637static void draw_sprites(IsoGfx* iso) { 720/// Draw a sprite in an isometric coordinate system.
638 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}
639 744
640 for (const SpriteInstance* sprite = iso->head_sprite; sprite; 745static void draw_sprites(Gfx2d* gfx) {
641 sprite = sprite->next) { 746 assert(gfx);
642 const Ss_SpriteSheet* sheet = sprite->sheet; 747 assert(gfx->map);
643 assert(sheet);
644 748
645 const ivec2 screen_origin = GetTileScreenOrigin( 749 const Tm_Flags* flags = (const Tm_Flags*)&gfx->map->flags;
646 iso->iso_space, iso->camera, sprite->position.x, sprite->position.y); 750 switch (flags->orientation) {
647 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;
648 } 763 }
649} 764}
650 765
651void isogfx_set_camera(IsoGfx* iso, int x, int y) { 766void gfx2d_set_camera(Gfx2d* gfx, int x, int y) {
652 assert(iso); 767 assert(gfx);
653 iso->camera = (ivec2){x, y}; 768 gfx->camera = (ivec2){x, y};
654} 769}
655 770
656void isogfx_render(IsoGfx* iso) { 771void gfx2d_render(Gfx2d* gfx) {
657 assert(iso); 772 assert(gfx);
658 draw_map(iso); 773 draw_map(gfx);
659 draw_sprites(iso); 774 draw_sprites(gfx);
660} 775}
661 776
662void isogfx_draw_tile(IsoGfx* iso, int x, int y, Tile tile) { 777void gfx2d_draw_tile(Gfx2d* gfx, int x, int y, Tile tile) {
663 assert(iso); 778 assert(gfx);
664 assert(x >= 0); 779 assert(gfx->map);
665 assert(y >= 0);
666 assert(x < iso->map->world_width);
667 assert(y < iso->map->world_height);
668 780
669 const ivec2 screen_origin = 781 const Tm_Flags* flags = (const Tm_Flags*)&gfx->map->flags;
670 GetTileScreenOrigin(iso->iso_space, iso->camera, x, y); 782 switch (flags->orientation) {
671 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 }
672} 790}
673 791
674void isogfx_get_screen_size(const IsoGfx* iso, int* width, int* height) { 792void gfx2d_get_screen_size(const Gfx2d* gfx, int* width, int* height) {
675 assert(iso); 793 assert(gfx);
676 assert(width); 794 assert(width);
677 assert(height); 795 assert(height);
678 *width = iso->screen.width; 796 *width = gfx->screen.width;
679 *height = iso->screen.height; 797 *height = gfx->screen.height;
680} 798}
681 799
682const Pixel* isogfx_get_screen_buffer(const IsoGfx* iso) { 800const Pixel* gfx2d_get_screen_buffer(const Gfx2d* gfx) {
683 assert(iso); 801 assert(gfx);
684 return iso->screen.pixels; 802 return gfx->screen.pixels;
685} 803}
686 804
687void isogfx_pick_tile( 805void gfx2d_pick_tile(
688 const IsoGfx* iso, double xcart, double ycart, int* xiso, int* yiso) { 806 const Gfx2d* gfx, double xcart, double ycart, int* xiso, int* yiso) {
689 assert(iso); 807 assert(gfx);
690 assert(xiso); 808 assert(xiso);
691 assert(yiso); 809 assert(yiso);
692 810
693 const vec2 camera = ivec2_to_vec2(iso->camera); 811 const vec2 camera = ivec2_to_vec2(gfx->camera);
694 const vec2 xy_cart = vec2_add(camera, (vec2){xcart, ycart}); 812 const vec2 xy_cart = vec2_add(camera, (vec2){xcart, ycart});
695 813
696 const vec2 xy_iso = cart2iso( 814 const vec2 xy_iso = cart2iso(
697 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,
698 iso->screen.width); 816 gfx->screen.width);
699 817
700 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) &&
701 (0 <= xy_iso.y) && (xy_iso.y < iso->map->world_height)) { 819 (0 <= xy_iso.y) && (xy_iso.y < gfx->map->world_height)) {
702 *xiso = (int)xy_iso.x; 820 *xiso = (int)xy_iso.x;
703 *yiso = (int)xy_iso.y; 821 *yiso = (int)xy_iso.y;
704 } else { 822 } else {
diff --git a/tools/mkasset.py b/tools/mkasset.py
index f21a2f9..a402e3c 100644
--- a/tools/mkasset.py
+++ b/tools/mkasset.py
@@ -65,11 +65,14 @@ def carve_image(rgba_bytes, tile_width, tile_height, columns) -> Generator[bytea
65 image_y = image_y0 + y # y of current pixel inside image 65 image_y = image_y0 + y # y of current pixel inside image
66 for x in range(tile_width): 66 for x in range(tile_width):
67 image_x = image_x0 + x # x of current pixel inside image 67 image_x = image_x0 + x # x of current pixel inside image
68 tile_bytes[(y * tile_width + x) * 4] = ( 68 for c in range(4):
69 rgba_bytes)[(image_y * image_width + image_x) * 4] 69 tile_bytes[((y * tile_width + x) * 4) + c] = (
70 rgba_bytes)[((image_y * image_width + image_x) * 4) + c]
70 yield tile_bytes.copy() 71 yield tile_bytes.copy()
71 72
72 73
74# TODO: Palettize it like we do for sprites. Use 2-byte indices to allow up to
75# 65k colours.
73def convert_tsx(input_filepath, output_filepath): 76def convert_tsx(input_filepath, output_filepath):
74 """Converts a Tiled .tsx tileset file to a .TS tile set file.""" 77 """Converts a Tiled .tsx tileset file to a .TS tile set file."""
75 xml = ElementTree.parse(input_filepath) 78 xml = ElementTree.parse(input_filepath)