src/scene.c

changeset 37
8a8cc6725b48
parent 33
e7ddb52facd3
child 38
6e5629ea4c5c
--- a/src/scene.c	Wed Mar 06 23:38:17 2024 +0100
+++ b/src/scene.c	Fri Mar 15 00:06:59 2024 +0100
@@ -28,19 +28,34 @@
 #include "ascension/scene.h"
 #include "ascension/error.h"
 
+#include "ascension/context.h"
+
 #include <cx/tree.h>
+#include <cx/utils.h>
+
+#include "ascension/shader.h"
+#include <GL/glew.h>
 
 #include <assert.h>
 
-#define child_list_off_ \
-    offsetof(AscSceneNode, children), offsetof(AscSceneNode, next)
-
 void asc_scene_init(AscScene *scene) {
     if (scene->root != NULL) {
         asc_error("Scene is already initialized.");
         return;
     }
+
+    // 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_none, 8);
+    cx_array_initialize(scene->rg_fonts, 8);
 }
 
 void asc_scene_destroy(AscScene *scene) {
@@ -52,13 +67,25 @@
     asc_node_update(node);
 }
 
-void asc_scene_draw(AscScene const *scene) {
-    CxTreeIterator iter = cx_tree_iterator(scene->root, false, child_list_off_);
+#define asc_scene_draw_render_group(rg) \
+    cx_for_n(i, rg##_size) { \
+        rg[i].draw(rg[i].node); \
+    } (void)0
+
+void asc_scene_draw(AscScene *scene) {
+    // reset render groups
+    scene->rg_none_size = 0;
+    scene->rg_fonts_size = 0;
 
     // skip the root node deliberately, we know it's just the container
+    CxTreeIterator iter = cx_tree_iterator(
+            scene->root, false,
+            offsetof(AscSceneNode, children),
+            offsetof(AscSceneNode, next)
+    );
     cxIteratorNext(iter);
 
-    // update the children
+    // update the children and add them to the render groups
     cx_foreach(AscSceneNode*, node, iter) {
         // execute behaviors, first
         AscBehaviorNode *behavior = node->behaviors;
@@ -68,22 +95,65 @@
         }
 
         // check if geometry needs update
-        if (node->need_update && node->update_func != NULL) {
-            node->need_update = false;
-            asc_transform_copy(node->transform, node->parent->transform);
+        if (node->need_full_update) {
+            assert(node->update_func != NULL);
+            node->need_full_update = false;
             node->update_func(node);
         }
+        if (node->need_transform_update) {
+            assert(node->transform_update_func != NULL);
+            node->need_transform_update = false;
+            asc_transform_identity(node->transform);
+            node->transform_update_func(node);
+        }
 
-        // TODO: don't visit the tree for drawing, visit the render groups
+        // add to render group
         if (node->draw_func != NULL) {
-            node->draw_func(node);
+            struct asc_render_group_entry entry = {
+                    node->draw_func, node
+            };
+            switch (node->render_group) {
+                case ASC_RENDER_GROUP_NONE:
+                    cx_array_simple_add(scene->rg_none, entry);
+                    break;
+                case ASC_RENDER_GROUP_FONTS:
+                    cx_array_simple_add(scene->rg_fonts, entry);
+                    break;
+            }
         }
     }
+
+    // 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
+    );
+
+    // -----------------------------------------
+    // process the render groups for each camera
+    // -----------------------------------------
+    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);
+
+        // for the NONE group, the draw func is expected to do everything
+        asc_scene_draw_render_group(scene->rg_none);
+
+        // draw the FONTS group
+        // TODO: see if we can really always ignore the view matrix
+        glUseProgram(ASC_SHADER_FONT.base.id);
+        glUniformMatrix4fv(ASC_SHADER_FONT.base.projection, 1,
+                           GL_FALSE, camera->projection);
+        asc_scene_draw_render_group(scene->rg_fonts);
+    }
 }
 
 AscSceneNode *asc_scene_node_empty(void) {
     AscSceneNode *node = calloc(1, sizeof(AscSceneNode));
-    assert(node != NULL);
     node->free_func = (asc_scene_free_func) free;
     asc_transform_identity(node->transform);
     return node;
@@ -96,7 +166,11 @@
     asc_scene_node_unlink(node);
 
     // free the entire subtree
-    CxTreeIterator iter = cx_tree_iterator(node, true, child_list_off_);
+    CxTreeIterator iter = cx_tree_iterator(
+            node, true,
+            offsetof(AscSceneNode, children),
+            offsetof(AscSceneNode, next)
+    );
     cx_foreach(AscSceneNode*, child, iter) {
         if (!iter.exiting) continue;
         if (child->free_func != NULL) {

mercurial