simple ubershader system, reflection debugging
[laserbrain_demo] / src / snode.cc
1 #include <float.h>
2 #include <assert.h>
3 #include <algorithm>
4 #include "snode.h"
5 #include "objmesh.h"
6 #include "dbg_gui.h"
7
8 SceneNode::SceneNode()
9         : scale(1, 1, 1)
10 {
11         scene = 0;
12         parent = 0;
13         name = 0;
14         visible = true;
15         local_bvol_valid = false;
16 }
17
18 SceneNode::SceneNode(Object *obj)
19         : scale(1, 1, 1)
20 {
21         scene = 0;
22         parent = 0;
23         name = 0;
24         visible = true;
25         local_bvol_valid = false;
26         add_object(obj);
27 }
28
29 SceneNode::~SceneNode()
30 {
31         delete [] name;
32 }
33
34 void SceneNode::set_name(const char *s)
35 {
36         delete [] name;
37         name = new char[strlen(s) + 1];
38         strcpy(name, s);
39 }
40
41 const char *SceneNode::get_name() const
42 {
43         return name;
44 }
45
46 void SceneNode::add_child(SceneNode *node)
47 {
48         if(!node) return;
49
50         if(node->parent) {
51                 if(node->parent == this) {
52                         return;
53                 }
54                 node->parent->remove_child(node);
55         }
56
57         children.push_back(node);
58         node->parent = this;
59         node->scene = scene;
60 }
61
62 bool SceneNode::remove_child(SceneNode *node)
63 {
64         if(!node) return false;
65
66         auto it = std::find(children.begin(), children.end(), node);
67         if(it != children.end()) {
68                 assert(node->parent == this);
69                 node->parent = 0;
70                 node->scene = 0;
71                 children.erase(it);
72                 return true;
73         }
74         return false;
75 }
76
77 int SceneNode::get_num_children() const
78 {
79         return (int)children.size();
80 }
81
82 SceneNode *SceneNode::get_child(int idx) const
83 {
84         return children[idx];
85 }
86
87 SceneNode *SceneNode::get_parent() const
88 {
89         return parent;
90 }
91
92 void SceneNode::add_object(Object *obj)
93 {
94         if(obj->node == this) return;
95
96         if(obj->node) {
97                 obj->node->remove_object(obj);
98         }
99
100         this->obj.push_back(obj);
101         obj->node = this;
102
103         local_bvol_valid = false;
104 }
105
106 bool SceneNode::remove_object(Object *o)
107 {
108         if(o->node != this) {
109                 return false;
110         }
111         o->node = 0;
112
113         auto it = std::find(obj.begin(), obj.end(), o);
114         if(it == obj.end()) {
115                 return false;
116         }
117         obj.erase(it);
118
119         local_bvol_valid = false;
120         return true;
121 }
122
123 int SceneNode::get_num_objects() const
124 {
125         return (int)obj.size();
126 }
127
128 Object *SceneNode::get_object(int idx) const
129 {
130         return obj[idx];
131 }
132
133 void SceneNode::set_position(const Vec3 &pos)
134 {
135         this->pos = pos;
136 }
137
138 void SceneNode::set_rotation(const Quat &rot)
139 {
140         this->rot = rot;
141 }
142
143 void SceneNode::set_scaling(const Vec3 &scale)
144 {
145         this->scale = scale;
146 }
147
148
149 const Vec3 &SceneNode::get_node_position() const
150 {
151         return pos;
152 }
153
154 const Quat &SceneNode::get_node_rotation() const
155 {
156         return rot;
157 }
158
159 const Vec3 &SceneNode::get_node_scaling() const
160 {
161         return scale;
162 }
163
164
165 Vec3 SceneNode::get_position() const
166 {
167         return xform.get_translation();
168 }
169
170 Quat SceneNode::get_rotation() const
171 {
172         return xform.get_rotation();
173 }
174
175 Vec3 SceneNode::get_scaling() const
176 {
177         return xform.get_scaling();
178 }
179
180 const Mat4 &SceneNode::get_matrix() const
181 {
182         return xform;
183 }
184
185 const Mat4 &SceneNode::get_inv_matrix() const
186 {
187         return inv_xform;
188 }
189
190
191 void SceneNode::update_node(float dt)
192 {
193         xform = Mat4::identity;
194         xform.pre_translate(pos);
195         xform.pre_rotate(rot);
196         xform.pre_scale(scale);
197
198         if(parent) {
199                 xform = xform * parent->xform;
200         }
201         inv_xform = inverse(xform);
202 }
203
204 void SceneNode::update(float dt)
205 {
206         bool expanded = false;
207
208         if(debug_gui) {
209                 if(parent_expanded) {
210                         ImGui::PushID(this);
211                         ImGui::AlignTextToFramePadding();
212
213                         int flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick;
214                         if(children.empty()) flags |= ImGuiTreeNodeFlags_Leaf;
215                         if(dbg_sel_node == this) flags |= ImGuiTreeNodeFlags_Selected;
216                         expanded = ImGui::TreeNodeEx(name ? name : "<nameless node>", flags);
217                         if(ImGui::IsItemClicked()) {
218                                 dbg_sel_node = this;
219                         }
220
221                         ImGui::NextColumn();
222                         ImGui::Checkbox("##vis", &visible);
223                         ImGui::SameLine();
224                         if(ImGui::Button("xform")) {
225                                 ImGui::OpenPopup("xform_popup");
226                         }
227                         if(ImGui::BeginPopup("xform_popup")) {
228                                 ImGui::Text("Local transform");
229                                 Vec3 p = get_node_position();
230                                 ImGui::BulletText("P: %g %g %g", p.x, p.y, p.z);
231                                 Quat q = get_node_rotation();
232                                 ImGui::BulletText("R: %g %g %g %g", q.x, q.y, q.z, q.w);
233                                 Vec3 s = get_node_scaling();
234                                 ImGui::BulletText("S: %g %g %g", s.x, s.y, s.z);
235
236                                 ImGui::Separator();
237                                 ImGui::Text("Global transform");
238                                 p = get_position();
239                                 ImGui::BulletText("P: %g %g %g", p.x, p.y, p.z);
240                                 q = get_rotation();
241                                 ImGui::BulletText("R: %g %g %g %g", q.x, q.y, q.z, q.w);
242                                 s = get_scaling();
243                                 ImGui::BulletText("S: %g %g %g", s.x, s.y, s.z);
244
245                                 const Mat4 &mat = get_matrix();
246                                 ImGui::BulletText("| %3.3f %3.3f %3.3f %3.3f |", mat[0][0], mat[0][1], mat[0][2], mat[0][3]);
247                                 ImGui::BulletText("| %3.3f %3.3f %3.3f %3.3f |", mat[1][0], mat[1][1], mat[1][2], mat[1][3]);
248                                 ImGui::BulletText("| %3.3f %3.3f %3.3f %3.3f |", mat[2][0], mat[2][1], mat[2][2], mat[2][3]);
249                                 ImGui::BulletText("| %3.3f %3.3f %3.3f %3.3f |", mat[3][0], mat[3][1], mat[3][2], mat[3][3]);
250
251                                 ImGui::EndPopup();
252                         }
253                         ImGui::NextColumn();
254                         ImGui::PopID();
255                 }
256         }
257
258         update_node(dt);
259
260         int num = children.size();
261         for(int i=0; i<num; i++) {
262                 parent_expanded = expanded;
263                 children[i]->update(dt);
264         }
265
266         if(debug_gui && expanded) {
267                 ImGui::TreePop();
268         }
269 }
270
271 void SceneNode::apply_xform()
272 {
273         update_node();
274
275         // apply post-order to make sure we don't affect the children xform by our reset
276
277         int nchild = children.size();
278         for(int i=0; i<nchild; i++) {
279                 children[i]->apply_xform();
280         }
281
282         int nobj = obj.size();
283         for(int i=0; i<nobj; i++) {
284                 if(obj[i]->get_type() == OBJ_MESH) {
285                         ObjMesh *om = (ObjMesh*)obj[i];
286                         if(om->mesh) {
287                                 om->mesh->apply_xform(xform);
288                         }
289                 }
290         }
291
292         pos = Vec3(0, 0, 0);
293         rot = Quat::identity;
294         scale = Vec3(1, 1, 1);
295 }
296
297 bool SceneNode::intersect(const Ray &ray, HitPoint *hit) const
298 {
299         Ray local_ray = inv_xform * ray;
300
301         HitPoint nearest;
302         nearest.dist = FLT_MAX;
303         for(size_t i=0; i<obj.size(); i++) {
304                 if(obj[i]->intersect(local_ray, hit)) {
305                         if(!hit) return true;
306                         if(hit->dist < nearest.dist) {
307                                 nearest = *hit;
308                                 nearest.data = (void*)this;
309                                 nearest.local_ray = local_ray;
310                         }
311                 }
312         }
313
314         for(size_t i=0; i<children.size(); i++) {
315                 if(children[i]->intersect(ray, hit)) {
316                         if(!hit) return true;
317                         if(hit->dist < nearest.dist) {
318                                 nearest = *hit;
319                         }
320                 }
321         }
322
323         if(nearest.dist < FLT_MAX) {
324                 *hit = nearest;
325                 hit->ray = ray;
326                 return true;
327         }
328         return false;
329 }
330
331 const AABox &SceneNode::calc_local_bounds()
332 {
333         local_bvol = AABox(Vec3(FLT_MAX, FLT_MAX, FLT_MAX), Vec3(-FLT_MAX, -FLT_MAX, -FLT_MAX));
334
335         // calculate the axis-aligned bounding box of all objects in this node
336         int nobj = obj.size();
337         for(int i=0; i<nobj; i++) {
338                 AABox tmp = obj[i]->get_aabox();
339                 calc_bounding_aabox(&local_bvol, &local_bvol, &tmp);
340         }
341
342         local_bvol_valid = true;
343         return local_bvol;
344 }
345
346 const AABox &SceneNode::get_local_bounds() const
347 {
348         if(!local_bvol_valid) {
349                 ((SceneNode*)this)->calc_local_bounds();
350         }
351         return local_bvol;
352 }
353
354 AABox SceneNode::get_node_bounds() const
355 {
356         get_local_bounds();     // validate local_bvol
357
358         // calculate the transformed local_bvol
359         Box node_bbox = Box(local_bvol, xform);
360
361         // then calculate the axis-aligned bounding box
362         AABox aabox;
363         calc_bounding_aabox(&aabox, &node_bbox);
364         return aabox;
365 }
366
367 AABox SceneNode::get_bounds() const
368 {
369         AABox sub_aabb = AABox(Vec3(FLT_MAX, FLT_MAX, FLT_MAX), Vec3(-FLT_MAX, -FLT_MAX, -FLT_MAX));
370
371         // calculate the bounding box of all children
372         int nchild = children.size();
373         for(int i=0; i<nchild; i++) {
374                 AABox tmp = children[i]->get_bounds();
375                 calc_bounding_aabox(&sub_aabb, &sub_aabb, &tmp);
376         }
377
378         AABox aabb = get_node_bounds();
379         calc_bounding_aabox(&aabb, &aabb, &sub_aabb);
380         return aabb;
381 }