--- a/src/scene.c Sun Oct 06 19:45:34 2024 +0200 +++ b/src/scene.c Sun Oct 06 20:49:43 2024 +0200 @@ -32,6 +32,7 @@ #include <cx/linked_list.h> #include <cx/array_list.h> +#include <cx/tree.h> #include <cx/utils.h> #include "ascension/shader.h" @@ -39,7 +40,24 @@ #include <assert.h> -// TODO: move to a sprite.c +static CxTreeIterator asc_scene_node_iterator( + AscSceneNode *node, + bool visit_on_exit +) { + return cx_tree_iterator( + node, visit_on_exit, + offsetof(AscSceneNode, children), + offsetof(AscSceneNode, next) + ); +} + +static CxTreeVisitor asc_scene_node_visitor(AscSceneNode *node) { + return cx_tree_visitor(node, + offsetof(AscSceneNode, children), + offsetof(AscSceneNode, next) + ); +} + static void asc_sprite_draw(AscSprite const *node) { // Obtain shader AscShaderSprite *shader = ASC_SHADER_SPRITE; @@ -58,7 +76,7 @@ asc_primitives_draw_plane(); } -void asc_scene_draw(AscScene scene, asc_recti viewport, AscCamera *camera) { +void asc_scene_draw(AscSceneNode *root, asc_recti viewport, AscCamera *camera) { // create render groups CxList *render_group[ASC_RENDER_GROUP_COUNT]; cx_for_n(i, ASC_RENDER_GROUP_COUNT) { @@ -66,7 +84,7 @@ } // skip the root node deliberately, we know it's just the container - CxTreeVisitor iter = cxTreeVisit(scene); + CxTreeVisitor iter = asc_scene_node_visitor(root); cxIteratorNext(iter); // update the children and add them to the render groups @@ -110,7 +128,7 @@ asc_mat4f_mulst( node->world_transform, node->transform, - asc_scene_node_parent(node)->world_transform + node->parent->world_transform ); } @@ -168,55 +186,54 @@ AscSceneNode *asc_scene_node_empty(void) { AscSceneNode *node = calloc(1, sizeof(AscSceneNode)); + node->free_func = (asc_scene_free_func) free; node->scale.x = node->scale.y = node->scale.z = 1; asc_transform_identity(node->transform); asc_transform_identity(node->world_transform); return node; } -void asc_scene_node_destructor(AscSceneNode *node) { - if (node->behaviors != NULL) { - cxListDestroy(node->behaviors); - } - if (node->free_func != NULL) { - node->free_func(node); - } else { - free(node); +void asc_scene_node_free(AscSceneNode *node) { + if (node == NULL) return; + + // remove this node from its parent + asc_scene_node_unlink(node); + + // free the entire subtree + CxTreeIterator iter = asc_scene_node_iterator(node, true); + cx_foreach(AscSceneNode*, child, iter) { + if (!iter.exiting) continue; + if (child->behaviors != NULL) { + cxListDestroy(child->behaviors); + } + if (child->free_func != NULL) { + child->free_func(child); + } else { + free(child); + } } } -AscScene asc_scene_create(void) { - AscSceneNode *root = asc_scene_node_empty(); - AscScene scene = cxTreeCreateWrapped( - cxDefaultAllocator, root, - cx_tree_node_base_layout +void asc_scene_node_link(AscSceneNode * restrict parent, AscSceneNode * restrict node) { + cx_tree_link( + parent, node, + offsetof(AscSceneNode, parent), + offsetof(AscSceneNode, children), + offsetof(AscSceneNode, last_child), + offsetof(AscSceneNode, prev), + offsetof(AscSceneNode, next) ); - root->scene = scene; - scene->simple_destructor = (cx_destructor_func) asc_scene_node_destructor; - return scene; -} - -void asc_scene_destroy(AscScene scene) { - cxTreeDestroy(scene); } -void asc_scene_node_add(AscSceneNode * restrict parent, AscSceneNode * restrict node) { - assert(node->scene == NULL || node->scene == parent->scene); - cxTreeSetParent(parent->scene, parent, node); - CxTreeVisitor visitor = cxTreeVisitSubtree(parent->scene, node); - cx_foreach(AscSceneNode *, n, visitor) { - n->scene = parent->scene; - } - asc_node_update_transform(node); -} - -void asc_scene_node_remove(AscSceneNode *node) { - if (node->scene == NULL) return; - cxTreeRemoveSubtree(node->scene, node); - CxTreeVisitor visitor = cxTreeVisitSubtree(node->scene, node); - cx_foreach(AscSceneNode *, n, visitor) { - n->scene = NULL; - } +void asc_scene_node_unlink(AscSceneNode *node) { + cx_tree_unlink( + node, + offsetof(AscSceneNode, parent), + offsetof(AscSceneNode, children), + offsetof(AscSceneNode, last_child), + offsetof(AscSceneNode, prev), + offsetof(AscSceneNode, next) + ); } void asc_scene_add_behavior( @@ -248,14 +265,7 @@ return; } - // if node is not part of a scene, just mark it and return - if (node->scene == NULL) { - asc_set_flag(node->flags, ASC_SCENE_NODE_UPDATE_TRANSFORM); - return; - } - - // creating the iterator this way saves us from needing scene as parameter - CxTreeIterator iter = cxTreeIterateSubtree(node->scene, node, false); + CxTreeIterator iter = asc_scene_node_iterator(node, false); cx_foreach(AscSceneNode*, n, iter) { if (asc_test_flag(n->flags, ASC_SCENE_NODE_UPDATE_TRANSFORM)) { cxTreeIteratorContinue(iter);