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