exhibit ui improvements, and exhibit repositioning fix
[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 SceneNode *SceneNode::find_object_node() const
93 {
94         if(!obj.empty()) return (SceneNode*)this;
95
96         int numc = get_num_children();
97         for(int i=0; i<numc; i++) {
98                 SceneNode *n = get_child(i)->find_object_node();
99                 if(n) return n;
100         }
101         return 0;
102 }
103
104 void SceneNode::add_object(Object *obj)
105 {
106         if(obj->node == this) return;
107
108         if(obj->node) {
109                 obj->node->remove_object(obj);
110         }
111
112         this->obj.push_back(obj);
113         obj->node = this;
114
115         local_bvol_valid = false;
116 }
117
118 bool SceneNode::remove_object(Object *o)
119 {
120         if(o->node != this) {
121                 return false;
122         }
123         o->node = 0;
124
125         auto it = std::find(obj.begin(), obj.end(), o);
126         if(it == obj.end()) {
127                 return false;
128         }
129         obj.erase(it);
130
131         local_bvol_valid = false;
132         return true;
133 }
134
135 int SceneNode::get_num_objects() const
136 {
137         return (int)obj.size();
138 }
139
140 Object *SceneNode::get_object(int idx) const
141 {
142         return obj[idx];
143 }
144
145 void SceneNode::set_position(const Vec3 &pos)
146 {
147         this->pos = pos;
148 }
149
150 void SceneNode::set_rotation(const Quat &rot)
151 {
152         this->rot = rot;
153 }
154
155 void SceneNode::set_scaling(const Vec3 &scale)
156 {
157         this->scale = scale;
158 }
159
160
161 const Vec3 &SceneNode::get_node_position() const
162 {
163         return pos;
164 }
165
166 const Quat &SceneNode::get_node_rotation() const
167 {
168         return rot;
169 }
170
171 const Vec3 &SceneNode::get_node_scaling() const
172 {
173         return scale;
174 }
175
176
177 Vec3 SceneNode::get_position() const
178 {
179         return xform.get_translation();
180 }
181
182 Quat SceneNode::get_rotation() const
183 {
184         return xform.get_rotation();
185 }
186
187 Vec3 SceneNode::get_scaling() const
188 {
189         return xform.get_scaling();
190 }
191
192 const Mat4 &SceneNode::get_matrix() const
193 {
194         return xform;
195 }
196
197 const Mat4 &SceneNode::get_inv_matrix() const
198 {
199         return inv_xform;
200 }
201
202
203 void SceneNode::update_node(float dt)
204 {
205         xform = Mat4::identity;
206         xform.pre_translate(pos);
207         xform.pre_rotate(rot);
208         xform.pre_scale(scale);
209
210         if(parent) {
211                 xform = xform * parent->xform;
212         }
213         inv_xform = inverse(xform);
214 }
215
216 void SceneNode::update(float dt)
217 {
218         bool expanded = false;
219
220         if(debug_gui) {
221                 if(parent_expanded) {
222                         ImGui::PushID(this);
223                         ImGui::AlignTextToFramePadding();
224
225                         int flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick;
226                         if(children.empty()) flags |= ImGuiTreeNodeFlags_Leaf;
227                         if(dbg_sel_node == this) flags |= ImGuiTreeNodeFlags_Selected;
228                         expanded = ImGui::TreeNodeEx(name ? name : "<nameless node>", flags);
229                         if(ImGui::IsItemClicked()) {
230                                 dbg_sel_node = this;
231                         }
232
233                         ImGui::NextColumn();
234                         ImGui::Checkbox("##vis", &visible);
235                         ImGui::SameLine();
236                         if(ImGui::Button("xform")) {
237                                 ImGui::OpenPopup("xform_popup");
238                         }
239                         ImGui::SameLine();
240                         if(ImGui::Button("bbox")) {
241                                 ImGui::OpenPopup("bbox_popup");
242                         }
243                         if(ImGui::BeginPopup("xform_popup")) {
244                                 ImGui::Text("Local transform");
245                                 Vec3 p = get_node_position();
246                                 ImGui::BulletText("P: %g %g %g", p.x, p.y, p.z);
247                                 Quat q = get_node_rotation();
248                                 ImGui::BulletText("R: %g %g %g %g", q.x, q.y, q.z, q.w);
249                                 Vec3 s = get_node_scaling();
250                                 ImGui::BulletText("S: %g %g %g", s.x, s.y, s.z);
251
252                                 ImGui::Separator();
253                                 ImGui::Text("Global transform");
254                                 p = get_position();
255                                 ImGui::BulletText("P: %g %g %g", p.x, p.y, p.z);
256                                 q = get_rotation();
257                                 ImGui::BulletText("R: %g %g %g %g", q.x, q.y, q.z, q.w);
258                                 s = get_scaling();
259                                 ImGui::BulletText("S: %g %g %g", s.x, s.y, s.z);
260
261                                 const Mat4 &mat = get_matrix();
262                                 ImGui::BulletText("| %3.3f %3.3f %3.3f %3.3f |", mat[0][0], mat[0][1], mat[0][2], mat[0][3]);
263                                 ImGui::BulletText("| %3.3f %3.3f %3.3f %3.3f |", mat[1][0], mat[1][1], mat[1][2], mat[1][3]);
264                                 ImGui::BulletText("| %3.3f %3.3f %3.3f %3.3f |", mat[2][0], mat[2][1], mat[2][2], mat[2][3]);
265                                 ImGui::BulletText("| %3.3f %3.3f %3.3f %3.3f |", mat[3][0], mat[3][1], mat[3][2], mat[3][3]);
266
267                                 ImGui::EndPopup();
268                         }
269                         if(ImGui::BeginPopup("bbox_popup")) {
270                                 AABox bloc = get_local_bounds();
271                                 ImGui::Text("Local bounds:");
272                                 if(bloc.max.x < bloc.min.x || bloc.max.y < bloc.min.y || bloc.max.z < bloc.min.z) {
273                                         ImGui::BulletText("invalid");
274                                 } else {
275                                         ImGui::BulletText("X: %f - %f", bloc.min.x, bloc.max.x);
276                                         ImGui::BulletText("Y: %f - %f", bloc.min.y, bloc.max.y);
277                                         ImGui::BulletText("Z: %f - %f", bloc.min.z, bloc.max.z);
278                                 }
279                                 ImGui::Separator();
280                                 AABox bbox = get_bounds();
281                                 ImGui::Text("Global bounds:");
282                                 if(bbox.max.x < bbox.min.x || bbox.max.y < bbox.min.y || bbox.max.z < bbox.min.z) {
283                                         ImGui::BulletText("invalid");
284                                 } else {
285                                         ImGui::BulletText("X: %f - %f", bbox.min.x, bbox.max.x);
286                                         ImGui::BulletText("Y: %f - %f", bbox.min.y, bbox.max.y);
287                                         ImGui::BulletText("Z: %f - %f", bbox.min.z, bbox.max.z);
288                                 }
289                                 ImGui::EndPopup();
290                         }
291                         ImGui::NextColumn();
292                         ImGui::PopID();
293                 }
294         }
295
296         update_node(dt);
297
298         int num = children.size();
299         for(int i=0; i<num; i++) {
300                 parent_expanded = expanded;
301                 children[i]->update(dt);
302         }
303
304         if(debug_gui && expanded) {
305                 ImGui::TreePop();
306         }
307 }
308
309 void SceneNode::apply_xform()
310 {
311         update_node();
312
313         // apply post-order to make sure we don't affect the children xform by our reset
314
315         int nchild = children.size();
316         for(int i=0; i<nchild; i++) {
317                 children[i]->apply_xform();
318         }
319
320         int nobj = obj.size();
321         for(int i=0; i<nobj; i++) {
322                 if(obj[i]->get_type() == OBJ_MESH) {
323                         ObjMesh *om = (ObjMesh*)obj[i];
324                         if(om->mesh) {
325                                 om->mesh->apply_xform(xform);
326                         }
327                 }
328         }
329
330         pos = Vec3(0, 0, 0);
331         rot = Quat::identity;
332         scale = Vec3(1, 1, 1);
333 }
334
335 bool SceneNode::intersect(const Ray &ray, HitPoint *hit) const
336 {
337         Ray local_ray = inv_xform * ray;
338
339         HitPoint nearest;
340         nearest.dist = FLT_MAX;
341         for(size_t i=0; i<obj.size(); i++) {
342                 if(obj[i]->intersect(local_ray, hit)) {
343                         if(!hit) return true;
344                         if(hit->dist < nearest.dist) {
345                                 nearest = *hit;
346                                 nearest.data = (void*)this;
347                                 nearest.local_ray = local_ray;
348                         }
349                 }
350         }
351
352         for(size_t i=0; i<children.size(); i++) {
353                 if(children[i]->intersect(ray, hit)) {
354                         if(!hit) return true;
355                         if(hit->dist < nearest.dist) {
356                                 nearest = *hit;
357                         }
358                 }
359         }
360
361         if(nearest.dist < FLT_MAX) {
362                 *hit = nearest;
363                 hit->ray = ray;
364                 return true;
365         }
366         return false;
367 }
368
369 const AABox &SceneNode::calc_local_bounds()
370 {
371         local_bvol = AABox(Vec3(FLT_MAX, FLT_MAX, FLT_MAX), Vec3(-FLT_MAX, -FLT_MAX, -FLT_MAX));
372
373         // calculate the axis-aligned bounding box of all objects in this node
374         int nobj = obj.size();
375         for(int i=0; i<nobj; i++) {
376                 AABox tmp = obj[i]->get_aabox();
377                 calc_bounding_aabox(&local_bvol, &local_bvol, &tmp);
378         }
379
380         local_bvol_valid = true;
381         return local_bvol;
382 }
383
384 const AABox &SceneNode::get_local_bounds() const
385 {
386         if(!local_bvol_valid) {
387                 ((SceneNode*)this)->calc_local_bounds();
388         }
389         return local_bvol;
390 }
391
392 AABox SceneNode::get_node_bounds() const
393 {
394         get_local_bounds();     // validate local_bvol
395
396         // calculate the transformed local_bvol
397         Box node_bbox = Box(local_bvol, xform);
398
399         // then calculate the axis-aligned bounding box
400         AABox aabox;
401         calc_bounding_aabox(&aabox, &node_bbox);
402         return aabox;
403 }
404
405 AABox SceneNode::get_bounds() const
406 {
407         AABox sub_aabb = AABox(Vec3(FLT_MAX, FLT_MAX, FLT_MAX), Vec3(-FLT_MAX, -FLT_MAX, -FLT_MAX));
408
409         // calculate the bounding box of all children
410         int nchild = children.size();
411         for(int i=0; i<nchild; i++) {
412                 AABox tmp = children[i]->get_bounds();
413                 calc_bounding_aabox(&sub_aabb, &sub_aabb, &tmp);
414         }
415
416         AABox aabb = get_node_bounds();
417         calc_bounding_aabox(&aabb, &aabb, &sub_aabb);
418         return aabb;
419 }