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) {