src/scene.c

changeset 47
44457f6cb0a2
parent 45
18de2af03531
child 49
77493525eac2
--- a/src/scene.c	Fri Mar 29 00:03:25 2024 +0100
+++ b/src/scene.c	Mon Apr 01 18:54:19 2024 +0200
@@ -30,6 +30,8 @@
 
 #include "ascension/context.h"
 
+#include <cx/linked_list.h>
+#include <cx/array_list.h>
 #include <cx/tree.h>
 #include <cx/utils.h>
 
@@ -56,53 +58,26 @@
     );
 }
 
-void asc_scene_init(AscScene *scene) {
-    if (scene->root != NULL) {
-        asc_error("Scene is already initialized.");
-        return;
+struct asc_render_group_entry {
+    asc_scene_draw_func draw;
+    AscSceneNode const *node;
+};
+
+#define asc_draw_render_group(iter) \
+    cx_foreach(struct asc_render_group_entry*, entry, iter) { \
+        entry->draw(entry->node); \
     }
 
-    // zero everything, first
-    memset(scene, 0, sizeof(AscScene));
-
-    // default viewport is the entire viewport of the active window
-    scene->viewport.size = asc_context.active_window->dimensions;
-
-    // create the root node
-    scene->root = asc_scene_node_empty();
-
-    // initialize the render groups
-    cx_array_initialize(scene->rg_sprites_opaque, 8);
-    cx_array_initialize(scene->rg_sprites_blended, 8);
-}
-
-void asc_scene_destroy(AscScene *scene) {
-    asc_scene_node_free(scene->root);
-}
-
-void asc_scene_add(AscScene *scene, AscSceneNode *node) {
-    asc_scene_node_link(scene->root, node);
-    asc_node_update(node);
-}
-
-#define asc_scene_draw_render_group(rg) \
-    cx_for_n(i, rg##_size) { \
-        rg[i].draw(rg[i].node); \
-    } (void)0
-
-#define asc_scene_draw_render_group_reversed(rg) \
-    for(size_t i = rg##_size ; i > 0 ; i--) { \
-        rg[i-1].draw(rg[i-1].node); \
-    } (void)0
-
-void asc_scene_draw(AscScene *scene) {
-    // reset render groups
-    // TODO: avoid recalculating the groups, if possible
-    scene->rg_sprites_opaque_size = 0;
-    scene->rg_sprites_blended_size = 0;
+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) {
+        render_group[i] = cxArrayListCreateSimple(
+                sizeof(struct asc_render_group_entry), 32);
+    }
 
     // skip the root node deliberately, we know it's just the container
-    CxTreeVisitor iter = asc_scene_node_visitor(scene->root);
+    CxTreeVisitor iter = asc_scene_node_visitor(root);
     cxIteratorNext(iter);
 
     // update the children and add them to the render groups
@@ -110,12 +85,16 @@
         node->depth = iter.depth;
 
         // execute behaviors, first
-        AscBehaviorNode *behavior = node->behaviors;
-        while (behavior) {
-            behavior->func(node);
-            behavior = behavior->next;
+        if (node->behaviors != NULL) {
+            CxIterator behavior_iter = cxListIterator(node->behaviors);
+            cx_foreach(asc_scene_update_func, behavior, behavior_iter) {
+                behavior(node);
+            }
         }
 
+        // TODO: implement culling
+        // TODO: implement a hidden flag (requires UCX tree-continue function)
+
         // check if geometry needs update
         if (node->need_graphics_update) {
             assert(node->update_func != NULL);
@@ -142,56 +121,51 @@
             struct asc_render_group_entry entry = {
                     node->draw_func, node
             };
-            switch (node->render_group) {
-                case ASC_RENDER_GROUP_SPRITE_OPAQUE:
-                    cx_array_simple_add(scene->rg_sprites_opaque, entry);
-                    break;
-                case ASC_RENDER_GROUP_SPRITE_BLEND:
-                    cx_array_simple_add(scene->rg_sprites_blended, entry);
-                    break;
-            }
+            cxListAdd(render_group[node->render_group], &entry);
         }
     }
 
     // set the viewport (in OpenGL we need to invert the Y axis)
     glViewport(
-            scene->viewport.pos.x,
-            -scene->viewport.pos.y,
-            scene->viewport.size.width,
-            scene->viewport.size.height
+            viewport.pos.x,
+            -viewport.pos.y,
+            viewport.size.width,
+            viewport.size.height
     );
-    glClear(GL_COLOR_BUFFER_BIT);
 
-    // -----------------------------------------
-    // process the render groups for each camera
-    // -----------------------------------------
+    // -------------------------
+    // process the render groups
+    // -------------------------
     AscShaderProgram *shader;
-    cx_for_n(cam_id, ASC_SCENE_CAMERAS_MAX) {
-        // update camera parameters, first
-        AscCamera *camera = &scene->cameras[cam_id];
-        if (camera->update == NULL) continue;
-        camera->update(camera);
+    CxIterator render_iter;
+
+    // 2D Elements
+    // ===========
+    glEnable(GL_DEPTH_TEST);
+    glClear(GL_DEPTH_BUFFER_BIT);
 
-        // 2D Elements
-        // ===========
-        glEnable(GL_DEPTH_TEST);
-        glClear(GL_DEPTH_BUFFER_BIT);
+    // Sprites
+    // -------
+    // TODO: implement view matrix for 2D worlds
+    shader = &asc_context.active_window->glctx.shader.sprite.base;
+    glUseProgram(shader->id);
+    glUniformMatrix4fv(shader->projection, 1,
+                       GL_FALSE, camera->projection);
 
-        // Sprites
-        // -------
-        // TODO: see if we can really always ignore the view matrix
-        shader = &asc_context.active_window->glctx.shader.sprite.base;
-        glUseProgram(shader->id);
-        glUniformMatrix4fv(shader->projection, 1,
-                           GL_FALSE, camera->projection);
+    // render opaque sprites from front to back
+    glDisable(GL_BLEND);
+    render_iter = cxListBackwardsIterator(render_group[ASC_RENDER_GROUP_SPRITE_OPAQUE]);
+    asc_draw_render_group(render_iter);
 
-        // render opaque sprites from front to back
-        glDisable(GL_BLEND);
-        asc_scene_draw_render_group_reversed(scene->rg_sprites_opaque);
-        // render sprites with alpha value from back to front
-        glEnable(GL_BLEND);
-        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-        asc_scene_draw_render_group(scene->rg_sprites_blended);
+    // render sprites with alpha value from back to front
+    glEnable(GL_BLEND);
+    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+    render_iter = cxListIterator(render_group[ASC_RENDER_GROUP_SPRITE_BLEND]);
+    asc_draw_render_group(render_iter);
+
+    // destroy render groups
+    cx_for_n(i, ASC_RENDER_GROUP_COUNT) {
+        cxListDestroy(render_group[i]);
     }
 }
 
@@ -213,6 +187,9 @@
     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 {
@@ -241,28 +218,23 @@
     );
 }
 
-AscBehaviorNode *asc_scene_add_behavior(AscSceneNode *node, asc_scene_update_func behavior) {
-    AscBehaviorNode *behavior_node = calloc(1, sizeof(AscBehaviorNode));
-    behavior_node->func = behavior;
-    cx_tree_link(
-            node,
-            behavior_node,
-            offsetof(AscBehaviorNode, parent),
-            offsetof(AscSceneNode, behaviors),
-            offsetof(AscBehaviorNode, prev),
-            offsetof(AscBehaviorNode, next)
-    );
-    return behavior_node;
+void asc_scene_add_behavior(
+        AscSceneNode *node,
+        asc_scene_update_func behavior
+) {
+    if (node->behaviors == NULL) {
+        node->behaviors = cxLinkedListCreateSimple(CX_STORE_POINTERS);
+    }
+    cxListAdd(node->behaviors, behavior);
 }
 
-void asc_scene_remove_behavior(AscBehaviorNode *node) {
-    cx_tree_unlink(
-            node,
-            offsetof(AscBehaviorNode, parent),
-            offsetof(AscSceneNode, behaviors),
-            offsetof(AscBehaviorNode, prev),
-            offsetof(AscBehaviorNode, next)
-    );
+void asc_scene_remove_behavior(
+        AscSceneNode *node,
+        asc_scene_update_func behavior
+) {
+    if (node->behaviors != NULL) {
+        cxListFindRemove(node->behaviors, behavior);
+    }
 }
 
 void asc_update_transform(AscSceneNode *node) {

mercurial