# HG changeset patch # User John Tsiombikas # Date 1423970060 -7200 # Node ID 8cce82794f90d8a5231e961d0d823995282b647c # Parent af24cfbdf9b69e802fb1cea2033c2067d1b65450 seems to work nicely diff -r af24cfbdf9b6 -r 8cce82794f90 Makefile --- a/Makefile Sat Feb 14 10:08:00 2015 +0200 +++ b/Makefile Sun Feb 15 05:14:20 2015 +0200 @@ -1,5 +1,6 @@ +csrc = $(wildcard src/*.c) ccsrc = $(wildcard src/*.cc) -obj = $(ccsrc:.cc=.o) +obj = $(ccsrc:.cc=.o) $(csrc:.c=.o) dep = $(obj:.o=.d) bin = test @@ -21,6 +22,9 @@ -include $(dep) +%.d: %.c + @$(CPP) $(CFLAGS) $< -MM -MT $(@:.d=.o) >$@ + %.d: %.cc @$(CPP) $(CXXFLAGS) $< -MM -MT $(@:.d=.o) >$@ diff -r af24cfbdf9b6 -r 8cce82794f90 src/co_collider.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/co_collider.cc Sun Feb 15 05:14:20 2015 +0200 @@ -0,0 +1,53 @@ +#include "co_collider.h" +#include "co_xform.h" +#include "gobj.h" + +static CoCollider reg_co_col; + +static Component *cons_collider() { return new CoCollider; } + +CoCollider::CoCollider() +{ + name = "collider"; + shape = 0; + + register_component(name, cons_collider); +} + +CoCollider::~CoCollider() +{ + delete shape; +} + +const char **CoCollider::update_before() const +{ + static const char *before[] = { "rigid", 0 }; + return before; +} + +void CoCollider::update(float dt) +{ + // set the shape transform according to the last value of the CoXForm + if(!shape) return; + + CoXForm *co_xform = COCAST(CoXForm, gobj->get_component("xform")); + if(co_xform) { + shape->set_transform(co_xform->xform); + } +} + +bool CoCollider::collide(const CoCollider *col2, HitPoint *hit) const +{ + if(!shape || !col2->shape) { + return false; + } + return shape->collide(col2->shape, hit); +} + +CoCollider *gobj_co_collider(const GObject *obj, bool nofail) +{ + CoCollider *co = COCAST(CoCollider, obj->get_component("collider")); + if(co) return co; + + return nofail ? ®_co_col : 0; +} diff -r af24cfbdf9b6 -r 8cce82794f90 src/co_collider.h --- a/src/co_collider.h Sat Feb 14 10:08:00 2015 +0200 +++ b/src/co_collider.h Sun Feb 15 05:14:20 2015 +0200 @@ -4,9 +4,21 @@ #include "comp.h" #include "geom.h" -class CoCollider { +class CoCollider : public Component { +protected: + const char **update_before() const; + public: GeomShape *shape; + + CoCollider(); + ~CoCollider(); + + void update(float dt); + + bool collide(const CoCollider *col2, HitPoint *hit = 0) const; }; +CoCollider *gobj_co_collider(const GObject *obj, bool nofail = true); + #endif // CO_COLLIDER_H_ diff -r af24cfbdf9b6 -r 8cce82794f90 src/co_mesh.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/co_mesh.cc Sun Feb 15 05:14:20 2015 +0200 @@ -0,0 +1,46 @@ +#include "opengl.h" +#include "co_mesh.h" +#include "co_xform.h" +#include "gobj.h" + +static CoMesh reg_co_mesh; +static Component *cons_co_mesh() { return new CoMesh; } + +CoMesh::CoMesh() +{ + name = "mesh"; + mesh = 0; + + register_component(name, cons_co_mesh); +} + +CoMesh::~CoMesh() +{ + delete mesh; +} + +void CoMesh::draw() const +{ + if(!mesh) return; + + CoXForm *co_xform = COCAST(CoXForm, gobj->get_component("xform")); + if(co_xform) { + glPushMatrix(); + glMultTransposeMatrixf(co_xform->xform[0]); + } + + mesh->draw(); + + if(co_xform) { + glPopMatrix(); + } +} + + +CoMesh *gobj_co_mesh(const GObject *obj, bool nofail) +{ + CoMesh *co = COCAST(CoMesh, obj->get_component("mesh")); + if(co) return co; + + return nofail ? ®_co_mesh : 0; +} diff -r af24cfbdf9b6 -r 8cce82794f90 src/co_mesh.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/co_mesh.h Sun Feb 15 05:14:20 2015 +0200 @@ -0,0 +1,20 @@ +#ifndef CO_MESH_H_ +#define CO_MESH_H_ + +#include "comp.h" +#include "mesh.h" +#include "gobj.h" + +class CoMesh : public Component { +public: + Mesh *mesh; + + CoMesh(); + ~CoMesh(); + + void draw() const; +}; + +CoMesh *gobj_co_mesh(const GObject *obj, bool nofail = true); + +#endif // CO_MESH_H_ diff -r af24cfbdf9b6 -r 8cce82794f90 src/co_phys.cc --- a/src/co_phys.cc Sat Feb 14 10:08:00 2015 +0200 +++ b/src/co_phys.cc Sun Feb 15 05:14:20 2015 +0200 @@ -1,6 +1,10 @@ #include +#include #include "co_phys.h" #include "sim.h" +#include "co_collider.h" + +#define FIXED_MASS 1e9 static CoRigid reg_co_rigid; @@ -9,10 +13,12 @@ CoRigid::CoRigid() { mass = 1.0; - elast = 0.5; + elast = 0.85; friction = 0.0; + fixed = false; name = "rigid"; + co_prs = 0; register_component(name, cons_rigid); } @@ -23,6 +29,26 @@ return before; } +void CoRigid::set_fixed(bool f) +{ + fixed = f; +} + +bool CoRigid::get_fixed() const +{ + return fixed; +} + +void CoRigid::set_mass(float mass) +{ + this->mass = mass; +} + +float CoRigid::get_mass() const +{ + return fixed ? FIXED_MASS : mass; +} + void CoRigid::add_impulse(const Vector3 &v) { impulse += v; @@ -30,7 +56,7 @@ void CoRigid::update(float dt) { - if(!gobj) return; + if(!gobj || fixed) return; if(!co_prs) { if(!(co_prs = COCAST(CoPRS, gobj->get_component("prs")))) { @@ -39,6 +65,23 @@ } } + // collision detection + CoCollider *co_col; + if(world && (co_col = gobj_co_collider(gobj, COGET_FAIL))) { + HitPoint hit; + + // dot product subexpression is to ignore collisions when moving OUT + // of the object after an interpenetration + if(world->collide(co_col, &hit) && dot_product(vel, hit.normal) <= 0.0) { + Vector3 refl = -vel.reflection(hit.normal); + Vector3 vert = hit.normal * dot_product(refl, hit.normal); + Vector3 tang = refl - vert; + vel = vert * elast + tang * friction; + // TODO add an impulse to the other object and take account of the relative masses + } + } + + float damping = world ? world->get_damping() : 0.005; Vector3 newpos = co_prs->pos + vel * dt; @@ -51,8 +94,16 @@ } vel = (vel - vel * damping * dt) + accel * dt; - - // TODO collisions - + if(vel.length() < 1e-3) { + vel.x = vel.y = vel.z = 0.0f; + } co_prs->pos = newpos; } + +CoRigid *gobj_co_rigid(const GObject *obj, bool nofail) +{ + CoRigid *co = COCAST(CoRigid, obj->get_component("rigid")); + if(co) return co; + + return nofail ? ®_co_rigid : 0; +} diff -r af24cfbdf9b6 -r 8cce82794f90 src/co_phys.h --- a/src/co_phys.h Sat Feb 14 10:08:00 2015 +0200 +++ b/src/co_phys.h Sun Feb 15 05:14:20 2015 +0200 @@ -15,15 +15,25 @@ public: SimWorld *world; + bool fixed; float mass, elast, friction; Vector3 vel, force; Vector3 impulse; CoRigid(); + void set_fixed(bool f); + bool get_fixed() const; + + void set_mass(float mass); + float get_mass() const; + void add_impulse(const Vector3 &v); void update(float dt); }; +// helper component accessors +CoRigid *gobj_co_rigid(const GObject *obj, bool nofail = true); + #endif // COMP_PHYS_H_ diff -r af24cfbdf9b6 -r 8cce82794f90 src/co_xform.cc --- a/src/co_xform.cc Sat Feb 14 10:08:00 2015 +0200 +++ b/src/co_xform.cc Sun Feb 15 05:14:20 2015 +0200 @@ -21,6 +21,7 @@ : scale(1, 1, 1) { name = "prs"; + co_xform = 0; register_component(name, cons_prs); } @@ -50,3 +51,19 @@ co_xform->xform = rmat * tmat * smat; } + +CoXForm *gobj_co_xform(const GObject *obj, bool nofail) +{ + CoXForm *co = COCAST(CoXForm, obj->get_component("xform")); + if(co) return co; + + return nofail ? ®_co_xform : 0; +} + +CoPRS *gobj_co_prs(const GObject *obj, bool nofail) +{ + CoPRS *co = COCAST(CoPRS, obj->get_component("prs")); + if(co) return co; + + return nofail ? ®_co_prs : 0; +} diff -r af24cfbdf9b6 -r 8cce82794f90 src/co_xform.h --- a/src/co_xform.h Sat Feb 14 10:08:00 2015 +0200 +++ b/src/co_xform.h Sun Feb 15 05:14:20 2015 +0200 @@ -26,5 +26,9 @@ void update(float dt); }; +// helper component accessors +CoXForm *gobj_co_xform(const GObject *obj, bool nofail = true); +CoPRS *gobj_co_prs(const GObject *obj, bool nofail = true); + #endif // CO_XFORM_H_ diff -r af24cfbdf9b6 -r 8cce82794f90 src/comp.cc --- a/src/comp.cc Sat Feb 14 10:08:00 2015 +0200 +++ b/src/comp.cc Sun Feb 15 05:14:20 2015 +0200 @@ -37,6 +37,11 @@ return name; } +GObject *Component::get_object() const +{ + return gobj; +} + void Component::update(float dt) { } diff -r af24cfbdf9b6 -r 8cce82794f90 src/comp.h --- a/src/comp.h Sat Feb 14 10:08:00 2015 +0200 +++ b/src/comp.h Sun Feb 15 05:14:20 2015 +0200 @@ -9,6 +9,10 @@ #define COCAST(type, x) dynamic_cast(x) #endif +// for the second arg of the various gobj_co_whatever() functions +#define COGET_NOFAIL true +#define COGET_FAIL false + class GObject; class Component { @@ -34,6 +38,7 @@ virtual ~Component(); const char *get_name() const; + GObject *get_object() const; virtual void update(float dt); virtual void draw() const; diff -r af24cfbdf9b6 -r 8cce82794f90 src/geom.cc --- a/src/geom.cc Sat Feb 14 10:08:00 2015 +0200 +++ b/src/geom.cc Sun Feb 15 05:14:20 2015 +0200 @@ -1,3 +1,4 @@ +#include "opengl.h" #include #include #include "geom.h" @@ -20,6 +21,22 @@ return type; } +void GeomShape::set_transform(const Matrix4x4 &m) +{ + xform = m; + inv_xform = xform.inverse(); +} + +const Matrix4x4 &GeomShape::get_transform() const +{ + return xform; +} + +const Matrix4x4 &GeomShape::get_inv_transform() const +{ + return inv_xform; +} + // ---- Sphere class ---- Sphere::Sphere() @@ -35,8 +52,15 @@ radius = rad; } -bool Sphere::intersect(const Ray &ray, HitPoint *hit) const +bool Sphere::contains(const Vector3 &pt) const { + return distance_sq(pt) <= 0.0; +} + +bool Sphere::intersect(const Ray &inray, HitPoint *hit) const +{ + Ray ray = inray.transformed(inv_xform); + float a = dot_product(ray.dir, ray.dir); float b = 2.0 * ray.dir.x * (ray.origin.x - center.x) + 2.0 * ray.dir.y * (ray.origin.y - center.y) + @@ -68,8 +92,10 @@ hit->shape = this; hit->data = 0; hit->dist = t; - hit->pos = ray.origin + ray.dir * t; - hit->normal = (hit->pos - center) / radius; + + Vector3 local_pos = ray.origin + ray.dir * t; + hit->pos = inray.origin + inray.dir * t; + hit->normal = (local_pos - center) / radius; } return true; } @@ -89,12 +115,19 @@ float Sphere::distance(const Vector3 &pt) const { - return sqrt(distance_sq(pt)); + Vector3 local_pt = pt.transformed(inv_xform); + return (local_pt - center).length() - radius; } float Sphere::distance_sq(const Vector3 &pt) const { - return (pt - center).length_sq(); + Vector3 local_pt = pt.transformed(inv_xform); + return (local_pt - center).length_sq() - radius * radius; +} + +void Sphere::draw() const +{ + // TODO } // ---- Plane class ---- @@ -142,10 +175,15 @@ dist = distance(p1); } +bool Plane::contains(const Vector3 &pt) const +{ + return distance(pt) <= 0.0; +} -bool Plane::intersect(const Ray &ray, HitPoint *hit) const +bool Plane::intersect(const Ray &inray, HitPoint *hit) const { - Vector3 pt = Vector3(0, 0, 0) + normal * dist; + Ray ray = inray.transformed(inv_xform); + Vector3 pt = normal * dist; float ndotdir = dot_product(normal, ray.dir); if(fabs(ndotdir) < 1e-4) { @@ -156,7 +194,7 @@ Vector3 ptdir = pt - ray.origin; float t = dot_product(normal, ptdir) / ndotdir; - hit->pos = ray.origin + ray.dir * t; + hit->pos = inray.origin + inray.dir * t; hit->normal = normal; hit->shape = this; } @@ -187,7 +225,7 @@ float Plane::distance(const Vector3 &v) const { Vector3 pt = normal * dist; - return dot_product(v - pt, normal); + return dot_product(v.transformed(inv_xform) - pt, normal); } float Plane::distance_sq(const Vector3 &v) const @@ -196,20 +234,187 @@ return d * d; } +void Plane::draw() const +{ + // TODO +} + +// ---- AABox ---- + +AABox::AABox() +{ + type = GEOM_AABOX; +} + +AABox::AABox(const Vector3 &vmin, const Vector3 &vmax) + : min(vmin), max(vmax) +{ + type = GEOM_AABOX; +} + +void AABox::set_union(const GeomShape *obj1, const GeomShape *obj2) +{ + const AABox *box1 = (const AABox*)obj1; + const AABox *box2 = (const AABox*)obj2; + + if(!box1 || !box2 || obj1->get_type() != GEOM_AABOX || obj2->get_type() != GEOM_AABOX) { + fprintf(stderr, "AABox::set_union: arguments must be AABoxes too\n"); + return; + } + + min.x = std::min(box1->min.x, box2->min.x); + min.y = std::min(box1->min.y, box2->min.y); + min.z = std::min(box1->min.z, box2->min.z); + + max.x = std::max(box1->max.x, box2->max.x); + max.y = std::max(box1->max.y, box2->max.y); + max.z = std::max(box1->max.z, box2->max.z); +} + +void AABox::set_intersection(const GeomShape *obj1, const GeomShape *obj2) +{ + const AABox *box1 = (const AABox*)obj1; + const AABox *box2 = (const AABox*)obj2; + + if(!box1 || !box2 || obj1->get_type() != GEOM_AABOX || obj2->get_type() != GEOM_AABOX) { + fprintf(stderr, "AABox::set_union: arguments must be AABoxes too\n"); + return; + } + + for(int i=0; i<3; i++) { + min[i] = std::max(box1->min[i], box2->min[i]); + max[i] = std::min(box1->max[i], box2->max[i]); + + if(max[i] < min[i]) { + max[i] = min[i]; + } + } +} + +bool AABox::contains(const Vector3 &pt) const +{ + return pt.x >= min.x && pt.x <= max.x && + pt.y >= min.y && pt.y <= max.y && + pt.z >= min.z && pt.z <= max.z; +} + +bool AABox::intersect(const Ray &inray, HitPoint *hit) const +{ + Ray ray = inray.transformed(inv_xform); + + Vector3 param[2] = {min, max}; + Vector3 inv_dir(1.0 / ray.dir.x, 1.0 / ray.dir.y, 1.0 / ray.dir.z); + int sign[3] = {inv_dir.x < 0, inv_dir.y < 0, inv_dir.z < 0}; + + float tmin = (param[sign[0]].x - ray.origin.x) * inv_dir.x; + float tmax = (param[1 - sign[0]].x - ray.origin.x) * inv_dir.x; + float tymin = (param[sign[1]].y - ray.origin.y) * inv_dir.y; + float tymax = (param[1 - sign[1]].y - ray.origin.y) * inv_dir.y; + + if(tmin > tymax || tymin > tmax) { + return false; + } + if(tymin > tmin) { + tmin = tymin; + } + if(tymax < tmax) { + tmax = tymax; + } + + float tzmin = (param[sign[2]].z - ray.origin.z) * inv_dir.z; + float tzmax = (param[1 - sign[2]].z - ray.origin.z) * inv_dir.z; + + if(tmin > tzmax || tzmin > tmax) { + return false; + } + if(tzmin > tmin) { + tmin = tzmin; + } + if(tzmax < tmax) { + tmax = tzmax; + } + + float t = tmin < 1e-4 ? tmax : tmin; + if(t >= 1e-4) { + + if(hit) { + hit->shape = this; + hit->dist = t; + hit->pos = inray.origin + inray.dir * t; + + float min_dist = FLT_MAX; + Vector3 offs = min + (max - min) / 2.0; + Vector3 local_hit = hit->pos - offs; + + static const Vector3 axis[] = { + Vector3(1, 0, 0), Vector3(0, 1, 0), Vector3(0, 0, 1) + }; + //int tcidx[][2] = {{2, 1}, {0, 2}, {0, 1}}; + + for(int i=0; i<3; i++) { + float dist = fabs((max[i] - offs[i]) - fabs(local_hit[i])); + if(dist < min_dist) { + min_dist = dist; + hit->normal = axis[i] * (local_hit[i] < 0.0 ? 1.0 : -1.0); + //hit->texcoord = Vector2(hit->pos[tcidx[i][0]], hit->pos[tcidx[i][1]]); + } + } + hit->normal.transform(Matrix3x3(xform)); + } + return true; + } + return false; +} + +bool AABox::collide(const GeomShape *geom, HitPoint *hit) const +{ + fprintf(stderr, "%s: not implemented yet\n", __FUNCTION__); + return false; +} + +float AABox::distance(const Vector3 &pt) const +{ + float d = distance_sq(pt); + return d * d; +} + +float AABox::distance_sq(const Vector3 &world_pt) const +{ + Vector3 pt = pt.transformed(inv_xform); + + float dx = std::max(pt.x - max.x, min.x - pt.x); + float dy = std::max(pt.y - max.y, min.y - pt.y); + float dz = std::max(pt.z - max.z, min.z - pt.z); + + return Vector3(dx, dy, dz).length_sq(); +} + +void AABox::draw() const +{ +} + // collision detection functions static bool col_sphere_sphere(const Sphere *a, const Sphere *b, HitPoint *hit) { - float sum_rad = a->radius + b->radius; - Vector3 dir = b->center - a->center; + const Matrix4x4 &xforma = a->get_transform(); + const Matrix4x4 &xformb = b->get_transform(); + + Vector3 ca = a->center.transformed(xforma); + Vector3 cb = b->center.transformed(xformb); + float rada = a->radius * xforma.get_scaling().x; + float radb = b->radius * xformb.get_scaling().x; + + float sum_rad = rada + radb; + Vector3 dir = cb - ca; float dist_sq = dir.length_sq(); bool res = dist_sq <= sum_rad * sum_rad; if(res && hit) { - hit->pos = a->center + dir * 0.5; + hit->pos = ca + dir * 0.5; hit->shape = b; - hit->normal = -dir; + hit->normal = -dir.normalized(); } return res; } @@ -222,18 +427,25 @@ static bool col_sphere_plane(const Sphere *sph, const Plane *plane, HitPoint *hit) { - float dist = plane->distance(sph->center); - bool res = fabs(dist) <= sph->radius; + const Matrix4x4 &xform = sph->get_transform(); + + Vector3 sph_center = sph->center.transformed(xform); + float sph_rad = sph->radius * xform.get_scaling().x; + + float dist = plane->distance(sph_center); + bool res = fabs(dist) <= sph_rad; if(res && hit) { - Vector3 planept = plane->normal * plane->dist; - Vector3 sphdir = sph->center - planept; - if(dot_product(sphdir, plane->normal) >= 0.0f) { - hit->normal = plane->normal; + Vector3 planept = (plane->normal * plane->dist).transformed(plane->get_transform()); + Vector3 planenorm = plane->normal.transformed(Matrix3x3(plane->get_transform())); + + Vector3 sphdir = sph_center - planept; + if(dot_product(sphdir, planenorm) >= 0.0f) { + hit->normal = planenorm; } else { - hit->normal = -plane->normal; + hit->normal = -planenorm; } - hit->pos = sph->center - plane->normal * fabs(dist); + hit->pos = sph_center - planenorm * fabs(dist); hit->shape = plane; } return res; diff -r af24cfbdf9b6 -r 8cce82794f90 src/geom.h --- a/src/geom.h Sat Feb 14 10:08:00 2015 +0200 +++ b/src/geom.h Sun Feb 15 05:14:20 2015 +0200 @@ -14,8 +14,12 @@ }; class GeomShape { +public: + enum Type { GEOM_UNKNOWN, GEOM_SPHERE, GEOM_PLANE, GEOM_AABOX }; + protected: - enum Type { GEOM_UNKNOWN, GEOM_SPHERE, GEOM_PLANE } type; + Matrix4x4 xform, inv_xform; + Type type; public: GeomShape(); @@ -23,11 +27,20 @@ virtual Type get_type() const; + // apply an arbitrary transformation before testing intersections and collisions + virtual void set_transform(const Matrix4x4 &m); + virtual const Matrix4x4 &get_transform() const; + virtual const Matrix4x4 &get_inv_transform() const; + + virtual bool contains(const Vector3 &pt) const = 0; virtual bool intersect(const Ray &ray, HitPoint *hit = 0) const = 0; virtual bool collide(const GeomShape *geom, HitPoint *hit = 0) const = 0; virtual float distance(const Vector3 &pt) const = 0; virtual float distance_sq(const Vector3 &pt) const = 0; + + // visualize the shape + virtual void draw() const = 0; }; class Sphere : public GeomShape { @@ -38,11 +51,14 @@ Sphere(); Sphere(const Vector3 &c, float rad); + bool contains(const Vector3 &pt) const; bool intersect(const Ray &ray, HitPoint *hit = 0) const; bool collide(const GeomShape *geom, HitPoint *hit = 0) const; float distance(const Vector3 &pt) const; float distance_sq(const Vector3 &pt) const; + + void draw() const; }; class Plane : public GeomShape { @@ -56,11 +72,34 @@ Plane(const Vector3 &pos, const Vector3 &norm); Plane(const Vector3 &p1, const Vector3 &p2, const Vector3 &p3); + bool contains(const Vector3 &pt) const; bool intersect(const Ray &ray, HitPoint *hit = 0) const; bool collide(const GeomShape *geom, HitPoint *hit = 0) const; float distance(const Vector3 &pt) const; float distance_sq(const Vector3 &pt) const; + + void draw() const; +}; + +class AABox : public GeomShape { +public: + Vector3 min, max; + + AABox(); + AABox(const Vector3 &min, const Vector3 &max); + + void set_union(const GeomShape *obj1, const GeomShape *obj2); + void set_intersection(const GeomShape *obj1, const GeomShape *obj2); + + bool contains(const Vector3 &pt) const; + bool intersect(const Ray &ray, HitPoint *hit = 0) const; + bool collide(const GeomShape *geom, HitPoint *hit = 0) const; + + float distance(const Vector3 &pt) const; + float distance_sq(const Vector3 &pt) const; + + void draw() const; }; #endif // GEOM_H_ diff -r af24cfbdf9b6 -r 8cce82794f90 src/gobj.cc --- a/src/gobj.cc Sat Feb 14 10:08:00 2015 +0200 +++ b/src/gobj.cc Sun Feb 15 05:14:20 2015 +0200 @@ -78,7 +78,9 @@ sorted = true; } + //printf("updating components\n"); for(size_t i=0; iget_name()); comp[i]->update(dt); } } diff -r af24cfbdf9b6 -r 8cce82794f90 src/mesh.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mesh.cc Sun Feb 15 05:14:20 2015 +0200 @@ -0,0 +1,1022 @@ +#include +#include +#include +#include +#include "opengl.h" +#include "mesh.h" +#include "shader.h" + +int Mesh::global_sdr_loc[NUM_MESH_ATTR] = { 0, 1, 2, 3, 4, 5 }; +unsigned int Mesh::intersect_mode = ISECT_DEFAULT; +float Mesh::vertex_sel_dist = 0.01; +float Mesh::vis_vecsize = 1.0; + +Mesh::Mesh() +{ + clear(); + + glGenBuffers(NUM_MESH_ATTR + 1, buffer_objects); + + for(int i=0; iname = name; +} + +const char *Mesh::get_name() const +{ + return name.c_str(); +} + +bool Mesh::has_attrib(int attr) const +{ + if(attr < 0 || attr >= NUM_MESH_ATTR) { + return false; + } + + // if neither of these is valid, then nobody has set this attribute + return vattr[attr].vbo_valid || vattr[attr].data_valid; +} + +void Mesh::clear() +{ + //bones.clear(); + + for(int i=0; i= NUM_MESH_ATTR) { + fprintf(stderr, "%s: invalid attrib: %d\n", __FUNCTION__, attrib); + return 0; + } + + if(nverts && num != nverts) { + fprintf(stderr, "%s: attribute count missmatch (%d instead of %d)\n", __FUNCTION__, num, nverts); + return 0; + } + nverts = num; + + vattr[attrib].data.clear(); + vattr[attrib].nelem = nelem; + vattr[attrib].data.resize(num * nelem); + + if(data) { + memcpy(&vattr[attrib].data[0], data, num * nelem * sizeof *data); + } + + vattr[attrib].data_valid = true; + vattr[attrib].vbo_valid = false; + return &vattr[attrib].data[0]; +} + +float *Mesh::get_attrib_data(int attrib) +{ + if(attrib < 0 || attrib >= NUM_MESH_ATTR) { + fprintf(stderr, "%s: invalid attrib: %d\n", __FUNCTION__, attrib); + return 0; + } + + vattr[attrib].vbo_valid = false; + return (float*)((const Mesh*)this)->get_attrib_data(attrib); +} + +const float *Mesh::get_attrib_data(int attrib) const +{ + if(attrib < 0 || attrib >= NUM_MESH_ATTR) { + fprintf(stderr, "%s: invalid attrib: %d\n", __FUNCTION__, attrib); + return 0; + } + + if(!vattr[attrib].data_valid) { +#if GL_ES_VERSION_2_0 + fprintf(stderr, "%s: can't read back attrib data on CrippledGL ES\n", __FUNCTION__); + return 0; +#else + if(!vattr[attrib].vbo_valid) { + fprintf(stderr, "%s: unavailable attrib: %d\n", __FUNCTION__, attrib); + return 0; + } + + // local data copy is unavailable, grab the data from the vbo + Mesh *m = (Mesh*)this; + m->vattr[attrib].data.resize(nverts * vattr[attrib].nelem); + + glBindBuffer(GL_ARRAY_BUFFER, vattr[attrib].vbo); + void *data = glMapBuffer(GL_ARRAY_BUFFER, GL_READ_ONLY); + memcpy(&m->vattr[attrib].data[0], data, nverts * vattr[attrib].nelem * sizeof(float)); + glUnmapBuffer(GL_ARRAY_BUFFER); + + vattr[attrib].data_valid = true; +#endif + } + + return &vattr[attrib].data[0]; +} + +void Mesh::set_attrib(int attrib, int idx, const Vector4 &v) +{ + float *data = get_attrib_data(attrib); + if(data) { + data += idx * vattr[attrib].nelem; + for(int i=0; iget_index_data(); +} + +const unsigned int *Mesh::get_index_data() const +{ + if(!idata_valid) { +#if GL_ES_VERSION_2_0 + fprintf(stderr, "%s: can't read back index data in CrippledGL ES\n", __FUNCTION__); + return 0; +#else + if(!ibo_valid) { + fprintf(stderr, "%s: indices unavailable\n", __FUNCTION__); + return 0; + } + + // local data copy is unavailable, gram the data from the ibo + Mesh *m = (Mesh*)this; + int nidx = nfaces * 3; + m->idata.resize(nidx); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo); + void *data = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_READ_ONLY); + memcpy(&m->idata[0], data, nidx * sizeof(unsigned int)); + glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER); + + idata_valid = true; +#endif + } + + return &idata[0]; +} + +void Mesh::append(const Mesh &mesh) +{ + unsigned int idxoffs = nverts; + + nverts += mesh.nverts; + nfaces += mesh.nfaces; + + for(int i=0; i= NUM_MESH_ATTR) { + return; + } + Mesh::global_sdr_loc[attr] = loc; +} + +/// static function +int Mesh::get_attrib_location(int attr) +{ + if(attr < 0 || attr >= NUM_MESH_ATTR) { + return -1; + } + return Mesh::global_sdr_loc[attr]; +} + +/// static function +void Mesh::clear_attrib_locations() +{ + for(int i=0; i= (int)bones.size()) { + return 0; + } + return bones[idx]; +} + +int Mesh::get_bones_count() const +{ + return (int)bones.size(); +} +*/ + +void Mesh::draw() const +{ + const ShaderProg *cur_sdr = get_current_shader(); +#ifdef GL_ES_VERSION_2_0 + if(!cur_sdr) { + fprintf(stderr, "%s: CrippledGL ES can't draw without a shader\n", __FUNCTION__); + return; + } +#endif + + ((Mesh*)this)->update_buffers(); + + if(!vattr[MESH_ATTR_VERTEX].vbo_valid) { + fprintf(stderr, "%s: invalid vertex buffer\n", __FUNCTION__); + return; + } + + if(cur_sdr) { + // rendering with shaders + if(global_sdr_loc[MESH_ATTR_VERTEX] == -1) { + fprintf(stderr, "%s: shader attribute location for vertices unset\n", __FUNCTION__); + return; + } + + for(int i=0; i= 0 && vattr[i].vbo_valid) { + glBindBuffer(GL_ARRAY_BUFFER, vattr[i].vbo); + glVertexAttribPointer(loc, vattr[i].nelem, GL_FLOAT, GL_FALSE, 0, 0); + glEnableVertexAttribArray(loc); + } + } + } else { +#ifndef GL_ES_VERSION_2_0 + // rendering with fixed-function (not available in GLES2) + glBindBuffer(GL_ARRAY_BUFFER, vattr[MESH_ATTR_VERTEX].vbo); + glVertexPointer(vattr[MESH_ATTR_VERTEX].nelem, GL_FLOAT, 0, 0); + glEnableClientState(GL_VERTEX_ARRAY); + + if(vattr[MESH_ATTR_NORMAL].vbo_valid) { + glBindBuffer(GL_ARRAY_BUFFER, vattr[MESH_ATTR_NORMAL].vbo); + glNormalPointer(GL_FLOAT, 0, 0); + glEnableClientState(GL_NORMAL_ARRAY); + } + if(vattr[MESH_ATTR_TEXCOORD].vbo_valid) { + glBindBuffer(GL_ARRAY_BUFFER, vattr[MESH_ATTR_TEXCOORD].vbo); + glTexCoordPointer(vattr[MESH_ATTR_TEXCOORD].nelem, GL_FLOAT, 0, 0); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + } + if(vattr[MESH_ATTR_COLOR].vbo_valid) { + glBindBuffer(GL_ARRAY_BUFFER, vattr[MESH_ATTR_COLOR].vbo); + glColorPointer(vattr[MESH_ATTR_COLOR].nelem, GL_FLOAT, 0, 0); + glEnableClientState(GL_COLOR_ARRAY); + } +#endif + } + glBindBuffer(GL_ARRAY_BUFFER, 0); + + if(ibo_valid) { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo); + glDrawElements(GL_TRIANGLES, nfaces * 3, GL_UNSIGNED_INT, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } else { + glDrawArrays(GL_TRIANGLES, 0, nverts); + } + + if(cur_sdr) { + // rendered with shaders + for(int i=0; i= 0 && vattr[i].vbo_valid) { + glDisableVertexAttribArray(loc); + } + } + } else { +#ifndef GL_ES_VERSION_2_0 + // rendered with fixed-function + glDisableClientState(GL_VERTEX_ARRAY); + if(vattr[MESH_ATTR_NORMAL].vbo_valid) { + glDisableClientState(GL_NORMAL_ARRAY); + } + if(vattr[MESH_ATTR_TEXCOORD].vbo_valid) { + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + } + if(vattr[MESH_ATTR_COLOR].vbo_valid) { + glDisableClientState(GL_COLOR_ARRAY); + } +#endif + } +} + +void Mesh::draw_wire() const +{ + ((Mesh*)this)->update_wire_ibo(); + + if(!vattr[MESH_ATTR_VERTEX].vbo_valid || !wire_ibo_valid) { + fprintf(stderr, "%s: invalid vertex buffer\n", __FUNCTION__); + return; + } + if(global_sdr_loc[MESH_ATTR_VERTEX] == -1) { + fprintf(stderr, "%s: shader attribute location for vertices unset\n", __FUNCTION__); + return; + } + + for(int i=0; i= 0 && vattr[i].vbo_valid) { + glBindBuffer(GL_ARRAY_BUFFER, vattr[i].vbo); + glVertexAttribPointer(loc, vattr[i].nelem, GL_FLOAT, GL_FALSE, 0, 0); + glEnableVertexAttribArray(loc); + } + } + glBindBuffer(GL_ARRAY_BUFFER, 0); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, wire_ibo); + glDrawElements(GL_LINES, nfaces * 6, GL_UNSIGNED_INT, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + for(int i=0; i= 0 && vattr[i].vbo_valid) { + glDisableVertexAttribArray(loc); + } + } +} + +void Mesh::draw_vertices() const +{ + ((Mesh*)this)->update_buffers(); + + if(!vattr[MESH_ATTR_VERTEX].vbo_valid) { + fprintf(stderr, "%s: invalid vertex buffer\n", __FUNCTION__); + return; + } + if(global_sdr_loc[MESH_ATTR_VERTEX] == -1) { + fprintf(stderr, "%s: shader attribute location for vertices unset\n", __FUNCTION__); + return; + } + + for(int i=0; i= 0 && vattr[i].vbo_valid) { + glBindBuffer(GL_ARRAY_BUFFER, vattr[i].vbo); + glVertexAttribPointer(loc, vattr[i].nelem, GL_FLOAT, GL_FALSE, 0, 0); + glEnableVertexAttribArray(loc); + } + } + glBindBuffer(GL_ARRAY_BUFFER, 0); + + glDrawArrays(GL_POINTS, 0, nverts); + + for(int i=0; i= 0 && vattr[i].vbo_valid) { + glDisableVertexAttribArray(loc); + } + } +} + +void Mesh::draw_normals() const +{ +#ifdef USE_OLDGL + int vert_loc = global_sdr_loc[MESH_ATTR_VERTEX]; + Vector3 *varr = (Vector3*)get_attrib_data(MESH_ATTR_VERTEX); + Vector3 *norm = (Vector3*)get_attrib_data(MESH_ATTR_NORMAL); + + if(!varr || !norm || vert_loc < 0) { + return; + } + + glBegin(GL_LINES); + for(size_t i=0; icalc_aabb(); + } + *vmin = aabb.min; + *vmax = aabb.max; +} + +const AABox &Mesh::get_aabbox() const +{ + if(!aabb_valid) { + ((Mesh*)this)->calc_aabb(); + } + return aabb; +} + +float Mesh::get_bsphere(Vector3 *center, float *rad) const +{ + if(!bsph_valid) { + ((Mesh*)this)->calc_bsph(); + } + *center = bsph.center; + *rad = bsph.radius; + return bsph.radius; +} + +const Sphere &Mesh::get_bsphere() const +{ + if(!bsph_valid) { + ((Mesh*)this)->calc_bsph(); + } + return bsph; +} + +/// static function +void Mesh::set_intersect_mode(unsigned int mode) +{ + Mesh::intersect_mode = mode; +} + +/// static function +unsigned int Mesh::get_intersect_mode() +{ + return Mesh::intersect_mode; +} + +/// static function +void Mesh::set_vertex_select_distance(float dist) +{ + Mesh::vertex_sel_dist = dist; +} + +/// static function +float Mesh::get_vertex_select_distance() +{ + return Mesh::vertex_sel_dist; +} + +bool Mesh::intersect(const Ray &ray, HitPoint *hit) const +{ + assert((Mesh::intersect_mode & (ISECT_VERTICES | ISECT_FACE)) != (ISECT_VERTICES | ISECT_FACE)); + + const Vector3 *varr = (Vector3*)get_attrib_data(MESH_ATTR_VERTEX); + const Vector3 *narr = (Vector3*)get_attrib_data(MESH_ATTR_NORMAL); + if(!varr) { + return false; + } + const unsigned int *idxarr = get_index_data(); + + // first test with the bounding box + AABox box; + get_aabbox(&box.min, &box.max); + if(!box.intersect(ray)) { + return false; + } + + HitPoint nearest_hit; + nearest_hit.dist = FLT_MAX; + nearest_hit.shape = 0; + + if(Mesh::intersect_mode & ISECT_VERTICES) { + // we asked for "intersections" with the vertices of the mesh + long nearest_vidx = -1; + float thres_sq = Mesh::vertex_sel_dist * Mesh::vertex_sel_dist; + + for(unsigned int i=0; i 0) { + continue; + } + + // project the vertex onto the ray line + float t = dot_product(varr[i] - ray.origin, ray.dir); + Vector3 vproj = ray.origin + ray.dir * t; + + float dist_sq = (vproj - varr[i]).length_sq(); + if(dist_sq < thres_sq) { + if(!hit) { + return true; + } + if(t < nearest_hit.dist) { + nearest_hit.dist = t; + nearest_vidx = i; + } + } + } + + if(nearest_vidx != -1) { + hitvert = varr[nearest_vidx]; + nearest_hit.data = (void*)&hitvert; + } + + } else { + // regular intersection test with polygons + + for(unsigned int i=0; i 0) { + continue; + } + + HitPoint fhit; + if(face.intersect(ray, hit ? &fhit : 0)) { + if(!hit) { + return true; + } + if(fhit.dist < nearest_hit.dist) { + nearest_hit = fhit; + hitface = face; + } + } + } + } + + if(nearest_hit.shape) { + 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->data = &hitface; + } else if(Mesh::intersect_mode & ISECT_VERTICES) { + hit->data = &hitvert; + } else { + hit->data = (void*)this; + } + } + return true; + } + return false; +} + + +// ------ private member functions ------ + +void Mesh::calc_aabb() +{ + // the cast is to force calling the const version which doesn't invalidate + if(!((const Mesh*)this)->get_attrib_data(MESH_ATTR_VERTEX)) { + return; + } + + aabb.min = Vector3(FLT_MAX, FLT_MAX, FLT_MAX); + aabb.max = -aabb.min; + + for(unsigned int i=0; i aabb.max[j]) { + aabb.max[j] = v[j]; + } + } + } + aabb_valid = true; +} + +void Mesh::calc_bsph() +{ + // the cast is to force calling the const version which doesn't invalidate + if(!((const Mesh*)this)->get_attrib_data(MESH_ATTR_VERTEX)) { + return; + } + + Vector3 v; + bsph.center = Vector3(0, 0, 0); + + // first find the center + for(unsigned int i=0; i bsph.radius) { + bsph.radius = dist_sq; + } + } + bsph.radius = sqrt(bsph.radius); + + bsph_valid = true; +} + +void Mesh::update_buffers() +{ + for(int i=0; iget_index_data(); + + for(unsigned int i=0; icalc_normal(); + } + return normal; +} + +void Triangle::transform(const Matrix4x4 &xform) +{ + v[0].transform(xform); + v[1].transform(xform); + v[2].transform(xform); + normal_valid = false; +} + +void Triangle::draw() const +{ + Vector3 n[3]; + n[0] = get_normal(); + n[1] = get_normal(); + n[2] = get_normal(); + + int vloc = Mesh::get_attrib_location(MESH_ATTR_VERTEX); + int nloc = Mesh::get_attrib_location(MESH_ATTR_NORMAL); + + glEnableVertexAttribArray(vloc); + glVertexAttribPointer(vloc, 3, GL_FLOAT, GL_FALSE, 0, &v[0].x); + glVertexAttribPointer(nloc, 3, GL_FLOAT, GL_FALSE, 0, &n[0].x); + + glDrawArrays(GL_TRIANGLES, 0, 3); + + glDisableVertexAttribArray(vloc); + glDisableVertexAttribArray(nloc); +} + +void Triangle::draw_wire() const +{ + static const int idxarr[] = {0, 1, 1, 2, 2, 0}; + int vloc = Mesh::get_attrib_location(MESH_ATTR_VERTEX); + + glEnableVertexAttribArray(vloc); + glVertexAttribPointer(vloc, 3, GL_FLOAT, GL_FALSE, 0, &v[0].x); + + glDrawElements(GL_LINES, 6, GL_UNSIGNED_INT, idxarr); + + glDisableVertexAttribArray(vloc); +} + +Vector3 Triangle::calc_barycentric(const Vector3 &pos) const +{ + Vector3 norm = get_normal(); + + float area_sq = fabs(dot_product(cross_product(v[1] - v[0], v[2] - v[0]), norm)); + if(area_sq < 1e-5) { + return Vector3(0, 0, 0); + } + + float asq0 = fabs(dot_product(cross_product(v[1] - pos, v[2] - pos), norm)); + float asq1 = fabs(dot_product(cross_product(v[2] - pos, v[0] - pos), norm)); + float asq2 = fabs(dot_product(cross_product(v[0] - pos, v[1] - pos), norm)); + + return Vector3(asq0 / area_sq, asq1 / area_sq, asq2 / area_sq); +} + +bool Triangle::intersect(const Ray &ray, HitPoint *hit) const +{ + Vector3 normal = get_normal(); + + float ndotdir = dot_product(ray.dir, normal); + if(fabs(ndotdir) < 1e-4) { + return false; + } + + Vector3 vertdir = v[0] - ray.origin; + float t = dot_product(normal, vertdir) / ndotdir; + + Vector3 pos = ray.origin + ray.dir * t; + Vector3 bary = calc_barycentric(pos); + + if(bary.x + bary.y + bary.z > 1.00001) { + return false; + } + + if(hit) { + hit->dist = t; + hit->pos = ray.origin + ray.dir * t; + hit->normal = normal; + hit->data = (void*)this; + } + return true; +} diff -r af24cfbdf9b6 -r 8cce82794f90 src/mesh.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mesh.h Sun Feb 15 05:14:20 2015 +0200 @@ -0,0 +1,209 @@ +#ifndef MESH_H_ +#define MESH_H_ + +#include +#include +#include +#include "geom.h" + +enum { + MESH_ATTR_VERTEX, + MESH_ATTR_NORMAL, + MESH_ATTR_TANGENT, + MESH_ATTR_TEXCOORD, + MESH_ATTR_COLOR, + MESH_ATTR_BONEWEIGHTS, + MESH_ATTR_BONEIDX, + + NUM_MESH_ATTR +}; + +// intersection mode flags +enum { + ISECT_DEFAULT = 0, // default (whole mesh, all intersections) + ISECT_FRONT = 1, // front-faces only + ISECT_FACE = 2, // return intersected face pointer instead of mesh + ISECT_VERTICES = 4 // return (?) TODO +}; + + +class Triangle { +public: + Vector3 v[3]; + Vector3 normal; + bool normal_valid; + int id; + + Triangle(); + Triangle(const Vector3 &v0, const Vector3 &v1, const Vector3 &v2); + Triangle(int n, const Vector3 *varr, const unsigned int *idxarr = 0); + + /// calculate normal (quite expensive) + void calc_normal(); + const Vector3 &get_normal() const; + + void transform(const Matrix4x4 &xform); + + void draw() const; + void draw_wire() const; + + /// calculate barycentric coordinates of a point + Vector3 calc_barycentric(const Vector3 &pos) const; + + bool intersect(const Ray &ray, HitPoint *hit = 0) const; +}; + + +class Mesh { +private: + std::string name; + unsigned int nverts, nfaces; + + // current value for each attribute for the immedate mode + // interface. + Vector4 cur_val[NUM_MESH_ATTR]; + + unsigned int buffer_objects[NUM_MESH_ATTR + 1]; + + // vertex attribute data and buffer objects + struct { + int nelem; // number of elements per attribute range: [1, 4] + std::vector data; + unsigned int vbo; + mutable bool vbo_valid; // if this is false, the vbo needs updating from the data + mutable bool data_valid; // if this is false, the data needs to be pulled from the vbo + //int sdr_loc; + } vattr[NUM_MESH_ATTR]; + + static int global_sdr_loc[NUM_MESH_ATTR]; + + //std::vector bones; // bones affecting this mesh + + // index data and buffer object + std::vector idata; + unsigned int ibo; + mutable bool ibo_valid; + mutable bool idata_valid; + + // index buffer object for wireframe rendering (constructed on demand) + unsigned int wire_ibo; + mutable bool wire_ibo_valid; + + // axis-aligned bounding box + mutable AABox aabb; + mutable bool aabb_valid; + + // bounding sphere + mutable Sphere bsph; + mutable bool bsph_valid; + + // keeps the last intersected face + mutable Triangle hitface; + // keeps the last intersected vertex position + mutable Vector3 hitvert; + + void calc_aabb(); + void calc_bsph(); + + static unsigned int intersect_mode; + static float vertex_sel_dist; + + static float vis_vecsize; + + /// update the VBOs after data has changed (invalid vbo/ibo) + void update_buffers(); + /// construct/update the wireframe index buffer (called from draw_wire). + void update_wire_ibo(); + + +public: + Mesh(); + ~Mesh(); + + void set_name(const char *name); + const char *get_name() const; + + bool has_attrib(int attr) const; + + // clears everything about this mesh, and returns to the newly constructed state + void clear(); + + // access the vertex attribute data + // if vdata == 0, space is just allocated + float *set_attrib_data(int attrib, int nelem, unsigned int num, const float *vdata = 0); // invalidates vbo + float *get_attrib_data(int attrib); // invalidates vbo + const float *get_attrib_data(int attrib) const; + + // simple access to any particular attribute + void set_attrib(int attrib, int idx, const Vector4 &v); // invalidates vbo + Vector4 get_attrib(int attrib, int idx) const; + + // ... same for index data + unsigned int *set_index_data(int num, const unsigned int *indices = 0); // invalidates ibo + unsigned int *get_index_data(); // invalidates ibo + const unsigned int *get_index_data() const; + + void append(const Mesh &mesh); + + // immediate-mode style mesh construction interface + void vertex(float x, float y, float z); + void normal(float nx, float ny, float nz); + void tangent(float tx, float ty, float tz); + void texcoord(float u, float v, float w); + void boneweights(float w1, float w2, float w3, float w4); + void boneidx(int idx1, int idx2, int idx3, int idx4); + + /* apply a transformation to the vertices and its inverse-transpose + * to the normals and tangents. + */ + void apply_xform(const Matrix4x4 &xform); + void apply_xform(const Matrix4x4 &xform, const Matrix4x4 &dir_xform); + + // adds a bone and returns its index + /*int add_bone(XFormNode *bone); + const XFormNode *get_bone(int idx) const; + int get_bones_count() const; + */ + + // access the shader attribute locations + static void set_attrib_location(int attr, int loc); + static int get_attrib_location(int attr); + static void clear_attrib_locations(); + + static void set_vis_vecsize(float sz); + static float get_vis_vecsize(); + + void draw() const; + void draw_wire() const; + void draw_vertices() const; + void draw_normals() const; + void draw_tangents() const; + + /** get the bounding box in local space. The result will be cached, and subsequent + * calls will return the same box. The cache gets invalidated by any functions that can affect + * the vertex data (non-const variant of get_attrib_data(MESH_ATTR_VERTEX, ...) included). + * @{ */ + void get_aabbox(Vector3 *vmin, Vector3 *vmax) const; + const AABox &get_aabbox() const; + /// @} + + /** get the bounding sphere in local space. The result will be cached, and subsequent + * calls will return the same box. The cache gets invalidated by any functions that can affect + * the vertex data (non-const variant of get_attrib_data(MESH_ATTR_VERTEX, ...) included). + * @{ */ + float get_bsphere(Vector3 *center, float *rad) const; + const Sphere &get_bsphere() const; + + static void set_intersect_mode(unsigned int mode); + static unsigned int get_intersect_mode(); + static void set_vertex_select_distance(float dist); + static float get_vertex_select_distance(); + + /** Find the intersection between the mesh and a ray. + * XXX Brute force at the moment, not intended to be used for anything other than picking in tools. + * If you intend to use it in a speed-critical part of the code, you'll *have* to optimize it! + */ + bool intersect(const Ray &ray, HitPoint *hit = 0) const; +}; + +#endif // MESH_H_ diff -r af24cfbdf9b6 -r 8cce82794f90 src/objfile.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/objfile.c Sun Feb 15 05:14:20 2015 +0200 @@ -0,0 +1,308 @@ +/* version 2: does not crash when there are no texture coordinates or normals */ +#include +#include +#include +#include +#include +#include +#include "objfile.h" + +#define INVALID_IDX INT_MIN + +struct objfile { + float *verts, *normals, *texcoords; + int num_faces; +}; + +struct vec3 { + float x, y, z; +}; + +struct face { + int vidx[4]; + int nidx[4]; + int tidx[4]; +}; + +static int objf_read(struct objfile *obj, FILE *fp, const char *fname); +static int count_elem(FILE *fp, const char *elem); +static char *strip(char *s); +static int mkface(char *line, struct face *face); +static int parse_face(char *s, int *vidx, int *nidx, int *tidx); +static int is_int(const char *str); + + +struct objfile *objf_load(const char *fname) +{ + FILE *fp; + struct objfile *obj; + + if(!(fp = fopen(fname, "rb"))) { + fprintf(stderr, "objf_load: failed to open file: %s: %s\n", fname, strerror(errno)); + return 0; + } + + if(!(obj = malloc(sizeof *obj))) { + perror("failed to allocate objfile structure\n"); + fclose(fp); + return 0; + } + + if(objf_read(obj, fp, fname) == -1) { + free(obj); + obj = 0; + } + fclose(fp); + return obj; +} + +void objf_free(struct objfile *obj) +{ + if(obj) { + free(obj->verts); + free(obj->normals); + free(obj->texcoords); + free(obj); + } +} + +int objf_vertex_count(struct objfile *obj) +{ + return obj->num_faces * 3; +} + +int objf_face_count(struct objfile *obj) +{ + return obj->num_faces; +} + +float *objf_vertices(struct objfile *obj) +{ + return obj->verts; +} + +float *objf_normals(struct objfile *obj) +{ + return obj->normals; +} + +float *objf_texcoords(struct objfile *obj) +{ + return obj->texcoords; +} + + +float *objf_vertex(struct objfile *obj, int idx) +{ + return obj->verts + idx * 3; +} + +float *objf_normal(struct objfile *obj, int idx) +{ + return obj->normals + idx * 3; +} + +float *objf_texcoord(struct objfile *obj, int idx) +{ + return obj->texcoords + idx * 2; +} + +static int objf_read(struct objfile *obj, FILE *fp, const char *fname) +{ + int i, j, res = -1; + int vcount, ncount, tcount, fcount; + int num_verts; + struct vec3 *varr, *narr, *tarr; + struct vec3 *vptr, *nptr, *tptr; + struct vec3 dummy_vec = {0, 0, 0}; + struct face *faces, *fptr; + float *vdest, *ndest, *tdest; + char buf[512]; + + vcount = count_elem(fp, "v"); + rewind(fp); + ncount = count_elem(fp, "vn"); + rewind(fp); + tcount = count_elem(fp, "vt"); + rewind(fp); + fcount = count_elem(fp, "f"); + rewind(fp); + + obj->num_faces = fcount; + + if(!vcount || !fcount) { + fprintf(stderr, "invalid or corrupted obj file: %s\n", fname); + return -1; + } + + printf("Reading obj file: %s (%d vertices, %d faces)\n", fname, vcount, fcount); + + vptr = varr = malloc(vcount * sizeof *varr); + nptr = narr = ncount ? malloc(ncount * sizeof *narr) : &dummy_vec; + tptr = tarr = tcount ? malloc(tcount * sizeof *tarr) : &dummy_vec; + fptr = faces = malloc(fcount * sizeof *faces); + + if(!varr || !narr || !tarr || !faces) { + perror("can't allocate enough memory for the geometry"); + goto cleanup; + } + + while(fgets(buf, sizeof buf, fp)) { + char *line; + + if(!(line = strip(buf)) || *line == '#') { + continue; /* ignore empty lines and comments */ + } + + if(sscanf(line, "v %f %f %f", &vptr->x, &vptr->y, &vptr->z) == 3) { + vptr++; + } else if(sscanf(line, "vn %f %f %f", &nptr->x, &nptr->y, &nptr->z) == 3) { + nptr++; + } else if(sscanf(line, "vt %f %f", &tptr->x, &tptr->y) == 2) { + tptr++; + } else if(mkface(line, fptr) == 0) { + fptr++; + } + } + + /* now go forth and create the straight-up 3-vert-per-face vertex arrays */ + num_verts = obj->num_faces * 3; + + vdest = obj->verts = malloc(num_verts * 3 * sizeof *obj->verts); + ndest = obj->normals = malloc(num_verts * 3 * sizeof *obj->normals); + tdest = obj->texcoords = malloc(num_verts * 2 * sizeof *obj->texcoords); + + if(!obj->verts || !obj->normals || !obj->texcoords) { + free(obj->verts); + free(obj->normals); + goto cleanup; + } + + /* for all faces */ + for(i=0; ividx, face->nidx, face->tidx)) == -1) { + return -1; + } + if(nverts > 3) { + fprintf(stderr, "warning face with more than 3 vertices found\n"); + } + return 0; +} + +#define SEP " \t\v\n\r" +/* returns the number of vertices on this face, or -1 on error. */ +static int parse_face(char *s, int *vidx, int *nidx, int *tidx) +{ + int i, num_verts = 0; + char *tok[] = {0, 0, 0, 0}; + char *subtok; + + for(i=0; i<4; i++) { + if((!(tok[i] = strtok(i == 0 ? s : 0, SEP)) || !is_int(tok[i]))) { + if(i < 3) return -1; + } else { + num_verts++; + } + } + + for(i=0; i<4; i++) { + subtok = tok[i]; + if(!subtok || !*subtok || !is_int(subtok)) { + if(i < 3) { + return -1; + } + vidx[i] = INVALID_IDX; + } else { + vidx[i] = atoi(subtok); + if(vidx[i] > 0) vidx[i]--; /* convert to 0-based */ + } + + while(subtok && *subtok && *subtok != '/') { + subtok++; + } + if(subtok && *subtok && *++subtok && is_int(subtok)) { + tidx[i] = atoi(subtok); + if(tidx[i] > 0) tidx[i]--; /* convert to 0-based */ + } else { + tidx[i] = 0; + } + + while(subtok && *subtok && *subtok != '/') { + subtok++; + } + if(subtok && *subtok && *++subtok && is_int(subtok)) { + nidx[i] = atoi(subtok); + if(nidx[i] > 0) nidx[i]--; /* convert to 0-based */ + } else { + nidx[i] = 0; + } + } + + return num_verts; +} + + +static int is_int(const char *str) +{ + return isdigit(str[0]) || + (str[0] == '-' && isdigit(str[1])) || + (str[0] == '+' && isdigit(str[1])); +} diff -r af24cfbdf9b6 -r 8cce82794f90 src/objfile.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/objfile.h Sun Feb 15 05:14:20 2015 +0200 @@ -0,0 +1,29 @@ +#ifndef OBJFILE_H_ +#define OBJFILE_H_ + +struct objfile; + +#ifdef __cplusplus +extern "C" { +#endif + +struct objfile *objf_load(const char *fname); +void objf_free(struct objfile *obj); + +int objf_vertex_count(struct objfile *obj); +int objf_face_count(struct objfile *obj); + +float *objf_vertices(struct objfile *obj); +float *objf_normals(struct objfile *obj); +float *objf_texcoords(struct objfile *obj); + +float *objf_vertex(struct objfile *obj, int idx); +float *objf_normal(struct objfile *obj, int idx); +float *objf_texcoord(struct objfile *obj, int idx); + + +#ifdef __cplusplus +} +#endif + +#endif /* OBJFILE_H_ */ diff -r af24cfbdf9b6 -r 8cce82794f90 src/opengl.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/opengl.cc Sun Feb 15 05:14:20 2015 +0200 @@ -0,0 +1,7 @@ +#include "opengl.h" + +bool init_opengl() +{ + glewInit(); + return true; +} diff -r af24cfbdf9b6 -r 8cce82794f90 src/opengl.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/opengl.h Sun Feb 15 05:14:20 2015 +0200 @@ -0,0 +1,8 @@ +#ifndef OPENGL_H_ +#define OPENGL_H_ + +#include + +bool init_opengl(); + +#endif // OPENGL_H_ diff -r af24cfbdf9b6 -r 8cce82794f90 src/shader.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/shader.cc Sun Feb 15 05:14:20 2015 +0200 @@ -0,0 +1,679 @@ +#include +#include +#include +#include +#include "opengl.h" +#include "shader.h" +#include "mesh.h" + +#ifdef _MSC_VER +#include +#else +#include +#endif + +#ifdef __GLEW_H__ +#define HAVE_GEOMETRY_SHADER +#define HAVE_TESSELATION_SHADER +#endif + +static std::string read_source(const char *fname); +static void bind_standard_attr(const ShaderProg *prog); + +ShaderProg *ShaderProg::current; + +void bind_shader(const ShaderProg *sdr) +{ + if(sdr) { + sdr->bind(); + } else { +#ifndef GL_ES_VERSION_2_0 + glUseProgram(0); + ShaderProg::current = 0; +#endif + } +} + +const ShaderProg *get_current_shader() +{ + return ShaderProg::current; +} + +Shader::Shader() +{ + sdr = type = 0; +} + +Shader::~Shader() +{ + destroy(); +} + +unsigned int Shader::get_id() const +{ + return sdr; +} + +void Shader::set_name(const char *name) +{ + this->name = std::string(name); +} + +const char *Shader::get_name() const +{ + return name.c_str(); +} + +bool Shader::create(const char *src, unsigned int type) +{ +#if !GL_ES_VERSION_2_0 + const char *src_arr[] = {src}; +#else + const char *src_arr[] = { "precision mediump float; ", src }; +#endif + + // keep a copy of the source code + this->src = std::string(src); + + if(!sdr) { + sdr = glCreateShader(type); + } + + printf("compiling shader: %s... ", name.c_str()); + + glShaderSource(sdr, sizeof src_arr / sizeof *src_arr, src_arr, 0); + glCompileShader(sdr); + + int status; + glGetShaderiv(sdr, GL_COMPILE_STATUS, &status); + + printf(status ? "success\n" : "failed\n"); + + int info_len; + glGetShaderiv(sdr, GL_INFO_LOG_LENGTH, &info_len); + if(info_len > 1) { + char *buf = (char*)alloca(info_len); + glGetShaderInfoLog(sdr, info_len, 0, buf); + buf[info_len - 1] = 0; + + if(status) { + printf("%s\n", buf); + } else { + fprintf(stderr, "%s\n", buf); + } + } + + return status == GL_TRUE; +} + +void Shader::destroy() +{ + if(sdr) { + glDeleteShader(sdr); + } + sdr = type = 0; + + src.clear(); + name.clear(); +} + +static std::string read_source(const char *fname) +{ + FILE *fp; + + if(!(fp = fopen(fname, "rb"))) { + fprintf(stderr, "failed to load shader: %s: %s\n", fname, strerror(errno)); + return std::string(); + } + + fseek(fp, 0, SEEK_END); + long sz = ftell(fp); + rewind(fp); + + char *src = (char*)alloca(sz + 1); + if(fread(src, 1, sz, fp) < (size_t)sz) { + fprintf(stderr, "failed to load shader: %s: %s\n", fname, strerror(errno)); + fclose(fp); + delete [] src; + return std::string(); + } + src[sz] = 0; + fclose(fp); + + return std::string(src); +} + +bool Shader::load(const char *fname, unsigned int type) +{ + std::string src = read_source(fname); + if(src.empty()) { + return false; + } + set_name(fname); + return create(src.c_str(), type); +} + +// ---- shader program ---- +ShaderProg::ShaderProg() +{ + prog = 0; + must_link = true; +} + +ShaderProg::~ShaderProg() +{ + destroy(); +} + +unsigned int ShaderProg::get_id() const +{ + return prog; +} + +bool ShaderProg::create(const char *src, unsigned int type, ...) +{ + va_list ap; + + va_start(ap, type); + bool res = create(src, type, ap); + va_end(ap); + + return res; +} + +bool ShaderProg::create(const char *src, unsigned int type, va_list ap) +{ + destroy(); + prog = glCreateProgram(); + + while(src) { + Shader *sdr = new Shader; + if(!sdr->create(src, type)) { + va_end(ap); + return false; + } + add_shader(sdr); + src = va_arg(ap, const char*); + type = va_arg(ap, unsigned int); + } + + return link(); +} + +bool ShaderProg::create(const char *vsrc, const char *psrc) +{ + return create(VSDR(vsrc), PSDR(psrc), 0); +} + +bool ShaderProg::create(Shader *sdr, ...) +{ + va_list ap; + + va_start(ap, sdr); + bool res = create(sdr, ap); + va_end(ap); + + return res; +} + +bool ShaderProg::create(Shader *sdr, va_list ap) +{ + destroy(); + prog = glCreateProgram(); + + while(sdr) { + add_shader(sdr); + sdr = va_arg(ap, Shader*); + } + return link(); +} + +bool ShaderProg::create(Shader *vsdr, Shader *psdr) +{ + return create(vsdr, psdr, 0); +} + +void ShaderProg::destroy() +{ + if(prog) { + glDeleteProgram(prog); + } + prog = 0; + + shaders.clear(); + // don't actually destroy the shaders, let the ShaderSet own them +} + +bool ShaderProg::load(const char *fname, unsigned int type, ...) +{ + va_list ap; + va_start(ap, type); + bool res = load(fname, type, ap); + va_end(ap); + + return res; +} + +bool ShaderProg::load(const char *fname, unsigned int type, va_list ap) +{ + destroy(); + prog = glCreateProgram(); + + while(fname) { + Shader *sdr = new Shader; + if(!sdr->load(fname, type)) { + delete sdr; + return false; + } + add_shader(sdr); + + if((fname = va_arg(ap, const char*))) { + type = va_arg(ap, unsigned int); + } + } + + return link(); +} + +bool ShaderProg::load(const char *vfname, const char *pfname) +{ + return load(VSDR(vfname), PSDR(pfname), 0); +} + +void ShaderProg::add_shader(Shader *sdr) +{ + glAttachShader(prog, sdr->get_id()); +} + +bool ShaderProg::link() const +{ + bind_standard_attr(this); + + printf("linking program ... "); + glLinkProgram(prog); + + int status; + glGetProgramiv(prog, GL_LINK_STATUS, &status); + + printf(status ? "success\n" : "failed\n"); + + int info_len; + glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &info_len); + if(info_len > 1) { + char *buf = (char*)alloca(info_len); + glGetProgramInfoLog(prog, info_len, 0, buf); + buf[info_len - 1] = 0; + + if(status) { + printf("%s\n", buf); + } else { + fprintf(stderr, "%s\n", buf); + } + } + + if(status) { + must_link = false; + //cache_state_uniforms(); + return true; + } + return false; +} + +void ShaderProg::bind() const +{ + if(must_link) { + if(!link()) { + return; + } + } + glUseProgram(prog); + ShaderProg::current = (ShaderProg*)this; + + //setup_state_uniforms(); +} + + +int ShaderProg::get_attrib_location(const char *name) const +{ + glUseProgram(prog); + return glGetAttribLocation(prog, name); +} + +void ShaderProg::set_attrib_location(const char *name, int loc) const +{ + glBindAttribLocation(prog, loc, name); + must_link = true; +} + +int ShaderProg::get_uniform_location(const char *name) const +{ + glUseProgram(prog); + return glGetUniformLocation(prog, name); +} + +bool ShaderProg::set_uniform(int loc, int val) const +{ + glUseProgram(prog); + if(loc >= 0) { + glUniform1i(loc, val); + return true; + } + return false; +} + +bool ShaderProg::set_uniform(int loc, float val) const +{ + glUseProgram(prog); + if(loc >= 0) { + glUniform1f(loc, val); + return true; + } + return false; +} + +bool ShaderProg::set_uniform(int loc, const Vector2 &v) const +{ + glUseProgram(prog); + if(loc >= 0) { + glUniform2f(loc, v.x, v.y); + return true; + } + return false; +} + +bool ShaderProg::set_uniform(int loc, const Vector3 &v) const +{ + glUseProgram(prog); + if(loc >= 0) { + glUniform3f(loc, v.x, v.y, v.z); + return true; + } + return false; +} + +bool ShaderProg::set_uniform(int loc, const Vector4 &v) const +{ + glUseProgram(prog); + if(loc >= 0) { + glUniform4f(loc, v.x, v.y, v.z, v.w); + return true; + } + return false; +} + +bool ShaderProg::set_uniform(int loc, const Matrix3x3 &m) const +{ + glUseProgram(prog); + if(loc >= 0) { + glUniformMatrix3fv(loc, 1, GL_TRUE, m[0]); + return true; + } + return false; +} + +bool ShaderProg::set_uniform(int loc, const Matrix4x4 &m) const +{ + glUseProgram(prog); + if(loc >= 0) { + glUniformMatrix4fv(loc, 1, GL_TRUE, m[0]); + return true; + } + return false; +} + + +bool ShaderProg::set_uniform(const char *name, int val) const +{ + return set_uniform(get_uniform_location(name), val); +} + +bool ShaderProg::set_uniform(const char *name, float val) const +{ + return set_uniform(get_uniform_location(name), val); +} + +bool ShaderProg::set_uniform(const char *name, const Vector2 &v) const +{ + return set_uniform(get_uniform_location(name), v); +} + +bool ShaderProg::set_uniform(const char *name, const Vector3 &v) const +{ + return set_uniform(get_uniform_location(name), v); +} + +bool ShaderProg::set_uniform(const char *name, const Vector4 &v) const +{ + return set_uniform(get_uniform_location(name), v); +} + +bool ShaderProg::set_uniform(const char *name, const Matrix3x3 &m) const +{ + return set_uniform(get_uniform_location(name), m); +} + +bool ShaderProg::set_uniform(const char *name, const Matrix4x4 &m) const +{ + return set_uniform(get_uniform_location(name), m); +} + +/* +static StType unist_type(GLenum type) +{ + switch(type) { + case GL_FLOAT: + return ST_FLOAT; + case GL_FLOAT_VEC2: + return ST_FLOAT2; + case GL_FLOAT_VEC3: + return ST_FLOAT3; + case GL_FLOAT_VEC4: + return ST_FLOAT4; + case GL_INT: + case GL_SAMPLER_2D: + case GL_SAMPLER_CUBE: +#if !GL_ES_VERSION_2_0 + case GL_SAMPLER_1D: + case GL_SAMPLER_3D: + case GL_SAMPLER_1D_SHADOW: + case GL_SAMPLER_2D_SHADOW: +#endif + return ST_INT; + case GL_INT_VEC2: + return ST_INT2; + case GL_INT_VEC3: + return ST_INT3; + case GL_INT_VEC4: + return ST_INT4; + case GL_FLOAT_MAT3: + return ST_MATRIX3; + case GL_FLOAT_MAT4: + return ST_MATRIX4; + default: + break; + } + return ST_UNKNOWN; +} + +void ShaderProg::cache_state_uniforms() const +{ + if(!glIsProgram(prog)) { + return; + } + + int num_uni; + glGetProgramiv(prog, GL_ACTIVE_UNIFORMS, &num_uni); + + char name[256]; + for(int i=0; isrc = src; + return true; +} + +bool Shader::done_shader(Shader *sdr, unsigned int type) +{ + return sdr->create(sdr->src.c_str(), type); +} + +/* +static bool done_vertex_shader(Shader *sdr) +{ + return Shader::done_shader(sdr, GL_VERTEX_SHADER); +} + +static bool done_pixel_shader(Shader *sdr) +{ + return Shader::done_shader(sdr, GL_FRAGMENT_SHADER); +} + +#ifdef HAVE_GEOMETRY_SHADER +static bool done_geom_shader(Shader *sdr) +{ + return Shader::done_shader(sdr, GL_GEOMETRY_SHADER); +} +#endif + +#ifdef HAVE_TESSELATION_SHADER +static bool done_tc_shader(Shader *sdr) +{ + return Shader::done_shader(sdr, GL_TESS_CONTROL_SHADER); +} + +static bool done_te_shader(Shader *sdr) +{ + return Shader::done_shader(sdr, GL_TESS_EVALUATION_SHADER); +} +#endif + +void Shader::destroy_shader(Shader *sdr) +{ + delete sdr; +} + +// ---- ShaderSet ---- + +ShaderSet::ShaderSet(unsigned int type) + : DataSet(Shader::create_shader, Shader::load_shader, 0, Shader::destroy_shader) +{ + switch(type) { + case GL_VERTEX_SHADER: + done = done_vertex_shader; + break; + + case GL_FRAGMENT_SHADER: + done = done_pixel_shader; + break; + +#ifdef HAVE_GEOMETRY_SHADER + case GL_GEOMETRY_SHADER: + done = done_geom_shader; + break; +#endif + +#ifdef HAVE_TESSELATION_SHADER + case GL_TESS_CONTROL_SHADER: + done = done_tc_shader; + break; + + case GL_TESS_EVALUATION_SHADER: + done = done_te_shader; + break; +#endif + + default: + fprintf(stderr, "ShaderSet constructed with invalid shader type!\n"); + } +} +*/ + +static struct { const char *name; int loc; } attr_loc[] = { + {"attr_vertex", MESH_ATTR_VERTEX}, + {"attr_normal", MESH_ATTR_NORMAL}, + {"attr_tangent", MESH_ATTR_TANGENT}, + {"attr_texcoord", MESH_ATTR_TEXCOORD}, + {"attr_color", MESH_ATTR_COLOR}, + {"attr_boneweights", MESH_ATTR_BONEWEIGHTS}, + {"attr_boneidx", MESH_ATTR_BONEIDX} +}; + +static void bind_standard_attr(const ShaderProg *prog) +{ + // we must link once to find out which are the active attributes + glLinkProgram(prog->get_id()); + + int num_attr; + glGetProgramiv(prog->get_id(), GL_ACTIVE_ATTRIBUTES, &num_attr); + + char name[256]; + for(int i=0; iget_id(), i, sizeof name - 1, 0, &sz, &type, name); + + for(int j=0; j<(int)(sizeof attr_loc / sizeof *attr_loc); j++) { + if(strcmp(name, attr_loc[j].name) == 0) { + prog->set_attrib_location(name, attr_loc[j].loc); + } + } + } +} + + +/* +static const char *strtype(unsigned int type) +{ + switch(type) { + case GL_VERTEX_SHADER: + return "vertex"; + case GL_FRAGMENT_SHADER: + return "fragment"; +#ifdef HAVE_GEOMETRY_SHADER + case GL_GEOMETRY_SHADER: + return "geometry"; +#endif +#ifdef HAVE_TESSELATION_SHADER + case GL_TESS_CONTROL_SHADER: + return "tesselation control"; + case GL_TESS_EVALUATION_SHADER: + return "tesselation evaluation"; +#endif + default: + break; + } + return ""; +} +*/ diff -r af24cfbdf9b6 -r 8cce82794f90 src/shader.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/shader.h Sun Feb 15 05:14:20 2015 +0200 @@ -0,0 +1,140 @@ +#ifndef SHADER_H_ +#define SHADER_H_ + +#include +#include +#include "vmath/vmath.h" +#include "opengl.h" + +class ShaderProg; + +void bind_shader(const ShaderProg *sdr); +const ShaderProg *get_current_shader(); + + +class Shader { +private: + unsigned int sdr; + unsigned int type; + std::string name, src; + +public: + Shader(); + ~Shader(); + + unsigned int get_id() const; + + void set_name(const char *name); + const char *get_name() const; + + bool create(const char *src, unsigned int type); + void destroy(); + + bool load(const char *fname, unsigned int type); + + + // these functions are only meant to be used by the ShaderSet + static Shader *create_shader(); + static bool load_shader(Shader *sdr, const char *fname); + static bool done_shader(Shader *sdr, unsigned int type); + static void destroy_shader(Shader *sdr); +}; + +#define VSDR(s) s, GL_VERTEX_SHADER +#define FSDR(s) s, GL_FRAGMENT_SHADER +#define PSDR(s) FSDR(s) +#define GSDR(s) s, GL_GEOMETRY_SHADER +#define TCSDR(s) s, GL_TESS_CONTROL_SHADER +#define TESDR(s) s, GL_TESS_EVALUATION_SHADER + +class ShaderProg { +private: + unsigned int prog; + mutable bool must_link; + std::vector shaders; + + struct StateLocCache { int sidx, loc; }; + /** a cache of all st_ prefixed uniform locations and their corresponding + * index in the global uniform state vector (see unistate.h) + */ + mutable std::vector stloc_cache; + + void cache_state_uniforms() const; + void setup_state_uniforms() const; + +public: + static ShaderProg *current; + + ShaderProg(); + ~ShaderProg(); + + /// returns the OpenGL object id for this shader program + unsigned int get_id() const; + + /** takes a series of shaders, and constructs a program object by linking + * them together. Terminate with a null pointer (don't use 0!) */ + bool create(Shader *sdr, ...); + /// same as above, but with a va_list instead of variable arguments. + bool create(Shader *sdr, va_list ap); + /** takes two shaders (vertex and pixel) and constructs a program object by + * linking them together. Either one can be null. */ + bool create(Shader *vsdr, Shader *psdr); + + /** takes a series of shader source/shader type pairs and constructs a program + * object by linking them together. Terminate with a null pointer (don't use 0!) + * You can use the VSDR, PSDR, GSDR, TCSDR, TESDR convenience macros for passing + * the pairs. + * Example: create(VSDR(vsrc0), VSDR(vsrc1), PSDR(psrc), NULL); + */ + bool create(const char *src, unsigned int type, ...); + /// same as above, but with a va_list instead of variable arguments. + bool create(const char *src, unsigned int type, va_list ap); + /** takes two shaders source strings (vertex and pixel) and constructs + * a program object by linking them together. Either one can be null. */ + bool create(const char *vsrc, const char *psrc); + + void destroy(); + + /** takes a series of shader filename/shader type pairs, loads the shaders and + * constructs a program object by linking them together. Terminate with a null + * pointer (don't use 0!). You can use the VSDR, PSDR, GSDR, TCSDR, TESDR convenience + * macros for passing the pairs. + * Example: load(VSDR("vsdr1.glsl"), VSDR("vsdr2.glsl"), PSDR("pixel.glsl"), NULL); + */ + bool load(const char *fname, unsigned int type, ...); + /// same as above, but with a va_list instead of variable arguments. + bool load(const char *fname, unsigned int type, va_list ap); + /** takes the filenames of two shader files (vertex and pixel), loads them and + * constructs a program object by linking them together. Either one can be null */ + bool load(const char *vsrc, const char *psrc); + + void add_shader(Shader *sdr); + bool link() const; + + void bind() const; + + int get_attrib_location(const char *name) const; + void set_attrib_location(const char *name, int loc) const; + + int get_uniform_location(const char *name) const; + + bool set_uniform(int loc, int val) const; + bool set_uniform(int loc, float val) const; + bool set_uniform(int loc, const Vector2 &v) const; + bool set_uniform(int loc, const Vector3 &v) const; + bool set_uniform(int loc, const Vector4 &v) const; + bool set_uniform(int loc, const Matrix3x3 &m) const; + bool set_uniform(int loc, const Matrix4x4 &m) const; + + bool set_uniform(const char *name, int val) const; + bool set_uniform(const char *name, float val) const; + bool set_uniform(const char *name, const Vector2 &v) const; + bool set_uniform(const char *name, const Vector3 &v) const; + bool set_uniform(const char *name, const Vector4 &v) const; + bool set_uniform(const char *name, const Matrix3x3 &m) const; + bool set_uniform(const char *name, const Matrix4x4 &m) const; + + friend void setup_unistate(const ShaderProg*); +}; + +#endif // SHADER_H_ diff -r af24cfbdf9b6 -r 8cce82794f90 src/sim.cc --- a/src/sim.cc Sat Feb 14 10:08:00 2015 +0200 +++ b/src/sim.cc Sun Feb 15 05:14:20 2015 +0200 @@ -64,3 +64,39 @@ { return gravity; } + +bool SimWorld::collide(const GObject *obj, HitPoint *hit) const +{ + CoCollider *co = COCAST(CoCollider, obj->get_component("collider")); + if(co) { + return collide(co, hit); + } + return false; // no collisions if it doesn't have a collider component +} + +bool SimWorld::collide(const CoCollider *col, HitPoint *hit) const +{ + if(!col->shape) { + return false; + } + + // TODO do something better + std::list::const_iterator it = rigid.begin(); + while(it != rigid.end()) { + const CoRigid *r = *it++; + const GObject *obj2 = r->get_object(); + if(!obj2 || obj2 == col->get_object()) { + continue; + } + + const CoCollider *col2 = COCAST(CoCollider, obj2->get_component("collider")); + if(!col2 || !col2->shape) { + continue; + } + + if(col->shape->collide(col2->shape, hit)) { + return true; + } + } + return false; +} diff -r af24cfbdf9b6 -r 8cce82794f90 src/sim.h --- a/src/sim.h Sat Feb 14 10:08:00 2015 +0200 +++ b/src/sim.h Sun Feb 15 05:14:20 2015 +0200 @@ -3,6 +3,7 @@ #include #include "co_phys.h" +#include "co_collider.h" #include "gobj.h" class SimWorld { @@ -25,6 +26,9 @@ void set_gravity(const Vector3 &v); const Vector3 &get_gravity() const; + + bool collide(const GObject *obj, HitPoint *hit = 0) const; + bool collide(const CoCollider *col, HitPoint *hit = 0) const; }; #endif // COENG_SIM_H_ diff -r af24cfbdf9b6 -r 8cce82794f90 src/test.cc --- a/src/test.cc Sat Feb 14 10:08:00 2015 +0200 +++ b/src/test.cc Sun Feb 15 05:14:20 2015 +0200 @@ -3,15 +3,20 @@ #include #include +#include "opengl.h" #ifndef __APPLE__ #include #else #include #endif +#include "objfile.h" +#include "mesh.h" #include "gobj.h" #include "co_xform.h" #include "co_dbgvis.h" +#include "co_mesh.h" +#include "co_phys.h" #include "sim.h" static bool init(); @@ -22,8 +27,9 @@ 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 Mesh *load_mesh(const char *fname); -float cam_theta, cam_phi = 25, cam_dist = 8; +float cam_theta, cam_phi = 25, cam_dist = 10; std::vector objects; SimWorld simworld; @@ -54,6 +60,8 @@ static bool init() { + init_opengl(); + glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); @@ -62,27 +70,51 @@ float ldir[] = {-1, 1, 2, 0}; glLightfv(GL_LIGHT0, GL_POSITION, ldir); - GObject *obj = new GObject; - obj->add_component(new CoSphereVis); + GObject *obj; + + Mesh *mesh = load_mesh("lucy.obj"); + if(!mesh) { + return false; + } + + obj = new GObject; obj->add_component(new CoXForm); obj->add_component(new CoPRS); - obj->add_component(new CoRigid); - COCAST(CoPRS, obj->get_component("prs"))->pos = Vector3(5, 2, 0); + obj->add_component(new CoMesh); + gobj_co_prs(obj)->pos = Vector3(-3, 0, 0); + gobj_co_mesh(obj)->mesh = mesh; objects.push_back(obj); - simworld.add_object(obj); obj = new GObject; obj->add_component(new CoSphereVis); obj->add_component(new CoXForm); obj->add_component(new CoPRS); - COCAST(CoPRS, obj->get_component("prs"))->pos = Vector3(-5, 1, 0); + gobj_co_prs(obj)->pos = Vector3(3, 3, 0); + obj->add_component(new CoRigid); + obj->add_component(new CoCollider); + gobj_co_collider(obj)->shape = new Sphere; objects.push_back(obj); + simworld.add_object(obj); + + obj = new GObject; + obj->add_component(new CoXForm); + obj->add_component(new CoPRS); + obj->add_component(new CoRigid); + obj->add_component(new CoCollider); + gobj_co_collider(obj)->shape = new Plane; + gobj_co_rigid(obj)->set_fixed(true); + objects.push_back(obj); + simworld.add_object(obj); return true; } static void cleanup() { + for(size_t i=0; iget_component("spherevis")); - if(co) { - co->draw(); - } + objects[i]->draw(); } glutSwapBuffers(); @@ -185,3 +218,19 @@ if(cam_dist < 0.0) cam_dist = 0.0; } } + +static Mesh *load_mesh(const char *fname) +{ + struct objfile *objf = objf_load(fname); + if(!objf) { + return 0; + } + int nverts = objf_vertex_count(objf); + + Mesh *m = new Mesh; + m->set_attrib_data(MESH_ATTR_VERTEX, 3, nverts, objf_vertices(objf)); + m->set_attrib_data(MESH_ATTR_NORMAL, 3, nverts, objf_normals(objf)); + m->set_attrib_data(MESH_ATTR_TEXCOORD, 2, nverts, objf_texcoords(objf)); + objf_free(objf); + return m; +}