assimp
authorJohn Tsiombikas <nuclear@mutantstargoat.com>
Sat, 29 Oct 2016 05:15:24 +0000 (08:15 +0300)
committerJohn Tsiombikas <nuclear@mutantstargoat.com>
Sat, 29 Oct 2016 05:15:24 +0000 (08:15 +0300)
19 files changed:
Makefile
src/exhibit.h [new file with mode: 0644]
src/geom.cc
src/geom.h
src/main.cc
src/material.cc [new file with mode: 0644]
src/material.h [new file with mode: 0644]
src/mech_exhibit.h [new file with mode: 0644]
src/mesh.cc
src/object.cc [new file with mode: 0644]
src/object.h [new file with mode: 0644]
src/objmesh.cc [new file with mode: 0644]
src/objmesh.h [new file with mode: 0644]
src/scene.cc [new file with mode: 0644]
src/scene.h [new file with mode: 0644]
src/sceneload.cc [new file with mode: 0644]
src/sceneload.h [new file with mode: 0644]
src/snode.cc [new file with mode: 0644]
src/snode.h [new file with mode: 0644]

index 3c0cb0a..82b42ca 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -14,7 +14,7 @@ warn = -pedantic -Wall
 
 CFLAGS = $(warn) $(opt) $(dbg) $(incpath)
 CXXFLAGS = -std=c++11 $(warn) $(opt) $(dbg) $(incpath)
-LDFLAGS = $(libpath) $(libgl_$(sys)) -lm -lgmath -lvmath -limago -lresman -lpthread
+LDFLAGS = $(libpath) $(libgl_$(sys)) -lm -lgmath -lvmath -limago -lresman -lpthread -lassimp
 
 sys = $(shell uname -s)
 libgl_Linux = -lGL -lGLU -lglut -lGLEW
diff --git a/src/exhibit.h b/src/exhibit.h
new file mode 100644 (file)
index 0000000..c607fdb
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef EXHIBIT_H_
+#define EXHIBIT_H_
+
+#include <gmath/gmath.h>
+#include "object.h"
+#include "geom.h"
+
+/*
+- select me aktina kai select me sfaira, epistrefei Selection
+- hover me aktina kai hover me sfaira
+- move me selection, origin, direction kai rotation (?)
+ */
+
+class Exhibit : public Object {
+public:
+       Exhibit();
+       virtual ~Exhibit() = default;
+
+       Exhibit(const Exhibit&) = delete;
+       Exhibit &operator =(const Exhibit &) = delete;
+
+       virtual void *select(const Ray &ray) const;
+       virtual void *select(const Sphere &sph) const;
+
+       virtual void update(float dt = 0.0f);
+       virtual void draw() const;
+};
+
+#endif // EXHIBIT_H_
index a6b26e8..55cfed7 100644 (file)
@@ -1,6 +1,5 @@
-#include <assert.h>
-#include <float.h>
 #include <algorithm>
+#include <float.h>
 #include "geom.h"
 
 GeomObject::~GeomObject()
@@ -19,15 +18,19 @@ Sphere::Sphere(const Vec3 &cent, float radius)
        this->radius = radius;
 }
 
-void Sphere::set_union(const GeomObject *obj1, const GeomObject *obj2)
+GeomObjectType Sphere::get_type() const
 {
-       const Sphere *sph1 = dynamic_cast<const Sphere*>(obj1);
-       const Sphere *sph2 = dynamic_cast<const Sphere*>(obj2);
+       return GOBJ_SPHERE;
+}
 
-       if(!sph1 || !sph2) {
+void Sphere::set_union(const GeomObject *obj1, const GeomObject *obj2)
+{
+       if(obj1->get_type() != GOBJ_SPHERE || obj2->get_type() != GOBJ_SPHERE) {
                fprintf(stderr, "Sphere::set_union: arguments must be spheres");
                return;
        }
+       const Sphere *sph1 = (const Sphere*)obj1;
+       const Sphere *sph2 = (const Sphere*)obj2;
 
        float dist = length(sph1->center - sph2->center);
        float surf_dist = dist - (sph1->radius + sph2->radius);
@@ -85,6 +88,15 @@ bool Sphere::intersect(const Ray &ray, HitPoint *hit) const
        return true;
 }
 
+bool Sphere::contains(const Vec3 &pt) const
+{
+       return length_sq(pt - center) <= radius * radius;
+}
+
+float Sphere::distance(const Vec3 &v) const
+{
+       return length(v - center) - radius;
+}
 
 AABox::AABox()
 {
@@ -95,15 +107,19 @@ AABox::AABox(const Vec3 &vmin, const Vec3 &vmax)
 {
 }
 
-void AABox::set_union(const GeomObject *obj1, const GeomObject *obj2)
+GeomObjectType AABox::get_type() const
 {
-       const AABox *box1 = dynamic_cast<const AABox*>(obj1);
-       const AABox *box2 = dynamic_cast<const AABox*>(obj2);
+       return GOBJ_AABOX;
+}
 
-       if(!box1 || !box2) {
+void AABox::set_union(const GeomObject *obj1, const GeomObject *obj2)
+{
+       if(obj1->get_type() != GOBJ_AABOX || obj2->get_type() != GOBJ_AABOX) {
                fprintf(stderr, "AABox::set_union: arguments must be AABoxes too\n");
                return;
        }
+       const AABox *box1 = (const AABox*)obj1;
+       const AABox *box2 = (const AABox*)obj2;
 
        min.x = std::min(box1->min.x, box2->min.x);
        min.y = std::min(box1->min.y, box2->min.y);
@@ -116,13 +132,12 @@ void AABox::set_union(const GeomObject *obj1, const GeomObject *obj2)
 
 void AABox::set_intersection(const GeomObject *obj1, const GeomObject *obj2)
 {
-       const AABox *box1 = dynamic_cast<const AABox*>(obj1);
-       const AABox *box2 = dynamic_cast<const AABox*>(obj2);
-
-       if(!box1 || !box2) {
+       if(obj1->get_type() != GOBJ_AABOX || obj2->get_type() != GOBJ_AABOX) {
                fprintf(stderr, "AABox::set_intersection: arguments must be AABoxes too\n");
                return;
        }
+       const AABox *box1 = (const AABox*)obj1;
+       const AABox *box2 = (const AABox*)obj2;
 
        for(int i=0; i<3; i++) {
                min[i] = std::max(box1->min[i], box2->min[i]);
@@ -197,9 +212,20 @@ bool AABox::intersect(const Ray &ray, HitPoint *hit) const
                return true;
        }
        return false;
+}
+
+bool AABox::contains(const Vec3 &v) const
+{
+       return v.x >= min.x && v.y >= min.y && v.z >= min.z &&
+               v.x <= max.x && v.y <= max.y && v.z <= max.z;
+}
 
+float AABox::distance(const Vec3 &v) const
+{
+       return 0.0;     // TODO
 }
 
+
 Plane::Plane()
        : normal(0.0, 1.0, 0.0)
 {
@@ -223,6 +249,11 @@ Plane::Plane(const Vec3 &normal, float dist)
        pt = this->normal * dist;
 }
 
+GeomObjectType Plane::get_type() const
+{
+       return GOBJ_PLANE;
+}
+
 void Plane::set_union(const GeomObject *obj1, const GeomObject *obj2)
 {
        fprintf(stderr, "Plane::set_union undefined\n");
@@ -252,84 +283,12 @@ bool Plane::intersect(const Ray &ray, HitPoint *hit) const
        return true;
 }
 
-float sphere_distance(const Vec3 &cent, float rad, const Vec3 &pt)
-{
-       return length(pt - cent) - rad;
-}
-
-// TODO version which takes both radii into account
-float capsule_distance(const Vec3 &a, float ra, const Vec3 &b, float rb, const Vec3 &pt)
+bool Plane::contains(const Vec3 &v) const
 {
-       Vec3 ab_dir = b - a;
-       float ab_len_sq = length_sq(ab_dir);
-
-       if(fabs(ab_len_sq) < 1e-5) {
-               // if a == b, the capsule is a sphere with radius the maximum of the capsule radii
-               return sphere_distance(a, std::max(ra, rb), pt);
-       }
-       float ab_len = sqrt(ab_len_sq);
-
-       Vec3 ap_dir = pt - a;
-
-       float t = dot(ap_dir, ab_dir / ab_len) / ab_len;
-       if(t < 0.0) {
-               return sphere_distance(a, ra, pt);
-       }
-       if(t >= 1.0) {
-               return sphere_distance(b, rb, pt);
-       }
-
-       Vec3 pproj = a + ab_dir * t;
-       return length(pproj - pt) - ra;
+       return dot(v, normal) <= 0.0;
 }
 
-#if 0
-float capsule_distance(const Vec3 &a, float ra, const Vec3 &b, float rb, const Vec3 &pt)
+float Plane::distance(const Vec3 &v) const
 {
-       Vec3 ab_dir = b - a;
-
-       if(fabs(length_sq(ab_dir)) < 1e-5) {
-               // if a == b, the capsule is a sphere with radius the maximum of the capsule radii
-               return sphere_distance(a, std::max(ra, rb), pt);
-       }
-       float ab_len = length(ab_dir);
-
-       Vec3 ap_dir = pt - a;
-       Vec3 rotaxis = normalize(cross(ab_dir, ap_dir));
-
-       Mat4 rmat;
-       rmat.set_rotation(rotaxis, M_PI / 2.0);
-       Vec3 right = rmat * ab_dir / ab_len;
-
-       // XXX I think this check is redundant, always false, due to the cross product order
-       //assert(dot(right, ab_dir) >= 0.0);
-       if(dot(right, ab_dir) < 0.0) {
-               right = -right;
-       }
-       Vec3 aa = a + right * ra;
-       Vec3 bb = b + right * rb;
-
-       // project pt to the line segment bb-aa, see if the projection lies within the interval [0, 1)
-       Vec3 aabb_dir = bb - aa;
-       float aabb_len = length(aabb_dir);
-       Vec3 aap_dir = pt - aa;
-
-       float t = dot(aap_dir, aabb_dir / aabb_len) / aabb_len;
-       if(t < 0.0) {
-               return sphere_distance(a, ra, pt);
-       }
-       if(t >= 1.0) {
-               return sphere_distance(b, rb, pt);
-       }
-
-       Vec3 ppt = aa + aabb_dir * t;
-       Vec3 norm = ppt - pt;
-       float dist = length(norm);
-
-       if(dot(norm, right) < 0.0) {
-               // inside the cone
-               dist = -dist;
-       }
-       return dist;
+       return dot(v - pt, normal);
 }
-#endif
index aabf266..e110193 100644 (file)
@@ -1,28 +1,38 @@
 #ifndef GEOMOBJ_H_
 #define GEOMOBJ_H_
 
-#include "gmath/gmath.h"
+#include <gmath/gmath.h>
+
+enum GeomObjectType {
+       GOBJ_UNKNOWN,
+       GOBJ_SPHERE,
+       GOBJ_AABOX,
+       GOBJ_PLANE
+};
 
 class GeomObject;
-class SceneNode;
 
 struct HitPoint {
-       float dist;                             //< parametric distance along the ray
-       Vec3 pos;                       //< position of intersection (orig + dir * dist)
-       Vec3 normal;                    //< normal at the point of intersection
-       const void *obj;                //< pointer to the intersected object
-       const SceneNode *node;
-       Ray ray;
+       float dist;                     // parametric distance along the ray
+       Vec3 pos;                       // position of intersection (orig + dir * dist)
+       Vec3 normal;            // normal at the point of intersection
+       Ray ray, local_ray;
+       const GeomObject *obj;  // pointer to the intersected geom-object
+       void *data;                     // place to hang extra data
 };
 
 class GeomObject {
 public:
        virtual ~GeomObject();
+       virtual GeomObjectType get_type() const = 0;
 
        virtual void set_union(const GeomObject *obj1, const GeomObject *obj2) = 0;
        virtual void set_intersection(const GeomObject *obj1, const GeomObject *obj2) = 0;
 
        virtual bool intersect(const Ray &ray, HitPoint *hit = 0) const = 0;
+       virtual bool contains(const Vec3 &pt) const = 0;
+
+       virtual float distance(const Vec3 &v) const = 0;
 };
 
 class Sphere : public GeomObject {
@@ -33,10 +43,15 @@ public:
        Sphere();
        Sphere(const Vec3 &center, float radius);
 
+       GeomObjectType get_type() const;
+
        void set_union(const GeomObject *obj1, const GeomObject *obj2);
        void set_intersection(const GeomObject *obj1, const GeomObject *obj2);
 
        bool intersect(const Ray &ray, HitPoint *hit = 0) const;
+       bool contains(const Vec3 &pt) const;
+
+       float distance(const Vec3 &v) const;
 };
 
 class AABox : public GeomObject {
@@ -46,10 +61,15 @@ public:
        AABox();
        AABox(const Vec3 &min, const Vec3 &max);
 
+       GeomObjectType get_type() const;
+
        void set_union(const GeomObject *obj1, const GeomObject *obj2);
        void set_intersection(const GeomObject *obj1, const GeomObject *obj2);
 
        bool intersect(const Ray &ray, HitPoint *hit = 0) const;
+       bool contains(const Vec3 &pt) const;
+
+       float distance(const Vec3 &v) const;
 };
 
 class Plane : public GeomObject {
@@ -61,13 +81,15 @@ public:
        Plane(const Vec3 &p1, const Vec3 &p2, const Vec3 &p3);
        Plane(const Vec3 &normal, float dist);
 
+       GeomObjectType get_type() const;
+
        void set_union(const GeomObject *obj1, const GeomObject *obj2);
        void set_intersection(const GeomObject *obj1, const GeomObject *obj2);
 
        bool intersect(const Ray &ray, HitPoint *hit = 0) const;
-};
+       bool contains(const Vec3 &pt) const;
 
-float sphere_distance(const Vec3 &cent, float rad, const Vec3 &pt);
-float capsule_distance(const Vec3 &a, float ra, const Vec3 &b, float rb, const Vec3 &pt);
+       float distance(const Vec3 &v) const;
+};
 
 #endif // GEOMOBJ_H_
index 4ef90c0..b170095 100644 (file)
 #include "sdr.h"
 #include "shadow.h"
 #include "texture.h"
-#include "machine.h"
+#include "mesh.h"
 #include "meshgen.h"
-#include "mparser.h"
 
 static bool init();
 static void cleanup();
 static void display();
 static void idle();
 static void draw_scene();
-static void draw_gears();
 static void reshape(int x, int y);
 static void keyb(unsigned char key, int x, int y);
 static void mouse(int bn, int st, int x, int y);
 static void motion(int x, int y);
 static void passive_motion(int x, int y);
-static Gear *pick_gear(int x, int y);
 
 static int win_width, win_height;
 
@@ -38,18 +35,8 @@ static bool bnstate[8];
 static Mat4 view_matrix;
 
 static unsigned int start_time, prev_msec;
-static Machine *machine;
-static Gear *hover_gear, *sel_gear;
-static HitPoint pick_hit;
-static Vec3 sel_hit_pos;
-
-static unsigned int sdr_shadow_notex;
-static int dbg_show_shadowmap;
 
 static TextureSet texman;
-static Texture *envmap;
-static Mesh *skydome;
-static unsigned int sdr_skydome;
 
 
 int main(int argc, char **argv)
@@ -88,34 +75,9 @@ static bool init()
 
        Mesh::use_custom_sdr_attr = false;
 
-       machine = new Machine;
-       if(!parse_machine(machine, "data/test.machine")) {
-               fprintf(stderr, "failed to parse machine\n");
-               return false;
-       }
-
-       // shadows
-       init_shadow(2048);
-
-       if(!(sdr_shadow_notex = create_program_load("sdr/shadow.v.glsl", "sdr/shadow-notex.p.glsl"))) {
-               return false;
-       }
-       set_uniform_int(sdr_shadow_notex, "shadowmap", 1);
-       set_uniform_int(sdr_shadow_notex, "envmap", 2);
-
        float ambient[] = {0.1, 0.1, 0.1, 0.0};
        glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient);
 
-       // env
-       envmap = texman.get_texture("data/stpeters_cross.jpg", TEX_CUBE);
-
-       skydome = new Mesh;
-       gen_sphere(skydome, 1.0, 16, 8);
-       skydome->flip_faces();
-
-       if(!(sdr_skydome = create_program_load("sdr/skydome.v.glsl", "sdr/skydome.p.glsl"))) {
-               return false;
-       }
        glUseProgram(0);
 
        start_time = glutGet(GLUT_ELAPSED_TIME);
@@ -125,19 +87,11 @@ static bool init()
 static void cleanup()
 {
        texman.clear();
-       delete machine;
 }
 
 static void update(float dt)
 {
        texman.update();
-
-       machine->update(dt);
-
-       if(sel_gear) {
-       }
-
-       hover_gear = pick_gear(prev_mx, prev_my);
 }
 
 static void set_light(int idx, const Vec3 &pos, const Vec3 &color)
@@ -175,72 +129,7 @@ static void display()
 
        update(dt);
 
-       // shadowmap pass
-       begin_shadow_pass(lpos[0], Vec3(0, 0, 0), 0.2, 100, 150);
        draw_scene();
-       end_shadow_pass();
-
-       // regular pass
-       const Mat4 &shadow_matrix = get_shadow_matrix();
-       Mat4 env_matrix = transpose(view_matrix.upper3x3());
-       set_uniform_matrix4(sdr_shadow_notex, "envmap_matrix", env_matrix[0]);
-
-       glActiveTexture(GL_TEXTURE1);
-       glBindTexture(GL_TEXTURE_2D, get_shadow_tex());
-       glMatrixMode(GL_TEXTURE);
-       glLoadMatrixf(shadow_matrix[0]);
-
-       glActiveTexture(GL_TEXTURE2);
-       glBindTexture(GL_TEXTURE_CUBE_MAP, envmap->get_id());
-
-       glActiveTexture(GL_TEXTURE0);
-       glMatrixMode(GL_MODELVIEW);
-
-       draw_scene();
-
-       glMatrixMode(GL_TEXTURE);
-       glLoadIdentity();
-       glMatrixMode(GL_MODELVIEW);
-
-
-       if(dbg_show_shadowmap) {
-               glMatrixMode(GL_PROJECTION);
-               glPushMatrix();
-               glLoadIdentity();
-               glMatrixMode(GL_MODELVIEW);
-               glPushMatrix();
-               glLoadIdentity();
-               glMatrixMode(GL_TEXTURE);
-               glLoadIdentity();
-
-               glPushAttrib(GL_ENABLE_BIT);
-               glUseProgram(0);
-               glBindTexture(GL_TEXTURE_2D, get_shadow_tex());
-               glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE);
-               glEnable(GL_TEXTURE_2D);
-               glDisable(GL_DEPTH_TEST);
-               glDisable(GL_LIGHTING);
-               glDisable(GL_BLEND);
-
-               glBegin(GL_QUADS);
-               glColor4f(1, 1, 1, 1);
-               glTexCoord2f(0, 0); glVertex2f(-0.95, -0.95);
-               glTexCoord2f(1, 0); glVertex2f(-0.5, -0.95);
-               glTexCoord2f(1, 1); glVertex2f(-0.5, -0.5);
-               glTexCoord2f(0, 1); glVertex2f(-0.95, -0.5);
-               glEnd();
-
-               glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);
-               glBindTexture(GL_TEXTURE_2D, 0);
-
-               glPopAttrib();
-
-               glMatrixMode(GL_PROJECTION);
-               glPopMatrix();
-               glMatrixMode(GL_MODELVIEW);
-               glPopMatrix();
-       }
-
 
        glutSwapBuffers();
        assert(glGetError() == GL_NO_ERROR);
@@ -253,59 +142,13 @@ static void idle()
 
 static void draw_scene()
 {
-       // draw skydome
-       glDepthMask(0);
-
-       Mat4 rot_view = view_matrix.upper3x3();
-       glMatrixMode(GL_MODELVIEW);
-       glPushMatrix();
-       glLoadMatrixf(rot_view[0]);
-
-       bind_texture(envmap, 0);
-
-       glUseProgram(sdr_skydome);
-       skydome->draw();
-       glUseProgram(0);
-
-       bind_texture(0, 0);
-
-       glPopMatrix();
-       glDepthMask(1);
-
-       // draw mechanism
-       draw_gears();
-}
-
-static void draw_gears()
-{
-       /* world scale is in meters, gears are in millimeters, scale by 1/1000 */
-       glPushMatrix();
-       glScalef(0.001, 0.001, 0.001);
-
-       if(!shadow_pass && (sel_gear || hover_gear)) {
-               glPushAttrib(GL_ENABLE_BIT | GL_LINE_BIT | GL_POLYGON_BIT);
-
-               glDisable(GL_LIGHTING);
-               glFrontFace(GL_CW);
-               glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
-               glLineWidth(3.0);
-
-               if(sel_gear) {
-                       glColor3f(0.2, 1.0, 0.3);
-                       sel_gear->draw();
-               } else {
-                       glColor3f(1.0, 0.75, 0.2);
-                       hover_gear->draw();
-               }
-
-               glPopAttrib();
-       }
-
-       glUseProgram(shadow_pass ? 0 : sdr_shadow_notex);
-       machine->draw();
-       glUseProgram(0);
-
-       glPopMatrix();
+       glBegin(GL_QUADS);
+       glNormal3f(0, 1, 0);
+       glVertex3f(-30, -10, 30);
+       glVertex3f(30, -10, 30);
+       glVertex3f(30, -10, -30);
+       glVertex3f(-30, -10, -30);
+       glEnd();
 }
 
 static void reshape(int x, int y)
@@ -325,16 +168,6 @@ static void keyb(unsigned char key, int x, int y)
        switch(key) {
        case 27:
                exit(0);
-
-       case 'w':
-               opt_gear_wireframe = !opt_gear_wireframe;
-               glutPostRedisplay();
-               break;
-
-       case 's':
-               dbg_show_shadowmap = !dbg_show_shadowmap;
-               glutPostRedisplay();
-               break;
        }
 }
 
@@ -346,23 +179,6 @@ static void mouse(int bn, int st, int x, int y)
        prev_mx = x;
        prev_my = y;
        bnstate[bidx] = down;
-
-       if(bidx == 0) {
-               if(down) {
-                       sel_gear = pick_gear(x, y);
-                       sel_hit_pos = pick_hit.pos;
-               } else {
-                       sel_gear = 0;
-               }
-       }
-
-       if(bidx == 3 || bidx == 4) {    /* wheel */
-               if(hover_gear) {
-                       float dz = bidx == 4 ? 1 : -1;
-                       hover_gear->set_position(hover_gear->get_position() + hover_gear->get_axis() * dz);
-                       machine->invalidate_meshing();
-               }
-       }
 }
 
 static void motion(int x, int y)
@@ -374,28 +190,18 @@ static void motion(int x, int y)
 
        if(!dx && !dy) return;
 
-       if(sel_gear) {
-               float speed = 0.5;
-               Vec3 offs = Vec3(dx * speed, -dy * speed, 0.0);
-               offs = sel_gear->get_dir_matrix() * offs;
-
-               sel_gear->set_position(sel_gear->get_position() + offs);
-               machine->invalidate_meshing();
-
-       } else {
-               if(bnstate[0]) {
-                       cam_theta += dx * 0.5;
-                       cam_phi += dy * 0.5;
-
-                       if(cam_phi < -90) cam_phi = -90;
-                       if(cam_phi > 90) cam_phi = 90;
-                       glutPostRedisplay();
-               }
-               if(bnstate[2]) {
-                       cam_dist += dy * 0.01;
-                       if(cam_dist < 0.0) cam_dist = 0.0;
-                       glutPostRedisplay();
-               }
+       if(bnstate[0]) {
+               cam_theta += dx * 0.5;
+               cam_phi += dy * 0.5;
+
+               if(cam_phi < -90) cam_phi = -90;
+               if(cam_phi > 90) cam_phi = 90;
+               glutPostRedisplay();
+       }
+       if(bnstate[2]) {
+               cam_dist += dy * 0.01;
+               if(cam_dist < 0.0) cam_dist = 0.0;
+               glutPostRedisplay();
        }
 }
 
@@ -404,25 +210,3 @@ static void passive_motion(int x, int y)
        prev_mx = x;
        prev_my = y;
 }
-
-static Gear *pick_gear(int x, int y)
-{
-       double pt[3];
-       double viewmat[16], projmat[16];
-       int vp[4];
-       Ray ray;
-
-       y = win_height - y;
-
-       glGetDoublev(GL_MODELVIEW_MATRIX, viewmat);
-       glGetDoublev(GL_PROJECTION_MATRIX, projmat);
-       glGetIntegerv(GL_VIEWPORT, vp);
-
-       gluUnProject(x, y, 0, viewmat, projmat, vp, pt, pt + 1, pt + 2);
-       ray.origin = Vec3(pt[0], pt[1], pt[2]) * 1000.0f;
-
-       gluUnProject(x, y, 1, viewmat, projmat, vp, pt, pt + 1, pt + 2);
-       ray.dir = Vec3(pt[0], pt[1], pt[2]) * 1000.0f - ray.origin;
-
-       return machine->intersect_gear(ray, &pick_hit);
-}
diff --git a/src/material.cc b/src/material.cc
new file mode 100644 (file)
index 0000000..962b10c
--- /dev/null
@@ -0,0 +1,19 @@
+#include "opengl.h"
+#include "material.h"
+
+Material::Material()
+       : diffuse(1.0f, 1.0f, 1.0f)
+{
+       shininess = 0.0f;
+       alpha = 1.0f;
+}
+
+void Material::setup() const
+{
+       float kd[] = {diffuse.x, diffuse.y, diffuse.z, alpha};
+       float ks[] = {specular.x, specular.y, specular.z, 1.0f};
+
+       glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, kd);
+       glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, ks);
+       glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, shininess);
+}
diff --git a/src/material.h b/src/material.h
new file mode 100644 (file)
index 0000000..f5c26ae
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef MATERIAL_H_
+#define MATERIAL_H_
+
+#include <gmath/gmath.h>
+
+class Material {
+public:
+       Vec3 diffuse, specular;
+       float shininess;
+       float alpha;
+
+       Material();
+       void setup() const;
+};
+
+#endif // MATERIAL_H_
diff --git a/src/mech_exhibit.h b/src/mech_exhibit.h
new file mode 100644 (file)
index 0000000..74fa613
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef MECH_EXHIBIT_H_
+#define MECH_EXHIBIT_H_
+
+#include "exhibit.h"
+#include "machine.h"
+
+class MechExhibit : public Exhibit {
+private:
+
+public:
+       MechExhibit();
+       ~MechExhibit();
+
+       void update(float dt);
+       void draw() const;
+};
+
+#endif // MECH_EXHIBIT_H_
index ed4fd98..beb5b49 100644 (file)
@@ -903,7 +903,7 @@ bool Mesh::intersect(const Ray &ray, HitPoint *hit) const
 
        HitPoint nearest_hit;
        nearest_hit.dist = FLT_MAX;
-       nearest_hit.obj = 0;
+       nearest_hit.data = 0;
 
        if(Mesh::intersect_mode & ISECT_VERTICES) {
                // we asked for "intersections" with the vertices of the mesh
@@ -934,7 +934,7 @@ bool Mesh::intersect(const Ray &ray, HitPoint *hit) const
 
                if(nearest_vidx != -1) {
                        hitvert = varr[nearest_vidx];
-                       nearest_hit.obj = &hitvert;
+                       nearest_hit.data = &hitvert;
                }
 
        } else {
@@ -961,17 +961,17 @@ bool Mesh::intersect(const Ray &ray, HitPoint *hit) const
                }
        }
 
-       if(nearest_hit.obj) {
+       if(nearest_hit.data) {
                if(hit) {
                        *hit = nearest_hit;
 
                        // if we are interested in the mesh and not the faces set obj to this
                        if(Mesh::intersect_mode & ISECT_FACE) {
-                               hit->obj = &hitface;
+                               hit->data = &hitface;
                        } else if(Mesh::intersect_mode & ISECT_VERTICES) {
-                               hit->obj = &hitvert;
+                               hit->data = &hitvert;
                        } else {
-                               hit->obj = this;
+                               hit->data = (void*)this;
                        }
                }
                return true;
@@ -1432,7 +1432,7 @@ bool Triangle::intersect(const Ray &ray, HitPoint *hit) const
                hit->dist = t;
                hit->pos = ray.origin + ray.dir * t;
                hit->normal = normal;
-               hit->obj = this;
+               hit->data = (void*)this;
        }
        return true;
 }
diff --git a/src/object.cc b/src/object.cc
new file mode 100644 (file)
index 0000000..d850d47
--- /dev/null
@@ -0,0 +1,37 @@
+#include "object.h"
+#include "snode.h"
+
+
+Object::Object()
+{
+       name = "<unnamed>";
+       node = 0;
+}
+
+ObjType Object::get_type() const
+{
+       return OBJ_NULL;
+}
+
+void Object::set_name(const char *name)
+{
+       this->name = name;
+}
+
+const char *Object::get_name() const
+{
+       return name.c_str();
+}
+
+bool Object::intersect(const Ray &ray, HitPoint *hit) const
+{
+       return false;
+}
+
+void Object::update(float dt)
+{
+}
+
+void Object::draw() const
+{
+}
diff --git a/src/object.h b/src/object.h
new file mode 100644 (file)
index 0000000..cc82d1b
--- /dev/null
@@ -0,0 +1,37 @@
+#ifndef OBJECT_H_
+#define OBJECT_H_
+
+#include <string>
+#include <gmath/gmath.h>
+#include "geom.h"
+#include "material.h"
+
+class Object;
+class SceneNode;
+
+enum ObjType { OBJ_NULL, OBJ_MESH };
+
+class Object {
+private:
+       std::string name;
+
+public:
+       Material mtl;
+       //GeomObject *bvol;
+       SceneNode *node;
+
+       Object();
+       virtual ~Object() = default;
+
+       virtual ObjType get_type() const;
+
+       virtual void set_name(const char *name);
+       virtual const char *get_name() const;
+
+       virtual bool intersect(const Ray &ray, HitPoint *hit = 0) const;
+
+       virtual void update(float dt = 0.0f);
+       virtual void draw() const;
+};
+
+#endif // OBJECT_H_
diff --git a/src/objmesh.cc b/src/objmesh.cc
new file mode 100644 (file)
index 0000000..60fa7f9
--- /dev/null
@@ -0,0 +1,32 @@
+#include "opengl.h"
+#include "objmesh.h"
+#include "snode.h"
+
+ObjMesh::ObjMesh()
+{
+       mesh = 0;
+}
+
+ObjType ObjMesh::get_type() const
+{
+       return OBJ_MESH;
+}
+
+void ObjMesh::draw() const
+{
+       if(!mesh) return;
+
+       mtl.setup();
+
+       if(node) {
+               glMatrixMode(GL_MODELVIEW);
+               glPushMatrix();
+               glMultMatrixf(node->get_matrix()[0]);
+       }
+
+       mesh->draw();
+
+       if(node) {
+               glPopMatrix();
+       }
+}
diff --git a/src/objmesh.h b/src/objmesh.h
new file mode 100644 (file)
index 0000000..811caf6
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef OBJMESH_H_
+#define OBJMESH_H_
+
+#include "object.h"
+#include "mesh.h"
+
+class ObjMesh : public Object {
+public:
+       Mesh *mesh;
+
+       ObjMesh();
+
+       ObjType get_type() const;
+
+       void draw() const;
+};
+
+#endif // OBJMESH_H_
diff --git a/src/scene.cc b/src/scene.cc
new file mode 100644 (file)
index 0000000..27be807
--- /dev/null
@@ -0,0 +1,63 @@
+#include "scene.h"
+
+static void destroy_node_tree(SceneNode *n);
+
+Scene::Scene()
+{
+       nodes = 0;
+}
+
+Scene::~Scene()
+{
+       destroy();
+}
+
+void Scene::destroy()
+{
+       destroy_node_tree(nodes);
+       nodes = 0;
+}
+
+// Scene::load defined in sceneload.cc
+
+void Scene::update(float dt)
+{
+       if(nodes) {
+               nodes->update(dt);
+       }
+
+       int nobj = objects.size();
+       for(int i=0; i<nobj; i++) {
+               if(!objects[i]->node) {
+                       // only update objects which don't belong to a scenegraph node
+                       // to avoid updating objects twice
+                       objects[i]->update(dt);
+               }
+       }
+}
+
+void Scene::draw() const
+{
+       if(!objects.empty()) {
+               int nobj = objects.size();
+               for(int i=0; i<nobj; i++) {
+                       objects[i]->draw();
+               }
+       } else {
+               int nmesh = meshes.size();
+               for(int i=0; i<nmesh; i++) {
+                       meshes[i]->draw();
+               }
+       }
+}
+
+static void destroy_node_tree(SceneNode *n)
+{
+       if(!n) return;
+
+       int nsub = n->get_num_children();
+       for(int i=0; i<nsub; i++) {
+               destroy_node_tree(n->get_child(i));
+       }
+       delete n;
+}
diff --git a/src/scene.h b/src/scene.h
new file mode 100644 (file)
index 0000000..7c30445
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef SCENE_H_
+#define SCENE_H_
+
+#include <vector>
+#include "mesh.h"
+#include "snode.h"
+
+class Scene {
+public:
+       std::vector<Mesh*> meshes;
+       std::vector<Object*> objects;
+       SceneNode *nodes;
+
+       Scene();
+       ~Scene();
+
+       Scene(const Scene &rhs) = delete;
+       Scene &operator =(const Scene &rhs) = delete;
+
+       void destroy();
+
+       bool load(const char *fname);
+
+       void update(float dt);
+       void draw() const;
+};
+
+#endif // SCENE_H_
diff --git a/src/sceneload.cc b/src/sceneload.cc
new file mode 100644 (file)
index 0000000..b2b00eb
--- /dev/null
@@ -0,0 +1,275 @@
+#include <stdio.h>
+#include <vector>
+#include <map>
+#include <gmath/gmath.h>
+#include <assimp/cimport.h>
+#include <assimp/postprocess.h>
+#include <assimp/scene.h>
+#include <assimp/mesh.h>
+#include <assimp/material.h>
+#include <assimp/anim.h>
+#include <assimp/vector3.h>
+#include <assimp/matrix4x4.h>
+#include <assimp/quaternion.h>
+#include "scene.h"
+#include "objmesh.h"
+
+static bool load_material(Material *mat, const aiMaterial *aimat);
+static SceneNode *load_node(const aiScene *aiscn, const aiNode *ainode);
+static Mesh *load_mesh(const aiScene *aiscn, const aiMesh *aimesh);
+
+/*static Vec3 assimp_vector(const aiVector3D &v);
+static Quat assimp_quat(const aiQuaternion &q);
+static Mat4 assimp_matrix(const aiMatrix4x4 &aim);
+static long assimp_time(const aiAnimation *anim, double aitime);
+static void print_hierarchy(const aiNode *node);
+*/
+
+static std::map<std::string, SceneNode*> node_by_name;
+static std::map<aiMesh*, Mesh*> mesh_by_aimesh;
+
+bool Scene::load(const char *fname)
+{
+       unsigned int ppflags = aiProcess_CalcTangentSpace |
+               aiProcess_GenNormals |
+               aiProcess_JoinIdenticalVertices |
+               aiProcess_Triangulate |
+               aiProcess_SortByPType |
+               aiProcess_TransformUVCoords;
+
+       const aiScene *aiscn = aiImportFile(fname, ppflags);
+       if(!aiscn) {
+               fprintf(stderr, "failed to load scene file: %s\n", fname);
+               return false;
+       }
+
+       /*
+       Vec3 root_pos, root_scaling(1.0, 1.0, 1.0);
+       Quat root_rot;
+
+       if(aiscn->mRootNode) {
+               Mat4 root_matrix = assimp_matrix(aiscn->mRootNode->mTransformation);
+               root_pos = root_matrix.get_translation();
+               root_rot = root_matrix.get_rotation_quat();
+               root_scaling = root_matrix.get_scaling();
+       }*/
+
+       // load all meshes
+       for(unsigned int i=0; i<aiscn->mNumMeshes; i++) {
+               aiMesh *aimesh = aiscn->mMeshes[i];
+               Mesh *mesh;
+
+               switch(aimesh->mPrimitiveTypes) {
+               case aiPrimitiveType_TRIANGLE:
+                       if((mesh = load_mesh(aiscn, aimesh))) {
+                               mesh_by_aimesh[aimesh] = mesh;
+                               meshes.push_back(mesh);
+                       }
+                       break;
+
+               default:
+                       fprintf(stderr, "unsupported primitive type: %u\n", aimesh->mPrimitiveTypes);
+                       break;
+               }
+       }
+
+       SceneNode *root = new SceneNode;
+
+       // load all the nodes recursively
+       for(unsigned int i=0; i<aiscn->mRootNode->mNumChildren; i++) {
+               SceneNode *node = load_node(aiscn, aiscn->mRootNode->mChildren[i]);
+               if(node) {
+                       root->add_child(node);
+               }
+       }
+
+       int nnodes = root->get_num_children();
+       if(nnodes <= 0) {
+               delete root;
+       } else if(nnodes == 1) {
+               nodes = root->get_child(0);
+               root->remove_child(nodes);
+               delete root;
+       } else {
+               nodes = root;
+       }
+
+       node_by_name.clear();
+       mesh_by_aimesh.clear();
+
+       aiReleaseImport(aiscn);
+       printf("loaded scene file: %s, %d meshes\n", fname, (int)meshes.size());
+       return true;
+}
+
+static bool load_material(Material *mat, const aiMaterial *aimat)
+{
+       aiColor4D aicol;
+       float shin, shin_str;
+
+       if(aiGetMaterialColor(aimat, AI_MATKEY_COLOR_DIFFUSE, &aicol) == 0) {
+               mat->diffuse = Vec3(aicol[0], aicol[1], aicol[2]);
+       }
+       if(aiGetMaterialColor(aimat, AI_MATKEY_COLOR_SPECULAR, &aicol) == 0) {
+               mat->specular = Vec3(aicol[0], aicol[1], aicol[2]);
+       }
+
+       unsigned int count = 1;
+       if(aiGetMaterialFloatArray(aimat, AI_MATKEY_SHININESS_STRENGTH, &shin_str, &count) != 0) {
+               shin_str = 1.0;
+       }
+       if(aiGetMaterialFloatArray(aimat, AI_MATKEY_SHININESS, &shin, &count) == 0) {
+               // XXX can't remember how I came up with this...
+               mat->shininess = shin * shin_str * 0.0001 * 128.0;
+       }
+
+       /*
+       // load textures
+       struct { int type; aiTextureType aitype; } textypes[] = {
+               {TEX_DIFFUSE, aiTextureType_DIFFUSE},
+               {TEX_NORMAL, aiTextureType_NORMALS},
+               {TEX_SPECULAR, aiTextureType_SPECULAR}
+       };
+
+       for(size_t i=0; i<sizeof textypes / sizeof *textypes; i++) {
+               aiString aipath;
+
+               if(aiGetMaterialTexture(aimat, textypes[i].aitype, 0, &aipath) == 0) {
+                       char *tmp, *fname = aipath.data;
+
+                       if((tmp = strrchr(fname, '/'))) {
+                               fname = tmp + 1;
+                       }
+                       if((tmp = strrchr(fname, '\\'))) {
+                               fname = tmp + 1;
+                       }
+
+                       if(*fname) {
+                               mat->tex[textypes[i].type] = texset.get(fname);
+                       }
+               }
+       }
+       */
+
+       return true;
+}
+
+static SceneNode *load_node(const aiScene *aiscn, const aiNode *ainode)
+{
+       SceneNode *node = new SceneNode;
+       node->set_name(ainode->mName.data);
+
+       for(unsigned int i=0; i<ainode->mNumMeshes; i++) {
+               aiMesh *aimesh = aiscn->mMeshes[ainode->mMeshes[0]];
+
+               Mesh *mesh = mesh_by_aimesh[aimesh];
+               if(mesh) {
+                       ObjMesh *obj = new ObjMesh;
+                       obj->mesh = mesh;
+                       // also grab the material of this mesh
+                       load_material(&obj->mtl, aiscn->mMaterials[aimesh->mMaterialIndex]);
+
+                       node->add_object(obj);
+               }
+       }
+
+       /* recurse to all children */
+       for(unsigned int i=0; i<ainode->mNumChildren; i++) {
+               SceneNode *child = load_node(aiscn, ainode->mChildren[i]);
+               if(child) {
+                       node->add_child(child);
+               }
+       }
+
+       node_by_name[node->get_name()] = node;
+       return node;
+}
+
+static Mesh *load_mesh(const aiScene *aiscn, const aiMesh *aimesh)
+{
+       Mesh *mesh = new Mesh;
+
+       int num_verts = aimesh->mNumVertices;
+       int num_faces = aimesh->mNumFaces;
+
+       mesh->set_attrib_data(MESH_ATTR_VERTEX, 3, num_verts, (float*)aimesh->mVertices);
+
+       if(aimesh->mNormals) {
+               mesh->set_attrib_data(MESH_ATTR_NORMAL, 3, num_verts, (float*)aimesh->mNormals);
+       }
+       if(aimesh->mTangents) {
+               mesh->set_attrib_data(MESH_ATTR_TANGENT, 3, num_verts, (float*)aimesh->mTangents);
+       }
+       if(aimesh->mTextureCoords[0]) {
+               mesh->set_attrib_data(MESH_ATTR_TEXCOORD, 3, num_verts, (float*)aimesh->mTextureCoords[0]);
+       }
+
+       unsigned int *iptr = mesh->set_index_data(num_faces * 3);
+       for(int i=0; i<num_faces; i++) {
+               for(int j=0; j<3; j++) {
+                       *iptr++ = aimesh->mFaces[i].mIndices[j];
+               }
+       }
+
+       return mesh;
+}
+
+#if 0
+static Vec3 assimp_vector(const aiVector3D &v)
+{
+       return Vec3(v[0], v[1], v[2]);
+}
+
+static Quat assimp_quat(const aiQuaternion &q)
+{
+       return Quat(q.x, q.y, q.z, q.w);
+}
+
+static Mat4 assimp_matrix(const aiMatrix4x4 &aim)
+{
+       Mat4 m;
+       memcpy(m[0], &aim, 16 * sizeof(float));
+       m.transpose();
+       return m;
+}
+
+/* convert an assimp keyframe time (ticks) into milliseconds */
+static long assimp_time(const aiAnimation *anim, double aitime)
+{
+       double sec;
+       if(anim->mTicksPerSecond < 1e-6) {
+               // assume time is in frames?
+               sec = aitime / 30.0;
+       } else {
+               sec = aitime / anim->mTicksPerSecond;
+       }
+       return (long)(sec * 1000.0);
+}
+
+static void print_hierarchy(const aiNode *node)
+{
+       static int lvl;
+       static int lvlopen[256];
+
+       for(int i=0; i<lvl; i++) {
+               putchar(' ');
+               if(lvlopen[i]) {
+                       putchar(i >= lvl - 1 ? '+' : '|');
+               } else {
+                       putchar(i >= lvl - 1 ? '+' : ' ');
+               }
+       }
+       printf("- \"%s\"\n", node->mName.data);
+
+       lvlopen[lvl] = 1;
+
+       lvl++;
+       for(unsigned int i=0; i<node->mNumChildren; i++) {
+               if(i == node->mNumChildren - 1) {
+                       lvlopen[lvl - 1] = 0;
+               }
+               print_hierarchy(node->mChildren[i]);
+       }
+       lvl--;
+}
+#endif /* 0 */
diff --git a/src/sceneload.h b/src/sceneload.h
new file mode 100644 (file)
index 0000000..2af093d
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef SCENELOAD_H_
+#define SCENELOAD_H_
+
+#include "scene.h"
+
+bool load_scene(
+
+#endif // SCENELOAD_H_
diff --git a/src/snode.cc b/src/snode.cc
new file mode 100644 (file)
index 0000000..fac6c65
--- /dev/null
@@ -0,0 +1,227 @@
+#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;
+}
diff --git a/src/snode.h b/src/snode.h
new file mode 100644 (file)
index 0000000..21cf16d
--- /dev/null
@@ -0,0 +1,67 @@
+#ifndef SNODE_H_
+#define SNODE_H_
+
+#include <vector>
+#include "object.h"
+#include "gmath/gmath.h"
+
+class SceneNode {
+private:
+       char *name;
+
+       Vec3 pos;
+       Quat rot;
+       Vec3 scale;
+
+       std::vector<Object*> obj;
+
+       SceneNode *parent;
+       std::vector<SceneNode*> children;
+
+       Mat4 xform;
+       Mat4 inv_xform;
+
+public:
+       SceneNode();
+       explicit SceneNode(Object *obj);
+       ~SceneNode();
+
+       void set_name(const char *s);
+       const char *get_name() const;
+
+       void add_child(SceneNode *node);
+       bool remove_child(SceneNode *node);
+
+       int get_num_children() const;
+       SceneNode *get_child(int idx) const;
+
+       SceneNode *get_parent() const;
+
+       void add_object(Object *obj);
+       bool remove_object(Object *obj);
+
+       int get_num_objects() const;
+       Object *get_object(int idx) const;
+
+       void set_position(const Vec3 &pos);
+       void set_rotation(const Quat &rot);
+       void set_scaling(const Vec3 &scale);
+
+       const Vec3 &get_node_position() const;
+       const Quat &get_node_rotation() const;
+       const Vec3 &get_node_scaling() const;
+
+       Vec3 get_position() const;
+       Quat get_rotation() const;
+       Vec3 get_scaling() const;
+
+       const Mat4 &get_matrix() const;
+       const Mat4 &get_inv_matrix() const;
+
+       void update_node(float dt = 0.0f);
+       void update(float dt = 0.0f);
+
+       bool intersect(const Ray &ray, HitPoint *hit) const;
+};
+
+#endif // SNODE_H_