26 */ |
26 */ |
27 |
27 |
28 #include "ascension/scene.h" |
28 #include "ascension/scene.h" |
29 |
29 |
30 #include "ascension/context.h" |
30 #include "ascension/context.h" |
31 #include "ascension/utils.h" |
|
32 |
31 |
33 #include "ascension/2d.h" |
32 #include "ascension/2d.h" |
34 |
33 |
|
34 #include <cx/tree.h> |
35 #include <cx/linked_list.h> |
35 #include <cx/linked_list.h> |
36 #include <cx/array_list.h> |
36 #include <cx/array_list.h> |
37 #include <cx/tree.h> |
|
38 #include <cx/utils.h> |
37 #include <cx/utils.h> |
39 |
38 |
40 #include <GL/glew.h> |
39 #include <GL/glew.h> |
41 |
40 |
42 #include <assert.h> |
41 #include <assert.h> |
43 |
42 |
44 static CxTreeIterator asc_scene_node_iterator( |
43 void asc_scene_init(AscScene *scene) { |
45 AscSceneNode *node, |
44 asc_dprintf("Initialized scene %"PRIxPTR, (uintptr_t) scene); |
46 bool visit_on_exit |
45 // TODO: how should we initialize the camera? |
47 ) { |
46 scene->root = asc_scene_node_empty(); |
48 return cx_tree_iterator( |
|
49 node, visit_on_exit, |
|
50 offsetof(AscSceneNode, children), |
|
51 offsetof(AscSceneNode, next) |
|
52 ); |
|
53 } |
47 } |
54 |
48 |
55 static CxTreeVisitor asc_scene_node_visitor(AscSceneNode *node) { |
49 void asc_scene_destroy(AscScene *scene) { |
56 return cx_tree_visitor(node, |
50 asc_dprintf("Destroyed scene %"PRIxPTR, (uintptr_t) scene); |
57 offsetof(AscSceneNode, children), |
51 asc_scene_node_free(scene->root); |
58 offsetof(AscSceneNode, next) |
|
59 ); |
|
60 } |
52 } |
61 |
53 |
62 void asc_scene_draw(AscSceneNode *root, asc_recti viewport, const AscCamera *camera) { |
54 void asc_scene_draw(AscScene *scene, asc_recti viewport) { |
63 // create render groups |
55 // create render groups |
64 CxList *render_group[ASC_RENDER_GROUP_COUNT]; |
56 CxList *render_group[ASC_RENDER_GROUP_COUNT]; |
65 cx_for_n(i, ASC_RENDER_GROUP_COUNT) { |
57 cx_for_n(i, ASC_RENDER_GROUP_COUNT) { |
66 render_group[i] = cxArrayListCreateSimple(CX_STORE_POINTERS, 32); |
58 render_group[i] = cxArrayListCreateSimple(CX_STORE_POINTERS, 32); |
67 } |
59 } |
68 |
60 |
69 // skip the root node deliberately, we know it's just the container |
61 // skip the root node deliberately; we know it's just the container |
70 CxTreeVisitor iter = asc_scene_node_visitor(root); |
62 CxTreeVisitor iter = cx_tree_visitor(scene->root, |
|
63 offsetof(AscSceneNode, children), |
|
64 offsetof(AscSceneNode, next) |
|
65 ); |
71 cxIteratorNext(iter); |
66 cxIteratorNext(iter); |
72 |
67 |
73 // update the children and add them to the render groups |
68 // update the children and add them to the render groups |
74 cx_foreach(AscSceneNode*, node, iter) { |
69 cx_foreach(AscSceneNode*, node, iter) { |
75 node->depth = iter.depth; |
70 node->depth = iter.depth; |
143 + cxListSize(render_group[ASC_RENDER_GROUP_SPRITE_BLEND]); |
140 + cxListSize(render_group[ASC_RENDER_GROUP_SPRITE_BLEND]); |
144 if (sprite_count > 0) { |
141 if (sprite_count > 0) { |
145 AscShaderProgram *shader = &ASC_SHADER_SPRITE->program; |
142 AscShaderProgram *shader = &ASC_SHADER_SPRITE->program; |
146 glUseProgram(shader->id); |
143 glUseProgram(shader->id); |
147 glUniformMatrix4fv(shader->projection, 1, |
144 glUniformMatrix4fv(shader->projection, 1, |
148 GL_FALSE, camera->projection); |
145 GL_FALSE, scene->camera.projection); |
149 glUniformMatrix4fv(shader->view, 1, |
146 glUniformMatrix4fv(shader->view, 1, |
150 GL_FALSE, camera->view); |
147 GL_FALSE, scene->camera.view); |
151 |
148 |
152 // render opaque sprites from front to back |
149 // render opaque sprites from front to back |
153 glDisable(GL_BLEND); |
150 glDisable(GL_BLEND); |
154 render_iter = cxListBackwardsIterator(render_group[ASC_RENDER_GROUP_SPRITE_OPAQUE]); |
151 render_iter = cxListBackwardsIterator(render_group[ASC_RENDER_GROUP_SPRITE_OPAQUE]); |
155 cx_foreach(AscSprite const *, node, render_iter) { |
152 cx_foreach(AscSprite const *, node, render_iter) { |
169 cx_for_n(i, ASC_RENDER_GROUP_COUNT) { |
166 cx_for_n(i, ASC_RENDER_GROUP_COUNT) { |
170 cxListFree(render_group[i]); |
167 cxListFree(render_group[i]); |
171 } |
168 } |
172 } |
169 } |
173 |
170 |
174 AscSceneNode *asc_scene_node_empty(void) { |
171 void asc_scene_add_node(AscScene *scene, AscSceneNode *node) { |
175 AscSceneNode *node = calloc(1, sizeof(AscSceneNode)); |
172 asc_scene_node_link(scene->root, node); |
176 node->free_func = (asc_scene_free_func) free; |
|
177 node->scale.x = node->scale.y = node->scale.z = 1; |
|
178 asc_transform_identity(node->transform); |
|
179 asc_transform_identity(node->world_transform); |
|
180 return node; |
|
181 } |
173 } |
182 |
174 |
183 void asc_scene_node_free(AscSceneNode *node) { |
175 void asc_scene_remove_node(AscSceneNode *node) { |
184 if (node == NULL) return; |
|
185 |
|
186 // remove this node from its parent |
|
187 asc_scene_node_unlink(node); |
176 asc_scene_node_unlink(node); |
188 |
|
189 // free the entire subtree |
|
190 CxTreeIterator iter = asc_scene_node_iterator(node, true); |
|
191 cx_foreach(AscSceneNode*, child, iter) { |
|
192 if (!iter.exiting) continue; |
|
193 if (child->behaviors != NULL) { |
|
194 cxListFree(child->behaviors); |
|
195 } |
|
196 if (child->free_func != NULL) { |
|
197 child->free_func(child); |
|
198 } else { |
|
199 free(child); |
|
200 } |
|
201 } |
|
202 } |
177 } |
203 |
|
204 void asc_scene_node_link(AscSceneNode * restrict parent, AscSceneNode * restrict node) { |
|
205 cx_tree_link( |
|
206 parent, node, |
|
207 offsetof(AscSceneNode, parent), |
|
208 offsetof(AscSceneNode, children), |
|
209 offsetof(AscSceneNode, last_child), |
|
210 offsetof(AscSceneNode, prev), |
|
211 offsetof(AscSceneNode, next) |
|
212 ); |
|
213 } |
|
214 |
|
215 void asc_scene_node_unlink(AscSceneNode *node) { |
|
216 cx_tree_unlink( |
|
217 node, |
|
218 offsetof(AscSceneNode, parent), |
|
219 offsetof(AscSceneNode, children), |
|
220 offsetof(AscSceneNode, last_child), |
|
221 offsetof(AscSceneNode, prev), |
|
222 offsetof(AscSceneNode, next) |
|
223 ); |
|
224 } |
|
225 |
|
226 void asc_scene_add_behavior( |
|
227 AscSceneNode *node, |
|
228 asc_scene_update_func behavior |
|
229 ) { |
|
230 if (node->behaviors == NULL) { |
|
231 node->behaviors = cxLinkedListCreateSimple(CX_STORE_POINTERS); |
|
232 } |
|
233 cxListAdd(node->behaviors, behavior); |
|
234 } |
|
235 |
|
236 void asc_scene_remove_behavior( |
|
237 AscSceneNode *node, |
|
238 asc_scene_update_func behavior |
|
239 ) { |
|
240 if (node->behaviors != NULL) { |
|
241 cxListFindRemove(node->behaviors, behavior); |
|
242 } |
|
243 } |
|
244 |
|
245 void asc_node_update(AscSceneNode *node) { |
|
246 asc_set_flag(node->flags, ASC_SCENE_NODE_UPDATE_GRAPHICS); |
|
247 } |
|
248 |
|
249 void asc_node_update_transform(AscSceneNode *node) { |
|
250 // fast skip if node is already marked |
|
251 if (asc_test_flag(node->flags, ASC_SCENE_NODE_UPDATE_TRANSFORM)) { |
|
252 return; |
|
253 } |
|
254 |
|
255 CxTreeIterator iter = asc_scene_node_iterator(node, false); |
|
256 cx_foreach(AscSceneNode*, n, iter) { |
|
257 if (asc_test_flag(n->flags, ASC_SCENE_NODE_UPDATE_TRANSFORM)) { |
|
258 cxTreeIteratorContinue(iter); |
|
259 } |
|
260 asc_set_flag(n->flags, ASC_SCENE_NODE_UPDATE_TRANSFORM); |
|
261 } |
|
262 } |
|