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