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