fogd-engine/src/core/scene.c
2024-09-25 11:58:37 +02:00

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