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
--- /dev/null
+#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_
-#include <assert.h>
-#include <float.h>
#include <algorithm>
+#include <float.h>
#include "geom.h"
GeomObject::~GeomObject()
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);
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()
{
{
}
-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);
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]);
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)
{
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");
return true;
}
-float sphere_distance(const Vec3 ¢, 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
#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 {
Sphere();
Sphere(const Vec3 ¢er, 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 {
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 {
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 ¢, 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_
#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;
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)
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);
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)
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);
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)
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;
}
}
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)
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();
}
}
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);
-}
--- /dev/null
+#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);
+}
--- /dev/null
+#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_
--- /dev/null
+#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_
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
if(nearest_vidx != -1) {
hitvert = varr[nearest_vidx];
- nearest_hit.obj = &hitvert;
+ nearest_hit.data = &hitvert;
}
} else {
}
}
- 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;
hit->dist = t;
hit->pos = ray.origin + ray.dir * t;
hit->normal = normal;
- hit->obj = this;
+ hit->data = (void*)this;
}
return true;
}
--- /dev/null
+#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
+{
+}
--- /dev/null
+#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_
--- /dev/null
+#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();
+ }
+}
--- /dev/null
+#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_
--- /dev/null
+#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;
+}
--- /dev/null
+#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_
--- /dev/null
+#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 */
--- /dev/null
+#ifndef SCENELOAD_H_
+#define SCENELOAD_H_
+
+#include "scene.h"
+
+bool load_scene(
+
+#endif // SCENELOAD_H_
--- /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;
+}
--- /dev/null
+#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_