--- /dev/null
+#include <float.h>
+#include <assert.h>
+#include <algorithm>
+#include "snode.h"
+
+SceneNode::SceneNode()
+ : scale(1, 1, 1)
+{
+ parent = 0;
+ name = 0;
+}
+
+SceneNode::SceneNode(Object *obj)
+ : scale(1, 1, 1)
+{
+ parent = 0;
+ name = 0;
+ add_object(obj);
+}
+
+SceneNode::~SceneNode()
+{
+ delete [] name;
+}
+
+void SceneNode::set_name(const char *s)
+{
+ delete [] name;
+ name = new char[strlen(s) + 1];
+ strcpy(name, s);
+}
+
+const char *SceneNode::get_name() const
+{
+ return name;
+}
+
+void SceneNode::add_child(SceneNode *node)
+{
+ if(node->parent) {
+ if(node->parent == this) {
+ return;
+ }
+ node->parent->remove_child(node);
+ }
+
+ children.push_back(node);
+ node->parent = this;
+}
+
+bool SceneNode::remove_child(SceneNode *node)
+{
+ auto it = std::find(children.begin(), children.end(), node);
+ if(it != children.end()) {
+ assert(node->parent == this);
+ node->parent = 0;
+ return true;
+ }
+ return false;
+}
+
+int SceneNode::get_num_children() const
+{
+ return (int)children.size();
+}
+
+SceneNode *SceneNode::get_child(int idx) const
+{
+ return children[idx];
+}
+
+SceneNode *SceneNode::get_parent() const
+{
+ return parent;
+}
+
+void SceneNode::add_object(Object *obj)
+{
+ if(obj->node == this) return;
+
+ if(obj->node) {
+ obj->node->remove_object(obj);
+ }
+
+ this->obj.push_back(obj);
+ obj->node = this;
+}
+
+bool SceneNode::remove_object(Object *o)
+{
+ if(o->node != this) {
+ return false;
+ }
+ o->node = 0;
+
+ auto it = std::find(obj.begin(), obj.end(), o);
+ if(it == obj.end()) {
+ return false;
+ }
+ obj.erase(it);
+ return true;
+}
+
+int SceneNode::get_num_objects() const
+{
+ return (int)obj.size();
+}
+
+Object *SceneNode::get_object(int idx) const
+{
+ return obj[idx];
+}
+
+void SceneNode::set_position(const Vec3 &pos)
+{
+ this->pos = pos;
+}
+
+void SceneNode::set_rotation(const Quat &rot)
+{
+ this->rot = rot;
+}
+
+void SceneNode::set_scaling(const Vec3 &scale)
+{
+ this->scale = scale;
+}
+
+
+const Vec3 &SceneNode::get_node_position() const
+{
+ return pos;
+}
+
+const Quat &SceneNode::get_node_rotation() const
+{
+ return rot;
+}
+
+const Vec3 &SceneNode::get_node_scaling() const
+{
+ return scale;
+}
+
+
+Vec3 SceneNode::get_position() const
+{
+ return xform * Vec3(0, 0, 0);
+}
+
+Quat SceneNode::get_rotation() const
+{
+ return rot; // TODO
+}
+
+Vec3 SceneNode::get_scaling() const
+{
+ return scale; // TODO
+}
+
+const Mat4 &SceneNode::get_matrix() const
+{
+ return xform;
+}
+
+const Mat4 &SceneNode::get_inv_matrix() const
+{
+ return inv_xform;
+}
+
+
+void SceneNode::update_node(float dt)
+{
+ xform = Mat4::identity;
+ xform.pre_translate(pos);
+ xform.pre_rotate(rot);
+ xform.pre_scale(scale);
+
+ if(parent) {
+ xform = parent->xform * xform;
+ }
+ inv_xform = inverse(xform);
+}
+
+void SceneNode::update(float dt)
+{
+ update_node(dt);
+
+ for(size_t i=0; i<children.size(); i++) {
+ children[i]->update(dt);
+ }
+}
+
+
+bool SceneNode::intersect(const Ray &ray, HitPoint *hit) const
+{
+ Ray local_ray = inv_xform * ray;
+
+ HitPoint nearest;
+ nearest.dist = FLT_MAX;
+ for(size_t i=0; i<obj.size(); i++) {
+ if(obj[i]->intersect(local_ray, hit)) {
+ if(!hit) return true;
+ if(hit->dist < nearest.dist) {
+ nearest = *hit;
+ nearest.data = (void*)this;
+ nearest.local_ray = local_ray;
+ }
+ }
+ }
+
+ for(size_t i=0; i<children.size(); i++) {
+ if(children[i]->intersect(ray, hit)) {
+ if(!hit) return true;
+ if(hit->dist < nearest.dist) {
+ nearest = *hit;
+ }
+ }
+ }
+
+ if(nearest.dist < FLT_MAX) {
+ *hit = nearest;
+ hit->ray = ray;
+ return true;
+ }
+ return false;
+}