From ff28bcb64cd2043164263ca0a03b41a560763d9a Mon Sep 17 00:00:00 2001 From: 3gg <3gg@shellblade.net> Date: Sat, 17 Feb 2024 13:47:33 -0800 Subject: Asset cache returns model clones. --- gfx/include/gfx/asset.h | 12 ++++-------- gfx/include/gfx/scene/node.h | 5 ++++- gfx/src/asset/asset_cache.c | 35 +++++++++++++++++++++++++++++++++-- gfx/src/scene/node.c | 37 +++++++++++++++++++++++++++++++++++++ gfx/src/scene/node_impl.h | 4 ++++ 5 files changed, 82 insertions(+), 11 deletions(-) diff --git a/gfx/include/gfx/asset.h b/gfx/include/gfx/asset.h index 9845b03..1a4e551 100644 --- a/gfx/include/gfx/asset.h +++ b/gfx/include/gfx/asset.h @@ -86,15 +86,11 @@ typedef struct LoadModelCmd { ShaderProgram* shader; } LoadModelCmd; -// TODO: We should return const resources. If the client wants a mutable copy, -// then let them clone the resource. - -/// Load a scene. -/// -/// Return a top-level node under which scene elements are rooted. +/// Load a model. /// -/// |parent_node| is made the parent of the returned top-level node. It may be -/// null. +/// For animated models, this function returns a (shallow) clone of the model +/// that is safe to mutate. For static models, this returns the original model +/// in the cache. /// /// Currently only supports the GLTF format. Model* gfx_load_model(Gfx*, const LoadModelCmd*); diff --git a/gfx/include/gfx/scene/node.h b/gfx/include/gfx/scene/node.h index 3877670..d048894 100644 --- a/gfx/include/gfx/scene/node.h +++ b/gfx/include/gfx/scene/node.h @@ -85,7 +85,7 @@ void gfx_destroy_node(SceneNode**); // TODO: Review constness of getters here. -/// Return the node's type. +/// Get the node's type. NodeType gfx_get_node_type(const SceneNode*); /// Get the node's anima. @@ -113,6 +113,9 @@ Model* gfx_get_node_model(const SceneNode*); /// The node must be of type ObjectNode. 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*); diff --git a/gfx/src/asset/asset_cache.c b/gfx/src/asset/asset_cache.c index 0320954..e17de9d 100644 --- a/gfx/src/asset/asset_cache.c +++ b/gfx/src/asset/asset_cache.c @@ -1,9 +1,15 @@ #include "asset_cache.h" #include "scene.h" +#include "scene/animation_impl.h" +#include "scene/model_impl.h" +#include "scene/node_impl.h" +#include "scene/scene_memory.h" #include "texture.h" + #include #include +#include #include #include @@ -97,6 +103,31 @@ static void log_model_loaded(const LoadModelCmd* cmd) { } } +static Model* clone_model(const Model* model) { + assert(model); + + // Only the Anima needs to be (shallow) cloned since everything else in the + // model is static. Also note that only the Anima's joints and animation state + // need to be cloned; all other members can be shared. So a shallow clone of + // the anima is sufficient. + const SceneNode* root = mem_get_node(model->root); + if (gfx_get_node_type(root) == AnimaNode) { + const Anima* anima = gfx_get_node_anima(root); + Anima* anima_copy = mem_alloc_anima(); + *anima_copy = *anima; // Shallow copy. + + SceneNode* root_copy = gfx_clone_scene_shallow(root); + root_copy->anima = mem_get_anima_index(anima_copy); + anima_copy->parent = mem_get_node_index(root_copy); + + Model* copy = mem_alloc_model(); + copy->root = mem_get_node_index(root_copy); + return copy; + } else { + return (Model*)model; // Static model, can't be mutated. + } +} + void gfx_init_asset_cache(AssetCache* cache) { assert(cache); mempool_make(&cache->assets); @@ -122,7 +153,7 @@ Model* gfx_load_model(Gfx* gfx, const LoadModelCmd* cmd) { Asset* asset = lookup_cache(cache, hash); if (asset) { log_model_cache_hit(cmd, hash); - return asset->model; + return clone_model(asset->model); } // Asset not found in the cache. @@ -136,7 +167,7 @@ Model* gfx_load_model(Gfx* gfx, const LoadModelCmd* cmd) { }; log_model_loaded(cmd); } - return model; + return clone_model(model); } const Texture* gfx_load_texture(Gfx* gfx, const LoadTextureCmd* cmd) { diff --git a/gfx/src/scene/node.c b/gfx/src/scene/node.c index c48d3dd..0fbb696 100644 --- a/gfx/src/scene/node.c +++ b/gfx/src/scene/node.c @@ -229,6 +229,11 @@ SceneObject* gfx_get_node_object(const SceneNode* node) { NODE_GET(node, object, ObjectNode); } +const SceneNode* gfx_get_node_parent(const SceneNode* node) { + assert(node); + return mem_get_node(node->parent); +} + NodeIter gfx_get_node_child(const SceneNode* node) { assert(node); return (NodeIter)node->child.val; @@ -344,3 +349,35 @@ void gfx_log_node_hierarchy(const SceneNode* node) { const sstring pad = sstring_make(""); log_node_hierarchy_rec(node, &pad); } + +static SceneNode* clone_scene_rec(const SceneNode* node) { + assert(node); + + SceneNode* copy = mem_alloc_node(); + *copy = *node; // Shallow clone of the node's resource. + + if (node->child.val) { + SceneNode* child = mem_get_node(node->child); + SceneNode* child_copy = clone_scene_rec(child); + copy->child = mem_get_node_index(child_copy); + child_copy->parent = mem_get_node_index(copy); + } + + if (node->next.val) { + SceneNode* next = mem_get_node(node->next); + SceneNode* next_copy = clone_scene_rec(next); + copy->next = mem_get_node_index(next_copy); + next_copy->prev = mem_get_node_index(copy); + } + + return copy; +} + +SceneNode* gfx_clone_scene_shallow(const SceneNode* node) { + assert(node); + // Must be a root node; not allowed to have siblings. + assert(!node->prev.val); + assert(!node->next.val); + + return clone_scene_rec(node); +} diff --git a/gfx/src/scene/node_impl.h b/gfx/src/scene/node_impl.h index cabdc60..c79f252 100644 --- a/gfx/src/scene/node_impl.h +++ b/gfx/src/scene/node_impl.h @@ -34,3 +34,7 @@ typedef struct SceneNode { /// /// This function is for the library's internal use only. void gfx_del_node(node_idx); + +/// Return a shallow clone of the scene rooted at the given node. +/// The given node must have no siblings (must be a root node). +SceneNode* gfx_clone_scene_shallow(const SceneNode*); -- cgit v1.2.3