aboutsummaryrefslogtreecommitdiff
path: root/game/src/plugins/viewer.c
diff options
context:
space:
mode:
Diffstat (limited to 'game/src/plugins/viewer.c')
-rw-r--r--game/src/plugins/viewer.c374
1 files changed, 0 insertions, 374 deletions
diff --git a/game/src/plugins/viewer.c b/game/src/plugins/viewer.c
deleted file mode 100644
index 23b9ffb..0000000
--- a/game/src/plugins/viewer.c
+++ /dev/null
@@ -1,374 +0,0 @@
1#include "plugin.h"
2
3#include <gfx/app.h>
4#include <gfx/asset.h>
5#include <gfx/renderer.h>
6#include <gfx/scene.h>
7#include <gfx/util/skyquad.h>
8#include <math/camera.h>
9#include <math/spatial3.h>
10
11#include <log/log.h>
12
13#include <stdlib.h>
14
15// Skybox.
16static const char* skybox[6] = {
17 "/home/jeanne/Nextcloud/assets/textures/skybox/clouds1/clouds1_east.bmp",
18 "/home/jeanne/Nextcloud/assets/textures/skybox/clouds1/clouds1_west.bmp",
19 "/home/jeanne/Nextcloud/assets/textures/skybox/clouds1/clouds1_up.bmp",
20 "/home/jeanne/Nextcloud/assets/textures/skybox/clouds1/clouds1_down.bmp",
21 "/home/jeanne/Nextcloud/assets/textures/skybox/clouds1/clouds1_south.bmp",
22 "/home/jeanne/Nextcloud/assets/textures/skybox/clouds1/clouds1_north.bmp",
23};
24
25// Paths to various scene files.
26static const char* BOX = "/home/jeanne/Nextcloud/assets/models/box.gltf";
27static const char* SUZANNE =
28 "/home/jeanne/Nextcloud/assets/models/suzanne.gltf";
29static const char* SPONZA = "/home/jeanne/Nextcloud/assets/glTF-Sample-Models/"
30 "2.0/Sponza/glTF/Sponza.gltf";
31static const char* FLIGHT_HELMET =
32 "/home/jeanne/Nextcloud/assets/glTF-Sample-Models/2.0/FlightHelmet/glTF/"
33 "FlightHelmet.gltf";
34static const char* DAMAGED_HELMET =
35 "/home/jeanne/Nextcloud/assets/glTF-Sample-Models/2.0/DamagedHelmet/glTF/"
36 "DamagedHelmet.gltf";
37static const char* GIRL =
38 "/home/jeanne/Nextcloud/assets/models/girl/girl-with-ground.gltf";
39static const char* BOXES =
40 "/home/jeanne/Nextcloud/assets/models/boxes/boxes.gltf";
41
42#define DEFAULT_SCENE_FILE GIRL
43
44static const bool RenderBoundingBoxes = false;
45static const R DefaultCameraSpeed = (R)6.0;
46static const R DefaultMouseSensitivity = (R)(10 * TO_RAD);
47static const vec3 DefaultCameraPosition = (vec3){0, 2, 5};
48
49typedef struct CameraCommand {
50 bool CameraMoveLeft : 1;
51 bool CameraMoveRight : 1;
52 bool CameraMoveForward : 1;
53 bool CameraMoveBackward : 1;
54} CameraCommand;
55
56typedef struct CameraController {
57 R camera_speed; // Camera movement speed.
58 R mouse_sensitivity; // Controls the degree with which mouse movements
59 // rotate the camera.
60 vec2 prev_mouse_position; // Mouse position in the previous frame.
61 bool rotating; // When true, subsequent mouse movements cause the
62 // camera to rotate.
63} CameraController;
64
65typedef struct State {
66 Scene* scene;
67 Model* model;
68 SceneCamera* camera;
69 CameraController camera_controller;
70} State;
71
72/// Load the skyquad texture.
73static const Texture* load_environment_map(Gfx* gfx) {
74 assert(gfx);
75 return gfx_load_texture(
76 gfx, &(LoadTextureCmd){
77 .origin = AssetFromFile,
78 .type = LoadCubemap,
79 .colour_space = sRGB,
80 .filtering = NearestFiltering,
81 .mipmaps = false,
82 .data.cubemap.filepaths = {
83 mstring_make(skybox[0]), mstring_make(skybox[1]),
84 mstring_make(skybox[2]), mstring_make(skybox[3]),
85 mstring_make(skybox[4]), mstring_make(skybox[5])}
86 });
87}
88
89/// Load the skyquad and return the environment light node.
90static SceneNode* load_skyquad(Gfx* gfx, SceneNode* root) {
91 assert(gfx);
92 assert(root);
93
94 GfxCore* gfxcore = gfx_get_core(gfx);
95
96 const Texture* environment_map = load_environment_map(gfx);
97 if (!environment_map) {
98 return 0;
99 }
100
101 return gfx_setup_skyquad(gfxcore, root, environment_map);
102}
103
104/// Load the 3D scene.
105/// Return the loaded model.
106static Model* load_scene(Game* game, State* state, const char* scene_filepath) {
107 assert(game);
108 assert(game->gfx);
109 assert(state);
110 assert(state->scene);
111
112 Camera* camera = gfx_get_camera_camera(state->camera);
113 spatial3_set_position(&camera->spatial, vec3_make(0, 0, 2));
114
115 SceneNode* root = gfx_get_scene_root(state->scene);
116 SceneNode* sky_light_node = load_skyquad(game->gfx, root);
117 if (!sky_light_node) {
118 return 0; // test
119 }
120
121 Model* model = gfx_load_model(
122 game->gfx, &(LoadModelCmd){.origin = AssetFromFile,
123 .filepath = mstring_make(scene_filepath)});
124 if (!model) {
125 return 0;
126 }
127 SceneNode* model_node = gfx_make_model_node(model);
128 if (!model_node) {
129 return 0;
130 }
131 gfx_set_node_parent(model_node, sky_light_node);
132
133 gfx_log_node_hierarchy(root);
134
135 return model;
136}
137
138bool init(Game* game, State** pp_state) {
139 assert(game);
140
141 // Usage: <scene file>
142 const char* scene_filepath =
143 game->argc > 1 ? game->argv[1] : DEFAULT_SCENE_FILE;
144
145 State* state = calloc(1, sizeof(State));
146 if (!state) {
147 goto cleanup;
148 }
149
150 if (!(state->scene = gfx_make_scene())) {
151 goto cleanup;
152 }
153 if (!(state->camera = gfx_make_camera())) {
154 goto cleanup;
155 }
156
157 state->model = load_scene(game, state, scene_filepath);
158 if (!state->model) {
159 goto cleanup;
160 }
161
162 Anima* anima = gfx_get_model_anima(state->model);
163 if (anima) {
164 gfx_play_animation(
165 anima, &(AnimationPlaySettings){.name = "Walk", .loop = true});
166 // TODO: Interpolate animations.
167 /*gfx_play_animation(
168 anima,
169 &(AnimationPlaySettings){.name = "Jumping-jack-lower", .loop = true});
170 gfx_play_animation(
171 anima, &(AnimationPlaySettings){
172 .name = "Jumping-jack-arms-mid", .loop = true});*/
173 }
174
175 spatial3_set_position(
176 &gfx_get_camera_camera(state->camera)->spatial, DefaultCameraPosition);
177
178 state->camera_controller.camera_speed = DefaultCameraSpeed;
179 state->camera_controller.mouse_sensitivity = DefaultMouseSensitivity;
180
181 *pp_state = state;
182 return true;
183
184cleanup:
185 shutdown(game, state);
186 if (state) {
187 free(state);
188 }
189 return false;
190}
191
192void shutdown(Game* game, State* state) {
193 assert(game);
194 if (state) {
195 gfx_destroy_camera(&state->camera);
196 gfx_destroy_scene(&state->scene);
197 // State freed by plugin engine.
198 }
199}
200
201static void update_camera(
202 CameraController* controller, R dt, vec2 mouse_position,
203 CameraCommand command, Spatial3* camera) {
204 assert(controller);
205 assert(camera);
206
207 // Translation.
208 const R move_x = (R)(command.CameraMoveLeft ? -1 : 0) +
209 (R)(command.CameraMoveRight ? 1 : 0);
210 const R move_y = (R)(command.CameraMoveForward ? 1 : 0) +
211 (R)(command.CameraMoveBackward ? -1 : 0);
212 const vec2 translation =
213 vec2_scale(vec2_make(move_x, move_y), controller->camera_speed * dt);
214 spatial3_move_right(camera, translation.x);
215 spatial3_move_forwards(camera, translation.y);
216
217 // Rotation.
218 if (controller->rotating) {
219 const vec2 mouse_delta =
220 vec2_sub(mouse_position, controller->prev_mouse_position);
221
222 const vec2 rotation =
223 vec2_scale(mouse_delta, controller->mouse_sensitivity * dt);
224
225 spatial3_global_yaw(camera, -rotation.x);
226 spatial3_pitch(camera, -rotation.y);
227 }
228
229 // Update controller state.
230 controller->prev_mouse_position = mouse_position;
231}
232
233void update(Game* game, State* state, double t, double dt) {
234 assert(game);
235 assert(state);
236 assert(state->scene);
237 assert(state->camera);
238
239 double mouse_x, mouse_y;
240 gfx_app_get_mouse_position(&mouse_x, &mouse_y);
241 const vec2 mouse_position = {(R)mouse_x, (R)mouse_y};
242
243 const CameraCommand camera_command = (CameraCommand){
244 .CameraMoveLeft = gfx_app_is_key_pressed(KeyA),
245 .CameraMoveRight = gfx_app_is_key_pressed(KeyD),
246 .CameraMoveForward = gfx_app_is_key_pressed(KeyW),
247 .CameraMoveBackward = gfx_app_is_key_pressed(KeyS),
248 };
249
250 state->camera_controller.rotating = gfx_app_is_mouse_button_pressed(LMB);
251
252 update_camera(
253 &state->camera_controller, (R)dt, mouse_position, camera_command,
254 &gfx_get_camera_camera(state->camera)->spatial);
255
256 // const vec3 orbit_point = vec3_make(0, 2, 0);
257 // Camera* camera = gfx_get_camera_camera(state->camera);
258 // spatial3_orbit(
259 // &camera->spatial, orbit_point,
260 // /*radius=*/5,
261 // /*azimuth=*/(R)(t * 0.5), /*zenith=*/0);
262 // spatial3_lookat(&camera->spatial, orbit_point);
263
264 gfx_update(state->scene, state->camera, (R)t);
265}
266
267/// Render the bounding boxes of all scene objects.
268static void render_bounding_boxes_rec(
269 ImmRenderer* imm, const Anima* anima, const mat4* parent_model_matrix,
270 const SceneNode* node) {
271 assert(imm);
272 assert(node);
273
274 const mat4 model_matrix =
275 mat4_mul(*parent_model_matrix, gfx_get_node_transform(node));
276
277 const NodeType node_type = gfx_get_node_type(node);
278
279 if (node_type == ModelNode) {
280 const Model* model = gfx_get_node_model(node);
281 const SceneNode* root = gfx_get_model_root(model);
282 render_bounding_boxes_rec(imm, anima, &model_matrix, root);
283 } else if (node_type == AnimaNode) {
284 anima = gfx_get_node_anima(node);
285 } else if (node_type == ObjectNode) {
286 gfx_imm_set_model_matrix(imm, &model_matrix);
287
288 const SceneObject* obj = gfx_get_node_object(node);
289 const Skeleton* skeleton = gfx_get_object_skeleton(obj);
290
291 if (skeleton) { // Animated model.
292 assert(anima);
293 const size_t num_joints = gfx_get_skeleton_num_joints(skeleton);
294 for (size_t i = 0; i < num_joints; ++i) {
295 if (gfx_joint_has_box(anima, skeleton, i)) {
296 const Box box = gfx_get_joint_box(anima, skeleton, i);
297 gfx_imm_draw_box3(imm, box.vertices);
298 }
299 }
300 } else { // Static model.
301 const aabb3 box = gfx_get_object_aabb(obj);
302 gfx_imm_draw_aabb3(imm, box);
303 }
304 }
305
306 // Render children's boxes.
307 const SceneNode* child = gfx_get_node_child(node);
308 while (child) {
309 render_bounding_boxes_rec(imm, anima, &model_matrix, child);
310 child = gfx_get_node_sibling(child);
311 }
312}
313
314/// Render the bounding boxes of all scene objects.
315static void render_bounding_boxes(const Game* game, const State* state) {
316 assert(game);
317 assert(state);
318
319 GfxCore* gfxcore = gfx_get_core(game->gfx);
320 ImmRenderer* imm = gfx_get_imm_renderer(game->gfx);
321 assert(gfxcore);
322 assert(imm);
323
324 const mat4 id = mat4_id();
325 Anima* anima = 0;
326
327 gfx_set_blending(gfxcore, true);
328 gfx_set_depth_mask(gfxcore, false);
329 gfx_set_polygon_offset(gfxcore, -1.5f, -1.0f);
330
331 gfx_imm_start(imm);
332 gfx_imm_set_camera(imm, gfx_get_camera_camera(state->camera));
333 gfx_imm_set_colour(imm, vec4_make(0.3, 0.3, 0.9, 0.1));
334 render_bounding_boxes_rec(imm, anima, &id, gfx_get_scene_root(state->scene));
335 gfx_imm_end(imm);
336
337 gfx_reset_polygon_offset(gfxcore);
338 gfx_set_depth_mask(gfxcore, true);
339 gfx_set_blending(gfxcore, false);
340}
341
342void render(const Game* game, const State* state) {
343 assert(state);
344 assert(game);
345 assert(game->gfx);
346 assert(state->scene);
347 assert(state->camera);
348
349 Renderer* renderer = gfx_get_renderer(game->gfx);
350 assert(renderer);
351
352 gfx_render_scene(
353 renderer, &(RenderSceneParams){.mode = RenderDefault,
354 .scene = state->scene,
355 .camera = state->camera});
356
357 if (RenderBoundingBoxes) {
358 render_bounding_boxes(game, state);
359 }
360}
361
362void resize(Game* game, State* state, int width, int height) {
363 assert(game);
364 assert(state);
365
366 const R fovy = 60 * TO_RAD;
367 const R aspect = (R)width / (R)height;
368 const R near = 0.1;
369 const R far = 1000;
370 const mat4 projection = mat4_perspective(fovy, aspect, near, far);
371
372 Camera* camera = gfx_get_camera_camera(state->camera);
373 camera->projection = projection;
374}