30 #include "ascension/context.h" |
30 #include "ascension/context.h" |
31 #include "ascension/utils.h" |
31 #include "ascension/utils.h" |
32 |
32 |
33 #include <cx/linked_list.h> |
33 #include <cx/linked_list.h> |
34 #include <cx/array_list.h> |
34 #include <cx/array_list.h> |
|
35 #include <cx/tree.h> |
35 #include <cx/utils.h> |
36 #include <cx/utils.h> |
36 |
37 |
37 #include "ascension/shader.h" |
38 #include "ascension/shader.h" |
38 #include <GL/glew.h> |
39 #include <GL/glew.h> |
39 |
40 |
40 #include <assert.h> |
41 #include <assert.h> |
41 |
42 |
42 // TODO: move to a sprite.c |
43 static CxTreeIterator asc_scene_node_iterator( |
|
44 AscSceneNode *node, |
|
45 bool visit_on_exit |
|
46 ) { |
|
47 return cx_tree_iterator( |
|
48 node, visit_on_exit, |
|
49 offsetof(AscSceneNode, children), |
|
50 offsetof(AscSceneNode, next) |
|
51 ); |
|
52 } |
|
53 |
|
54 static CxTreeVisitor asc_scene_node_visitor(AscSceneNode *node) { |
|
55 return cx_tree_visitor(node, |
|
56 offsetof(AscSceneNode, children), |
|
57 offsetof(AscSceneNode, next) |
|
58 ); |
|
59 } |
|
60 |
43 static void asc_sprite_draw(AscSprite const *node) { |
61 static void asc_sprite_draw(AscSprite const *node) { |
44 // Obtain shader |
62 // Obtain shader |
45 AscShaderSprite *shader = ASC_SHADER_SPRITE; |
63 AscShaderSprite *shader = ASC_SHADER_SPRITE; |
46 |
64 |
47 // Upload model matrix |
65 // Upload model matrix |
56 |
74 |
57 // Draw mesh |
75 // Draw mesh |
58 asc_primitives_draw_plane(); |
76 asc_primitives_draw_plane(); |
59 } |
77 } |
60 |
78 |
61 void asc_scene_draw(AscScene scene, asc_recti viewport, AscCamera *camera) { |
79 void asc_scene_draw(AscSceneNode *root, asc_recti viewport, AscCamera *camera) { |
62 // create render groups |
80 // create render groups |
63 CxList *render_group[ASC_RENDER_GROUP_COUNT]; |
81 CxList *render_group[ASC_RENDER_GROUP_COUNT]; |
64 cx_for_n(i, ASC_RENDER_GROUP_COUNT) { |
82 cx_for_n(i, ASC_RENDER_GROUP_COUNT) { |
65 render_group[i] = cxArrayListCreateSimple(CX_STORE_POINTERS, 32); |
83 render_group[i] = cxArrayListCreateSimple(CX_STORE_POINTERS, 32); |
66 } |
84 } |
67 |
85 |
68 // skip the root node deliberately, we know it's just the container |
86 // skip the root node deliberately, we know it's just the container |
69 CxTreeVisitor iter = cxTreeVisit(scene); |
87 CxTreeVisitor iter = asc_scene_node_visitor(root); |
70 cxIteratorNext(iter); |
88 cxIteratorNext(iter); |
71 |
89 |
72 // update the children and add them to the render groups |
90 // update the children and add them to the render groups |
73 cx_foreach(AscSceneNode*, node, iter) { |
91 cx_foreach(AscSceneNode*, node, iter) { |
74 node->depth = iter.depth; |
92 node->depth = iter.depth; |
166 } |
184 } |
167 } |
185 } |
168 |
186 |
169 AscSceneNode *asc_scene_node_empty(void) { |
187 AscSceneNode *asc_scene_node_empty(void) { |
170 AscSceneNode *node = calloc(1, sizeof(AscSceneNode)); |
188 AscSceneNode *node = calloc(1, sizeof(AscSceneNode)); |
|
189 node->free_func = (asc_scene_free_func) free; |
171 node->scale.x = node->scale.y = node->scale.z = 1; |
190 node->scale.x = node->scale.y = node->scale.z = 1; |
172 asc_transform_identity(node->transform); |
191 asc_transform_identity(node->transform); |
173 asc_transform_identity(node->world_transform); |
192 asc_transform_identity(node->world_transform); |
174 return node; |
193 return node; |
175 } |
194 } |
176 |
195 |
177 void asc_scene_node_destructor(AscSceneNode *node) { |
196 void asc_scene_node_free(AscSceneNode *node) { |
178 if (node->behaviors != NULL) { |
197 if (node == NULL) return; |
179 cxListDestroy(node->behaviors); |
198 |
180 } |
199 // remove this node from its parent |
181 if (node->free_func != NULL) { |
200 asc_scene_node_unlink(node); |
182 node->free_func(node); |
201 |
183 } else { |
202 // free the entire subtree |
184 free(node); |
203 CxTreeIterator iter = asc_scene_node_iterator(node, true); |
185 } |
204 cx_foreach(AscSceneNode*, child, iter) { |
186 } |
205 if (!iter.exiting) continue; |
187 |
206 if (child->behaviors != NULL) { |
188 AscScene asc_scene_create(void) { |
207 cxListDestroy(child->behaviors); |
189 AscSceneNode *root = asc_scene_node_empty(); |
208 } |
190 AscScene scene = cxTreeCreateWrapped( |
209 if (child->free_func != NULL) { |
191 cxDefaultAllocator, root, |
210 child->free_func(child); |
192 cx_tree_node_base_layout |
211 } else { |
193 ); |
212 free(child); |
194 root->scene = scene; |
213 } |
195 scene->simple_destructor = (cx_destructor_func) asc_scene_node_destructor; |
214 } |
196 return scene; |
215 } |
197 } |
216 |
198 |
217 void asc_scene_node_link(AscSceneNode * restrict parent, AscSceneNode * restrict node) { |
199 void asc_scene_destroy(AscScene scene) { |
218 cx_tree_link( |
200 cxTreeDestroy(scene); |
219 parent, node, |
201 } |
220 offsetof(AscSceneNode, parent), |
202 |
221 offsetof(AscSceneNode, children), |
203 void asc_scene_node_add(AscSceneNode * restrict parent, AscSceneNode * restrict node) { |
222 offsetof(AscSceneNode, last_child), |
204 assert(node->scene == NULL || node->scene == parent->scene); |
223 offsetof(AscSceneNode, prev), |
205 cxTreeSetParent(parent->scene, parent, node); |
224 offsetof(AscSceneNode, next) |
206 CxTreeVisitor visitor = cxTreeVisitSubtree(parent->scene, node); |
225 ); |
207 cx_foreach(AscSceneNode *, n, visitor) { |
226 } |
208 n->scene = parent->scene; |
227 |
209 } |
228 void asc_scene_node_unlink(AscSceneNode *node) { |
210 asc_node_update_transform(node); |
229 cx_tree_unlink( |
211 } |
230 node, |
212 |
231 offsetof(AscSceneNode, parent), |
213 void asc_scene_node_remove(AscSceneNode *node) { |
232 offsetof(AscSceneNode, children), |
214 if (node->scene == NULL) return; |
233 offsetof(AscSceneNode, last_child), |
215 cxTreeRemoveSubtree(node->scene, node); |
234 offsetof(AscSceneNode, prev), |
216 CxTreeVisitor visitor = cxTreeVisitSubtree(node->scene, node); |
235 offsetof(AscSceneNode, next) |
217 cx_foreach(AscSceneNode *, n, visitor) { |
236 ); |
218 n->scene = NULL; |
|
219 } |
|
220 } |
237 } |
221 |
238 |
222 void asc_scene_add_behavior( |
239 void asc_scene_add_behavior( |
223 AscSceneNode *node, |
240 AscSceneNode *node, |
224 asc_scene_update_func behavior |
241 asc_scene_update_func behavior |
246 // fast skip if node is already marked |
263 // fast skip if node is already marked |
247 if (asc_test_flag(node->flags, ASC_SCENE_NODE_UPDATE_TRANSFORM)) { |
264 if (asc_test_flag(node->flags, ASC_SCENE_NODE_UPDATE_TRANSFORM)) { |
248 return; |
265 return; |
249 } |
266 } |
250 |
267 |
251 // if node is not part of a scene, just mark it and return |
268 CxTreeIterator iter = asc_scene_node_iterator(node, false); |
252 if (node->scene == NULL) { |
|
253 asc_set_flag(node->flags, ASC_SCENE_NODE_UPDATE_TRANSFORM); |
|
254 return; |
|
255 } |
|
256 |
|
257 // creating the iterator this way saves us from needing scene as parameter |
|
258 CxTreeIterator iter = cxTreeIterateSubtree(node->scene, node, false); |
|
259 cx_foreach(AscSceneNode*, n, iter) { |
269 cx_foreach(AscSceneNode*, n, iter) { |
260 if (asc_test_flag(n->flags, ASC_SCENE_NODE_UPDATE_TRANSFORM)) { |
270 if (asc_test_flag(n->flags, ASC_SCENE_NODE_UPDATE_TRANSFORM)) { |
261 cxTreeIteratorContinue(iter); |
271 cxTreeIteratorContinue(iter); |
262 } |
272 } |
263 asc_set_flag(n->flags, ASC_SCENE_NODE_UPDATE_TRANSFORM); |
273 asc_set_flag(n->flags, ASC_SCENE_NODE_UPDATE_TRANSFORM); |