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