src/scene.c

changeset 47
44457f6cb0a2
parent 45
18de2af03531
child 49
77493525eac2
     1.1 --- a/src/scene.c	Fri Mar 29 00:03:25 2024 +0100
     1.2 +++ b/src/scene.c	Mon Apr 01 18:54:19 2024 +0200
     1.3 @@ -30,6 +30,8 @@
     1.4  
     1.5  #include "ascension/context.h"
     1.6  
     1.7 +#include <cx/linked_list.h>
     1.8 +#include <cx/array_list.h>
     1.9  #include <cx/tree.h>
    1.10  #include <cx/utils.h>
    1.11  
    1.12 @@ -56,53 +58,26 @@
    1.13      );
    1.14  }
    1.15  
    1.16 -void asc_scene_init(AscScene *scene) {
    1.17 -    if (scene->root != NULL) {
    1.18 -        asc_error("Scene is already initialized.");
    1.19 -        return;
    1.20 +struct asc_render_group_entry {
    1.21 +    asc_scene_draw_func draw;
    1.22 +    AscSceneNode const *node;
    1.23 +};
    1.24 +
    1.25 +#define asc_draw_render_group(iter) \
    1.26 +    cx_foreach(struct asc_render_group_entry*, entry, iter) { \
    1.27 +        entry->draw(entry->node); \
    1.28      }
    1.29  
    1.30 -    // zero everything, first
    1.31 -    memset(scene, 0, sizeof(AscScene));
    1.32 -
    1.33 -    // default viewport is the entire viewport of the active window
    1.34 -    scene->viewport.size = asc_context.active_window->dimensions;
    1.35 -
    1.36 -    // create the root node
    1.37 -    scene->root = asc_scene_node_empty();
    1.38 -
    1.39 -    // initialize the render groups
    1.40 -    cx_array_initialize(scene->rg_sprites_opaque, 8);
    1.41 -    cx_array_initialize(scene->rg_sprites_blended, 8);
    1.42 -}
    1.43 -
    1.44 -void asc_scene_destroy(AscScene *scene) {
    1.45 -    asc_scene_node_free(scene->root);
    1.46 -}
    1.47 -
    1.48 -void asc_scene_add(AscScene *scene, AscSceneNode *node) {
    1.49 -    asc_scene_node_link(scene->root, node);
    1.50 -    asc_node_update(node);
    1.51 -}
    1.52 -
    1.53 -#define asc_scene_draw_render_group(rg) \
    1.54 -    cx_for_n(i, rg##_size) { \
    1.55 -        rg[i].draw(rg[i].node); \
    1.56 -    } (void)0
    1.57 -
    1.58 -#define asc_scene_draw_render_group_reversed(rg) \
    1.59 -    for(size_t i = rg##_size ; i > 0 ; i--) { \
    1.60 -        rg[i-1].draw(rg[i-1].node); \
    1.61 -    } (void)0
    1.62 -
    1.63 -void asc_scene_draw(AscScene *scene) {
    1.64 -    // reset render groups
    1.65 -    // TODO: avoid recalculating the groups, if possible
    1.66 -    scene->rg_sprites_opaque_size = 0;
    1.67 -    scene->rg_sprites_blended_size = 0;
    1.68 +void asc_scene_draw(AscSceneNode *root, asc_recti viewport, AscCamera *camera) {
    1.69 +    // create render groups
    1.70 +    CxList *render_group[ASC_RENDER_GROUP_COUNT];
    1.71 +    cx_for_n(i, ASC_RENDER_GROUP_COUNT) {
    1.72 +        render_group[i] = cxArrayListCreateSimple(
    1.73 +                sizeof(struct asc_render_group_entry), 32);
    1.74 +    }
    1.75  
    1.76      // skip the root node deliberately, we know it's just the container
    1.77 -    CxTreeVisitor iter = asc_scene_node_visitor(scene->root);
    1.78 +    CxTreeVisitor iter = asc_scene_node_visitor(root);
    1.79      cxIteratorNext(iter);
    1.80  
    1.81      // update the children and add them to the render groups
    1.82 @@ -110,12 +85,16 @@
    1.83          node->depth = iter.depth;
    1.84  
    1.85          // execute behaviors, first
    1.86 -        AscBehaviorNode *behavior = node->behaviors;
    1.87 -        while (behavior) {
    1.88 -            behavior->func(node);
    1.89 -            behavior = behavior->next;
    1.90 +        if (node->behaviors != NULL) {
    1.91 +            CxIterator behavior_iter = cxListIterator(node->behaviors);
    1.92 +            cx_foreach(asc_scene_update_func, behavior, behavior_iter) {
    1.93 +                behavior(node);
    1.94 +            }
    1.95          }
    1.96  
    1.97 +        // TODO: implement culling
    1.98 +        // TODO: implement a hidden flag (requires UCX tree-continue function)
    1.99 +
   1.100          // check if geometry needs update
   1.101          if (node->need_graphics_update) {
   1.102              assert(node->update_func != NULL);
   1.103 @@ -142,56 +121,51 @@
   1.104              struct asc_render_group_entry entry = {
   1.105                      node->draw_func, node
   1.106              };
   1.107 -            switch (node->render_group) {
   1.108 -                case ASC_RENDER_GROUP_SPRITE_OPAQUE:
   1.109 -                    cx_array_simple_add(scene->rg_sprites_opaque, entry);
   1.110 -                    break;
   1.111 -                case ASC_RENDER_GROUP_SPRITE_BLEND:
   1.112 -                    cx_array_simple_add(scene->rg_sprites_blended, entry);
   1.113 -                    break;
   1.114 -            }
   1.115 +            cxListAdd(render_group[node->render_group], &entry);
   1.116          }
   1.117      }
   1.118  
   1.119      // set the viewport (in OpenGL we need to invert the Y axis)
   1.120      glViewport(
   1.121 -            scene->viewport.pos.x,
   1.122 -            -scene->viewport.pos.y,
   1.123 -            scene->viewport.size.width,
   1.124 -            scene->viewport.size.height
   1.125 +            viewport.pos.x,
   1.126 +            -viewport.pos.y,
   1.127 +            viewport.size.width,
   1.128 +            viewport.size.height
   1.129      );
   1.130 -    glClear(GL_COLOR_BUFFER_BIT);
   1.131  
   1.132 -    // -----------------------------------------
   1.133 -    // process the render groups for each camera
   1.134 -    // -----------------------------------------
   1.135 +    // -------------------------
   1.136 +    // process the render groups
   1.137 +    // -------------------------
   1.138      AscShaderProgram *shader;
   1.139 -    cx_for_n(cam_id, ASC_SCENE_CAMERAS_MAX) {
   1.140 -        // update camera parameters, first
   1.141 -        AscCamera *camera = &scene->cameras[cam_id];
   1.142 -        if (camera->update == NULL) continue;
   1.143 -        camera->update(camera);
   1.144 +    CxIterator render_iter;
   1.145  
   1.146 -        // 2D Elements
   1.147 -        // ===========
   1.148 -        glEnable(GL_DEPTH_TEST);
   1.149 -        glClear(GL_DEPTH_BUFFER_BIT);
   1.150 +    // 2D Elements
   1.151 +    // ===========
   1.152 +    glEnable(GL_DEPTH_TEST);
   1.153 +    glClear(GL_DEPTH_BUFFER_BIT);
   1.154  
   1.155 -        // Sprites
   1.156 -        // -------
   1.157 -        // TODO: see if we can really always ignore the view matrix
   1.158 -        shader = &asc_context.active_window->glctx.shader.sprite.base;
   1.159 -        glUseProgram(shader->id);
   1.160 -        glUniformMatrix4fv(shader->projection, 1,
   1.161 -                           GL_FALSE, camera->projection);
   1.162 +    // Sprites
   1.163 +    // -------
   1.164 +    // TODO: implement view matrix for 2D worlds
   1.165 +    shader = &asc_context.active_window->glctx.shader.sprite.base;
   1.166 +    glUseProgram(shader->id);
   1.167 +    glUniformMatrix4fv(shader->projection, 1,
   1.168 +                       GL_FALSE, camera->projection);
   1.169  
   1.170 -        // render opaque sprites from front to back
   1.171 -        glDisable(GL_BLEND);
   1.172 -        asc_scene_draw_render_group_reversed(scene->rg_sprites_opaque);
   1.173 -        // render sprites with alpha value from back to front
   1.174 -        glEnable(GL_BLEND);
   1.175 -        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
   1.176 -        asc_scene_draw_render_group(scene->rg_sprites_blended);
   1.177 +    // render opaque sprites from front to back
   1.178 +    glDisable(GL_BLEND);
   1.179 +    render_iter = cxListBackwardsIterator(render_group[ASC_RENDER_GROUP_SPRITE_OPAQUE]);
   1.180 +    asc_draw_render_group(render_iter);
   1.181 +
   1.182 +    // render sprites with alpha value from back to front
   1.183 +    glEnable(GL_BLEND);
   1.184 +    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
   1.185 +    render_iter = cxListIterator(render_group[ASC_RENDER_GROUP_SPRITE_BLEND]);
   1.186 +    asc_draw_render_group(render_iter);
   1.187 +
   1.188 +    // destroy render groups
   1.189 +    cx_for_n(i, ASC_RENDER_GROUP_COUNT) {
   1.190 +        cxListDestroy(render_group[i]);
   1.191      }
   1.192  }
   1.193  
   1.194 @@ -213,6 +187,9 @@
   1.195      CxTreeIterator iter = asc_scene_node_iterator(node, true);
   1.196      cx_foreach(AscSceneNode*, child, iter) {
   1.197          if (!iter.exiting) continue;
   1.198 +        if (child->behaviors != NULL) {
   1.199 +            cxListDestroy(child->behaviors);
   1.200 +        }
   1.201          if (child->free_func != NULL) {
   1.202              child->free_func(child);
   1.203          } else {
   1.204 @@ -241,28 +218,23 @@
   1.205      );
   1.206  }
   1.207  
   1.208 -AscBehaviorNode *asc_scene_add_behavior(AscSceneNode *node, asc_scene_update_func behavior) {
   1.209 -    AscBehaviorNode *behavior_node = calloc(1, sizeof(AscBehaviorNode));
   1.210 -    behavior_node->func = behavior;
   1.211 -    cx_tree_link(
   1.212 -            node,
   1.213 -            behavior_node,
   1.214 -            offsetof(AscBehaviorNode, parent),
   1.215 -            offsetof(AscSceneNode, behaviors),
   1.216 -            offsetof(AscBehaviorNode, prev),
   1.217 -            offsetof(AscBehaviorNode, next)
   1.218 -    );
   1.219 -    return behavior_node;
   1.220 +void asc_scene_add_behavior(
   1.221 +        AscSceneNode *node,
   1.222 +        asc_scene_update_func behavior
   1.223 +) {
   1.224 +    if (node->behaviors == NULL) {
   1.225 +        node->behaviors = cxLinkedListCreateSimple(CX_STORE_POINTERS);
   1.226 +    }
   1.227 +    cxListAdd(node->behaviors, behavior);
   1.228  }
   1.229  
   1.230 -void asc_scene_remove_behavior(AscBehaviorNode *node) {
   1.231 -    cx_tree_unlink(
   1.232 -            node,
   1.233 -            offsetof(AscBehaviorNode, parent),
   1.234 -            offsetof(AscSceneNode, behaviors),
   1.235 -            offsetof(AscBehaviorNode, prev),
   1.236 -            offsetof(AscBehaviorNode, next)
   1.237 -    );
   1.238 +void asc_scene_remove_behavior(
   1.239 +        AscSceneNode *node,
   1.240 +        asc_scene_update_func behavior
   1.241 +) {
   1.242 +    if (node->behaviors != NULL) {
   1.243 +        cxListFindRemove(node->behaviors, behavior);
   1.244 +    }
   1.245  }
   1.246  
   1.247  void asc_update_transform(AscSceneNode *node) {

mercurial