summaryrefslogtreecommitdiff
path: root/gfx-iso
diff options
context:
space:
mode:
Diffstat (limited to 'gfx-iso')
-rw-r--r--gfx-iso/CMakeLists.txt15
-rw-r--r--gfx-iso/demos/checkerboard/CMakeLists.txt3
-rw-r--r--gfx-iso/demos/checkerboard/checkerboard.c94
-rw-r--r--gfx-iso/demos/isomap/CMakeLists.txt3
-rw-r--r--gfx-iso/demos/isomap/isomap.c88
-rw-r--r--gfx-iso/include/isogfx/app.h24
-rw-r--r--gfx-iso/include/isogfx/backend.h28
-rw-r--r--gfx-iso/src/app.c196
-rw-r--r--gfx-iso/src/backend.c199
9 files changed, 366 insertions, 284 deletions
diff --git a/gfx-iso/CMakeLists.txt b/gfx-iso/CMakeLists.txt
index 673cb68..e4a677d 100644
--- a/gfx-iso/CMakeLists.txt
+++ b/gfx-iso/CMakeLists.txt
@@ -21,22 +21,21 @@ target_link_libraries(isogfx PUBLIC
21 21
22target_compile_options(isogfx PRIVATE -Wall -Wextra -Wpedantic) 22target_compile_options(isogfx PRIVATE -Wall -Wextra -Wpedantic)
23 23
24# App 24# Backend
25 25
26add_library(isogfx-app 26add_library(isogfx-backend
27 src/app.c) 27 src/backend.c)
28 28
29target_include_directories(isogfx-app PUBLIC 29target_include_directories(isogfx-backend PUBLIC
30 include) 30 include)
31 31
32target_link_libraries(isogfx-app PUBLIC 32target_link_libraries(isogfx-backend PUBLIC
33 gfx-app
34 isogfx) 33 isogfx)
35 34
36target_link_libraries(isogfx-app PRIVATE 35target_link_libraries(isogfx-backend PRIVATE
37 gfx) 36 gfx)
38 37
39target_compile_options(isogfx-app PRIVATE -Wall -Wextra -Wpedantic) 38target_compile_options(isogfx-backend PRIVATE -Wall -Wextra -Wpedantic)
40 39
41# Demos 40# Demos
42 41
diff --git a/gfx-iso/demos/checkerboard/CMakeLists.txt b/gfx-iso/demos/checkerboard/CMakeLists.txt
index f178262..d1691c6 100644
--- a/gfx-iso/demos/checkerboard/CMakeLists.txt
+++ b/gfx-iso/demos/checkerboard/CMakeLists.txt
@@ -10,6 +10,7 @@ add_executable(checkerboard
10 checkerboard.c) 10 checkerboard.c)
11 11
12target_link_libraries(checkerboard PRIVATE 12target_link_libraries(checkerboard PRIVATE
13 isogfx-app) 13 gfx-app
14 isogfx-backend)
14 15
15target_compile_options(checkerboard PRIVATE -Wall -Wextra -Wpedantic) 16target_compile_options(checkerboard PRIVATE -Wall -Wextra -Wpedantic)
diff --git a/gfx-iso/demos/checkerboard/checkerboard.c b/gfx-iso/demos/checkerboard/checkerboard.c
index e684bc3..0f01b72 100644
--- a/gfx-iso/demos/checkerboard/checkerboard.c
+++ b/gfx-iso/demos/checkerboard/checkerboard.c
@@ -1,10 +1,20 @@
1#include <isogfx/app.h> 1#include <isogfx/backend.h>
2#include <isogfx/isogfx.h> 2#include <isogfx/isogfx.h>
3 3
4#include <gfx/app.h>
5
4#include <assert.h> 6#include <assert.h>
5#include <stdbool.h> 7#include <stdbool.h>
6#include <stdio.h> 8#include <stdio.h>
7 9
10static const int WINDOW_WIDTH = 1408;
11static const int WINDOW_HEIGHT = 960;
12static const int MAX_FPS = 60;
13
14// Virtual screen dimensions.
15static const int SCREEN_WIDTH = 704;
16static const int SCREEN_HEIGHT = 480;
17
8static const int TILE_WIDTH = 64; 18static const int TILE_WIDTH = 64;
9static const int TILE_HEIGHT = TILE_WIDTH / 2; 19static const int TILE_HEIGHT = TILE_WIDTH / 2;
10static const int WORLD_WIDTH = 20; 20static const int WORLD_WIDTH = 20;
@@ -31,11 +41,13 @@ typedef enum Colour {
31 Red, 41 Red,
32} Colour; 42} Colour;
33 43
34typedef struct IsoGfxAppState { 44typedef struct GfxAppState {
35 Tile red; 45 IsoBackend* backend;
36 int xpick; 46 IsoGfx* iso;
37 int ypick; 47 Tile red;
38} IsoGfxAppState; 48 int xpick;
49 int ypick;
50} GfxAppState;
39 51
40static void make_checkerboard(IsoGfx* iso, Tile black, Tile white) { 52static void make_checkerboard(IsoGfx* iso, Tile black, Tile white) {
41 assert(iso); 53 assert(iso);
@@ -49,14 +61,20 @@ static void make_checkerboard(IsoGfx* iso, Tile black, Tile white) {
49 } 61 }
50} 62}
51 63
52static bool init( 64static bool init(GfxAppState* state, int argc, const char** argv) {
53 IsoGfxAppState* state, IsoGfx* iso, int argc, const char** argv) {
54 assert(state); 65 assert(state);
55 assert(iso);
56 66
57 (void)argc; 67 (void)argc;
58 (void)argv; 68 (void)argv;
59 69
70 if (!(state->iso = isogfx_new(&(IsoGfxDesc){
71 .screen_width = SCREEN_WIDTH, .screen_height = SCREEN_HEIGHT}))) {
72 return false;
73 }
74 IsoGfx* iso = state->iso;
75
76 isogfx_resize(iso, SCREEN_WIDTH, SCREEN_HEIGHT);
77
60 if (!isogfx_make_world( 78 if (!isogfx_make_world(
61 iso, &(WorldDesc){ 79 iso, &(WorldDesc){
62 .tile_width = TILE_WIDTH, 80 .tile_width = TILE_WIDTH,
@@ -71,50 +89,78 @@ static bool init(
71 state->red = isogfx_make_tile(iso, &tile_set[Red]); 89 state->red = isogfx_make_tile(iso, &tile_set[Red]);
72 make_checkerboard(iso, black, white); 90 make_checkerboard(iso, black, white);
73 91
92 if (!(state->backend = IsoBackendInit(iso))) {
93 return false;
94 }
95
74 return true; 96 return true;
75} 97}
76 98
77static void shutdown(IsoGfxAppState* state, IsoGfx* iso) { 99static void shutdown(GfxAppState* state) {
78 assert(state); 100 assert(state);
79 assert(iso); 101
102 IsoBackendShutdown(&state->backend);
103 isogfx_del(&state->iso);
80} 104}
81 105
82static void update(IsoGfxAppState* state, IsoGfx* iso, double t, double dt) { 106static void update(GfxAppState* state, double t, double dt) {
83 assert(state); 107 assert(state);
84 assert(iso);
85
86 (void)t;
87 (void)dt; 108 (void)dt;
88 109
110 IsoGfx* iso = state->iso;
111
112 isogfx_update(iso, t);
113
114 // Get mouse position in window coordinates.
89 double mouse_x, mouse_y; 115 double mouse_x, mouse_y;
90 gfx_app_get_mouse_position(&mouse_x, &mouse_y); 116 gfx_app_get_mouse_position(&mouse_x, &mouse_y);
91 117
118 // Map from window coordinates to virtual screen coordinates.
119 IsoBackendGetMousePosition(
120 state->backend, mouse_x, mouse_y, &mouse_x, &mouse_y);
121
92 isogfx_pick_tile(iso, mouse_x, mouse_y, &state->xpick, &state->ypick); 122 isogfx_pick_tile(iso, mouse_x, mouse_y, &state->xpick, &state->ypick);
93 123
94 printf("Picked tile: (%d, %d)\n", state->xpick, state->ypick); 124 printf("Picked tile: (%d, %d)\n", state->xpick, state->ypick);
95} 125}
96 126
97static void render(IsoGfxAppState* state, IsoGfx* iso) { 127static void render(GfxAppState* state) {
98 assert(state); 128 assert(state);
99 assert(iso); 129
130 IsoGfx* iso = state->iso;
100 131
101 isogfx_render(iso); 132 isogfx_render(iso);
102 133
103 if ((state->xpick != -1) && (state->ypick != -1)) { 134 if ((state->xpick != -1) && (state->ypick != -1)) {
104 isogfx_draw_tile(iso, state->xpick, state->ypick, state->red); 135 isogfx_draw_tile(iso, state->xpick, state->ypick, state->red);
105 } 136 }
137
138 IsoBackendRender(state->backend, iso);
139}
140
141static void resize(GfxAppState* state, int width, int height) {
142 assert(state);
143
144 IsoBackendResizeWindow(state->backend, state->iso, width, height);
106} 145}
107 146
108int main(int argc, const char** argv) { 147int main(int argc, const char** argv) {
109 IsoGfxAppState state = {0}; 148 GfxAppState state = {0};
110 iso_run( 149 gfx_app_run(
111 argc, argv, 150 &(GfxAppDesc){
112 &(IsoGfxApp){ 151 .argc = argc,
113 .state = &state, 152 .argv = argv,
153 .width = WINDOW_WIDTH,
154 .height = WINDOW_HEIGHT,
155 .max_fps = MAX_FPS,
156 .update_delta_time = MAX_FPS > 0 ? 1.0 / (double)MAX_FPS : 0.0,
157 .title = "Isometric Renderer",
158 .app_state = &state},
159 &(GfxAppCallbacks){
114 .init = init, 160 .init = init,
115 .shutdown = shutdown,
116 .update = update, 161 .update = update,
117 .render = render, 162 .render = render,
118 }); 163 .resize = resize,
164 .shutdown = shutdown});
119 return 0; 165 return 0;
120} 166}
diff --git a/gfx-iso/demos/isomap/CMakeLists.txt b/gfx-iso/demos/isomap/CMakeLists.txt
index 13edcc7..2dbfd32 100644
--- a/gfx-iso/demos/isomap/CMakeLists.txt
+++ b/gfx-iso/demos/isomap/CMakeLists.txt
@@ -10,6 +10,7 @@ add_executable(isomap
10 isomap.c) 10 isomap.c)
11 11
12target_link_libraries(isomap PRIVATE 12target_link_libraries(isomap PRIVATE
13 isogfx-app) 13 gfx-app
14 isogfx-backend)
14 15
15target_compile_options(isomap PRIVATE -Wall -Wextra -Wpedantic) 16target_compile_options(isomap PRIVATE -Wall -Wextra -Wpedantic)
diff --git a/gfx-iso/demos/isomap/isomap.c b/gfx-iso/demos/isomap/isomap.c
index 5ab926c..a233659 100644
--- a/gfx-iso/demos/isomap/isomap.c
+++ b/gfx-iso/demos/isomap/isomap.c
@@ -1,23 +1,41 @@
1#include <isogfx/app.h> 1#include <isogfx/backend.h>
2#include <isogfx/isogfx.h> 2#include <isogfx/isogfx.h>
3 3
4#include <gfx/app.h>
5
4#include <assert.h> 6#include <assert.h>
5#include <stdbool.h> 7#include <stdbool.h>
6 8
7typedef struct IsoGfxAppState { 9static const int WINDOW_WIDTH = 1408;
10static const int WINDOW_HEIGHT = 960;
11static const int MAX_FPS = 60;
12
13// Virtual screen dimensions.
14static const int SCREEN_WIDTH = 704;
15static const int SCREEN_HEIGHT = 480;
16
17typedef struct GfxAppState {
18 IsoBackend* backend;
19 IsoGfx* iso;
8 int xpick; 20 int xpick;
9 int ypick; 21 int ypick;
10 SpriteSheet stag_sheet; 22 SpriteSheet stag_sheet;
11 Sprite stag; 23 Sprite stag;
12} IsoGfxAppState; 24} GfxAppState;
13 25
14static bool init( 26static bool init(GfxAppState* state, int argc, const char** argv) {
15 IsoGfxAppState* state, IsoGfx* iso, int argc, const char** argv) {
16 assert(state); 27 assert(state);
17 assert(iso);
18 (void)argc; 28 (void)argc;
19 (void)argv; 29 (void)argv;
20 30
31 if (!(state->iso = isogfx_new(&(IsoGfxDesc){
32 .screen_width = SCREEN_WIDTH, .screen_height = SCREEN_HEIGHT}))) {
33 return false;
34 }
35 IsoGfx* iso = state->iso;
36
37 isogfx_resize(iso, SCREEN_WIDTH, SCREEN_HEIGHT);
38
21 if (!isogfx_load_world(iso, "/home/jeanne/assets/tilemaps/demo1.tm")) { 39 if (!isogfx_load_world(iso, "/home/jeanne/assets/tilemaps/demo1.tm")) {
22 return false; 40 return false;
23 } 41 }
@@ -31,47 +49,57 @@ static bool init(
31 state->stag = isogfx_make_sprite(iso, state->stag_sheet); 49 state->stag = isogfx_make_sprite(iso, state->stag_sheet);
32 isogfx_set_sprite_position(iso, state->stag, 5, 4); 50 isogfx_set_sprite_position(iso, state->stag, 5, 4);
33 51
52 if (!(state->backend = IsoBackendInit(iso))) {
53 return false;
54 }
55
34 return true; 56 return true;
35} 57}
36 58
37static void shutdown(IsoGfxAppState* state, IsoGfx* iso) { 59static void shutdown(GfxAppState* state) {
38 assert(state); 60 assert(state);
39 assert(iso); 61 //
40} 62}
41 63
42static void update(IsoGfxAppState* state, IsoGfx* iso, double t, double dt) { 64static void update(GfxAppState* state, double t, double dt) {
43 assert(state); 65 assert(state);
44 assert(iso);
45
46 (void)t;
47 (void)dt; 66 (void)dt;
48 67
49 double mouse_x, mouse_y; 68 IsoGfx* iso = state->iso;
50 gfx_app_get_mouse_position(&mouse_x, &mouse_y); 69 isogfx_update(iso, t);
70}
51 71
52 isogfx_pick_tile(iso, mouse_x, mouse_y, &state->xpick, &state->ypick); 72static void render(GfxAppState* state) {
73 assert(state);
53 74
54 // printf("Picked tile: (%d, %d)\n", state->xpick, state->ypick); 75 IsoGfx* iso = state->iso;
76 isogfx_render(iso);
77 IsoBackendRender(state->backend, iso);
55} 78}
56 79
57static void render(IsoGfxAppState* state, IsoGfx* iso) { 80static void resize(GfxAppState* state, int width, int height) {
58 assert(state); 81 assert(state);
59 assert(iso);
60 82
61 isogfx_render(iso); 83 IsoBackendResizeWindow(state->backend, state->iso, width, height);
62} 84}
63 85
64int main(int argc, const char** argv) { 86int main(int argc, const char** argv) {
65 IsoGfxAppState state = {0}; 87 GfxAppState state = {0};
66 iso_run( 88 gfx_app_run(
67 argc, argv, 89 &(GfxAppDesc){
68 &(IsoGfxApp){ 90 .argc = argc,
69 .pixel_scale = 2, 91 .argv = argv,
70 .state = &state, 92 .width = WINDOW_WIDTH,
71 .init = init, 93 .height = WINDOW_HEIGHT,
72 .shutdown = shutdown, 94 .max_fps = MAX_FPS,
73 .update = update, 95 .update_delta_time = MAX_FPS > 0 ? 1.0 / (double)MAX_FPS : 0.0,
74 .render = render, 96 .title = "Isometric Renderer",
75 }); 97 .app_state = &state},
98 &(GfxAppCallbacks){
99 .init = init,
100 .update = update,
101 .render = render,
102 .resize = resize,
103 .shutdown = shutdown});
76 return 0; 104 return 0;
77} 105}
diff --git a/gfx-iso/include/isogfx/app.h b/gfx-iso/include/isogfx/app.h
deleted file mode 100644
index fe60d04..0000000
--- a/gfx-iso/include/isogfx/app.h
+++ /dev/null
@@ -1,24 +0,0 @@
1#pragma once
2
3#include <gfx/app.h>
4
5#include <stdbool.h>
6
7// TODO: Define an isogfx-gl backend library. Remove all these callbacks.
8
9typedef struct IsoGfx IsoGfx;
10typedef struct IsoGfxApp IsoGfxApp;
11
12typedef struct IsoGfxAppState IsoGfxAppState;
13
14typedef struct IsoGfxApp {
15 int pixel_scale; // Use 0 or 1 for 1:1 scaling.
16 IsoGfxAppState* state;
17
18 bool (*init)(IsoGfxAppState*, IsoGfx*, int argc, const char** argv);
19 void (*shutdown)(IsoGfxAppState*, IsoGfx*);
20 void (*update)(IsoGfxAppState*, IsoGfx*, double t, double dt);
21 void (*render)(IsoGfxAppState*, IsoGfx*);
22} IsoGfxApp;
23
24void iso_run(int argc, const char** argv, IsoGfxApp*);
diff --git a/gfx-iso/include/isogfx/backend.h b/gfx-iso/include/isogfx/backend.h
new file mode 100644
index 0000000..172991d
--- /dev/null
+++ b/gfx-iso/include/isogfx/backend.h
@@ -0,0 +1,28 @@
1#pragma once
2
3#include <stdbool.h>
4
5typedef struct Gfx Gfx;
6typedef struct IsoGfx IsoGfx;
7
8typedef struct IsoBackend IsoBackend;
9
10/// Initialize the backend.
11IsoBackend* IsoBackendInit(const IsoGfx*);
12
13/// Shut down the backend.
14void IsoBackendShutdown(IsoBackend**);
15
16/// Notify the backend of a window resize event.
17/// This allows the backend to determine how to position and scale the iso
18/// screen buffer on the graphics window.
19void IsoBackendResizeWindow(IsoBackend*, const IsoGfx*, int width, int height);
20
21/// Render the iso screen to the graphics window.
22void IsoBackendRender(const IsoBackend*, const IsoGfx*);
23
24/// Map window coordinates to iso space coordinates.
25/// This takes into account any possible resizing done by the backend in
26/// response to calls to IsoBackendResizeWindow().
27bool IsoBackendGetMousePosition(
28 const IsoBackend*, double window_x, double window_y, double* x, double* y);
diff --git a/gfx-iso/src/app.c b/gfx-iso/src/app.c
deleted file mode 100644
index 2f2241f..0000000
--- a/gfx-iso/src/app.c
+++ /dev/null
@@ -1,196 +0,0 @@
1#include <isogfx/app.h>
2#include <isogfx/isogfx.h>
3
4#include <gfx/app.h>
5#include <gfx/core.h>
6#include <gfx/gfx.h>
7#include <gfx/renderer.h>
8#include <gfx/scene.h>
9#include <gfx/util/geometry.h>
10#include <gfx/util/shader.h>
11
12#include <assert.h>
13#include <stdbool.h>
14#include <stdlib.h>
15
16static const int WINDOW_WIDTH = 1408;
17static const int WINDOW_HEIGHT = 960;
18static const int MAX_FPS = 60;
19
20typedef struct AppState {
21 Gfx* gfx;
22 IsoGfx* iso;
23 IsoGfxApp* app;
24 Texture* screen_texture;
25 Scene* scene;
26} AppState;
27
28typedef struct GfxAppState {
29 AppState state;
30} GfxAppState;
31
32static bool init(GfxAppState* gfx_app_state, int argc, const char** argv) {
33 assert(gfx_app_state);
34 AppState* state = &gfx_app_state->state;
35
36 IsoGfxApp* app = state->app;
37
38 // Virtual screen dimensions.
39 const int scale = app->pixel_scale == 0 ? 1 : app->pixel_scale;
40 const int screen_width = WINDOW_WIDTH / scale;
41 const int screen_height = WINDOW_HEIGHT / scale;
42
43 if (!(state->iso = isogfx_new(&(IsoGfxDesc){
44 .screen_width = screen_width, .screen_height = screen_height}))) {
45 goto cleanup;
46 }
47
48 if (!(*app->init)(app->state, state->iso, argc, argv)) {
49 goto cleanup;
50 }
51
52 isogfx_resize(state->iso, screen_width, screen_height);
53
54 if (!(state->gfx = gfx_init())) {
55 goto cleanup;
56 }
57 GfxCore* gfxcore = gfx_get_core(state->gfx);
58
59 if (!(state->screen_texture = gfx_make_texture(
60 gfxcore, &(TextureDesc){
61 .width = screen_width,
62 .height = screen_height,
63 .dimension = Texture2D,
64 .format = TextureSRGBA8,
65 .filtering = NearestFiltering,
66 .wrap = ClampToEdge,
67 .mipmaps = false}))) {
68 goto cleanup;
69 }
70
71 ShaderProgram* shader = gfx_make_view_texture_shader(gfxcore);
72 if (!shader) {
73 goto cleanup;
74 }
75
76 Geometry* geometry = gfx_make_quad_11(gfxcore);
77 if (!geometry) {
78 goto cleanup;
79 }
80
81 MaterialDesc material_desc = (MaterialDesc){.num_uniforms = 1};
82 material_desc.uniforms[0] = (ShaderUniform){
83 .type = UniformTexture,
84 .value.texture = state->screen_texture,
85 .name = sstring_make("Texture")};
86 Material* material = gfx_make_material(&material_desc);
87 if (!material) {
88 return false;
89 }
90
91 const MeshDesc mesh_desc =
92 (MeshDesc){.geometry = geometry, .material = material, .shader = shader};
93 Mesh* mesh = gfx_make_mesh(&mesh_desc);
94 if (!mesh) {
95 goto cleanup;
96 }
97
98 SceneObject* object =
99 gfx_make_object(&(ObjectDesc){.num_meshes = 1, .meshes = {mesh}});
100 if (!object) {
101 goto cleanup;
102 }
103
104 state->scene = gfx_make_scene();
105 SceneNode* node = gfx_make_object_node(object);
106 SceneNode* root = gfx_get_scene_root(state->scene);
107 gfx_set_node_parent(node, root);
108
109 return true;
110
111cleanup:
112 if (state->gfx) {
113 gfx_destroy(&state->gfx);
114 }
115 free(state);
116 return false;
117}
118
119static void shutdown(GfxAppState* gfx_app_state) {
120 assert(gfx_app_state);
121 AppState* state = &gfx_app_state->state;
122
123 if (state->app) {
124 assert(state->iso);
125 (*state->app->shutdown)(state->app->state, state->iso);
126 }
127
128 isogfx_del(&state->iso);
129 gfx_destroy(&state->gfx);
130}
131
132static void update(GfxAppState* gfx_app_state, double t, double dt) {
133 assert(gfx_app_state);
134 AppState* state = &gfx_app_state->state;
135
136 isogfx_update(state->iso, t);
137
138 assert(state->app->update);
139 (*state->app->update)(state->app->state, state->iso, t, dt);
140}
141
142static void render(GfxAppState* gfx_app_state) {
143 assert(gfx_app_state);
144 AppState* state = &gfx_app_state->state;
145
146 assert(state->app->render);
147 (*state->app->render)(state->app->state, state->iso);
148
149 const Pixel* screen = isogfx_get_screen_buffer(state->iso);
150 assert(screen);
151 gfx_update_texture(
152 state->screen_texture, &(TextureDataDesc){.pixels = screen});
153
154 GfxCore* gfxcore = gfx_get_core(state->gfx);
155 Renderer* renderer = gfx_get_renderer(state->gfx);
156
157 // TODO: Prevent stretching of the virtual screen onto the window; preserve
158 // aspect ratio.
159 gfx_start_frame(gfxcore);
160 gfx_render_scene(
161 renderer, &(RenderSceneParams){
162 .mode = RenderDefault, .scene = state->scene, .camera = 0});
163 gfx_end_frame(gfxcore);
164}
165
166static void resize(GfxAppState* gfx_app_state, int width, int height) {
167 assert(gfx_app_state);
168 AppState* state = &gfx_app_state->state;
169
170 GfxCore* gfxcore = gfx_get_core(state->gfx);
171 gfx_set_viewport(gfxcore, width, height);
172}
173
174void iso_run(int argc, const char** argv, IsoGfxApp* app) {
175 GfxAppState app_state = {
176 .state = (AppState){
177 .app = app,
178 }
179 };
180 gfx_app_run(
181 &(GfxAppDesc){
182 .argc = argc,
183 .argv = argv,
184 .width = WINDOW_WIDTH,
185 .height = WINDOW_HEIGHT,
186 .max_fps = MAX_FPS,
187 .update_delta_time = MAX_FPS > 0 ? 1.0 / (double)MAX_FPS : 0.0,
188 .title = "Isometric Renderer",
189 .app_state = &app_state},
190 &(GfxAppCallbacks){
191 .init = init,
192 .update = update,
193 .render = render,
194 .resize = resize,
195 .shutdown = shutdown});
196}
diff --git a/gfx-iso/src/backend.c b/gfx-iso/src/backend.c
new file mode 100644
index 0000000..db91647
--- /dev/null
+++ b/gfx-iso/src/backend.c
@@ -0,0 +1,199 @@
1#include <isogfx/backend.h>
2#include <isogfx/isogfx.h>
3
4#include <gfx/core.h>
5#include <gfx/gfx.h>
6#include <gfx/renderer.h>
7#include <gfx/scene.h>
8#include <gfx/util/geometry.h>
9#include <gfx/util/shader.h>
10
11#include <assert.h>
12#include <stdlib.h>
13
14typedef struct IsoBackend {
15 Gfx* gfx;
16 Scene* scene;
17 /// The screen or "iso screen" refers to the colour buffer of the iso graphics
18 /// library. This texture is used to draw the iso screen onto the graphics
19 /// window.
20 Texture* screen_texture;
21 /// Window size.
22 int window_width;
23 int window_height;
24 /// The viewport refers to the area inside the window to which screen_texture
25 /// is drawn. It is a scaled version of the iso screen, scaled while
26 /// respecting the iso screen's aspect ratio to prevent distortion.
27 int viewport_x, viewport_y, viewport_width, viewport_height;
28 double stretch; // Stretch factor from iso screen dimensions to viewport
29 // dimensions.
30} IsoBackend;
31
32IsoBackend* IsoBackendInit(const IsoGfx* iso) {
33 assert(iso);
34
35 IsoBackend* backend = calloc(1, sizeof(IsoBackend));
36 if (!backend) {
37 return 0;
38 }
39
40 if (!(backend->gfx = gfx_init())) {
41 goto cleanup;
42 }
43 GfxCore* gfxcore = gfx_get_core(backend->gfx);
44
45 int screen_width, screen_height;
46 isogfx_get_screen_size(iso, &screen_width, &screen_height);
47
48 if (!(backend->screen_texture = gfx_make_texture(
49 gfxcore, &(TextureDesc){
50 .width = screen_width,
51 .height = screen_height,
52 .dimension = Texture2D,
53 .format = TextureSRGBA8,
54 .filtering = NearestFiltering,
55 .wrap = ClampToEdge,
56 .mipmaps = false}))) {
57 goto cleanup;
58 }
59
60 ShaderProgram* shader = gfx_make_view_texture_shader(gfxcore);
61 if (!shader) {
62 goto cleanup;
63 }
64
65 Geometry* geometry = gfx_make_quad_11(gfxcore);
66 if (!geometry) {
67 goto cleanup;
68 }
69
70 MaterialDesc material_desc = (MaterialDesc){.num_uniforms = 1};
71 material_desc.uniforms[0] = (ShaderUniform){
72 .type = UniformTexture,
73 .value.texture = backend->screen_texture,
74 .name = sstring_make("Texture")};
75 Material* material = gfx_make_material(&material_desc);
76 if (!material) {
77 return false;
78 }
79
80 const MeshDesc mesh_desc =
81 (MeshDesc){.geometry = geometry, .material = material, .shader = shader};
82 Mesh* mesh = gfx_make_mesh(&mesh_desc);
83 if (!mesh) {
84 goto cleanup;
85 }
86
87 SceneObject* object =
88 gfx_make_object(&(ObjectDesc){.num_meshes = 1, .meshes = {mesh}});
89 if (!object) {
90 goto cleanup;
91 }
92
93 backend->scene = gfx_make_scene();
94 SceneNode* node = gfx_make_object_node(object);
95 SceneNode* root = gfx_get_scene_root(backend->scene);
96 gfx_set_node_parent(node, root);
97
98 return backend;
99
100cleanup:
101 if (backend->gfx) {
102 gfx_destroy(&backend->gfx);
103 }
104 free(backend);
105 return 0;
106}
107
108void IsoBackendShutdown(IsoBackend** ppApp) {
109 assert(ppApp);
110
111 IsoBackend* app = *ppApp;
112 if (!app) {
113 return;
114 }
115
116 gfx_destroy(&app->gfx);
117}
118
119void IsoBackendResizeWindow(
120 IsoBackend* app, const IsoGfx* iso, int width, int height) {
121 assert(app);
122 assert(iso);
123
124 app->window_width = width;
125 app->window_height = height;
126
127 // Virtual screen dimensions.
128 int screen_width, screen_height;
129 isogfx_get_screen_size(iso, &screen_width, &screen_height);
130
131 // Stretch the virtual screen onto the viewport while respecting the screen's
132 // aspect ratio to prevent distortion.
133 if (width > height) { // Wide screen.
134 app->stretch = (double)height / (double)screen_height;
135 app->viewport_width = (int)((double)screen_width * app->stretch);
136 app->viewport_height = height;
137 app->viewport_x = (width - app->viewport_width) / 2;
138 app->viewport_y = 0;
139 } else { // Tall screen.
140 app->stretch = (double)width / (double)screen_width;
141 app->viewport_width = width;
142 app->viewport_height = (int)((float)screen_height * app->stretch);
143 app->viewport_x = 0;
144 app->viewport_y = (height - app->viewport_height) / 2;
145 }
146}
147
148void IsoBackendRender(const IsoBackend* app, const IsoGfx* iso) {
149 assert(app);
150 assert(iso);
151
152 const Pixel* screen = isogfx_get_screen_buffer(iso);
153 assert(screen);
154 gfx_update_texture(app->screen_texture, &(TextureDataDesc){.pixels = screen});
155
156 GfxCore* gfxcore = gfx_get_core(app->gfx);
157 Renderer* renderer = gfx_get_renderer(app->gfx);
158
159 // Clear the whole window.
160 gfx_set_viewport(gfxcore, 0, 0, app->window_width, app->window_height);
161 gfx_clear(gfxcore, vec4_make(0, 0, 0, 0));
162
163 // Draw to the subregion where the virtual screen can stretch without
164 // distortion.
165 gfx_set_viewport(
166 gfxcore, app->viewport_x, app->viewport_y, app->viewport_width,
167 app->viewport_height);
168
169 // Render the iso screen.
170 gfx_start_frame(gfxcore);
171 gfx_render_scene(
172 renderer, &(RenderSceneParams){
173 .mode = RenderDefault, .scene = app->scene, .camera = 0});
174 gfx_end_frame(gfxcore);
175}
176
177bool IsoBackendGetMousePosition(
178 const IsoBackend* app, double window_x, double window_y, double* x,
179 double* y) {
180 assert(app);
181
182 // Translate from window coordinates to the subregion where the stretched
183 // iso screen is rendered.
184 const double screen_x = window_x - app->viewport_x;
185 const double screen_y = window_y - app->viewport_y;
186
187 // Position may be out of bounds.
188 if ((0 <= screen_x) && (screen_x < app->viewport_width) && (0 <= screen_y) &&
189 (screen_y < app->viewport_height)) {
190 // Scale back from the stretched subregion to the iso screen dimensions.
191 *x = screen_x / app->stretch;
192 *y = screen_y / app->stretch;
193 return true;
194 } else {
195 *x = -1;
196 *y = -1;
197 return false;
198 }
199}