src/scene.c

changeset 47
44457f6cb0a2
parent 45
18de2af03531
child 49
77493525eac2
equal deleted inserted replaced
46:d3285aed65b3 47:44457f6cb0a2
28 #include "ascension/scene.h" 28 #include "ascension/scene.h"
29 #include "ascension/error.h" 29 #include "ascension/error.h"
30 30
31 #include "ascension/context.h" 31 #include "ascension/context.h"
32 32
33 #include <cx/linked_list.h>
34 #include <cx/array_list.h>
33 #include <cx/tree.h> 35 #include <cx/tree.h>
34 #include <cx/utils.h> 36 #include <cx/utils.h>
35 37
36 #include "ascension/shader.h" 38 #include "ascension/shader.h"
37 #include <GL/glew.h> 39 #include <GL/glew.h>
54 offsetof(AscSceneNode, children), 56 offsetof(AscSceneNode, children),
55 offsetof(AscSceneNode, next) 57 offsetof(AscSceneNode, next)
56 ); 58 );
57 } 59 }
58 60
59 void asc_scene_init(AscScene *scene) { 61 struct asc_render_group_entry {
60 if (scene->root != NULL) { 62 asc_scene_draw_func draw;
61 asc_error("Scene is already initialized."); 63 AscSceneNode const *node;
62 return; 64 };
63 } 65
64 66 #define asc_draw_render_group(iter) \
65 // zero everything, first 67 cx_foreach(struct asc_render_group_entry*, entry, iter) { \
66 memset(scene, 0, sizeof(AscScene)); 68 entry->draw(entry->node); \
67 69 }
68 // default viewport is the entire viewport of the active window 70
69 scene->viewport.size = asc_context.active_window->dimensions; 71 void asc_scene_draw(AscSceneNode *root, asc_recti viewport, AscCamera *camera) {
70 72 // create render groups
71 // create the root node 73 CxList *render_group[ASC_RENDER_GROUP_COUNT];
72 scene->root = asc_scene_node_empty(); 74 cx_for_n(i, ASC_RENDER_GROUP_COUNT) {
73 75 render_group[i] = cxArrayListCreateSimple(
74 // initialize the render groups 76 sizeof(struct asc_render_group_entry), 32);
75 cx_array_initialize(scene->rg_sprites_opaque, 8); 77 }
76 cx_array_initialize(scene->rg_sprites_blended, 8);
77 }
78
79 void asc_scene_destroy(AscScene *scene) {
80 asc_scene_node_free(scene->root);
81 }
82
83 void asc_scene_add(AscScene *scene, AscSceneNode *node) {
84 asc_scene_node_link(scene->root, node);
85 asc_node_update(node);
86 }
87
88 #define asc_scene_draw_render_group(rg) \
89 cx_for_n(i, rg##_size) { \
90 rg[i].draw(rg[i].node); \
91 } (void)0
92
93 #define asc_scene_draw_render_group_reversed(rg) \
94 for(size_t i = rg##_size ; i > 0 ; i--) { \
95 rg[i-1].draw(rg[i-1].node); \
96 } (void)0
97
98 void asc_scene_draw(AscScene *scene) {
99 // reset render groups
100 // TODO: avoid recalculating the groups, if possible
101 scene->rg_sprites_opaque_size = 0;
102 scene->rg_sprites_blended_size = 0;
103 78
104 // skip the root node deliberately, we know it's just the container 79 // skip the root node deliberately, we know it's just the container
105 CxTreeVisitor iter = asc_scene_node_visitor(scene->root); 80 CxTreeVisitor iter = asc_scene_node_visitor(root);
106 cxIteratorNext(iter); 81 cxIteratorNext(iter);
107 82
108 // update the children and add them to the render groups 83 // update the children and add them to the render groups
109 cx_foreach(AscSceneNode*, node, iter) { 84 cx_foreach(AscSceneNode*, node, iter) {
110 node->depth = iter.depth; 85 node->depth = iter.depth;
111 86
112 // execute behaviors, first 87 // execute behaviors, first
113 AscBehaviorNode *behavior = node->behaviors; 88 if (node->behaviors != NULL) {
114 while (behavior) { 89 CxIterator behavior_iter = cxListIterator(node->behaviors);
115 behavior->func(node); 90 cx_foreach(asc_scene_update_func, behavior, behavior_iter) {
116 behavior = behavior->next; 91 behavior(node);
117 } 92 }
93 }
94
95 // TODO: implement culling
96 // TODO: implement a hidden flag (requires UCX tree-continue function)
118 97
119 // check if geometry needs update 98 // check if geometry needs update
120 if (node->need_graphics_update) { 99 if (node->need_graphics_update) {
121 assert(node->update_func != NULL); 100 assert(node->update_func != NULL);
122 node->need_graphics_update = false; 101 node->need_graphics_update = false;
140 // add to render group 119 // add to render group
141 if (node->draw_func != NULL) { 120 if (node->draw_func != NULL) {
142 struct asc_render_group_entry entry = { 121 struct asc_render_group_entry entry = {
143 node->draw_func, node 122 node->draw_func, node
144 }; 123 };
145 switch (node->render_group) { 124 cxListAdd(render_group[node->render_group], &entry);
146 case ASC_RENDER_GROUP_SPRITE_OPAQUE:
147 cx_array_simple_add(scene->rg_sprites_opaque, entry);
148 break;
149 case ASC_RENDER_GROUP_SPRITE_BLEND:
150 cx_array_simple_add(scene->rg_sprites_blended, entry);
151 break;
152 }
153 } 125 }
154 } 126 }
155 127
156 // set the viewport (in OpenGL we need to invert the Y axis) 128 // set the viewport (in OpenGL we need to invert the Y axis)
157 glViewport( 129 glViewport(
158 scene->viewport.pos.x, 130 viewport.pos.x,
159 -scene->viewport.pos.y, 131 -viewport.pos.y,
160 scene->viewport.size.width, 132 viewport.size.width,
161 scene->viewport.size.height 133 viewport.size.height
162 ); 134 );
163 glClear(GL_COLOR_BUFFER_BIT); 135
164 136 // -------------------------
165 // ----------------------------------------- 137 // process the render groups
166 // process the render groups for each camera 138 // -------------------------
167 // -----------------------------------------
168 AscShaderProgram *shader; 139 AscShaderProgram *shader;
169 cx_for_n(cam_id, ASC_SCENE_CAMERAS_MAX) { 140 CxIterator render_iter;
170 // update camera parameters, first 141
171 AscCamera *camera = &scene->cameras[cam_id]; 142 // 2D Elements
172 if (camera->update == NULL) continue; 143 // ===========
173 camera->update(camera); 144 glEnable(GL_DEPTH_TEST);
174 145 glClear(GL_DEPTH_BUFFER_BIT);
175 // 2D Elements 146
176 // =========== 147 // Sprites
177 glEnable(GL_DEPTH_TEST); 148 // -------
178 glClear(GL_DEPTH_BUFFER_BIT); 149 // TODO: implement view matrix for 2D worlds
179 150 shader = &asc_context.active_window->glctx.shader.sprite.base;
180 // Sprites 151 glUseProgram(shader->id);
181 // ------- 152 glUniformMatrix4fv(shader->projection, 1,
182 // TODO: see if we can really always ignore the view matrix 153 GL_FALSE, camera->projection);
183 shader = &asc_context.active_window->glctx.shader.sprite.base; 154
184 glUseProgram(shader->id); 155 // render opaque sprites from front to back
185 glUniformMatrix4fv(shader->projection, 1, 156 glDisable(GL_BLEND);
186 GL_FALSE, camera->projection); 157 render_iter = cxListBackwardsIterator(render_group[ASC_RENDER_GROUP_SPRITE_OPAQUE]);
187 158 asc_draw_render_group(render_iter);
188 // render opaque sprites from front to back 159
189 glDisable(GL_BLEND); 160 // render sprites with alpha value from back to front
190 asc_scene_draw_render_group_reversed(scene->rg_sprites_opaque); 161 glEnable(GL_BLEND);
191 // render sprites with alpha value from back to front 162 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
192 glEnable(GL_BLEND); 163 render_iter = cxListIterator(render_group[ASC_RENDER_GROUP_SPRITE_BLEND]);
193 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 164 asc_draw_render_group(render_iter);
194 asc_scene_draw_render_group(scene->rg_sprites_blended); 165
166 // destroy render groups
167 cx_for_n(i, ASC_RENDER_GROUP_COUNT) {
168 cxListDestroy(render_group[i]);
195 } 169 }
196 } 170 }
197 171
198 AscSceneNode *asc_scene_node_empty(void) { 172 AscSceneNode *asc_scene_node_empty(void) {
199 AscSceneNode *node = calloc(1, sizeof(AscSceneNode)); 173 AscSceneNode *node = calloc(1, sizeof(AscSceneNode));
211 185
212 // free the entire subtree 186 // free the entire subtree
213 CxTreeIterator iter = asc_scene_node_iterator(node, true); 187 CxTreeIterator iter = asc_scene_node_iterator(node, true);
214 cx_foreach(AscSceneNode*, child, iter) { 188 cx_foreach(AscSceneNode*, child, iter) {
215 if (!iter.exiting) continue; 189 if (!iter.exiting) continue;
190 if (child->behaviors != NULL) {
191 cxListDestroy(child->behaviors);
192 }
216 if (child->free_func != NULL) { 193 if (child->free_func != NULL) {
217 child->free_func(child); 194 child->free_func(child);
218 } else { 195 } else {
219 free(child); 196 free(child);
220 } 197 }
239 offsetof(AscSceneNode, prev), 216 offsetof(AscSceneNode, prev),
240 offsetof(AscSceneNode, next) 217 offsetof(AscSceneNode, next)
241 ); 218 );
242 } 219 }
243 220
244 AscBehaviorNode *asc_scene_add_behavior(AscSceneNode *node, asc_scene_update_func behavior) { 221 void asc_scene_add_behavior(
245 AscBehaviorNode *behavior_node = calloc(1, sizeof(AscBehaviorNode)); 222 AscSceneNode *node,
246 behavior_node->func = behavior; 223 asc_scene_update_func behavior
247 cx_tree_link( 224 ) {
248 node, 225 if (node->behaviors == NULL) {
249 behavior_node, 226 node->behaviors = cxLinkedListCreateSimple(CX_STORE_POINTERS);
250 offsetof(AscBehaviorNode, parent), 227 }
251 offsetof(AscSceneNode, behaviors), 228 cxListAdd(node->behaviors, behavior);
252 offsetof(AscBehaviorNode, prev), 229 }
253 offsetof(AscBehaviorNode, next) 230
254 ); 231 void asc_scene_remove_behavior(
255 return behavior_node; 232 AscSceneNode *node,
256 } 233 asc_scene_update_func behavior
257 234 ) {
258 void asc_scene_remove_behavior(AscBehaviorNode *node) { 235 if (node->behaviors != NULL) {
259 cx_tree_unlink( 236 cxListFindRemove(node->behaviors, behavior);
260 node, 237 }
261 offsetof(AscBehaviorNode, parent),
262 offsetof(AscSceneNode, behaviors),
263 offsetof(AscBehaviorNode, prev),
264 offsetof(AscBehaviorNode, next)
265 );
266 } 238 }
267 239
268 void asc_update_transform(AscSceneNode *node) { 240 void asc_update_transform(AscSceneNode *node) {
269 if (node->need_transform_update) return; 241 if (node->need_transform_update) return;
270 242

mercurial