#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); }