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