remove unnecessary scene container

Mon, 01 Apr 2024 18:54:19 +0200

author
Mike Becker <universe@uap-core.de>
date
Mon, 01 Apr 2024 18:54:19 +0200
changeset 47
44457f6cb0a2
parent 46
d3285aed65b3
child 48
6e5b5ba2752c

remove unnecessary scene container

src/ascension/camera.h file | annotate | diff | comparison | revisions
src/ascension/scene.h file | annotate | diff | comparison | revisions
src/ascension/window.h file | annotate | diff | comparison | revisions
src/camera.c file | annotate | diff | comparison | revisions
src/context.c file | annotate | diff | comparison | revisions
src/scene.c file | annotate | diff | comparison | revisions
src/window.c file | annotate | diff | comparison | revisions
test/snake.c file | annotate | diff | comparison | revisions
     1.1 --- a/src/ascension/camera.h	Fri Mar 29 00:03:25 2024 +0100
     1.2 +++ b/src/ascension/camera.h	Mon Apr 01 18:54:19 2024 +0200
     1.3 @@ -30,30 +30,14 @@
     1.4  
     1.5  #include "datatypes.h"
     1.6  
     1.7 -enum AscCameraType {
     1.8 -    ASC_CAMERA_DISABLED,
     1.9 -    ASC_CAMERA_ORTHO,
    1.10 -    ASC_CAMERA_PERSPECTIVE,
    1.11 -};
    1.12 -
    1.13  typedef struct AscCamera AscCamera;
    1.14 -typedef void(*asc_camera_update_func)(AscCamera*);
    1.15  
    1.16  struct AscCamera {
    1.17 -    asc_camera_update_func update;
    1.18      asc_mat4f projection;
    1.19      asc_mat4f view;
    1.20 -    asc_vec3f right;
    1.21 -    asc_vec3f up;
    1.22 -    asc_vec3f direction;
    1.23 -    asc_recti rect;
    1.24 -    // TODO: fov?
    1.25  };
    1.26  
    1.27  __attribute__((__nonnull__))
    1.28  void asc_camera_ortho(AscCamera *camera, asc_recti rect);
    1.29  
    1.30 -__attribute__((__nonnull__))
    1.31 -void asc_camera_disable(AscCamera *camera);
    1.32 -
    1.33  #endif //ASCENSION_CAMERA_H
     2.1 --- a/src/ascension/scene.h	Fri Mar 29 00:03:25 2024 +0100
     2.2 +++ b/src/ascension/scene.h	Mon Apr 01 18:54:19 2024 +0200
     2.3 @@ -32,25 +32,18 @@
     2.4  #include "transform.h"
     2.5  #include "camera.h"
     2.6  
     2.7 -#include <cx/array_list.h>
     2.8 +#include <cx/list.h>
     2.9  
    2.10  typedef struct AscSceneNode AscSceneNode;
    2.11 -typedef struct AscBehaviorNode AscBehaviorNode;
    2.12  
    2.13  typedef void(*asc_scene_free_func)(AscSceneNode*);
    2.14  typedef void(*asc_scene_update_func)(AscSceneNode*);
    2.15  typedef void(*asc_scene_draw_func)(AscSceneNode const*);
    2.16  
    2.17 -struct AscBehaviorNode {
    2.18 -    AscSceneNode *parent;
    2.19 -    AscBehaviorNode *prev;
    2.20 -    AscBehaviorNode *next;
    2.21 -    asc_scene_update_func func;
    2.22 -};
    2.23 -
    2.24  enum AscRenderGroup {
    2.25      ASC_RENDER_GROUP_SPRITE_OPAQUE,
    2.26 -    ASC_RENDER_GROUP_SPRITE_BLEND
    2.27 +    ASC_RENDER_GROUP_SPRITE_BLEND,
    2.28 +    ASC_RENDER_GROUP_COUNT
    2.29  };
    2.30  
    2.31  struct AscSceneNode {
    2.32 @@ -58,7 +51,7 @@
    2.33      AscSceneNode *prev;
    2.34      AscSceneNode *next;
    2.35      AscSceneNode *children;
    2.36 -    AscBehaviorNode *behaviors;
    2.37 +    CxList *behaviors;
    2.38      asc_scene_free_func free_func;
    2.39      asc_scene_update_func update_func;
    2.40      asc_scene_draw_func draw_func;
    2.41 @@ -74,42 +67,19 @@
    2.42  };
    2.43  
    2.44  /**
    2.45 - * Place this as first member of a structure that shall be usable as a scene node.
    2.46 + * Place this as first member of a structure that shall be used as a scene node.
    2.47   */
    2.48  #define extend_asc_scene_node AscSceneNode base
    2.49  
    2.50 -struct asc_render_group_entry {
    2.51 -    asc_scene_draw_func draw;
    2.52 -    AscSceneNode const *node;
    2.53 -};
    2.54 -
    2.55 -#define ASC_SCENE_CAMERAS_MAX 4
    2.56 -
    2.57 -typedef struct AscScene {
    2.58 -    AscSceneNode *root;
    2.59 -    asc_recti viewport;
    2.60 -    AscCamera cameras[ASC_SCENE_CAMERAS_MAX];
    2.61 -    CX_ARRAY_DECLARE(struct asc_render_group_entry, rg_sprites_opaque);
    2.62 -    CX_ARRAY_DECLARE(struct asc_render_group_entry, rg_sprites_blended);
    2.63 -} AscScene;
    2.64 -
    2.65  /**
    2.66 - * Initializes the scene using the active window as reference.
    2.67 + * Draws the scene with the specified root node.
    2.68   *
    2.69 - * @param scene the scene to initialize
    2.70 - * @param type determines the type of camera to use
    2.71 + * @param root the root node of the scene graph
    2.72 + * @param viewport the window viewport the scene shall be drawn to
    2.73 + * @param camera the camera to obtain the view and projection matrix from
    2.74   */
    2.75  __attribute__((__nonnull__))
    2.76 -void asc_scene_init(AscScene *scene);
    2.77 -
    2.78 -__attribute__((__nonnull__))
    2.79 -void asc_scene_destroy(AscScene *scene);
    2.80 -
    2.81 -__attribute__((__nonnull__))
    2.82 -void asc_scene_draw(AscScene *scene);
    2.83 -
    2.84 -__attribute__((__nonnull__))
    2.85 -void asc_scene_add(AscScene *scene, AscSceneNode *node);
    2.86 +void asc_scene_draw(AscSceneNode *root, asc_recti viewport, AscCamera *camera);
    2.87  
    2.88  /**
    2.89   * Creates an empty node that may serve as a container for other nodes.
    2.90 @@ -120,25 +90,68 @@
    2.91   */
    2.92  AscSceneNode *asc_scene_node_empty(void);
    2.93  
    2.94 +/**
    2.95 + * Unlinks the node from its parent and frees the entire subtree.
    2.96 + *
    2.97 + * The free_func of this node and all child nodes is called, starting
    2.98 + * with the leaf nodes and terminating with \p node.
    2.99 + *
   2.100 + * @param node the node to unlink
   2.101 + */
   2.102  void asc_scene_node_free(AscSceneNode *node);
   2.103  
   2.104 +/**
   2.105 + * Links a node to a (new) parent.
   2.106 + *
   2.107 + * @param parent the (new) parent
   2.108 + * @param node the node to link
   2.109 + */
   2.110  __attribute__((__nonnull__))
   2.111  void asc_scene_node_link(
   2.112          AscSceneNode *restrict parent,
   2.113          AscSceneNode *restrict node
   2.114  );
   2.115  
   2.116 +/**
   2.117 + * Unlinks a node from its parent.
   2.118 + *
   2.119 + * This might be useful to temporarily remove a subtree from a scene.
   2.120 + * To permanently remove the node use asc_scene_node_free().
   2.121 + *
   2.122 + * @param node the node to unlink
   2.123 + */
   2.124  __attribute__((__nonnull__))
   2.125  void asc_scene_node_unlink(AscSceneNode *node);
   2.126  
   2.127 +/**
   2.128 + * Adds a behavior function to the node.
   2.129 + *
   2.130 + * A behavior function MUST NOT be added more than once to the same node.
   2.131 + * This will not be checked.
   2.132 + *
   2.133 + * @param node the node
   2.134 + * @param behavior the behavior function
   2.135 + */
   2.136  __attribute__((__nonnull__))
   2.137 -AscBehaviorNode *asc_scene_add_behavior(
   2.138 +void asc_scene_add_behavior(
   2.139          AscSceneNode *node,
   2.140          asc_scene_update_func behavior
   2.141  );
   2.142  
   2.143 +/**
   2.144 + * Removes a behavior function from the node.
   2.145 + *
   2.146 + * If the behavior function is not attached to this node, this function
   2.147 + * does nothing.
   2.148 + *
   2.149 + * @param node the node
   2.150 + * @param behavior the behavior function
   2.151 + */
   2.152  __attribute__((__nonnull__))
   2.153 -void asc_scene_remove_behavior(AscBehaviorNode *node);
   2.154 +void asc_scene_remove_behavior(
   2.155 +        AscSceneNode *node,
   2.156 +        asc_scene_update_func behavior
   2.157 +);
   2.158  
   2.159  #define asc_node_update(node) \
   2.160      ((AscSceneNode*)node)->need_graphics_update = true
     3.1 --- a/src/ascension/window.h	Fri Mar 29 00:03:25 2024 +0100
     3.2 +++ b/src/ascension/window.h	Mon Apr 01 18:54:19 2024 +0200
     3.3 @@ -52,7 +52,7 @@
     3.4      asc_vec2i dimensions;
     3.5      bool resized;
     3.6      AscGLContext glctx;
     3.7 -    AscScene ui;
     3.8 +    AscSceneNode *ui;
     3.9  } AscWindow;
    3.10  
    3.11  #define asc_window_active asc_context.active_window
     4.1 --- a/src/camera.c	Fri Mar 29 00:03:25 2024 +0100
     4.2 +++ b/src/camera.c	Mon Apr 01 18:54:19 2024 +0200
     4.3 @@ -27,20 +27,11 @@
     4.4  
     4.5  #include "ascension/camera.h"
     4.6  
     4.7 -static void asc_camera_update_ortho(AscCamera *camera) {
     4.8 -    float left = (float) camera->rect.pos.x;
     4.9 -    float right = left + (float) camera->rect.size.width;
    4.10 -    float top = (float) camera->rect.pos.y;
    4.11 -    float bottom = top + (float) camera->rect.size.height;
    4.12 +void asc_camera_ortho(AscCamera *camera, asc_recti rect) {
    4.13 +    asc_mat4f_unit(camera->view);
    4.14 +    float left = (float) rect.pos.x;
    4.15 +    float right = left + (float) rect.size.width;
    4.16 +    float top = (float) rect.pos.y;
    4.17 +    float bottom = top + (float) rect.size.height;
    4.18      asc_mat4f_ortho(camera->projection, left, right, bottom, top, -1, 1);
    4.19  }
    4.20 -
    4.21 -void asc_camera_ortho(AscCamera *camera, asc_recti rect) {
    4.22 -    asc_mat4f_unit(camera->view);
    4.23 -    camera->update = asc_camera_update_ortho;
    4.24 -    camera->rect = rect;
    4.25 -}
    4.26 -
    4.27 -void asc_camera_disable(AscCamera *camera) {
    4.28 -    camera->update = NULL;
    4.29 -}
     5.1 --- a/src/context.c	Fri Mar 29 00:03:25 2024 +0100
     5.2 +++ b/src/context.c	Mon Apr 01 18:54:19 2024 +0200
     5.3 @@ -94,8 +94,6 @@
     5.4              asc_vec2i dimensions = (asc_vec2i) {width, height};
     5.5              asc_context.windows[i].resized = true;
     5.6              asc_context.windows[i].dimensions = dimensions;
     5.7 -            asc_context.windows[i].ui.viewport.size = dimensions;
     5.8 -            asc_context.windows[i].ui.cameras[0].rect.size = dimensions;
     5.9              return;
    5.10          }
    5.11      }
     6.1 --- a/src/scene.c	Fri Mar 29 00:03:25 2024 +0100
     6.2 +++ b/src/scene.c	Mon Apr 01 18:54:19 2024 +0200
     6.3 @@ -30,6 +30,8 @@
     6.4  
     6.5  #include "ascension/context.h"
     6.6  
     6.7 +#include <cx/linked_list.h>
     6.8 +#include <cx/array_list.h>
     6.9  #include <cx/tree.h>
    6.10  #include <cx/utils.h>
    6.11  
    6.12 @@ -56,53 +58,26 @@
    6.13      );
    6.14  }
    6.15  
    6.16 -void asc_scene_init(AscScene *scene) {
    6.17 -    if (scene->root != NULL) {
    6.18 -        asc_error("Scene is already initialized.");
    6.19 -        return;
    6.20 +struct asc_render_group_entry {
    6.21 +    asc_scene_draw_func draw;
    6.22 +    AscSceneNode const *node;
    6.23 +};
    6.24 +
    6.25 +#define asc_draw_render_group(iter) \
    6.26 +    cx_foreach(struct asc_render_group_entry*, entry, iter) { \
    6.27 +        entry->draw(entry->node); \
    6.28      }
    6.29  
    6.30 -    // zero everything, first
    6.31 -    memset(scene, 0, sizeof(AscScene));
    6.32 -
    6.33 -    // default viewport is the entire viewport of the active window
    6.34 -    scene->viewport.size = asc_context.active_window->dimensions;
    6.35 -
    6.36 -    // create the root node
    6.37 -    scene->root = asc_scene_node_empty();
    6.38 -
    6.39 -    // initialize the render groups
    6.40 -    cx_array_initialize(scene->rg_sprites_opaque, 8);
    6.41 -    cx_array_initialize(scene->rg_sprites_blended, 8);
    6.42 -}
    6.43 -
    6.44 -void asc_scene_destroy(AscScene *scene) {
    6.45 -    asc_scene_node_free(scene->root);
    6.46 -}
    6.47 -
    6.48 -void asc_scene_add(AscScene *scene, AscSceneNode *node) {
    6.49 -    asc_scene_node_link(scene->root, node);
    6.50 -    asc_node_update(node);
    6.51 -}
    6.52 -
    6.53 -#define asc_scene_draw_render_group(rg) \
    6.54 -    cx_for_n(i, rg##_size) { \
    6.55 -        rg[i].draw(rg[i].node); \
    6.56 -    } (void)0
    6.57 -
    6.58 -#define asc_scene_draw_render_group_reversed(rg) \
    6.59 -    for(size_t i = rg##_size ; i > 0 ; i--) { \
    6.60 -        rg[i-1].draw(rg[i-1].node); \
    6.61 -    } (void)0
    6.62 -
    6.63 -void asc_scene_draw(AscScene *scene) {
    6.64 -    // reset render groups
    6.65 -    // TODO: avoid recalculating the groups, if possible
    6.66 -    scene->rg_sprites_opaque_size = 0;
    6.67 -    scene->rg_sprites_blended_size = 0;
    6.68 +void asc_scene_draw(AscSceneNode *root, asc_recti viewport, AscCamera *camera) {
    6.69 +    // create render groups
    6.70 +    CxList *render_group[ASC_RENDER_GROUP_COUNT];
    6.71 +    cx_for_n(i, ASC_RENDER_GROUP_COUNT) {
    6.72 +        render_group[i] = cxArrayListCreateSimple(
    6.73 +                sizeof(struct asc_render_group_entry), 32);
    6.74 +    }
    6.75  
    6.76      // skip the root node deliberately, we know it's just the container
    6.77 -    CxTreeVisitor iter = asc_scene_node_visitor(scene->root);
    6.78 +    CxTreeVisitor iter = asc_scene_node_visitor(root);
    6.79      cxIteratorNext(iter);
    6.80  
    6.81      // update the children and add them to the render groups
    6.82 @@ -110,12 +85,16 @@
    6.83          node->depth = iter.depth;
    6.84  
    6.85          // execute behaviors, first
    6.86 -        AscBehaviorNode *behavior = node->behaviors;
    6.87 -        while (behavior) {
    6.88 -            behavior->func(node);
    6.89 -            behavior = behavior->next;
    6.90 +        if (node->behaviors != NULL) {
    6.91 +            CxIterator behavior_iter = cxListIterator(node->behaviors);
    6.92 +            cx_foreach(asc_scene_update_func, behavior, behavior_iter) {
    6.93 +                behavior(node);
    6.94 +            }
    6.95          }
    6.96  
    6.97 +        // TODO: implement culling
    6.98 +        // TODO: implement a hidden flag (requires UCX tree-continue function)
    6.99 +
   6.100          // check if geometry needs update
   6.101          if (node->need_graphics_update) {
   6.102              assert(node->update_func != NULL);
   6.103 @@ -142,56 +121,51 @@
   6.104              struct asc_render_group_entry entry = {
   6.105                      node->draw_func, node
   6.106              };
   6.107 -            switch (node->render_group) {
   6.108 -                case ASC_RENDER_GROUP_SPRITE_OPAQUE:
   6.109 -                    cx_array_simple_add(scene->rg_sprites_opaque, entry);
   6.110 -                    break;
   6.111 -                case ASC_RENDER_GROUP_SPRITE_BLEND:
   6.112 -                    cx_array_simple_add(scene->rg_sprites_blended, entry);
   6.113 -                    break;
   6.114 -            }
   6.115 +            cxListAdd(render_group[node->render_group], &entry);
   6.116          }
   6.117      }
   6.118  
   6.119      // set the viewport (in OpenGL we need to invert the Y axis)
   6.120      glViewport(
   6.121 -            scene->viewport.pos.x,
   6.122 -            -scene->viewport.pos.y,
   6.123 -            scene->viewport.size.width,
   6.124 -            scene->viewport.size.height
   6.125 +            viewport.pos.x,
   6.126 +            -viewport.pos.y,
   6.127 +            viewport.size.width,
   6.128 +            viewport.size.height
   6.129      );
   6.130 -    glClear(GL_COLOR_BUFFER_BIT);
   6.131  
   6.132 -    // -----------------------------------------
   6.133 -    // process the render groups for each camera
   6.134 -    // -----------------------------------------
   6.135 +    // -------------------------
   6.136 +    // process the render groups
   6.137 +    // -------------------------
   6.138      AscShaderProgram *shader;
   6.139 -    cx_for_n(cam_id, ASC_SCENE_CAMERAS_MAX) {
   6.140 -        // update camera parameters, first
   6.141 -        AscCamera *camera = &scene->cameras[cam_id];
   6.142 -        if (camera->update == NULL) continue;
   6.143 -        camera->update(camera);
   6.144 +    CxIterator render_iter;
   6.145  
   6.146 -        // 2D Elements
   6.147 -        // ===========
   6.148 -        glEnable(GL_DEPTH_TEST);
   6.149 -        glClear(GL_DEPTH_BUFFER_BIT);
   6.150 +    // 2D Elements
   6.151 +    // ===========
   6.152 +    glEnable(GL_DEPTH_TEST);
   6.153 +    glClear(GL_DEPTH_BUFFER_BIT);
   6.154  
   6.155 -        // Sprites
   6.156 -        // -------
   6.157 -        // TODO: see if we can really always ignore the view matrix
   6.158 -        shader = &asc_context.active_window->glctx.shader.sprite.base;
   6.159 -        glUseProgram(shader->id);
   6.160 -        glUniformMatrix4fv(shader->projection, 1,
   6.161 -                           GL_FALSE, camera->projection);
   6.162 +    // Sprites
   6.163 +    // -------
   6.164 +    // TODO: implement view matrix for 2D worlds
   6.165 +    shader = &asc_context.active_window->glctx.shader.sprite.base;
   6.166 +    glUseProgram(shader->id);
   6.167 +    glUniformMatrix4fv(shader->projection, 1,
   6.168 +                       GL_FALSE, camera->projection);
   6.169  
   6.170 -        // render opaque sprites from front to back
   6.171 -        glDisable(GL_BLEND);
   6.172 -        asc_scene_draw_render_group_reversed(scene->rg_sprites_opaque);
   6.173 -        // render sprites with alpha value from back to front
   6.174 -        glEnable(GL_BLEND);
   6.175 -        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
   6.176 -        asc_scene_draw_render_group(scene->rg_sprites_blended);
   6.177 +    // render opaque sprites from front to back
   6.178 +    glDisable(GL_BLEND);
   6.179 +    render_iter = cxListBackwardsIterator(render_group[ASC_RENDER_GROUP_SPRITE_OPAQUE]);
   6.180 +    asc_draw_render_group(render_iter);
   6.181 +
   6.182 +    // render sprites with alpha value from back to front
   6.183 +    glEnable(GL_BLEND);
   6.184 +    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
   6.185 +    render_iter = cxListIterator(render_group[ASC_RENDER_GROUP_SPRITE_BLEND]);
   6.186 +    asc_draw_render_group(render_iter);
   6.187 +
   6.188 +    // destroy render groups
   6.189 +    cx_for_n(i, ASC_RENDER_GROUP_COUNT) {
   6.190 +        cxListDestroy(render_group[i]);
   6.191      }
   6.192  }
   6.193  
   6.194 @@ -213,6 +187,9 @@
   6.195      CxTreeIterator iter = asc_scene_node_iterator(node, true);
   6.196      cx_foreach(AscSceneNode*, child, iter) {
   6.197          if (!iter.exiting) continue;
   6.198 +        if (child->behaviors != NULL) {
   6.199 +            cxListDestroy(child->behaviors);
   6.200 +        }
   6.201          if (child->free_func != NULL) {
   6.202              child->free_func(child);
   6.203          } else {
   6.204 @@ -241,28 +218,23 @@
   6.205      );
   6.206  }
   6.207  
   6.208 -AscBehaviorNode *asc_scene_add_behavior(AscSceneNode *node, asc_scene_update_func behavior) {
   6.209 -    AscBehaviorNode *behavior_node = calloc(1, sizeof(AscBehaviorNode));
   6.210 -    behavior_node->func = behavior;
   6.211 -    cx_tree_link(
   6.212 -            node,
   6.213 -            behavior_node,
   6.214 -            offsetof(AscBehaviorNode, parent),
   6.215 -            offsetof(AscSceneNode, behaviors),
   6.216 -            offsetof(AscBehaviorNode, prev),
   6.217 -            offsetof(AscBehaviorNode, next)
   6.218 -    );
   6.219 -    return behavior_node;
   6.220 +void asc_scene_add_behavior(
   6.221 +        AscSceneNode *node,
   6.222 +        asc_scene_update_func behavior
   6.223 +) {
   6.224 +    if (node->behaviors == NULL) {
   6.225 +        node->behaviors = cxLinkedListCreateSimple(CX_STORE_POINTERS);
   6.226 +    }
   6.227 +    cxListAdd(node->behaviors, behavior);
   6.228  }
   6.229  
   6.230 -void asc_scene_remove_behavior(AscBehaviorNode *node) {
   6.231 -    cx_tree_unlink(
   6.232 -            node,
   6.233 -            offsetof(AscBehaviorNode, parent),
   6.234 -            offsetof(AscSceneNode, behaviors),
   6.235 -            offsetof(AscBehaviorNode, prev),
   6.236 -            offsetof(AscBehaviorNode, next)
   6.237 -    );
   6.238 +void asc_scene_remove_behavior(
   6.239 +        AscSceneNode *node,
   6.240 +        asc_scene_update_func behavior
   6.241 +) {
   6.242 +    if (node->behaviors != NULL) {
   6.243 +        cxListFindRemove(node->behaviors, behavior);
   6.244 +    }
   6.245  }
   6.246  
   6.247  void asc_update_transform(AscSceneNode *node) {
     7.1 --- a/src/window.c	Fri Mar 29 00:03:25 2024 +0100
     7.2 +++ b/src/window.c	Mon Apr 01 18:54:19 2024 +0200
     7.3 @@ -43,13 +43,6 @@
     7.4      settings->title = "Ascended Window";
     7.5  }
     7.6  
     7.7 -static void asc_window_init_scenes(AscWindow *window) {
     7.8 -    asc_scene_init(&window->ui);
     7.9 -    asc_camera_ortho(&window->ui.cameras[0], (asc_recti){
    7.10 -        0, 0, window->dimensions
    7.11 -    });
    7.12 -}
    7.13 -
    7.14  AscWindow *asc_window_initialize(unsigned int index, AscWindowSettings const *settings) {
    7.15      if (index >= ASC_MAX_WINDOWS) {
    7.16          asc_error("Maximum number of windows exceeded.");
    7.17 @@ -58,9 +51,13 @@
    7.18      AscWindow *window = &asc_context.windows[index];
    7.19      if (window->id > 0) {
    7.20          asc_error("Cannot create window - slot already occupied.");
    7.21 -        asc_dprintf("Tried to create window with index %u", index);
    7.22 +        asc_dprintf("Tried to create window with index %u twice", index);
    7.23          return NULL;
    7.24      }
    7.25 +    if (window->ui != NULL) {
    7.26 +        asc_dprintf("Window with index %u has a dangling UI pointer", index);
    7.27 +        asc_scene_node_free(window->ui);
    7.28 +    }
    7.29  
    7.30      Uint32 flags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN;
    7.31      flags |= settings->fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : SDL_WINDOW_RESIZABLE;
    7.32 @@ -86,9 +83,9 @@
    7.33      window->resized = true; // count initial sizing as resize
    7.34  
    7.35      if (asc_gl_context_initialize(&window->glctx, window->window, &settings->glsettings)) {
    7.36 +        window->ui = asc_scene_node_empty();
    7.37          asc_dprintf("Window %u initialized", window->id);
    7.38          asc_context.active_window = window;
    7.39 -        asc_window_init_scenes(window);
    7.40          return window;
    7.41      } else {
    7.42          asc_dprintf("Creating GL context failed for window %u", window->id);
    7.43 @@ -110,7 +107,8 @@
    7.44      }
    7.45  
    7.46      // destroy all scenes
    7.47 -    asc_scene_destroy(&window->ui);
    7.48 +    asc_scene_node_free(window->ui);
    7.49 +    window->ui = NULL;
    7.50  
    7.51      // release context related data
    7.52      asc_gl_context_destroy(&window->glctx);
    7.53 @@ -136,8 +134,17 @@
    7.54          asc_window_activate(window);
    7.55      }
    7.56  
    7.57 +    // Clear the color buffer for the window frame
    7.58 +    int window_width = window->dimensions.width;
    7.59 +    int window_height = window->dimensions.height;
    7.60 +    glViewport(0, 0, window_width, window_height);
    7.61 +    glClear(GL_COLOR_BUFFER_BIT);
    7.62 +    asc_recti viewport = {0, 0, window_width, window_height};
    7.63 +
    7.64      // Draw the UI
    7.65 -    asc_scene_draw(&window->ui);
    7.66 +    AscCamera ui_camera;
    7.67 +    asc_camera_ortho(&ui_camera, viewport);
    7.68 +    asc_scene_draw(window->ui, viewport, &ui_camera);
    7.69  
    7.70      // Swap Buffers
    7.71      SDL_GL_SwapWindow(window->window);
     8.1 --- a/test/snake.c	Fri Mar 29 00:03:25 2024 +0100
     8.2 +++ b/test/snake.c	Mon Apr 01 18:54:19 2024 +0200
     8.3 @@ -50,7 +50,7 @@
     8.4      asc_ink_rgb(255, 0, 0);
     8.5      AscSceneNode* node = asc_text(10, 10, "XXXXXXX FPS");
     8.6      asc_scene_add_behavior(node, update_fps_counter);
     8.7 -    asc_scene_add(&asc_window_active->ui, node);
     8.8 +    asc_scene_node_link(asc_window_active->ui, node);
     8.9  }
    8.10  
    8.11  static void update_score_counter(AscSceneNode *node) {
    8.12 @@ -71,7 +71,7 @@
    8.13      asc_ink_rgb(0, 255, 0);
    8.14      AscSceneNode* node = asc_text(0, 0, "Score: 0");
    8.15      asc_scene_add_behavior(node, update_score_counter);
    8.16 -    asc_scene_add(&asc_window_active->ui, node);
    8.17 +    asc_scene_node_link(asc_window_active->ui, node);
    8.18  }
    8.19  
    8.20  int main(int argc, char** argv) {

mercurial