# HG changeset patch # User Mike Becker # Date 1728236588 -7200 # Node ID cfa1d05754ac6ccaa07d6071c27bc4f2c25373fc # Parent 84472fb3adbdcd1a6205ed22a182bfd2f45d455c update to recent snapshot of ucx 3.1 diff -r 84472fb3adbd -r cfa1d05754ac src/ascension/scene.h --- a/src/ascension/scene.h Thu Aug 15 17:33:42 2024 +0200 +++ b/src/ascension/scene.h Sun Oct 06 19:43:08 2024 +0200 @@ -34,6 +34,7 @@ #include "texture.h" #include +#include typedef struct AscSceneNode AscSceneNode; @@ -46,11 +47,11 @@ ASC_RENDER_GROUP_COUNT }; +typedef CxTree* AscScene; + struct AscSceneNode { - AscSceneNode *parent; - AscSceneNode *prev; - AscSceneNode *next; - AscSceneNode *children; + struct cx_tree_node_base_s base; + AscScene scene; CxList *behaviors; asc_scene_free_func free_func; asc_scene_update_func update_func; @@ -68,6 +69,7 @@ uint32_t flags; }; +// TODO: move to sprite.h typedef struct AscSprite { AscSceneNode data; AscTexture tex; @@ -99,56 +101,57 @@ #define ASC_SCENE_NODE_HIDDEN 0x80000000 /** - * Draws the scene with the specified root node. + * Draws the specified scene. * - * @param root the root node of the scene graph + * @param scene 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(AscSceneNode *root, asc_recti viewport, AscCamera *camera); +void asc_scene_draw(AscScene scene, asc_recti viewport, AscCamera *camera); + +AscScene asc_scene_create(void); +void asc_scene_destroy(AscScene scene); /** * 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); /** - * 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. + * Adds a node to a (new) parent. * * @param parent the (new) parent - * @param node the node to link + * @param node the node to add */ __attribute__((__nonnull__)) -void asc_scene_node_link( +void asc_scene_node_add( AscSceneNode *restrict parent, AscSceneNode *restrict node ); /** - * Unlinks a node from its parent. + * Removes a node from its scene. * * 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 unlink + * @param node the node to remove from its scene */ __attribute__((__nonnull__)) -void asc_scene_node_unlink(AscSceneNode *node); +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; +} /** * Adds a behavior function to the node. diff -r 84472fb3adbd -r cfa1d05754ac src/ascension/ui.h --- a/src/ascension/ui.h Thu Aug 15 17:33:42 2024 +0200 +++ b/src/ascension/ui.h Sun Oct 06 19:43:08 2024 +0200 @@ -31,7 +31,7 @@ #include "ui/text.h" #define asc_add_ui_node(node) \ - asc_scene_node_link(asc_active_window->ui, node) + asc_scene_node_add(asc_active_window->ui->root, node) #endif /* ASCENSION_UI_H */ diff -r 84472fb3adbd -r cfa1d05754ac src/ascension/window.h --- a/src/ascension/window.h Thu Aug 15 17:33:42 2024 +0200 +++ b/src/ascension/window.h Sun Oct 06 19:43:08 2024 +0200 @@ -53,7 +53,7 @@ SDL_Window* window; asc_vec2i dimensions; AscGLContext glctx; - AscSceneNode *ui; + AscScene ui; } AscWindow; /** diff -r 84472fb3adbd -r cfa1d05754ac src/scene.c --- a/src/scene.c Thu Aug 15 17:33:42 2024 +0200 +++ b/src/scene.c Sun Oct 06 19:43:08 2024 +0200 @@ -32,7 +32,6 @@ #include #include -#include #include #include "ascension/shader.h" @@ -40,24 +39,7 @@ #include -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) - ); -} - +// TODO: move to a sprite.c static void asc_sprite_draw(AscSprite const *node) { // Obtain shader AscShaderSprite *shader = ASC_SHADER_SPRITE; @@ -76,7 +58,7 @@ asc_primitives_draw_plane(); } -void asc_scene_draw(AscSceneNode *root, asc_recti viewport, AscCamera *camera) { +void asc_scene_draw(AscScene scene, asc_recti viewport, AscCamera *camera) { // create render groups CxList *render_group[ASC_RENDER_GROUP_COUNT]; cx_for_n(i, ASC_RENDER_GROUP_COUNT) { @@ -84,7 +66,7 @@ } // skip the root node deliberately, we know it's just the container - CxTreeVisitor iter = asc_scene_node_visitor(root); + CxTreeVisitor iter = cxTreeVisit(scene); cxIteratorNext(iter); // update the children and add them to the render groups @@ -128,7 +110,7 @@ asc_mat4f_mulst( node->world_transform, node->transform, - node->parent->world_transform + asc_scene_node_parent(node)->world_transform ); } @@ -186,52 +168,55 @@ 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_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); - } +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_link(AscSceneNode * restrict parent, AscSceneNode * restrict node) { - cx_tree_link( - parent, node, - offsetof(AscSceneNode, parent), - offsetof(AscSceneNode, children), - offsetof(AscSceneNode, prev), - offsetof(AscSceneNode, next) +AscScene asc_scene_create(void) { + AscSceneNode *root = asc_scene_node_empty(); + AscScene scene = cxTreeCreateWrapped( + cxDefaultAllocator, root, + cx_tree_node_base_layout ); + 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_unlink(AscSceneNode *node) { - cx_tree_unlink( - node, - offsetof(AscSceneNode, parent), - offsetof(AscSceneNode, children), - offsetof(AscSceneNode, prev), - offsetof(AscSceneNode, next) - ); +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_add_behavior( @@ -263,7 +248,14 @@ return; } - CxTreeIterator iter = asc_scene_node_iterator(node, false); + // 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); cx_foreach(AscSceneNode*, n, iter) { if (asc_test_flag(n->flags, ASC_SCENE_NODE_UPDATE_TRANSFORM)) { cxTreeIteratorContinue(iter); diff -r 84472fb3adbd -r cfa1d05754ac src/window.c --- a/src/window.c Thu Aug 15 17:33:42 2024 +0200 +++ b/src/window.c Sun Oct 06 19:43:08 2024 +0200 @@ -56,7 +56,7 @@ } if (window->ui != NULL) { asc_dprintf("Window with index %u has a dangling UI pointer", index); - asc_scene_node_free(window->ui); + asc_scene_destroy(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_node_empty(); + window->ui = asc_scene_create(); 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_node_free(window->ui); + asc_scene_destroy(window->ui); window->ui = NULL; // release context related data