summaryrefslogtreecommitdiff
path: root/gltfview/src
diff options
context:
space:
mode:
Diffstat (limited to 'gltfview/src')
-rw-r--r--gltfview/src/game.c295
-rw-r--r--gltfview/src/game.h32
-rw-r--r--gltfview/src/main.c65
3 files changed, 392 insertions, 0 deletions
diff --git a/gltfview/src/game.c b/gltfview/src/game.c
new file mode 100644
index 0000000..d7352d4
--- /dev/null
+++ b/gltfview/src/game.c
@@ -0,0 +1,295 @@
1#include "game.h"
2
3#include <gfx/error.h>
4#include <gfx/render_backend.h>
5#include <gfx/scene/light.h>
6#include <gfx/scene/material.h>
7#include <gfx/scene/mesh.h>
8#include <gfx/scene/node.h>
9#include <gfx/scene/object.h>
10#include <gfx/util/geometry.h>
11#include <gfx/util/ibl.h>
12#include <gfx/util/scene.h>
13#include <gfx/util/shader.h>
14#include <gfx/util/skyquad.h>
15#include <gfx/util/texture.h>
16
17#include <log/log.h>
18#include <math/camera.h>
19
20#include <assert.h>
21#include <stdbool.h>
22#include <stdio.h>
23#include <string.h>
24#include <unistd.h> // usleep; TODO Remove.
25
26// Paths to various scene files.
27static const char* BOX = "/assets/models/box.gltf";
28static const char* SUZANNE = "/assets/models/suzanne.gltf";
29static const char* SPONZA =
30 "/assets/glTF-Sample-Models/2.0/Sponza/glTF/Sponza.gltf";
31static const char* FLIGHT_HELMET =
32 "/assets/glTF-Sample-Models/2.0/FlightHelmet/glTF/FlightHelmet.gltf";
33static const char* DAMAGED_HELMET =
34 "/assets/glTF-Sample-Models/2.0/DamagedHelmet/glTF/DamagedHelmet.gltf";
35
36#define DEFAULT_SCENE_FILE DAMAGED_HELMET
37
38static const char* CLOUDS1_TEXTURE = "/assets/skybox/clouds1/clouds1_west.bmp";
39
40static ShaderProgram* load_shader(RenderBackend* render_backend,
41 const char* view_mode) {
42 ShaderProgram* shader = 0;
43 if (strcmp(view_mode, "debug") == 0) {
44 shader = gfx_make_debug3d_shader(render_backend);
45 } else if (strcmp(view_mode, "normals") == 0) {
46 shader = gfx_make_view_normals_shader(render_backend);
47 } else if (strcmp(view_mode, "normal_mapped_normals") == 0) {
48 shader = gfx_make_view_normal_mapped_normals_shader(render_backend);
49 } else if (strcmp(view_mode, "tangents") == 0) {
50 shader = gfx_make_view_tangents_shader(render_backend);
51 } else {
52 shader = gfx_make_cook_torrance_shader(render_backend);
53 }
54 return shader;
55}
56
57/// Loads the skyquad texture.
58static Texture* load_environment_map(RenderBackend* render_backend) {
59 return gfx_load_texture(
60 render_backend,
61 &(LoadTextureCmd){
62 .origin = TextureFromFile,
63 .type = LoadCubemap,
64 .colour_space = sRGB,
65 .filtering = NearestFiltering,
66 .mipmaps = false,
67 .data.cubemap.filepaths = {
68 mstring_make("/assets/skybox/clouds1/clouds1_east.bmp"),
69 mstring_make("/assets/skybox/clouds1/clouds1_west.bmp"),
70 mstring_make("/assets/skybox/clouds1/clouds1_up.bmp"),
71 mstring_make("/assets/skybox/clouds1/clouds1_down.bmp"),
72 mstring_make("/assets/skybox/clouds1/clouds1_north.bmp"),
73 mstring_make("/assets/skybox/clouds1/clouds1_south.bmp")}});
74}
75
76/// Creates an object to render the skyquad in the background.
77static SceneNode* make_skyquad_object_node(Game* game,
78 const Texture* environment_map) {
79 assert(game);
80
81 SceneObject* skyquad_object =
82 gfx_make_skyquad(game->gfx, game->scene, environment_map);
83 if (!skyquad_object) {
84 return 0;
85 }
86 SceneNode* skyquad_node = gfx_make_object_node(skyquad_object);
87 if (!skyquad_node) {
88 return 0;
89 }
90 gfx_set_node_parent(skyquad_node, gfx_get_scene_root(game->scene));
91 return skyquad_node;
92}
93
94/// Creates an environment light.
95static SceneNode* make_environment_light(Game* game,
96 const Texture* environment_light) {
97 assert(game);
98
99 Light* light = gfx_make_light(&(LightDesc){
100 .type = EnvironmentLightType,
101 .light = (EnvironmentLightDesc){.environment_map = environment_light}});
102 if (!light) {
103 return 0;
104 }
105 SceneNode* light_node = gfx_make_light_node(light);
106 if (!light_node) {
107 return 0;
108 }
109 gfx_set_node_parent(light_node, gfx_get_scene_root(game->scene));
110 return light_node;
111}
112
113/// Loads the skyquad and returns the SceneNode with the environment light.
114static bool load_skyquad(Game* game, SceneNode** node) {
115 assert(game);
116 assert(node);
117
118 Texture* environment_map = load_environment_map(game->render_backend);
119 if (!environment_map) {
120 return false;
121 }
122
123 make_skyquad_object_node(game, environment_map);
124 *node = make_environment_light(game, environment_map);
125
126 return true;
127}
128
129/// Loads the 3D scene.
130static bool load_scene(Game* game, const char* scene_filepath,
131 const char* view_mode) {
132 assert(game);
133
134 game->camera = gfx_make_camera();
135 if (!game->camera) {
136 return false;
137 }
138 Camera* camera = gfx_get_camera_camera(game->camera);
139 // Sponza.
140 // spatial3_set_position(&camera->spatial, vec3_make(0, 100, 50));
141 // Damaged helmet.
142 spatial3_set_position(&camera->spatial, vec3_make(0, 0, 2));
143
144 SceneNode* sky_node = 0;
145 if (!load_skyquad(game, &sky_node) || !sky_node) {
146 return false;
147 }
148
149 ShaderProgram* shader = load_shader(game->render_backend, view_mode);
150 if (!shader) {
151 return false;
152 }
153
154 if (!gfx_load_scene(game->gfx, sky_node, shader,
155 &(LoadSceneCmd){.origin = SceneFromFile,
156 .filepath = scene_filepath})) {
157 return false;
158 }
159
160 return true;
161}
162
163/// Loads a scene for debugging textures.
164static bool load_texture_debugger_scene(Game* game) {
165 assert(game);
166
167 Texture* texture =
168 gfx_load_texture(game->render_backend,
169 &(LoadTextureCmd){.origin = TextureFromFile,
170 .type = LoadTexture,
171 .filtering = LinearFiltering,
172 .mipmaps = false,
173 .data.texture.filepath =
174 mstring_make(CLOUDS1_TEXTURE)});
175
176 game->camera = gfx_make_camera();
177 if (!game->camera) {
178 return false;
179 }
180 Camera* camera = gfx_get_camera_camera(game->camera);
181 spatial3_set_position(&camera->spatial, vec3_make(0, 0, 1));
182
183 ShaderProgram* shader = gfx_make_view_texture_shader(game->render_backend);
184 if (!shader) {
185 return false;
186 }
187
188 Geometry* geometry = gfx_make_quad_11(game->render_backend);
189 if (!geometry) {
190 return false;
191 }
192
193 MaterialDesc material_desc = (MaterialDesc){0};
194 material_desc.shader = shader;
195 material_desc.uniforms[0] = (ShaderUniform){.type = UniformTexture,
196 .value.texture = texture,
197 .name = sstring_make("Texture")};
198 material_desc.num_uniforms = 1;
199 Material* material = gfx_make_material(&material_desc);
200 if (!material) {
201 return false;
202 }
203
204 MeshDesc mesh_desc = (MeshDesc){0};
205 mesh_desc.geometry = geometry;
206 mesh_desc.material = material;
207 Mesh* mesh = gfx_make_mesh(&mesh_desc);
208 if (!mesh) {
209 return false;
210 }
211
212 SceneObject* object = gfx_make_object();
213 if (!object) {
214 return false;
215 }
216 gfx_add_object_mesh(object, mesh);
217
218 SceneNode* node = gfx_make_object_node(object);
219 SceneNode* root = gfx_get_scene_root(game->scene);
220 gfx_set_node_parent(node, root);
221
222 return true;
223}
224
225bool game_new(Game* game, int argc, const char** argv) {
226 // TODO: getopt() to implement proper argument parsing.
227 const char* view_mode = argc > 1 ? argv[1] : "";
228 const char* scene_filepath = argc > 2 ? argv[2] : DEFAULT_SCENE_FILE;
229
230 game->gfx = gfx_init();
231 if (!game->gfx) {
232 goto cleanup;
233 }
234
235 game->render_backend = gfx_get_render_backend(game->gfx);
236 game->renderer = gfx_get_renderer(game->gfx);
237
238 game->scene = gfx_make_scene(game->gfx);
239 if (!game->scene) {
240 goto cleanup;
241 }
242
243 if (!load_scene(game, scene_filepath, view_mode)) {
244 goto cleanup;
245 }
246 /*if (!load_texture_debugger_scene(game)) {
247 goto cleanup;
248 }*/
249
250 return true;
251
252cleanup:
253 LOGE("Gfx error: %s", gfx_get_error());
254 if (game->scene) {
255 gfx_destroy_scene(game->gfx, &game->scene);
256 }
257 if (game->gfx) {
258 gfx_destroy(&game->gfx);
259 }
260 return false;
261}
262
263void game_end(Game* game) { gfx_destroy(&game->gfx); }
264
265void game_update(Game* game, double t, double dt) {
266 game->elapsed += dt;
267 while (game->elapsed >= 1.0) {
268 // LOGD("Tick");
269 usleep(1000);
270 game->elapsed -= 1.0;
271 }
272 Camera* camera = gfx_get_camera_camera(game->camera);
273 spatial3_orbit(&camera->spatial, vec3_make(0, 0, 0),
274 /*radius=*/2,
275 /*azimuth=*/t * 0.5, /*zenith=*/0);
276 spatial3_lookat(&camera->spatial, vec3_make(0, 0, 0));
277}
278
279void game_render(const Game* game) {
280 gfx_render_scene(game->renderer, game->render_backend, game->scene,
281 game->camera);
282}
283
284void game_set_viewport(Game* game, int width, int height) {
285 gfx_set_viewport(game->render_backend, width, height);
286
287 const R fovy = 90 * TO_RAD;
288 const R aspect = (R)width / (R)height;
289 const R near = 0.1;
290 const R far = 1000;
291 const mat4 projection = mat4_perspective(fovy, aspect, near, far);
292
293 Camera* camera = gfx_get_camera_camera(game->camera);
294 camera->projection = projection;
295}
diff --git a/gltfview/src/game.h b/gltfview/src/game.h
new file mode 100644
index 0000000..92c0885
--- /dev/null
+++ b/gltfview/src/game.h
@@ -0,0 +1,32 @@
1#pragma once
2
3#include <gfx/gfx.h>
4#include <gfx/render_backend.h>
5#include <gfx/renderer.h>
6#include <gfx/scene/camera.h>
7#include <gfx/scene/scene.h>
8
9#include <stdbool.h>
10
11/// The delta time the game should be updated with.
12static const double game_dt = 1.0 / 60.0;
13
14/// Game state.
15typedef struct {
16 Gfx* gfx;
17 RenderBackend* render_backend;
18 Renderer* renderer;
19 Scene* scene;
20 SceneCamera* camera;
21 double elapsed;
22} Game;
23
24bool game_new(Game*, int argc, const char** argv);
25
26void game_end(Game*);
27
28void game_update(Game*, double t, double dt);
29
30void game_render(const Game*);
31
32void game_set_viewport(Game*, int width, int height);
diff --git a/gltfview/src/main.c b/gltfview/src/main.c
new file mode 100644
index 0000000..f7c372c
--- /dev/null
+++ b/gltfview/src/main.c
@@ -0,0 +1,65 @@
1#include "game.h"
2
3#include <gfx/gfx_app.h>
4#include <log/log.h>
5
6#include <stdlib.h>
7
8static bool init(const GfxAppDesc* desc, void** app_state) {
9 Game* game = calloc(1, sizeof(Game));
10 if (!game) {
11 LOGE("Failed to allocate game state");
12 return false;
13 }
14 if (!game_new(game, desc->argc, desc->argv)) {
15 LOGE("Failed to initialize game\n");
16 return false;
17 }
18 *app_state = game;
19 return true;
20}
21
22static void shutdown(void* app_state) {
23 assert(app_state);
24 Game* game = (Game*)(app_state);
25 game_end(game);
26}
27
28static void update(void* app_state, double t, double dt) {
29 assert(app_state);
30 Game* game = (Game*)(app_state);
31 game_update(game, t, dt);
32}
33
34static void render(void* app_state) {
35 assert(app_state);
36 Game* game = (Game*)(app_state);
37 game_render(game);
38}
39
40static void resize(void* app_state, int width, int height) {
41 assert(app_state);
42 Game* game = (Game*)(app_state);
43 game_set_viewport(game, width, height);
44}
45
46int main(int argc, const char** argv) {
47 const int initial_width = 1350;
48 const int initial_height = 900;
49 const int max_fps = 60;
50
51 gfx_app_run(&(GfxAppDesc){.argc = argc,
52 .argv = argv,
53 .width = initial_width,
54 .height = initial_height,
55 .max_fps = max_fps,
56 .update_delta_time =
57 max_fps > 0 ? 1.0 / (double)max_fps : 0.0},
58 &(GfxAppCallbacks){.init = init,
59 .update = update,
60 .render = render,
61 .resize = resize,
62 .shutdown = shutdown});
63
64 return 0;
65}