From bd57f345ed9dbed1d81683e48199626de2ea9044 Mon Sep 17 00:00:00 2001 From: 3gg <3gg@shellblade.net> Date: Fri, 27 Jun 2025 10:18:39 -0700 Subject: Restructure project --- src/scene/node.c | 409 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 409 insertions(+) create mode 100644 src/scene/node.c (limited to 'src/scene/node.c') diff --git a/src/scene/node.c b/src/scene/node.c new file mode 100644 index 0000000..67ce93c --- /dev/null +++ b/src/scene/node.c @@ -0,0 +1,409 @@ +#include "node_impl.h" + +#include "animation_impl.h" +#include "camera_impl.h" +#include "light_impl.h" +#include "model_impl.h" +#include "object_impl.h" +#include "scene_graph.h" +#include "scene_memory.h" + +#include "gfx_assert.h" + +#include +#include + +static void scene_node_make(SceneNode* node) { + assert(node); + node->type = LogicalNode; + node->transform = mat4_id(); +} + +SceneNode* gfx_make_node() { + SceneNode* node = mem_alloc_node(); + scene_node_make(node); + return node; +} + +SceneNode* gfx_make_anima_node(Anima* anima) { + assert(anima); + SceneNode* node = gfx_make_node(); + node->type = AnimaNode; + node->anima = mem_get_anima_index(anima); + anima->parent = mem_get_node_index(node); + return node; +} + +SceneNode* gfx_make_camera_node(SceneCamera* camera) { + assert(camera); + SceneNode* node = gfx_make_node(); + node->type = CameraNode; + node->camera = mem_get_camera_index(camera); + camera->parent = mem_get_node_index(node); + return node; +} + +SceneNode* gfx_make_light_node(Light* light) { + assert(light); + SceneNode* node = gfx_make_node(); + node->type = LightNode; + node->light = mem_get_light_index(light); + light->parent = mem_get_node_index(node); + return node; +} + +SceneNode* gfx_make_model_node(Model* model) { + assert(model); + SceneNode* node = gfx_make_node(); + node->type = ModelNode; + node->model = mem_get_model_index(model); + model->parent = mem_get_node_index(node); + return node; +} + +SceneNode* gfx_make_object_node(SceneObject* object) { + assert(object); + SceneNode* node = gfx_make_node(); + node->type = ObjectNode; + node->object = mem_get_object_index(object); + object->parent = mem_get_node_index(node); + return node; +} + +/// Frees the node's resource. +static void free_node_resource(SceneNode* node) { + assert(node); + + // Set the resource's parent node back to 0 to avoid a recursive call into + // gfx_del_node(). + switch (node->type) { + case AnimaNode: { + Anima* anima = mem_get_anima(node->anima); + anima->parent.val = 0; + gfx_destroy_anima(&anima); + return; + } + case CameraNode: { + SceneCamera* camera = mem_get_camera(node->camera); + camera->parent.val = 0; + gfx_destroy_camera(&camera); + return; + } + case LightNode: { + Light* light = mem_get_light(node->light); + light->parent.val = 0; + gfx_destroy_light(&light); + return; + } + case ModelNode: { + return; // Model data is owned by the asset cache. + } + case ObjectNode: { + SceneObject* object = mem_get_object(node->object); + object->parent.val = 0; + gfx_destroy_object(&object); + return; + } + case LogicalNode: + return; // Logical nodes have no resource. + } + FAIL("unhandled node type"); +} + +void gfx_construct_anima_node(SceneNode* node, Anima* anima) { + assert(node); + assert(anima); + free_node_resource(node); + node->type = AnimaNode; + node->anima = mem_get_anima_index(anima); + anima->parent = mem_get_node_index(node); +} + +void gfx_construct_camera_node(SceneNode* node, SceneCamera* camera) { + assert(node); + assert(camera); + free_node_resource(node); + node->type = CameraNode; + node->camera = mem_get_camera_index(camera); + camera->parent = mem_get_node_index(node); +} + +// TODO: Add a common helper function between each gfx_make_xyz_node() and +// gfx_construct_xyz_node() pair. +void gfx_construct_light_node(SceneNode* node, Light* light) { + assert(node); + assert(light); + free_node_resource(node); + node->type = LightNode; + node->light = mem_get_light_index(light); + light->parent = mem_get_node_index(node); +} + +void gfx_construct_model_node(SceneNode* node, Model* model) { + assert(node); + assert(model); + free_node_resource(node); + node->type = ModelNode; + node->model = mem_get_model_index(model); + model->parent = mem_get_node_index(node); +} + +void gfx_construct_object_node(SceneNode* node, SceneObject* object) { + assert(node); + assert(object); + free_node_resource(node); + node->type = ObjectNode; + node->object = mem_get_object_index(object); + object->parent = mem_get_node_index(node); +} + +static void destroy_node_rec(SceneNode* node) { + assert(node); + + // First child. + if (node->child.val) { + destroy_node_rec(mem_get_node(node->child)); + } + + // Right sibling. + if (node->next.val) { + destroy_node_rec(mem_get_node(node->next)); + } + + free_node_resource(node); + mem_free_node(&node); +} + +void gfx_destroy_node(SceneNode** node) { + assert(node); + if (*node) { + // Since the node and the whole hierarchy under it gets destroyed, there is + // no need to individually detach every node from its hierarchy. We can + // simply detach the given node and then destroy it and its sub-hierarchy. + TREE_REMOVE(*node); + destroy_node_rec(*node); + *node = 0; + } +} + +// TODO: Think more about ownership of nodes and resources. Should this function +// even exist? +void gfx_del_node(node_idx index) { + assert(index.val); + SceneNode* node = mem_get_node(index); + assert(node); + // TODO: Should destroy children recursively? + TREE_REMOVE(node); + mem_free_node(&node); +} + +NodeType gfx_get_node_type(const SceneNode* node) { + assert(node); + return node->type; +} + +#define NODE_GET(node, field, expected_type) \ + { \ + assert(node); \ + assert(node->type == expected_type); \ + return mem_get_##field(node->field); \ + } + +const Anima* gfx_get_node_anima(const SceneNode* node) { + NODE_GET(node, anima, AnimaNode); +} + +Anima* gfx_get_node_anima_mut(SceneNode* node) { + NODE_GET(node, anima, AnimaNode); +} + +const SceneCamera* gfx_get_node_camera(const SceneNode* node) { + NODE_GET(node, camera, CameraNode); +} + +SceneCamera* gfx_get_node_camera_mut(SceneNode* node) { + NODE_GET(node, camera, CameraNode); +} + +const Light* gfx_get_node_light(const SceneNode* node) { + NODE_GET(node, light, LightNode); +} + +Light* gfx_get_node_light_mut(SceneNode* node) { + NODE_GET(node, light, LightNode); +} + +const Model* gfx_get_node_model(const SceneNode* node) { + NODE_GET(node, model, ModelNode); +} + +Model* gfx_get_node_model_mut(SceneNode* node) { + NODE_GET(node, model, ModelNode); +} + +const SceneObject* gfx_get_node_object(const SceneNode* node) { + NODE_GET(node, object, ObjectNode); +} + +SceneObject* gfx_get_node_object_mut(SceneNode* node) { + NODE_GET(node, object, ObjectNode); +} + +const SceneNode* gfx_get_node_parent(const SceneNode* node) { + assert(node); + return mem_get_node(node->parent); +} + +SceneNode* gfx_get_node_parent_mut(SceneNode* node) { + assert(node); + return mem_get_node(node->parent); +} + +const SceneNode* gfx_get_node_child(const SceneNode* node) { + assert(node); + if (node->child.val) { + return mem_get_node(node->child); + } else { + return 0; + } +} + +SceneNode* gfx_get_node_child_mut(SceneNode* node) { + return (SceneNode*)gfx_get_node_child(node); +} + +const SceneNode* gfx_get_node_sibling(const SceneNode* node) { + assert(node); + if (node->next.val) { + return mem_get_node(node->next); + } else { + return 0; + } +} + +SceneNode* gfx_get_node_sibling_mut(SceneNode* node) { + return (SceneNode*)gfx_get_node_sibling(node); +} + +mat4 gfx_get_node_transform(const SceneNode* node) { + assert(node); + return node->transform; +} + +mat4 gfx_get_node_global_transform(const SceneNode* node) { + assert(node); + mat4 transform = node->transform; + node_idx parent_index = node->parent; + while (parent_index.val != 0) { + const SceneNode* parent = mem_get_node(parent_index); + transform = mat4_mul(parent->transform, transform); + parent_index = parent->parent; + } + return transform; +} + +void gfx_set_node_parent(SceneNode* child, SceneNode* parent_node) { + assert(child); + // Parent can be null. + SET_PARENT(child, parent_node); +} + +void gfx_set_node_transform(SceneNode* node, const mat4* transform) { + assert(node); + assert(transform); + node->transform = *transform; +} + +void gfx_set_node_position(SceneNode* node, const vec3* position) { + assert(node); + assert(position); + mat4_set_v3(&node->transform, *position); +} + +void gfx_set_node_rotation(SceneNode* node, const quat* rotation) { + assert(node); + assert(rotation); + mat4_set_3x3(&node->transform, mat4_from_quat(*rotation)); +} + +void gfx_set_node_rotation_mat(SceneNode* node, const mat4* rotation) { + assert(node); + assert(rotation); + mat4_set_3x3(&node->transform, *rotation); +} + +static const char* get_node_type_str(NodeType type) { + switch (type) { + case LogicalNode: + return "LogicalNode"; + case AnimaNode: + return "AnimaNode"; + case CameraNode: + return "CameraNode"; + case LightNode: + return "LightNode"; + case ModelNode: + return "ModelNode"; + case ObjectNode: + return "ObjectNode"; + } + FAIL("Unhandled node type"); + return ""; +} + +static void log_node_hierarchy_rec(const SceneNode* node, const sstring* pad) { + assert(node); + assert(pad); + + LOGI( + "%s%s (%u)", sstring_cstr(pad), get_node_type_str(node->type), + mem_get_node_index(node).val); + + // Log the children. + if (node->child.val) { + const sstring new_pad = sstring_concat_cstr(*pad, " "); + log_node_hierarchy_rec(mem_get_node(node->child), &new_pad); + } + + // Then log the siblings. + if (node->next.val) { + log_node_hierarchy_rec(mem_get_node(node->next), pad); + } +} + +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); +} -- cgit v1.2.3