26 */ |
26 */ |
27 |
27 |
28 #include "ascension/scene.h" |
28 #include "ascension/scene.h" |
29 #include "ascension/error.h" |
29 #include "ascension/error.h" |
30 |
30 |
|
31 #include "ascension/context.h" |
|
32 |
31 #include <cx/tree.h> |
33 #include <cx/tree.h> |
|
34 #include <cx/utils.h> |
|
35 |
|
36 #include "ascension/shader.h" |
|
37 #include <GL/glew.h> |
32 |
38 |
33 #include <assert.h> |
39 #include <assert.h> |
34 |
|
35 #define child_list_off_ \ |
|
36 offsetof(AscSceneNode, children), offsetof(AscSceneNode, next) |
|
37 |
40 |
38 void asc_scene_init(AscScene *scene) { |
41 void asc_scene_init(AscScene *scene) { |
39 if (scene->root != NULL) { |
42 if (scene->root != NULL) { |
40 asc_error("Scene is already initialized."); |
43 asc_error("Scene is already initialized."); |
41 return; |
44 return; |
42 } |
45 } |
|
46 |
|
47 // zero everything, first |
|
48 memset(scene, 0, sizeof(AscScene)); |
|
49 |
|
50 // default viewport is the entire viewport of the active window |
|
51 scene->viewport.size = asc_context.active_window->dimensions; |
|
52 |
|
53 // create the root node |
43 scene->root = asc_scene_node_empty(); |
54 scene->root = asc_scene_node_empty(); |
|
55 |
|
56 // initialize the render groups |
|
57 cx_array_initialize(scene->rg_none, 8); |
|
58 cx_array_initialize(scene->rg_fonts, 8); |
44 } |
59 } |
45 |
60 |
46 void asc_scene_destroy(AscScene *scene) { |
61 void asc_scene_destroy(AscScene *scene) { |
47 asc_scene_node_free(scene->root); |
62 asc_scene_node_free(scene->root); |
48 } |
63 } |
50 void asc_scene_add(AscScene *scene, AscSceneNode *node) { |
65 void asc_scene_add(AscScene *scene, AscSceneNode *node) { |
51 asc_scene_node_link(scene->root, node); |
66 asc_scene_node_link(scene->root, node); |
52 asc_node_update(node); |
67 asc_node_update(node); |
53 } |
68 } |
54 |
69 |
55 void asc_scene_draw(AscScene const *scene) { |
70 #define asc_scene_draw_render_group(rg) \ |
56 CxTreeIterator iter = cx_tree_iterator(scene->root, false, child_list_off_); |
71 cx_for_n(i, rg##_size) { \ |
|
72 rg[i].draw(rg[i].node); \ |
|
73 } (void)0 |
|
74 |
|
75 void asc_scene_draw(AscScene *scene) { |
|
76 // reset render groups |
|
77 scene->rg_none_size = 0; |
|
78 scene->rg_fonts_size = 0; |
57 |
79 |
58 // skip the root node deliberately, we know it's just the container |
80 // skip the root node deliberately, we know it's just the container |
|
81 CxTreeIterator iter = cx_tree_iterator( |
|
82 scene->root, false, |
|
83 offsetof(AscSceneNode, children), |
|
84 offsetof(AscSceneNode, next) |
|
85 ); |
59 cxIteratorNext(iter); |
86 cxIteratorNext(iter); |
60 |
87 |
61 // update the children |
88 // update the children and add them to the render groups |
62 cx_foreach(AscSceneNode*, node, iter) { |
89 cx_foreach(AscSceneNode*, node, iter) { |
63 // execute behaviors, first |
90 // execute behaviors, first |
64 AscBehaviorNode *behavior = node->behaviors; |
91 AscBehaviorNode *behavior = node->behaviors; |
65 while (behavior) { |
92 while (behavior) { |
66 behavior->func(node); |
93 behavior->func(node); |
67 behavior = behavior->next; |
94 behavior = behavior->next; |
68 } |
95 } |
69 |
96 |
70 // check if geometry needs update |
97 // check if geometry needs update |
71 if (node->need_update && node->update_func != NULL) { |
98 if (node->need_full_update) { |
72 node->need_update = false; |
99 assert(node->update_func != NULL); |
73 asc_transform_copy(node->transform, node->parent->transform); |
100 node->need_full_update = false; |
74 node->update_func(node); |
101 node->update_func(node); |
75 } |
102 } |
76 |
103 if (node->need_transform_update) { |
77 // TODO: don't visit the tree for drawing, visit the render groups |
104 assert(node->transform_update_func != NULL); |
|
105 node->need_transform_update = false; |
|
106 asc_transform_identity(node->transform); |
|
107 node->transform_update_func(node); |
|
108 } |
|
109 |
|
110 // add to render group |
78 if (node->draw_func != NULL) { |
111 if (node->draw_func != NULL) { |
79 node->draw_func(node); |
112 struct asc_render_group_entry entry = { |
80 } |
113 node->draw_func, node |
|
114 }; |
|
115 switch (node->render_group) { |
|
116 case ASC_RENDER_GROUP_NONE: |
|
117 cx_array_simple_add(scene->rg_none, entry); |
|
118 break; |
|
119 case ASC_RENDER_GROUP_FONTS: |
|
120 cx_array_simple_add(scene->rg_fonts, entry); |
|
121 break; |
|
122 } |
|
123 } |
|
124 } |
|
125 |
|
126 // set the viewport (in OpenGL we need to invert the Y axis) |
|
127 glViewport( |
|
128 scene->viewport.pos.x, |
|
129 -scene->viewport.pos.y, |
|
130 scene->viewport.size.width, |
|
131 scene->viewport.size.height |
|
132 ); |
|
133 |
|
134 // ----------------------------------------- |
|
135 // process the render groups for each camera |
|
136 // ----------------------------------------- |
|
137 cx_for_n(cam_id, ASC_SCENE_CAMERAS_MAX) { |
|
138 // update camera parameters, first |
|
139 AscCamera *camera = &scene->cameras[cam_id]; |
|
140 if (camera->update == NULL) continue; |
|
141 camera->update(camera); |
|
142 |
|
143 // for the NONE group, the draw func is expected to do everything |
|
144 asc_scene_draw_render_group(scene->rg_none); |
|
145 |
|
146 // draw the FONTS group |
|
147 // TODO: see if we can really always ignore the view matrix |
|
148 glUseProgram(ASC_SHADER_FONT.base.id); |
|
149 glUniformMatrix4fv(ASC_SHADER_FONT.base.projection, 1, |
|
150 GL_FALSE, camera->projection); |
|
151 asc_scene_draw_render_group(scene->rg_fonts); |
81 } |
152 } |
82 } |
153 } |
83 |
154 |
84 AscSceneNode *asc_scene_node_empty(void) { |
155 AscSceneNode *asc_scene_node_empty(void) { |
85 AscSceneNode *node = calloc(1, sizeof(AscSceneNode)); |
156 AscSceneNode *node = calloc(1, sizeof(AscSceneNode)); |
86 assert(node != NULL); |
|
87 node->free_func = (asc_scene_free_func) free; |
157 node->free_func = (asc_scene_free_func) free; |
88 asc_transform_identity(node->transform); |
158 asc_transform_identity(node->transform); |
89 return node; |
159 return node; |
90 } |
160 } |
91 |
161 |
94 |
164 |
95 // remove this node from its parent |
165 // remove this node from its parent |
96 asc_scene_node_unlink(node); |
166 asc_scene_node_unlink(node); |
97 |
167 |
98 // free the entire subtree |
168 // free the entire subtree |
99 CxTreeIterator iter = cx_tree_iterator(node, true, child_list_off_); |
169 CxTreeIterator iter = cx_tree_iterator( |
|
170 node, true, |
|
171 offsetof(AscSceneNode, children), |
|
172 offsetof(AscSceneNode, next) |
|
173 ); |
100 cx_foreach(AscSceneNode*, child, iter) { |
174 cx_foreach(AscSceneNode*, child, iter) { |
101 if (!iter.exiting) continue; |
175 if (!iter.exiting) continue; |
102 if (child->free_func != NULL) { |
176 if (child->free_func != NULL) { |
103 child->free_func(child); |
177 child->free_func(child); |
104 } else { |
178 } else { |