From f03ee01bc593ad2271f697bf286ebc04c91da0b1 Mon Sep 17 00:00:00 2001
From: 3gg <3gg@shellblade.net>
Date: Tue, 13 Feb 2024 18:27:18 -0800
Subject: Make loaded scene node an anima node only if the scene has
 skins/skeletons.

---
 game/src/plugins/viewer.c    |  8 ++++--
 gfx/include/gfx/asset.h      | 14 ++++++----
 gfx/include/gfx/scene/node.h |  2 +-
 gfx/include/gfx/util/scene.h | 10 +++----
 gfx/src/asset/asset_cache.c  |  4 +--
 gfx/src/scene/node.c         |  4 +--
 gfx/src/util/scene.c         | 65 ++++++++++++++++++++++++++------------------
 7 files changed, 60 insertions(+), 47 deletions(-)

diff --git a/game/src/plugins/viewer.c b/game/src/plugins/viewer.c
index 3000b30..769887f 100644
--- a/game/src/plugins/viewer.c
+++ b/game/src/plugins/viewer.c
@@ -120,9 +120,11 @@ bool init(Game* game, State** pp_state) {
     goto cleanup;
   }
 
-  Anima* anima = gfx_get_node_anima(node);
-  gfx_play_animation(
-      anima, &(AnimationPlaySettings){.name = "Walk", .loop = true});
+  if (gfx_get_node_type(node) == AnimaNode) {
+    Anima* anima = gfx_get_node_anima(node);
+    gfx_play_animation(
+        anima, &(AnimationPlaySettings){.name = "Walk", .loop = true});
+  }
 
   *pp_state = state;
   return true;
diff --git a/gfx/include/gfx/asset.h b/gfx/include/gfx/asset.h
index 28b8557..c2bac57 100644
--- a/gfx/include/gfx/asset.h
+++ b/gfx/include/gfx/asset.h
@@ -68,6 +68,10 @@ typedef struct LoadTextureCmd {
 } LoadTextureCmd;
 
 /// Describes a command to load a scene.
+///
+/// |shader| is an optional shader program assigned to the loaded scene objects.
+/// If no shader is given, a Cook-Torrance shader based on the object's
+/// characteristics (presence of normals, tangents, etc) is assigned.
 typedef struct LoadSceneCmd {
   AssetOrigin origin;
   union {
@@ -84,15 +88,13 @@ typedef struct LoadSceneCmd {
 
 /// Load a scene.
 ///
-/// Return a top-level node under which scene elements are rooted. |root_node|
-/// is made the parent of this top-level node.
+/// Return a top-level node under which scene elements are rooted.
 ///
-/// |shader| is an optional shader program assigned to the loaded scene objects.
-/// If no shader is given, a Cook-Torrance shader based on the object's
-/// characteristics (presence of normals, tangents, etc) is assigned.
+/// |parent_node| is made the parent of the returned top-level node. It may be
+/// null.
 ///
 /// Currently only supports the GLTF format.
-SceneNode* gfx_load_scene(Gfx*, SceneNode* root_node, const LoadSceneCmd*);
+SceneNode* gfx_load_scene(Gfx*, SceneNode* parent_node, const LoadSceneCmd*);
 
 /// Load a texture.
 Texture* gfx_load_texture(Gfx*, const LoadTextureCmd*);
diff --git a/gfx/include/gfx/scene/node.h b/gfx/include/gfx/scene/node.h
index 9507392..aedac92 100644
--- a/gfx/include/gfx/scene/node.h
+++ b/gfx/include/gfx/scene/node.h
@@ -138,7 +138,7 @@ mat4 gfx_get_node_global_transform(const SceneNode*);
 /// Set the node's parent.
 ///
 /// Pass in null to unwire from the existing parent, if one exists.
-void gfx_set_node_parent(SceneNode*, SceneNode* parent);
+void gfx_set_node_parent(SceneNode*, SceneNode* parent_node);
 
 /// Set the node's (local) transform.
 void gfx_set_node_transform(SceneNode*, const mat4* transform);
diff --git a/gfx/include/gfx/util/scene.h b/gfx/include/gfx/util/scene.h
index c03e2cb..5c47196 100644
--- a/gfx/include/gfx/util/scene.h
+++ b/gfx/include/gfx/util/scene.h
@@ -8,12 +8,10 @@ typedef struct SceneNode SceneNode;
 
 /// Load a scene.
 ///
-/// Return a top-level node under which scene elements are rooted. |root_node|
-/// is made the parent of this top-level node.
+/// Return the top-level node under which scene elements are rooted.
 ///
-/// |shader| is an optional shader program assigned to the loaded scene objects.
-/// If no shader is given, a Cook-Torrance shader based on the object's
-/// characteristics (presence of normals, tangents, etc) is assigned.
+/// |parent_node| is made the parent of the returned top-level node. It may be
+/// null.
 ///
 /// Currently only supports the GLTF format.
-SceneNode* gfx_scene_load(Gfx*, SceneNode* root_node, const LoadSceneCmd*);
+SceneNode* gfx_scene_load(Gfx*, SceneNode* parent_node, const LoadSceneCmd*);
diff --git a/gfx/src/asset/asset_cache.c b/gfx/src/asset/asset_cache.c
index 2fa57ad..fd9f1f8 100644
--- a/gfx/src/asset/asset_cache.c
+++ b/gfx/src/asset/asset_cache.c
@@ -113,7 +113,7 @@ void gfx_destroy_asset_cache(AssetCache* cache) {
 }
 
 SceneNode* gfx_load_scene(
-    Gfx* gfx, SceneNode* root_node, const LoadSceneCmd* cmd) {
+    Gfx* gfx, SceneNode* parent_node, const LoadSceneCmd* cmd) {
   assert(gfx);
 
   AssetCache* cache = gfx_get_asset_cache(gfx);
@@ -130,7 +130,7 @@ SceneNode* gfx_load_scene(
 
   // Asset not found in the cache.
   // Load it, insert it into the cache, and return it.
-  SceneNode* node = gfx_scene_load(gfx, root_node, cmd);
+  SceneNode* node = gfx_scene_load(gfx, parent_node, cmd);
   if (node) {
     *(Asset*)mempool_alloc(&cache->assets) = (Asset){
         .type = SceneAsset,
diff --git a/gfx/src/scene/node.c b/gfx/src/scene/node.c
index 2f761a2..333dc28 100644
--- a/gfx/src/scene/node.c
+++ b/gfx/src/scene/node.c
@@ -271,10 +271,10 @@ mat4 gfx_get_node_global_transform(const SceneNode* node) {
   return transform;
 }
 
-void gfx_set_node_parent(SceneNode* child, SceneNode* parent) {
+void gfx_set_node_parent(SceneNode* child, SceneNode* parent_node) {
   assert(child);
   // Parent can be null.
-  SET_PARENT(child, parent);
+  SET_PARENT(child, parent_node);
 }
 
 void gfx_set_node_transform(SceneNode* node, const mat4* transform) {
diff --git a/gfx/src/util/scene.c b/gfx/src/util/scene.c
index 5bf45aa..a491238 100644
--- a/gfx/src/util/scene.c
+++ b/gfx/src/util/scene.c
@@ -1303,7 +1303,6 @@ static void load_nodes(
   assert(root_node);
   assert(objects);
   assert(cameras);
-  assert(anima);
   assert(nodes);
 
   cgltf_size next_camera = 0;
@@ -1320,6 +1319,8 @@ static void load_nodes(
 
       gfx_construct_object_node(nodes[n], object);
       if (node->skin) {
+        assert(anima);
+        
         const cgltf_size skin_index = node->skin - data->skins;
         assert(skin_index < data->skins_count);
         const Skeleton* skeleton = gfx_get_anima_skeleton(anima, skin_index);
@@ -1406,7 +1407,7 @@ static void load_nodes(
 /// This function ignores the many scenes and default scene of the glTF spec
 /// and instead just loads all scenes into a single gfx Scene.
 static SceneNode* load_scene(
-    cgltf_data* data, Gfx* gfx, SceneNode* root_node, const mstring* filepath,
+    cgltf_data* data, Gfx* gfx, SceneNode* parent_node, const mstring* filepath,
     ShaderProgram* shader, const cgltfTangentBuffer* cgltf_tangent_buffers,
     cgltf_size num_tangent_buffers) {
   // In a GLTF scene, buffers can be shared among meshes, meshes among nodes,
@@ -1427,7 +1428,8 @@ static SceneNode* load_scene(
   //     stage.
   assert(data);
   assert(gfx);
-  assert(root_node);
+  assert(filepath);
+  assert((num_tangent_buffers == 0) || (cgltf_tangent_buffers != 0));
 
   bool success = false;
 
@@ -1450,7 +1452,7 @@ static SceneNode* load_scene(
   SceneCamera**   scene_cameras     = 0;
   SceneNode**     scene_nodes       = 0;
   Anima*          anima             = 0;
-  SceneNode*      anima_node        = 0;
+  SceneNode*      root_node         = 0;
 
   tangent_buffers = calloc(num_tangent_buffers, sizeof(Buffer*));
   buffers         = calloc(data->buffers_count, sizeof(Buffer*));
@@ -1458,7 +1460,6 @@ static SceneNode* load_scene(
   materials       = calloc(data->materials_count, sizeof(Material*));
   geometries      = calloc(primitive_count, sizeof(Geometry*));
   meshes          = calloc(primitive_count, sizeof(Mesh*));
-  anima_desc      = calloc(1, sizeof(AnimaDesc));
   scene_objects   = calloc(data->meshes_count, sizeof(SceneObject*));
   scene_cameras   = calloc(data->cameras_count, sizeof(SceneCamera**));
   scene_nodes     = calloc(data->nodes_count, sizeof(SceneNode**));
@@ -1511,22 +1512,30 @@ static SceneNode* load_scene(
     scene_nodes[i] = gfx_make_node();
   }
 
-  // TODO: If the scene does not have animations, then a top-level LogicalNode
-  // would make more sense than an AnimaNode.
-  anima_node = gfx_make_node();
-  load_skins(data, buffers, scene_nodes, anima_desc->skeletons);
-  load_animations(data, scene_nodes, anima_desc->animations);
-  anima_desc->num_skeletons  = data->skins_count;
-  anima_desc->num_animations = data->animations_count;
-  anima                      = gfx_make_anima(anima_desc);
-  gfx_construct_anima_node(anima_node, anima);
-  gfx_set_node_parent(anima_node, root_node);
+  // Create the scene's root node.
+  // This is an anima node if the scene has skins; otherwise it is a logical
+  // node.
+  root_node = gfx_make_node();
+  if (data->skins_count > 0) {
+    anima_desc = calloc(1, sizeof(AnimaDesc));
+    if (!anima_desc) {
+      goto cleanup;
+    }
+
+    load_skins(data, buffers, scene_nodes, anima_desc->skeletons);
+    load_animations(data, scene_nodes, anima_desc->animations);
 
-  // The anima node becomes the root of all scene nodes.
-  load_nodes(
-      data, anima_node, scene_objects, scene_cameras, anima, scene_nodes);
+    anima_desc->num_skeletons  = data->skins_count;
+    anima_desc->num_animations = data->animations_count;
+    anima                      = gfx_make_anima(anima_desc);
+    gfx_construct_anima_node(root_node, anima);
+  }
+  gfx_set_node_parent(root_node, parent_node);
+
+  // The root node becomes the root of all scene nodes.
+  load_nodes(data, root_node, scene_objects, scene_cameras, anima, scene_nodes);
 
-  success = anima_node != 0;
+  success = true;
 
 cleanup:
   // The arrays of resources are no longer needed. The resources themselves are
@@ -1627,18 +1636,20 @@ cleanup:
     }
     free(scene_nodes);
   }
-  if (!success && anima_node) {
-    gfx_destroy_node(&anima_node); // Node owns the anima.
-  } else if (!success && anima) {
-    gfx_destroy_anima(&anima);
+  if (!success) {
+    if (root_node) {
+      gfx_destroy_node(&root_node); // Node owns the anima.
+    } else if (anima) {
+      gfx_destroy_anima(&anima);
+    }
   }
-  return anima_node;
+  return root_node;
 }
 
 SceneNode* gfx_scene_load(
-    Gfx* gfx, SceneNode* root_node, const LoadSceneCmd* cmd) {
+    Gfx* gfx, SceneNode* parent_node, const LoadSceneCmd* cmd) {
   assert(gfx);
-  assert(root_node);
+  assert(parent_node);
   assert(cmd);
 
   SceneNode* scene_node = 0;
@@ -1674,7 +1685,7 @@ SceneNode* gfx_scene_load(
       &options, data, &tangent_buffers, &num_tangent_buffers);
 
   scene_node = load_scene(
-      data, gfx, root_node, &cmd->filepath, cmd->shader, tangent_buffers,
+      data, gfx, parent_node, &cmd->filepath, cmd->shader, tangent_buffers,
       num_tangent_buffers);
 
 cleanup:
-- 
cgit v1.2.3