#include "scene.h" #include "scene_node_entity.h" #include "transformable.h" #include "utils/debug.h" #include "utils/drop.h" #include "utils/mirror.h" //! Update global transform of a node static void Internal_SceneNodeUpdateTransform(SceneNode *self) { if(TC_MIRRORS(self->entity, Transformable)) { Transformable transformable = TC_CAST(self->entity, Transformable); if(transformable.tc->get_dirty_bit(transformable.data)) { transformable.tc->force_update(transformable.data); } } } //! Update global transforms of all children recursively static void Internal_SceneNodeUpdateTransformRecursive(SceneNode *self, bool is_dirty) { if(TC_MIRRORS(self->entity, Transformable)) { Transformable transformable = TC_CAST(self->entity, Transformable); is_dirty = is_dirty || transformable.tc->get_dirty_bit(transformable.data); if(is_dirty) { transformable.tc->force_update(transformable.data); transformable.tc->clear_dirty_bit(transformable.data); } } else { is_dirty = false; } list_foreach(SceneNode **,child, &self->children) Internal_SceneNodeUpdateTransformRecursive(*child, is_dirty); } //! Tick a node and all children recursively static void Internal_SceneNodeTick(SceneNode *self, double delta) { // tick self first self->entity.tc->tick(self->entity.data, delta); // fix the number of elements to update now, // so that if/when new children are added, they don't get ticked until next frame size_t const len = self->children.len; for(size_t i = 0; i < len; ++i) { Internal_SceneNodeTick(*list_at_as(SceneNode*, &self->children, i), delta); } } //! Remove a single node (does NOT recurse to children children) from this scene. static void Internal_SceneRemoveNode(Scene *self, SceneNode *node) { // notify the node entity that it has exited the tree node->entity.tc->exit_tree(node->entity.data); // sever relationship node->scene = NULL; size_t idx = list_find(&self->nodes, &node); list_erase(&self->nodes, idx); } //! Remove a node and all children from this scene recursively starting at the bottom of the tree. static void Internal_SceneRemoveNodeRecursive(Scene *self, SceneNode *node) { // recurse to node's children first list_foreach(SceneNode **,child, &node->children) Internal_SceneRemoveNodeRecursive(self, *child); // remove node Internal_SceneRemoveNode(self, node); } //! Set the scene on a node (does NOT recurse to children), calls enter_tree static void Internal_SceneNodeSetScene(SceneNode *self, Scene *scene) { ASSERT_RETURN(scene != NULL,, "Internal_SceneNodeSetScene: Attempting to set node scene to NULL. Use Internal_SceneRemoveNodeRercursive instead."); self->scene = scene; self->entity.tc->enter_tree(self->entity.data); } //! Register a node and it's children as part of a scene recursively. //! Will call enter_tree on all node entities static void Internal_SceneAddNode(Scene *self, SceneNode *node) { ASSERT_RETURN(node->scene == NULL,, "Attempting to add node that is already in a scene, remove it from that scene first"); ASSERT_RETURN(list_find(&self->nodes, &node) == self->nodes.len,, "Attempting to add node that is already in this scene"); // add to internal list list_add(&self->nodes, &node); // establish two-way relationship Internal_SceneNodeSetScene(node, self); Internal_SceneNodeUpdateTransform(node); // do the same for all children (and their children) recursively list_foreach(SceneNode **,child, &node->children) Internal_SceneAddNode(self, *child); } SceneNode *CreateSceneNode(SceneNodeEntity entity) { // initialize memory SceneNode *self = new(SceneNode); ASSERT_RETURN(self != NULL, NULL, "CreateSceneNode: Failed to allocate new SceneNode"); self->children = list_from_type(SceneNode*); self->entity = tc_null(SceneNodeEntity); self->parent = NULL; self->scene = NULL; // attach initial entity SceneNodeAttachEntity(self, entity); return self; } void DestroySceneNode(SceneNode *self) { // destroy children *first*, then delete self list_foreach(SceneNode **, child, &self->children) DestroySceneNode(*child); // remove node from scene if it is part of one if(self->scene != NULL) Internal_SceneRemoveNode(self->scene, self); // destroy entity as well if(!tc_is_null(self->entity)) self->entity.drop->drop(self->entity.data); free(self); } SceneNode *SceneNodeGetChild(SceneNode *self, size_t idx) { return *list_at_as(SceneNode*, &self->children, idx); } void SceneNodeAddChild(SceneNode *self, SceneNode *child) { // catch logic error of trying to attach a child that already has a parent ASSERT_RETURN(child->parent == NULL,, "SceneNodeAddChild: New child node already has a parent"); // register parent-child relationship list_add(&self->children, &child); child->parent = self; // add child to same scene as self if(self->scene != NULL) Internal_SceneAddNode(self->scene, child); } void SceneNodeRemoveChild(SceneNode *self, SceneNode *child) { // catch error of attempting to remove non-child node ASSERT_RETURN(child->parent == self,, "SceneNodeRemoveChild: Cannot remove node that is not a child of this node."); // get the index and use it to ensure that the child is actually registered with the parent size_t const idx = list_find(&self->children, child); ASSERT_RETURN(idx == self->children.len,, "IMPORTANT: SceneNodeRemoveChild: child is not registered with parent."); if(self->scene != NULL) { Internal_SceneRemoveNodeRecursive(child->scene, child); } // use the index to erase the object from the list list_erase(&self->children, idx); } void SceneNodeAttachEntity(SceneNode *self, SceneNodeEntity entity) { ASSERT_RETURN_WARN(!tc_is_null(entity),, "SceneNodeAttachEntity: Cannot attach null entity, use SceneNodeDetachEntity to remove entity instead."); // establish two-way relationship entity.tc->set_node(entity.data, self); self->entity = entity; // if this node is in a tree, notify the entity that it has entered one. if(self->scene != NULL) self->entity.tc->enter_tree(self->entity.data); } SceneNodeEntity SceneNodeDetachEntity(SceneNode *self) { ASSERT_RETURN_WARN(!tc_is_null(self->entity), tc_null(SceneNodeEntity), "SceneNodeDetachEntity: Cannot detach entity from node with no entity."); // store the entity to return it. SceneNodeEntity e = self->entity; // null entity <-> node relationship e.tc->set_node(e.data, NULL); self->entity = tc_null(SceneNodeEntity); return e; } Scene *CreateScene(SceneNode *root) { Scene *scene = new(Scene); ASSERT_RETURN(scene != NULL, NULL, "CreateScene: Failed to allocate scene"); scene->main_camera = NULL; scene->nodes = list_from_type(SceneNode*); scene->root = root; Internal_SceneAddNode(scene, root); return scene; } void DestroyScene(Scene *self) { DestroySceneNode(self->root); // recurses to the rest of the scene list_empty(&self->nodes); free(self); } void SceneTick(Scene* self, double delta_time) { Internal_SceneNodeUpdateTransformRecursive(self->root, false); Internal_SceneNodeTick(self->root, delta_time); }