diff options
-rw-r--r-- | demos/checkerboard/checkerboard.c | 65 | ||||
-rw-r--r-- | demos/isomap/isomap.c | 50 | ||||
-rw-r--r-- | include/isogfx/backend.h | 29 | ||||
-rw-r--r-- | include/isogfx/gfx2d.h | 70 | ||||
-rw-r--r-- | src/backend.c | 28 | ||||
-rw-r--r-- | src/gfx2d.c | 520 | ||||
-rw-r--r-- | tools/mkasset.py | 7 |
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 | ||
49 | typedef struct GfxAppState { | 49 | typedef 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 | ||
57 | static void make_checkerboard(IsoGfx* iso, Tile black, Tile white) { | 57 | static 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 | ||
111 | static void update(GfxApp* app, GfxAppState* state, double t, double dt) { | 112 | static 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 | ||
148 | static void resize(GfxApp* app, GfxAppState* state, int width, int height) { | 149 | static 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 | ||
155 | int main(int argc, const char** argv) { | 156 | int 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; | |||
22 | uint8_t MEMORY[MEMORY_SIZE]; | 22 | uint8_t MEMORY[MEMORY_SIZE]; |
23 | 23 | ||
24 | typedef struct GfxAppState { | 24 | typedef 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 | ||
34 | static bool init(GfxApp* app, GfxAppState* state, int argc, const char** argv) { | 34 | static 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 | ||
109 | static void render(const GfxApp* app, GfxAppState* state) { | 109 | static 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 | ||
118 | static void resize(GfxApp* app, GfxAppState* state, int width, int height) { | 118 | static 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 | ||
125 | int main(int argc, const char** argv) { | 125 | int 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 | ||
5 | typedef struct Gfx Gfx; | 5 | typedef struct Gfx Gfx; |
6 | typedef struct IsoGfx IsoGfx; | 6 | typedef struct Gfx2d Gfx2d; |
7 | 7 | ||
8 | typedef struct IsoBackend IsoBackend; | 8 | typedef struct Gfx2dBackend Gfx2dBackend; |
9 | 9 | ||
10 | /// Initialize the backend. | 10 | /// Initialize the backend. |
11 | IsoBackend* iso_backend_init(const IsoGfx*); | 11 | Gfx2dBackend* gfx2d_backend_init(const Gfx2d*); |
12 | 12 | ||
13 | /// Shut down the backend. | 13 | /// Shut down the backend. |
14 | void iso_backend_shutdown(IsoBackend**); | 14 | void 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. |
19 | void iso_backend_resize_window( | 19 | void 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. |
23 | void iso_backend_render(const IsoBackend*, const IsoGfx*); | 23 | void 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(). |
28 | bool iso_backend_get_mouse_position( | 28 | bool 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 | ||
11 | typedef struct IsoGfx IsoGfx; | 11 | typedef struct Gfx2d Gfx2d; |
12 | 12 | ||
13 | /// Sprite sheet handle. | 13 | /// Sprite sheet handle. |
14 | typedef uintptr_t SpriteSheet; | 14 | typedef uintptr_t SpriteSheet; |
@@ -37,12 +37,18 @@ typedef struct TileDesc { | |||
37 | }; | 37 | }; |
38 | } TileDesc; | 38 | } TileDesc; |
39 | 39 | ||
40 | typedef enum MapOrientation { | ||
41 | MapOrthogonal, | ||
42 | MapIsometric, | ||
43 | } MapOrientation; | ||
44 | |||
40 | typedef struct MapDesc { | 45 | typedef 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 | ||
48 | typedef struct IsoGfxDesc { | 54 | typedef 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. |
56 | IsoGfx* isogfx_new(const IsoGfxDesc*); | 62 | Gfx2d* gfx2d_new(const Gfx2dDesc*); |
57 | 63 | ||
58 | /// Destroy the isometric graphics engine. | 64 | /// Destroy the isometric graphics engine. |
59 | void isogfx_del(IsoGfx**); | 65 | void gfx2d_del(Gfx2d**); |
60 | 66 | ||
61 | /// Clear all loaded worlds and sprites. | 67 | /// Clear all loaded worlds and sprites. |
62 | void isogfx_clear(IsoGfx*); | 68 | void gfx2d_clear(Gfx2d*); |
63 | 69 | ||
64 | /// Create an empty map. | 70 | /// Create an empty map. |
65 | void isogfx_make_map(IsoGfx*, const MapDesc*); | 71 | void gfx2d_make_map(Gfx2d*, const MapDesc*); |
66 | 72 | ||
67 | /// Load a tile map (.TM) file. | 73 | /// Load a tile map (.TM) file. |
68 | bool isogfx_load_map(IsoGfx*, const char* filepath); | 74 | bool gfx2d_load_map(Gfx2d*, const char* filepath); |
69 | 75 | ||
70 | /// Return the world's width. | 76 | /// Return the world's width. |
71 | int isogfx_world_width(const IsoGfx*); | 77 | int gfx2d_world_width(const Gfx2d*); |
72 | 78 | ||
73 | /// Return the world's height. | 79 | /// Return the world's height. |
74 | int isogfx_world_height(const IsoGfx*); | 80 | int gfx2d_world_height(const Gfx2d*); |
75 | 81 | ||
76 | /// Create a new tile. | 82 | /// Create a new tile. |
77 | Tile isogfx_make_tile(IsoGfx*, const TileDesc*); | 83 | Tile gfx2d_make_tile(Gfx2d*, const TileDesc*); |
78 | 84 | ||
79 | /// Set the tile at position (x,y). | 85 | /// Set the tile at position (x,y). |
80 | void isogfx_set_tile(IsoGfx*, int x, int y, Tile); | 86 | void 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). |
83 | void isogfx_set_tiles(IsoGfx*, int x0, int y0, int x1, int y1, Tile); | 89 | void 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. |
86 | SpriteSheet isogfx_load_sprite_sheet(IsoGfx*, const char* filepath); | 92 | SpriteSheet gfx2d_load_sprite_sheet(Gfx2d*, const char* filepath); |
87 | 93 | ||
88 | /// Create an animated sprite. | 94 | /// Create an animated sprite. |
89 | Sprite isogfx_make_sprite(IsoGfx*, SpriteSheet); | 95 | Sprite 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. |
97 | void isogfx_set_sprite_position(IsoGfx*, Sprite, int x, int y); | 103 | void 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. |
100 | void isogfx_set_sprite_animation(IsoGfx*, Sprite, int animation); | 106 | void 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. |
105 | void isogfx_update(IsoGfx*, double t); | 111 | void 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. |
112 | void isogfx_set_camera(IsoGfx*, int x, int y); | 118 | void gfx2d_set_camera(Gfx2d*, int x, int y); |
113 | 119 | ||
114 | /// Render the world. | 120 | /// Render the world. |
115 | void isogfx_render(IsoGfx*); | 121 | void 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(). |
122 | void isogfx_draw_tile(IsoGfx*, int x, int y, Tile); | 128 | void 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. |
125 | void isogfx_get_screen_size(const IsoGfx*, int* width, int* height); | 131 | void 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. |
130 | const Pixel* isogfx_get_screen_buffer(const IsoGfx*); | 136 | const Pixel* gfx2d_get_screen_buffer(const Gfx2d*); |
131 | 137 | ||
132 | /// Translate Cartesian to isometric coordinates. | 138 | /// Translate Cartesian to isometric coordinates. |
133 | void isogfx_pick_tile( | 139 | void 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 | ||
16 | typedef struct IsoBackend { | 16 | typedef 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 | ||
34 | IsoBackend* iso_backend_init(const IsoGfx* iso) { | 34 | Gfx2dBackend* 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 | ||
98 | void iso_backend_shutdown(IsoBackend** ppApp) { | 98 | void 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 | ||
109 | void iso_backend_resize_window( | 109 | void 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 | ||
138 | void iso_backend_render(const IsoBackend* app, const IsoGfx* iso) { | 138 | void 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 | ||
165 | bool iso_backend_get_mouse_position( | 165 | bool 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 | ||
35 | typedef struct CoordSystem { | 35 | typedef 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 | ||
41 | typedef struct Screen { | 41 | typedef 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 | ||
55 | typedef struct IsoGfx { | 55 | typedef 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 | ||
80 | static inline ivec2 ivec2_neg(ivec2 a) { return (ivec2){.x = -a.x, .y = -a.y}; } | 80 | static inline ivec2 ivec2_neg(ivec2 a) { return (ivec2){.x = -a.x, .y = -a.y}; } |
81 | 81 | ||
82 | static 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 | |||
87 | static inline vec2 vec2_add(vec2 a, vec2 b) { | 82 | static 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 | ||
91 | static inline vec2 ivec2_to_vec2(ivec2 a) { return (vec2){a.x, a.y}; } | 86 | static inline vec2 ivec2_to_vec2(ivec2 a) { return (vec2){a.x, a.y}; } |
92 | 87 | ||
88 | /// Map map coordinates to screen coordinates, both Cartesian. | ||
89 | static 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. | ||
105 | static 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. | ||
123 | static 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. | ||
130 | static 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 | ||
146 | IsoGfx* isogfx_new(const IsoGfxDesc* desc) { | 173 | Gfx2d* 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 | ||
175 | cleanup: | 202 | cleanup: |
176 | isogfx_del(&iso); | 203 | gfx2d_del(&gfx); |
177 | return nullptr; | 204 | return nullptr; |
178 | } | 205 | } |
179 | 206 | ||
180 | void isogfx_clear(IsoGfx* iso) { | 207 | void 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 | ||
192 | void isogfx_del(IsoGfx** ppIso) { | 219 | void 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 | ||
201 | void isogfx_make_map(IsoGfx* iso, const MapDesc* desc) { | 228 | void 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 | ||
249 | bool isogfx_load_map(IsoGfx* iso, const char* filepath) { | 278 | bool 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 | ||
302 | cleanup: | 331 | cleanup: |
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 | ||
309 | int isogfx_world_width(const IsoGfx* iso) { | 338 | int 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 | ||
314 | int isogfx_world_height(const IsoGfx* iso) { | 343 | int 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 | ||
319 | static void make_tile_from_colour( | 348 | static void make_tile_from_colour( |
@@ -341,19 +370,19 @@ static void make_tile_from_colour( | |||
341 | } | 370 | } |
342 | } | 371 | } |
343 | 372 | ||
344 | Tile isogfx_make_tile(IsoGfx* iso, const TileDesc* desc) { | 373 | Tile 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 | ||
392 | void isogfx_set_tile(IsoGfx* iso, int x, int y, Tile tile) { | 421 | void 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 | ||
401 | void isogfx_set_tiles(IsoGfx* iso, int x0, int y0, int x1, int y1, Tile tile) { | 430 | void 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 | ||
410 | SpriteSheet isogfx_load_sprite_sheet(IsoGfx* iso, const char* filepath) { | 439 | SpriteSheet 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) { | |||
434 | cleanup: | 463 | cleanup: |
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 | ||
443 | Sprite isogfx_make_sprite(IsoGfx* iso, SpriteSheet sheet) { | 472 | Sprite 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 | ||
460 | void isogfx_set_sprite_position(IsoGfx* iso, Sprite hSprite, int x, int y) { | 489 | void 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 | ||
467 | void isogfx_set_sprite_animation(IsoGfx* iso, Sprite hSprite, int animation) { | 496 | void 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 | ||
473 | void isogfx_update(IsoGfx* iso, double t) { | 502 | void 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). | ||
503 | static 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 | |||
513 | static Pixel alpha_blend(Pixel src, Pixel dst) { | 530 | static 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 | /// | 590 | static 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); |
576 | static 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. | ||
610 | static 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 | ||
595 | static void draw_map(IsoGfx* iso) { | 639 | static 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)); | 655 | static 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 | ||
620 | static void draw_sprite( | 675 | static 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. | ||
697 | static 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 | ||
637 | static void draw_sprites(IsoGfx* iso) { | 720 | /// Draw a sprite in an isometric coordinate system. |
638 | assert(iso); | 721 | static 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; | 745 | static 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 | ||
651 | void isogfx_set_camera(IsoGfx* iso, int x, int y) { | 766 | void 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 | ||
656 | void isogfx_render(IsoGfx* iso) { | 771 | void 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 | ||
662 | void isogfx_draw_tile(IsoGfx* iso, int x, int y, Tile tile) { | 777 | void 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 | ||
674 | void isogfx_get_screen_size(const IsoGfx* iso, int* width, int* height) { | 792 | void 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 | ||
682 | const Pixel* isogfx_get_screen_buffer(const IsoGfx* iso) { | 800 | const 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 | ||
687 | void isogfx_pick_tile( | 805 | void 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. | ||
73 | def convert_tsx(input_filepath, output_filepath): | 76 | def 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) |