From 3cd5b0bcca694630fcb4b977ddf7be7cd1bce153 Mon Sep 17 00:00:00 2001
From: 3gg <3gg@shellblade.net>
Date: Sat, 20 Jan 2024 15:54:54 -0800
Subject: Rename gltfview -> game.

---
 game/src/plugins/gltf_view.c | 196 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 196 insertions(+)
 create mode 100644 game/src/plugins/gltf_view.c

(limited to 'game/src/plugins/gltf_view.c')

diff --git a/game/src/plugins/gltf_view.c b/game/src/plugins/gltf_view.c
new file mode 100644
index 0000000..c19d1b8
--- /dev/null
+++ b/game/src/plugins/gltf_view.c
@@ -0,0 +1,196 @@
+#include "plugin.h"
+
+#include <gfx/renderer.h>
+#include <gfx/scene.h>
+#include <gfx/util/scene.h>
+#include <gfx/util/skyquad.h>
+#include <gfx/util/texture.h>
+#include <math/camera.h>
+#include <math/spatial3.h>
+
+#include <stdlib.h>
+
+// Paths to various scene files.
+static const char* BOX     = "/assets/models/box.gltf";
+static const char* SUZANNE = "/assets/models/suzanne.gltf";
+static const char* SPONZA =
+    "/assets/glTF-Sample-Models/2.0/Sponza/glTF/Sponza.gltf";
+static const char* FLIGHT_HELMET =
+    "/assets/glTF-Sample-Models/2.0/FlightHelmet/glTF/FlightHelmet.gltf";
+static const char* DAMAGED_HELMET =
+    "/assets/glTF-Sample-Models/2.0/DamagedHelmet/glTF/DamagedHelmet.gltf";
+static const char* GIRL =
+    "/home/jeanne/Nextcloud/assets/models/girl/girl-with-ground.gltf";
+
+#define DEFAULT_SCENE_FILE GIRL
+
+struct State {
+  Scene*       scene;
+  SceneCamera* camera;
+};
+
+/// Load the skyquad texture.
+static Texture* load_environment_map(RenderBackend* render_backend) {
+  return gfx_load_texture(
+      render_backend,
+      &(LoadTextureCmd){
+          .origin                 = TextureFromFile,
+          .type                   = LoadCubemap,
+          .colour_space           = sRGB,
+          .filtering              = NearestFiltering,
+          .mipmaps                = false,
+          .data.cubemap.filepaths = {
+                                     mstring_make("/assets/skybox/clouds1/clouds1_east.bmp"),
+                                     mstring_make("/assets/skybox/clouds1/clouds1_west.bmp"),
+                                     mstring_make("/assets/skybox/clouds1/clouds1_up.bmp"),
+                                     mstring_make("/assets/skybox/clouds1/clouds1_down.bmp"),
+                                     mstring_make("/assets/skybox/clouds1/clouds1_south.bmp"),
+                                     mstring_make("/assets/skybox/clouds1/clouds1_north.bmp")}
+  });
+}
+
+/// Load the skyquad and return the environment light node.
+static SceneNode* load_skyquad(RenderBackend* render_backend, SceneNode* root) {
+  assert(render_backend);
+  assert(root);
+
+  Texture* environment_map = load_environment_map(render_backend);
+  if (!environment_map) {
+    return 0;
+  }
+
+  return gfx_setup_skyquad(render_backend, root, environment_map);
+}
+
+/// Load the 3D scene.
+static SceneNode* load_scene(
+    Game* game, State* state, const char* scene_filepath) {
+  assert(game);
+  assert(game->gfx);
+  assert(state);
+  assert(state->scene);
+
+  SceneNode*     root           = gfx_get_scene_root(state->scene);
+  RenderBackend* render_backend = gfx_get_render_backend(game->gfx);
+
+  Camera* camera = gfx_get_camera_camera(state->camera);
+  spatial3_set_position(&camera->spatial, vec3_make(0, 0, 2));
+
+  SceneNode* sky_light_node = load_skyquad(render_backend, root);
+  if (!sky_light_node) {
+    return 0;
+  }
+
+  SceneNode* scene_node = gfx_load_scene(
+      game->gfx, sky_light_node,
+      &(LoadSceneCmd){.origin = SceneFromFile, .filepath = scene_filepath});
+  if (!scene_node) {
+    return 0;
+  }
+
+  gfx_log_node_hierarchy(root);
+
+  return scene_node;
+}
+
+bool init(Game* game, State** pp_state) {
+  assert(game);
+
+  State* state = calloc(1, sizeof(State));
+  if (!state) {
+    goto cleanup;
+  }
+
+  if (!(state->scene = gfx_make_scene())) {
+    goto cleanup;
+  }
+  if (!(state->camera = gfx_make_camera())) {
+    goto cleanup;
+  }
+
+  const int    argc = game->argc;
+  const char** argv = game->argv;
+
+  // Usage: <scene file>
+  const char* scene_filepath = argc > 1 ? argv[1] : DEFAULT_SCENE_FILE;
+
+  SceneNode* node = load_scene(game, state, scene_filepath);
+  if (!node) {
+    goto cleanup;
+  }
+  Anima* anima = gfx_get_node_anima(node);
+  gfx_play_animation(
+      anima, &(AnimationPlaySettings){.name = "Walk", .loop = true});
+
+  *pp_state = state;
+  return true;
+
+cleanup:
+  shutdown(game, state);
+  if (state) {
+    free(state);
+  }
+  return false;
+}
+
+void shutdown(Game* game, State* state) {
+  assert(game);
+  if (state) {
+    gfx_destroy_camera(&state->camera);
+    gfx_destroy_scene(&state->scene);
+    // State freed by plugin engine.
+  }
+}
+
+void update(Game* game, State* state, double t, double dt) {
+  assert(game);
+  assert(state);
+  assert(state->scene);
+  assert(state->camera);
+
+  gfx_animate_scene(state->scene, (R)t);
+
+  const vec3 orbit_point = vec3_make(0, 2, 0);
+  Camera*    camera      = gfx_get_camera_camera(state->camera);
+  spatial3_orbit(
+      &camera->spatial, orbit_point,
+      /*radius=*/2.5,
+      /*azimuth=*/t * 0.5, /*zenith=*/0);
+  spatial3_lookat(&camera->spatial, orbit_point);
+}
+
+/// Render the bounding boxes of all scene objects.
+static void render_bounding_boxes(ImmRenderer* imm, const SceneNode* node) {
+  if (gfx_get_node_type(node) == ObjectNode) {
+    // TODO: Look at the scene log. The JointNodes are detached from the
+    // ObjectNodes. This is why the boxes are not being transformed as expected
+    // here. Anima needs to animate boxes? Use OOBB in addition to AABB?
+    const mat4         model = gfx_get_node_global_transform(node);
+    const SceneObject* obj   = gfx_get_node_object(node);
+    const aabb3        box   = gfx_calc_object_aabb(obj);
+    gfx_imm_set_model_matrix(imm, &model);
+    gfx_imm_draw_aabb(imm, box);
+  }
+
+  // Render children's boxes.
+  for (NodeIter it = gfx_get_node_child(node); it;
+       it          = gfx_get_next_child(it)) {
+    render_bounding_boxes(imm, gfx_get_iter_node(it));
+  }
+}
+
+void render(const Game* game, const State* state) {
+  assert(state);
+  assert(game);
+  assert(game->gfx);
+  assert(state->scene);
+  assert(state->camera);
+
+  ImmRenderer* imm = gfx_get_imm_renderer(game->gfx);
+  assert(imm);
+  gfx_imm_start(imm);
+  gfx_imm_set_camera(imm, gfx_get_camera_camera(state->camera));
+  gfx_imm_set_colour(imm, vec4_make(0.2, 0.2, 1.0, 0.3));
+  render_bounding_boxes(imm, gfx_get_scene_root(state->scene));
+  gfx_imm_end(imm);
+}
-- 
cgit v1.2.3