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