From 520e4e67cd9ff53f3c3512c80d07193625e07e3e Mon Sep 17 00:00:00 2001
From: 3gg <3gg@shellblade.net>
Date: Fri, 16 Jun 2023 09:38:15 -0700
Subject: New plugin architecture.

---
 gltfview/src/plugins/CMakeLists.txt |  17 ++++
 gltfview/src/plugins/gltf_view.c    | 167 ++++++++++++++++++++++++++++++++++++
 gltfview/src/plugins/gltf_view.h    |   9 ++
 gltfview/src/plugins/plugin.h       |  49 +++++++++++
 gltfview/src/plugins/texture_view.c |  94 ++++++++++++++++++++
 gltfview/src/plugins/texture_view.h |   9 ++
 6 files changed, 345 insertions(+)
 create mode 100644 gltfview/src/plugins/CMakeLists.txt
 create mode 100644 gltfview/src/plugins/gltf_view.c
 create mode 100644 gltfview/src/plugins/gltf_view.h
 create mode 100644 gltfview/src/plugins/plugin.h
 create mode 100644 gltfview/src/plugins/texture_view.c
 create mode 100644 gltfview/src/plugins/texture_view.h

(limited to 'gltfview/src/plugins')

diff --git a/gltfview/src/plugins/CMakeLists.txt b/gltfview/src/plugins/CMakeLists.txt
new file mode 100644
index 0000000..ecb2a45
--- /dev/null
+++ b/gltfview/src/plugins/CMakeLists.txt
@@ -0,0 +1,17 @@
+cmake_minimum_required(VERSION 3.0)
+
+project(plugins)
+
+set(LINK_LIBRARIES cstring math gfx)
+
+add_library(gltf_view SHARED
+  gltf_view.c)
+
+add_library(texture_view SHARED
+  texture_view.c)
+
+target_link_libraries(gltf_view PUBLIC
+  ${LINK_LIBRARIES})
+
+target_link_libraries(texture_view PUBLIC
+  ${LINK_LIBRARIES})
diff --git a/gltfview/src/plugins/gltf_view.c b/gltfview/src/plugins/gltf_view.c
new file mode 100644
index 0000000..511c2e8
--- /dev/null
+++ b/gltfview/src/plugins/gltf_view.c
@@ -0,0 +1,167 @@
+#include "gltf_view.h"
+
+#include <gfx/renderer.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
+
+/// 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, const char* scene_filepath) {
+  assert(game);
+  assert(game->gfx);
+  assert(game->scene);
+
+  SceneNode*     root           = gfx_get_scene_root(game->scene);
+  RenderBackend* render_backend = gfx_get_render_backend(game->gfx);
+
+  Camera* camera = gfx_get_camera_camera(game->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;
+}
+
+State* init(Game* game) {
+  assert(game);
+
+  State* state = calloc(1, sizeof(State));
+  return state;
+}
+
+bool boot(State* state, Game* game) {
+  assert(state);
+  assert(game);
+
+  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, scene_filepath);
+  if (!node) {
+    return false;
+  }
+  Anima* anima = gfx_get_node_anima(node);
+  gfx_play_animation(
+      anima, &(AnimationPlaySettings){.name = "Walk", .loop = true});
+
+  return true;
+}
+
+void update(State* state, Game* game, double t, double dt) {
+  assert(state);
+  assert(game);
+  assert(game->scene);
+  assert(game->camera);
+
+  gfx_animate_scene(game->scene, (R)t);
+
+  const vec3 orbit_point = vec3_make(0, 2, 0);
+  Camera*    camera      = gfx_get_camera_camera(game->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(State* state, const Game* game) {
+  assert(state);
+  assert(game);
+  assert(game->gfx);
+  assert(game->scene);
+  assert(game->camera);
+
+  ImmRenderer* imm = gfx_get_imm_renderer(game->gfx);
+  assert(imm);
+  gfx_imm_start(imm);
+  gfx_imm_set_camera(imm, gfx_get_camera_camera(game->camera));
+  gfx_imm_set_colour(imm, vec4_make(0.2, 0.2, 1.0, 0.3));
+  render_bounding_boxes(imm, gfx_get_scene_root(game->scene));
+  gfx_imm_end(imm);
+}
diff --git a/gltfview/src/plugins/gltf_view.h b/gltfview/src/plugins/gltf_view.h
new file mode 100644
index 0000000..670d88d
--- /dev/null
+++ b/gltfview/src/plugins/gltf_view.h
@@ -0,0 +1,9 @@
+#pragma once
+
+#include "plugin.h"
+
+#include <gfx/scene.h>
+
+typedef struct State {
+  int unused;
+} State;
diff --git a/gltfview/src/plugins/plugin.h b/gltfview/src/plugins/plugin.h
new file mode 100644
index 0000000..0e0e12c
--- /dev/null
+++ b/gltfview/src/plugins/plugin.h
@@ -0,0 +1,49 @@
+/*
+ * Game plugin.
+ *
+ * A game plugin exposes three functions:
+ * - boot(): called once when the plugin is first loaded during the lifetime of
+ * the game.
+ * - init() -> state: creates and returns the plugin's state.
+ * - update(state): takes and updates the state, possibly with side effects.
+ * - render(): performs custom rendering.
+ *
+ * boot() is convenient for one-time initialization of the scene.
+ *
+ * init() is called every time the plugin is loaded. It is assumed that the
+ * plugin's state is encapsulated in the object returned.
+ *
+ * update() updates the plugin state and has side effects on the scene. It is
+ * assumed that update does not reference any global, mutable state outside of
+ * the scene and the plugin state returned by init().
+ */
+#pragma once
+
+#include "../game.h"
+
+#include <gfx/gfx.h>
+#include <gfx/scene.h>
+
+#include <stdbool.h>
+
+typedef struct State State;
+
+/// Initialize the plugin's state.
+State* init(Game*);
+
+/// Function called the first time the plugin is loaded throughout the
+/// application's lifetime. Allows the plugin to do one-time initialization of
+/// the game state.
+bool boot(State*, Game*);
+
+/// Update the plugin's and the game's state.
+void update(State*, Game*, double t, double dt);
+
+/// Optional plugin rendering hook.
+void render(State*, const Game*);
+
+// Signatures for the plugin's exposed functions.
+typedef void* (*plugin_init)(Game*);
+typedef bool (*plugin_boot)(State*, Game*);
+typedef void (*plugin_update)(State*, Game*, double t, double dt);
+typedef void (*plugin_render)(State*, const Game*);
diff --git a/gltfview/src/plugins/texture_view.c b/gltfview/src/plugins/texture_view.c
new file mode 100644
index 0000000..f2c650f
--- /dev/null
+++ b/gltfview/src/plugins/texture_view.c
@@ -0,0 +1,94 @@
+#include "texture_view.h"
+
+#include <gfx/render_backend.h>
+#include <gfx/util/geometry.h>
+#include <gfx/util/shader.h>
+#include <gfx/util/texture.h>
+
+#include <math/camera.h>
+
+#include <assert.h>
+#include <stdlib.h>
+
+// Default texture to load if no texture is provided.
+static const char* CLOUDS1_TEXTURE = "/assets/skybox/clouds1/clouds1_west.bmp";
+
+State* init(Game* game) {
+  assert(game);
+
+  State* state = calloc(1, sizeof(State));
+  return state;
+}
+
+bool boot(State* state, Game* game) {
+  assert(state);
+  assert(game);
+
+  // Usage: [texture file]
+  const char* texture_file = game->argc > 1 ? game->argv[1] : CLOUDS1_TEXTURE;
+
+  RenderBackend* render_backend = gfx_get_render_backend(game->gfx);
+
+  Texture* texture = gfx_load_texture(
+      render_backend, &(LoadTextureCmd){
+                          .origin                = TextureFromFile,
+                          .type                  = LoadTexture,
+                          .filtering             = LinearFiltering,
+                          .mipmaps               = false,
+                          .data.texture.filepath = mstring_make(texture_file)});
+
+  Camera* camera = gfx_get_camera_camera(game->camera);
+  spatial3_set_position(&camera->spatial, vec3_make(0, 0, 1));
+
+  ShaderProgram* shader = gfx_make_view_texture_shader(render_backend);
+  if (!shader) {
+    return false;
+  }
+
+  Geometry* geometry = gfx_make_quad_11(render_backend);
+  if (!geometry) {
+    return false;
+  }
+
+  MaterialDesc material_desc = (MaterialDesc){0};
+  material_desc.uniforms[0]  = (ShaderUniform){
+       .type          = UniformTexture,
+       .value.texture = texture,
+       .name          = sstring_make("Texture")};
+  material_desc.num_uniforms = 1;
+  Material* material         = gfx_make_material(&material_desc);
+  if (!material) {
+    return false;
+  }
+
+  MeshDesc mesh_desc = (MeshDesc){0};
+  mesh_desc.geometry = geometry;
+  mesh_desc.material = material;
+  mesh_desc.shader   = shader;
+  Mesh* mesh         = gfx_make_mesh(&mesh_desc);
+  if (!mesh) {
+    return false;
+  }
+
+  SceneObject* object = gfx_make_object();
+  if (!object) {
+    return false;
+  }
+  gfx_add_object_mesh(object, mesh);
+
+  SceneNode* node = gfx_make_object_node(object);
+  SceneNode* root = gfx_get_scene_root(game->scene);
+  gfx_set_node_parent(node, root);
+
+  return true;
+}
+
+void update(State* state, Game* game, double t, double dt) {
+  assert(state);
+  assert(game);
+}
+
+void render(State* state, const Game* game) {
+  assert(state);
+  assert(game);
+}
diff --git a/gltfview/src/plugins/texture_view.h b/gltfview/src/plugins/texture_view.h
new file mode 100644
index 0000000..670d88d
--- /dev/null
+++ b/gltfview/src/plugins/texture_view.h
@@ -0,0 +1,9 @@
+#pragma once
+
+#include "plugin.h"
+
+#include <gfx/scene.h>
+
+typedef struct State {
+  int unused;
+} State;
-- 
cgit v1.2.3