| 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 } |
|