# HG changeset patch # User Mike Becker # Date 1728240583 -7200 # Node ID 0ce35348550912d820087d5ca1db4db3c6f67c3c # Parent 25fb39f8863a5e9f65d9760bf0df5f98fcc4f757 revert introduction of high level ucx trees and stick to the low level API diff -r 25fb39f8863a -r 0ce353485509 src/ascension/scene.h --- a/src/ascension/scene.h Sun Oct 06 19:45:34 2024 +0200 +++ b/src/ascension/scene.h Sun Oct 06 20:49:43 2024 +0200 @@ -34,7 +34,6 @@ #include "texture.h" #include -#include typedef struct AscSceneNode AscSceneNode; @@ -47,11 +46,12 @@ ASC_RENDER_GROUP_COUNT }; -typedef CxTree* AscScene; - struct AscSceneNode { - struct cx_tree_node_base_s base; - AscScene scene; + AscSceneNode *parent; + AscSceneNode *prev; + AscSceneNode *next; + AscSceneNode *children; + AscSceneNode *last_child; CxList *behaviors; asc_scene_free_func free_func; asc_scene_update_func update_func; @@ -69,7 +69,6 @@ uint32_t flags; }; -// TODO: move to sprite.h typedef struct AscSprite { AscSceneNode data; AscTexture tex; @@ -101,57 +100,56 @@ #define ASC_SCENE_NODE_HIDDEN 0x80000000 /** - * Draws the specified scene. + * Draws the scene with the specified root node. * - * @param scene the scene graph + * @param root the root node of the scene graph * @param viewport the window viewport the scene shall be drawn to * @param camera the camera to obtain the view and projection matrix from */ __attribute__((__nonnull__)) -void asc_scene_draw(AscScene scene, asc_recti viewport, AscCamera *camera); - -AscScene asc_scene_create(void); -void asc_scene_destroy(AscScene scene); +void asc_scene_draw(AscSceneNode *root, asc_recti viewport, AscCamera *camera); /** * Creates an empty node that may serve as a container for other nodes. * + * The free_func of this node will be a simple free(). + * * @return the new node */ AscSceneNode *asc_scene_node_empty(void); /** - * Adds a node to a (new) parent. + * Unlinks the node from its parent and frees the entire subtree. + * + * The free_func of this node and all child nodes is called, starting + * with the leaf nodes and terminating with \p node. + * + * @param node the node to unlink + */ +void asc_scene_node_free(AscSceneNode *node); + +/** + * Links a node to a (new) parent. * * @param parent the (new) parent - * @param node the node to add + * @param node the node to link */ __attribute__((__nonnull__)) -void asc_scene_node_add( +void asc_scene_node_link( AscSceneNode *restrict parent, AscSceneNode *restrict node ); /** - * Removes a node from its scene. + * Unlinks a node from its parent. * * This might be useful to temporarily remove a subtree from a scene. + * To permanently remove the node use asc_scene_node_free(). * - * @param node the node to remove from its scene + * @param node the node to unlink */ __attribute__((__nonnull__)) -void asc_scene_node_remove(AscSceneNode *node); - -/** - * Retrieves the parent of a particular node. - * - * @param node the node - * @return the parent of \p node (or NULL when it is the root node) - */ -__attribute__((__nonnull__)) -static inline AscSceneNode *asc_scene_node_parent(AscSceneNode *node) { - return (AscSceneNode *) node->base.parent; -} +void asc_scene_node_unlink(AscSceneNode *node); /** * Adds a behavior function to the node. diff -r 25fb39f8863a -r 0ce353485509 src/ascension/ui.h --- a/src/ascension/ui.h Sun Oct 06 19:45:34 2024 +0200 +++ b/src/ascension/ui.h Sun Oct 06 20:49:43 2024 +0200 @@ -31,7 +31,7 @@ #include "ui/text.h" #define asc_add_ui_node(node) \ - asc_scene_node_add(asc_active_window->ui->root, node) + asc_scene_node_link(asc_active_window->ui, node) #endif /* ASCENSION_UI_H */ diff -r 25fb39f8863a -r 0ce353485509 src/ascension/window.h --- a/src/ascension/window.h Sun Oct 06 19:45:34 2024 +0200 +++ b/src/ascension/window.h Sun Oct 06 20:49:43 2024 +0200 @@ -53,7 +53,7 @@ SDL_Window* window; asc_vec2i dimensions; AscGLContext glctx; - AscScene ui; + AscSceneNode *ui; } AscWindow; /** diff -r 25fb39f8863a -r 0ce353485509 src/scene.c --- 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 #include +#include #include #include "ascension/shader.h" @@ -39,7 +40,24 @@ #include -// 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); diff -r 25fb39f8863a -r 0ce353485509 src/window.c --- a/src/window.c Sun Oct 06 19:45:34 2024 +0200 +++ b/src/window.c Sun Oct 06 20:49:43 2024 +0200 @@ -56,7 +56,7 @@ } if (window->ui != NULL) { asc_dprintf("Window with index %u has a dangling UI pointer", index); - asc_scene_destroy(window->ui); + asc_scene_node_free(window->ui); } Uint32 flags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN; @@ -83,7 +83,7 @@ window->resized = true; // count initial sizing as resize if (asc_gl_context_initialize(&window->glctx, window->window, &settings->glsettings)) { - window->ui = asc_scene_create(); + window->ui = asc_scene_node_empty(); asc_dprintf("Window %u initialized", window->id); asc_context.active_window = index; } else { @@ -111,7 +111,7 @@ asc_gl_context_activate(&window->glctx); // destroy all scenes - asc_scene_destroy(window->ui); + asc_scene_node_free(window->ui); window->ui = NULL; // release context related data