188 lines
7.3 KiB
C
188 lines
7.3 KiB
C
#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);
|
|
}
|