From 6799f7bd263202d8697c5e27bf12f4e66e4c0ed1 Mon Sep 17 00:00:00 2001
From: 3gg <3gg@shellblade.net>
Date: Sat, 17 Feb 2024 14:35:11 -0800
Subject: Introduce gfx_update().

---
 game/src/plugins/viewer.c     | 19 +++++++------------
 gfx/include/gfx/renderer.h    |  4 ++++
 gfx/include/gfx/scene/model.h |  3 +++
 gfx/include/gfx/scene/node.h  | 21 ++++++++-------------
 gfx/src/renderer/renderer.c   | 33 +++++++++++++++++++++++++++++++++
 gfx/src/scene/model.c         |  4 ++++
 gfx/src/scene/node.c          | 29 +++++++++++++++--------------
 7 files changed, 74 insertions(+), 39 deletions(-)

diff --git a/game/src/plugins/viewer.c b/game/src/plugins/viewer.c
index 945a422..88821af 100644
--- a/game/src/plugins/viewer.c
+++ b/game/src/plugins/viewer.c
@@ -155,21 +155,15 @@ void update(Game* game, State* state, double t, double dt) {
   assert(state->scene);
   assert(state->camera);
 
-  // TODO: Move this to some sort of update_scene(). Note that models do not
-  //  need to be animated if they are not visible to the camera. The camera
-  //  update also should happen first.
-  Anima* anima = gfx_get_model_anima(state->model);
-  if (anima) {
-    gfx_update_animation(anima, (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=*/5,
-      /*azimuth=*/t * 0.5, /*zenith=*/0);
+      /*azimuth=*/(R)(t * 0.5), /*zenith=*/0);
   spatial3_lookat(&camera->spatial, orbit_point);
+
+  gfx_update(state->scene, state->camera, (R)t);
 }
 
 /// Render the bounding boxes of all scene objects.
@@ -207,9 +201,10 @@ static void render_bounding_boxes_rec(ImmRenderer* imm, const SceneNode* node) {
   }
 
   // Render children's boxes.
-  for (NodeIter it = gfx_get_node_child(node); it;
-       it          = gfx_get_next_child(it)) {
-    render_bounding_boxes_rec(imm, gfx_get_iter_node(it));
+  const SceneNode* child = gfx_get_node_child(node);
+  while (child) {
+    render_bounding_boxes_rec(imm, child);
+    child = gfx_get_node_sibling(child);
   }
 }
 
diff --git a/gfx/include/gfx/renderer.h b/gfx/include/gfx/renderer.h
index c420889..78d8bbd 100644
--- a/gfx/include/gfx/renderer.h
+++ b/gfx/include/gfx/renderer.h
@@ -3,6 +3,7 @@
 #include <math/aabb2.h>
 #include <math/aabb3.h>
 #include <math/camera.h>
+#include <math/defs.h>
 #include <math/mat4.h>
 #include <math/vec3.h>
 #include <math/vec4.h>
@@ -35,6 +36,9 @@ typedef struct RenderSceneParams {
 /// Render the scene.
 void gfx_render_scene(Renderer*, const RenderSceneParams*);
 
+/// Update the scene.
+void gfx_update(Scene*, const SceneCamera*, R t);
+
 // -----------------------------------------------------------------------------
 // Immediate Mode Renderer.
 // -----------------------------------------------------------------------------
diff --git a/gfx/include/gfx/scene/model.h b/gfx/include/gfx/scene/model.h
index f4972a9..c55fc8c 100644
--- a/gfx/include/gfx/scene/model.h
+++ b/gfx/include/gfx/scene/model.h
@@ -9,3 +9,6 @@ Anima* gfx_get_model_anima(Model*);
 
 /// Return the model's root node.
 const SceneNode* gfx_get_model_root(const Model*);
+
+/// Return the model's root node.
+SceneNode* gfx_get_model_root_mut(Model*);
diff --git a/gfx/include/gfx/scene/node.h b/gfx/include/gfx/scene/node.h
index d048894..77db85c 100644
--- a/gfx/include/gfx/scene/node.h
+++ b/gfx/include/gfx/scene/node.h
@@ -13,9 +13,6 @@ typedef struct Model       Model;
 typedef struct SceneCamera SceneCamera;
 typedef struct SceneObject SceneObject;
 
-/// Scene node iterator.
-typedef uint32_t NodeIter;
-
 /// Scene node type.
 typedef enum NodeType {
   LogicalNode,
@@ -116,19 +113,17 @@ SceneObject* gfx_get_node_object(const SceneNode*);
 /// Get the node's parent.
 const SceneNode* gfx_get_node_parent(const SceneNode*);
 
-/// Get an iterator to the node's first child.
-NodeIter gfx_get_node_child(const SceneNode*);
-
-/// Get the iterator's next sibling node.
-NodeIter gfx_get_next_child(NodeIter);
+/// Get the node's first child.
+const SceneNode* gfx_get_node_child(const SceneNode*);
 
-// TODO: Think about NodeIter and NodeIterMut for const safety.
+/// Get the node's first child.
+SceneNode* gfx_get_node_child_mut(SceneNode*);
 
-/// Get a scene node from the iterator.
-const SceneNode* gfx_get_iter_node(NodeIter);
+/// Get the node's immediate sibling.
+const SceneNode* gfx_get_node_sibling(const SceneNode*);
 
-/// Get a scene node from the iterator.
-SceneNode* gfx_get_iter_node_mut(NodeIter);
+/// Get the node's immediate sibling.
+SceneNode* gfx_get_node_sibling_mut(SceneNode*);
 
 /// Get the node's (local) transform.
 mat4 gfx_get_node_transform(const SceneNode*);
diff --git a/gfx/src/renderer/renderer.c b/gfx/src/renderer/renderer.c
index 30704e3..c72f719 100644
--- a/gfx/src/renderer/renderer.c
+++ b/gfx/src/renderer/renderer.c
@@ -363,3 +363,36 @@ void gfx_render_scene(Renderer* renderer, const RenderSceneParams* params) {
 
   draw_recursively(&state, mat4_id(), scene->root);
 }
+
+static void update_rec(SceneNode* node, const SceneCamera* camera, R t) {
+  assert(node);
+  assert(camera);
+
+  const NodeType node_type = gfx_get_node_type(node);
+
+  // TODO: Models do not need to be animated if they are not visible to the
+  //  camera.
+  if (node_type == AnimaNode) {
+    Anima* anima = gfx_get_node_anima(node);
+    gfx_update_animation(anima, (R)t);
+  } else if (node_type == ModelNode) {
+    Model*     model = gfx_get_node_model(node);
+    SceneNode* root  = gfx_get_model_root_mut(model);
+    update_rec(root, camera, t);
+  }
+
+  // Children.
+  SceneNode* child = gfx_get_node_child_mut(node);
+  while (child) {
+    update_rec(child, camera, t);
+    child = gfx_get_node_sibling_mut(child);
+  }
+}
+
+void gfx_update(Scene* scene, const SceneCamera* camera, R t) {
+  assert(scene);
+  assert(camera);
+
+  SceneNode* node = gfx_get_scene_root(scene);
+  update_rec(node, camera, t);
+}
diff --git a/gfx/src/scene/model.c b/gfx/src/scene/model.c
index 89ff0c3..507c478 100644
--- a/gfx/src/scene/model.c
+++ b/gfx/src/scene/model.c
@@ -39,3 +39,7 @@ const SceneNode* gfx_get_model_root(const Model* model) {
   assert(model);
   return mem_get_node(model->root);
 }
+
+SceneNode* gfx_get_model_root_mut(Model* model) {
+  return (SceneNode*)gfx_get_model_root(model);
+}
diff --git a/gfx/src/scene/node.c b/gfx/src/scene/node.c
index 0fbb696..05f6428 100644
--- a/gfx/src/scene/node.c
+++ b/gfx/src/scene/node.c
@@ -234,29 +234,30 @@ const SceneNode* gfx_get_node_parent(const SceneNode* node) {
   return mem_get_node(node->parent);
 }
 
-NodeIter gfx_get_node_child(const SceneNode* node) {
+const SceneNode* gfx_get_node_child(const SceneNode* node) {
   assert(node);
-  return (NodeIter)node->child.val;
+  if (node->child.val) {
+    return mem_get_node(node->child);
+  } else {
+    return 0;
+  }
 }
 
-NodeIter gfx_get_next_child(NodeIter it) {
-  if (!it) {
-    return it;
-  }
-  const SceneNode* cur_child  = mem_get_node((node_idx){.val = it});
-  const node_idx   next_child = cur_child->next;
-  return (NodeIter)next_child.val;
+SceneNode* gfx_get_node_child_mut(SceneNode* node) {
+  return (SceneNode*)gfx_get_node_child(node);
 }
 
-const SceneNode* gfx_get_iter_node(NodeIter it) {
-  if (!it) {
+const SceneNode* gfx_get_node_sibling(const SceneNode* node) {
+  assert(node);
+  if (node->next.val) {
+    return mem_get_node(node->next);
+  } else {
     return 0;
   }
-  return mem_get_node((node_idx){.val = it});
 }
 
-SceneNode* gfx_get_iter_node_mut(NodeIter it) {
-  return (SceneNode*)gfx_get_iter_node(it);
+SceneNode* gfx_get_node_sibling_mut(SceneNode* node) {
+  return (SceneNode*)gfx_get_node_sibling(node);
 }
 
 mat4 gfx_get_node_transform(const SceneNode* node) {
-- 
cgit v1.2.3