--- /dev/null
+*.o
+*.swp
+*.d
+anti
--- /dev/null
+src = $(wildcard src/*.cc)
+obj = $(src:.cc=.o)
+dep = $(obj:.o=.d)
+bin = anti
+
+CXXFLAGS = -std=c++11 -pedantic -Wall -g
+LDFLAGS = $(libgl_$(sys)) -lm -lgmath
+
+sys = $(shell uname -s)
+libgl_Linux = -lGL -lGLU -lglut -lGLEW
+libgl_Darwin = -framework OpenGL -framework GLUT -lGLEW
+
+$(bin): $(obj)
+ $(CXX) -o $@ $(obj) $(LDFLAGS)
+
+-include $(dep)
+
+%.d: %.cc
+ @$(CPP) $(CXXFLAGS) $< -MM -MT $(@:.d=.o) >$@
+
+.PHONY: clean
+clean:
+ rm -f $(obj) $(bin)
+
+.PHONY: cleandep
+cleandep:
+ rm -f $(dep)
--- /dev/null
+#include <GL/glew.h>
+#include "gear.h"
+#include "meshgen.h"
+
+Gear::Gear()
+ : axis(0, 0, 1)
+{
+ pdist = 0;
+ angle = 0;
+ nteeth = 42;
+ radius = 80;
+ teeth_length = 5;
+ thickness = 5;
+ bevel = 1.5;
+
+ mesh = 0;
+}
+
+Gear::~Gear()
+{
+ delete mesh;
+}
+
+void Gear::draw() const
+{
+ if(!mesh) {
+ if(!((Gear*)this)->gen_mesh()) {
+ abort();
+ }
+ }
+ calc_matrix();
+
+ glPushMatrix();
+ glMultMatrixf(xform[0]);
+
+ mesh->draw();
+
+ /*
+ glPushAttrib(GL_ENABLE_BIT);
+ glDisable(GL_LIGHTING);
+ glColor3f(0, 1, 0);
+ mesh->draw_normals();
+ glPopAttrib();
+ */
+
+ glPopMatrix();
+}
+
+static Vec2 rev_pos(float u, float v, void *cls)
+{
+ Gear *gear = (Gear*)cls;
+
+ float y = ((v - 1.0 / 3.0) / (1.0 / 3.0) - 0.5);
+ if(y < -0.5) y = -0.5;
+ if(y > 0.5) y = 0.5;
+ y *= gear->thickness;
+
+ if(v < 0.001 || v > 0.999) {
+ return Vec2(0, -y);
+ }
+
+ float nt = (float)gear->nteeth * 4.0;
+
+ int part = (int)(u * nt) % 4; /* 4 parts to a tooth /#\_ */
+ float t = fmod(u * nt * 4.0, 1.0);
+
+ float offs;
+ switch(part) {
+ case 0:
+ offs = t;
+ break;
+ case 1:
+ offs = 1.0f;
+ break;
+ case 2:
+ offs = 1.0f - t;
+ break;
+ case 3:
+ offs = 0.0f;
+ }
+
+ float inner_rad = gear->radius - gear->teeth_length;
+ return Vec2(inner_rad + offs * gear->teeth_length, -y);
+}
+
+bool Gear::gen_mesh()
+{
+ mesh = new Mesh;
+ gen_revol(mesh, nteeth * 4, 3, rev_pos, 0, this);
+ mesh->explode();
+ mesh->calc_face_normals();
+
+ Mat4 rot;
+ rot.rotation_x(M_PI / 2.0);
+ mesh->apply_xform(rot, rot);
+
+ mesh->set_vis_vecsize(6.0);
+ return true;
+}
+
+void Gear::calc_matrix() const
+{
+ Vec3 up = Vec3(0, 1, 0);
+ if(1.0 - fabs(dot(up, axis)) < 1e-4) {
+ up = Vec3(0, 0, -1);
+ }
+ Vec3 right = normalize(cross(up, axis));
+ up = cross(axis, right);
+
+ xform = Mat4(right, up, axis);
+ xform.translate(pos);
+}
--- /dev/null
+#ifndef GEAR_H_
+#define GEAR_H_
+
+#include <vector>
+#include <gmath/gmath.h>
+#include "mesh.h"
+
+/* distance unit: millimeters
+ * angle unit: radians
+ */
+
+class Gear;
+struct GearPin;
+struct GearSlot;
+
+struct GearPin {
+ float radius;
+ float height;
+
+ Gear *parent;
+ /* position in polar coordinates on the parent gear */
+ float pos_dist, pos_angle;
+
+ GearSlot *conn_slot; /* slot connection */
+};
+
+struct GearSlot {
+ float radius, length;
+
+ Gear *parent;
+ /* position in polar coordinates on the parent gear */
+ float pos_dist_min, pos_dist_max, pos_angle;
+
+ GearPin *conn_pin; /* pin connection */
+};
+
+class Gear {
+private:
+ Mesh *mesh;
+
+ mutable Mat4 xform;
+ void calc_matrix() const;
+
+ float contour(float u);
+
+public:
+ Vec3 pos, axis; /* implicitly defines a plane eqn. */
+ float pdist; /* derived: distance of plane from origin */
+
+ float angle; /* current angle of the gear */
+
+ int nteeth; /* number of teeth */
+
+ float radius; /* total radius of the gear, including teeth */
+ float teeth_length; /* how far teeth extend past the radius */
+ float thickness; /* thickness of the gear along the Z axis */
+
+ float bevel; /* bevel size */
+
+ std::vector<GearPin> pins;
+ std::vector<GearSlot> slots;
+
+ Gear();
+ ~Gear();
+
+ void draw() const;
+
+ bool gen_mesh();
+};
+
+#endif // GEAR_H_
--- /dev/null
+#include <assert.h>
+#include <float.h>
+#include <algorithm>
+#include "geom.h"
+
+GeomObject::~GeomObject()
+{
+}
+
+
+Sphere::Sphere()
+{
+ radius = 1.0;
+}
+
+Sphere::Sphere(const Vec3 ¢, float radius)
+ : center(cent)
+{
+ this->radius = radius;
+}
+
+void Sphere::set_union(const GeomObject *obj1, const GeomObject *obj2)
+{
+ const Sphere *sph1 = dynamic_cast<const Sphere*>(obj1);
+ const Sphere *sph2 = dynamic_cast<const Sphere*>(obj2);
+
+ if(!sph1 || !sph2) {
+ fprintf(stderr, "Sphere::set_union: arguments must be spheres");
+ return;
+ }
+
+ float dist = length(sph1->center - sph2->center);
+ float surf_dist = dist - (sph1->radius + sph2->radius);
+ float d1 = sph1->radius + surf_dist / 2.0;
+ float d2 = sph2->radius + surf_dist / 2.0;
+ float t = d1 / (d1 + d2);
+
+ if(t < 0.0) t = 0.0;
+ if(t > 1.0) t = 1.0;
+
+ center = sph1->center * t + sph2->center * (1.0 - t);
+ radius = std::max(dist * t + sph2->radius, dist * (1.0f - t) + sph1->radius);
+}
+
+void Sphere::set_intersection(const GeomObject *obj1, const GeomObject *obj2)
+{
+ fprintf(stderr, "Sphere::intersection undefined\n");
+}
+
+bool Sphere::intersect(const Ray &ray, HitPoint *hit) const
+{
+ float a = dot(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) +
+ 2.0 * ray.dir.z * (ray.origin.z - center.z);
+ float c = dot(ray.origin, ray.origin) + dot(center, center) -
+ 2.0 * dot(ray.origin, center) - radius * radius;
+
+ float discr = b * b - 4.0 * a * c;
+ if(discr < 1e-4) {
+ return false;
+ }
+
+ float sqrt_discr = sqrt(discr);
+ float t0 = (-b + sqrt_discr) / (2.0 * a);
+ float t1 = (-b - sqrt_discr) / (2.0 * a);
+
+ if(t0 < 1e-4)
+ t0 = t1;
+ if(t1 < 1e-4)
+ t1 = t0;
+
+ float t = t0 < t1 ? t0 : t1;
+ if(t < 1e-4) {
+ return false;
+ }
+
+ // fill the HitPoint structure
+ if(hit) {
+ hit->obj = this;
+ hit->dist = t;
+ hit->pos = ray.origin + ray.dir * t;
+ hit->normal = (hit->pos - center) / radius;
+ }
+ return true;
+}
+
+
+AABox::AABox()
+{
+}
+
+AABox::AABox(const Vec3 &vmin, const Vec3 &vmax)
+ : min(vmin), max(vmax)
+{
+}
+
+void AABox::set_union(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) {
+ 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 GeomObject *obj1, const GeomObject *obj2)
+{
+ const AABox *box1 = dynamic_cast<const AABox*>(obj1);
+ const AABox *box2 = dynamic_cast<const AABox*>(obj2);
+
+ if(!box1 || !box2) {
+ fprintf(stderr, "AABox::set_intersection: 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::intersect(const Ray &ray, HitPoint *hit) const
+{
+ Vec3 param[2] = {min, max};
+ Vec3 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->obj = this;
+ hit->dist = t;
+ hit->pos = ray.origin + ray.dir * t;
+
+ float min_dist = FLT_MAX;
+ Vec3 offs = min + (max - min) / 2.0;
+ Vec3 local_hit = hit->pos - offs;
+
+ static const Vec3 axis[] = {
+ Vec3(1, 0, 0), Vec3(0, 1, 0), Vec3(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 = Vec2(hit->pos[tcidx[i][0]], hit->pos[tcidx[i][1]]);
+ }
+ }
+ }
+ return true;
+ }
+ return false;
+
+}
+
+Plane::Plane()
+ : normal(0.0, 1.0, 0.0)
+{
+}
+
+Plane::Plane(const Vec3 &p, const Vec3 &norm)
+ : pt(p)
+{
+ normal = normalize(norm);
+}
+
+Plane::Plane(const Vec3 &p1, const Vec3 &p2, const Vec3 &p3)
+ : pt(p1)
+{
+ normal = normalize(cross(p2 - p1, p3 - p1));
+}
+
+Plane::Plane(const Vec3 &normal, float dist)
+{
+ this->normal = normalize(normal);
+ pt = this->normal * dist;
+}
+
+void Plane::set_union(const GeomObject *obj1, const GeomObject *obj2)
+{
+ fprintf(stderr, "Plane::set_union undefined\n");
+}
+
+void Plane::set_intersection(const GeomObject *obj1, const GeomObject *obj2)
+{
+ fprintf(stderr, "Plane::set_intersection undefined\n");
+}
+
+bool Plane::intersect(const Ray &ray, HitPoint *hit) const
+{
+ float ndotdir = dot(normal, ray.dir);
+ if(fabs(ndotdir) < 1e-4) {
+ return false;
+ }
+
+ if(hit) {
+ Vec3 ptdir = pt - ray.origin;
+ float t = dot(normal, ptdir) / ndotdir;
+
+ hit->pos = ray.origin + ray.dir * t;
+ hit->normal = normal;
+ hit->obj = this;
+ }
+ 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)
+{
+ 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;
+}
+
+#if 0
+float capsule_distance(const Vec3 &a, float ra, const Vec3 &b, float rb, const Vec3 &pt)
+{
+ 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;
+}
+#endif
--- /dev/null
+#ifndef GEOMOBJ_H_
+#define GEOMOBJ_H_
+
+#include "gmath/gmath.h"
+
+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;
+};
+
+class GeomObject {
+public:
+ virtual ~GeomObject();
+
+ 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;
+};
+
+class Sphere : public GeomObject {
+public:
+ Vec3 center;
+ float radius;
+
+ Sphere();
+ Sphere(const Vec3 ¢er, float radius);
+
+ 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;
+};
+
+class AABox : public GeomObject {
+public:
+ Vec3 min, max;
+
+ AABox();
+ AABox(const Vec3 &min, const Vec3 &max);
+
+ 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;
+};
+
+class Plane : public GeomObject {
+public:
+ Vec3 pt, normal;
+
+ Plane();
+ Plane(const Vec3 &pt, const Vec3 &normal);
+ Plane(const Vec3 &p1, const Vec3 &p2, const Vec3 &p3);
+ Plane(const Vec3 &normal, float dist);
+
+ 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;
+};
+
+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);
+
+#endif // GEOMOBJ_H_
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <GL/glew.h>
+#ifdef __APPLE__
+#include <GLUT/glut.h>
+#else
+#include <GL/glut.h>
+#endif
+#include "gear.h"
+
+bool init();
+void cleanup();
+void display();
+void draw_gears();
+void reshape(int x, int y);
+void keyb(unsigned char key, int x, int y);
+void mouse(int bn, int st, int x, int y);
+void motion(int x, int y);
+
+static float cam_dist = 2;
+static float cam_theta, cam_phi;
+static int prev_mx, prev_my;
+static bool bnstate[8];
+
+static Gear *test_gear;
+
+int main(int argc, char **argv)
+{
+ glutInit(&argc, argv);
+ glutInitWindowSize(1024, 768);
+ glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE);
+ glutCreateWindow("Antikythera");
+
+ glutDisplayFunc(display);
+ glutReshapeFunc(reshape);
+ glutKeyboardFunc(keyb);
+ glutMouseFunc(mouse);
+ glutMotionFunc(motion);
+
+ if(!init()) {
+ return 1;
+ }
+ atexit(cleanup);
+
+ glutMainLoop();
+ return 0;
+}
+
+bool init()
+{
+ glewInit();
+
+ glEnable(GL_DEPTH_TEST);
+ glEnable(GL_CULL_FACE);
+ glEnable(GL_LIGHTING);
+ glEnable(GL_LIGHT0);
+ glEnable(GL_NORMALIZE);
+
+ Mesh::use_custom_sdr_attr = false;
+
+ test_gear = new Gear;
+ test_gear->gen_mesh();
+
+ return true;
+}
+
+void cleanup()
+{
+ delete test_gear;
+}
+
+void display()
+{
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ glTranslatef(0, 0, -cam_dist);
+ glRotatef(cam_phi, 1, 0, 0);
+ glRotatef(cam_theta, 0, 1, 0);
+
+ draw_gears();
+
+ glutSwapBuffers();
+ assert(glGetError() == GL_NO_ERROR);
+}
+
+void draw_gears()
+{
+ /* world scale is in meters, gears are in millimeters, sclae by 1/1000 */
+ glPushMatrix();
+ glScalef(0.001, 0.001, 0.001);
+
+ test_gear->draw();
+
+ glPopMatrix();
+}
+
+void reshape(int x, int y)
+{
+ glViewport(0, 0, x, y);
+
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ gluPerspective(50.0, (float)x / (float)y, 0.05, 100.0);
+}
+
+void keyb(unsigned char key, int x, int y)
+{
+ switch(key) {
+ case 27:
+ exit(0);
+ }
+}
+
+void mouse(int bn, int st, int x, int y)
+{
+ prev_mx = x;
+ prev_my = y;
+ bnstate[bn - GLUT_LEFT_BUTTON] = st == GLUT_DOWN;
+}
+
+void motion(int x, int y)
+{
+ int dx = x - prev_mx;
+ int dy = y - prev_my;
+ prev_mx = x;
+ prev_my = y;
+
+ if(!dx && !dy) return;
+
+ 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.05;
+ if(cam_dist < 0.0) cam_dist = 0.0;
+ glutPostRedisplay();
+ }
+}
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <float.h>
+#include <assert.h>
+#include "opengl.h"
+#include "mesh.h"
+//#include "xform_node.h"
+
+#define USE_OLDGL
+
+bool Mesh::use_custom_sdr_attr = true;
+int Mesh::global_sdr_loc[NUM_MESH_ATTR] = { 0, 1, 2, 3, 4, 5, 6 };
+/*
+ (int)SDR_ATTR_VERTEX,
+ (int)SDR_ATTR_NORMAL,
+ (int)SDR_ATTR_TANGENT,
+ (int)SDR_ATTR_TEXCOORD,
+ (int)SDR_ATTR_COLOR,
+ -1, -1};
+*/
+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; i<NUM_MESH_ATTR; i++) {
+ vattr[i].vbo = buffer_objects[i];
+ }
+ ibo = buffer_objects[NUM_MESH_ATTR];
+ wire_ibo = 0;
+}
+
+Mesh::~Mesh()
+{
+ glDeleteBuffers(NUM_MESH_ATTR + 1, buffer_objects);
+
+ if(wire_ibo) {
+ glDeleteBuffers(1, &wire_ibo);
+ }
+}
+
+Mesh::Mesh(const Mesh &rhs)
+{
+ clear();
+
+ glGenBuffers(NUM_MESH_ATTR + 1, buffer_objects);
+
+ for(int i=0; i<NUM_MESH_ATTR; i++) {
+ vattr[i].vbo = buffer_objects[i];
+ }
+ ibo = buffer_objects[NUM_MESH_ATTR];
+ wire_ibo = 0;
+
+ clone(rhs);
+}
+
+Mesh &Mesh::operator =(const Mesh &rhs)
+{
+ if(&rhs != this) {
+ clone(rhs);
+ }
+ return *this;
+}
+
+bool Mesh::clone(const Mesh &m)
+{
+ clear();
+
+ for(int i=0; i<NUM_MESH_ATTR; i++) {
+ if(m.has_attrib(i)) {
+ m.get_attrib_data(i); // force validation of the actual data on the source mesh
+
+ vattr[i].nelem = m.vattr[i].nelem;
+ vattr[i].data = m.vattr[i].data; // copy the actual data
+ vattr[i].data_valid = true;
+ }
+ }
+
+ if(m.is_indexed()) {
+ m.get_index_data(); // again, force validation
+
+ // copy the index data
+ idata = m.idata;
+ idata_valid = true;
+ }
+
+ name = m.name;
+ nverts = m.nverts;
+ nfaces = m.nfaces;
+
+ //bones = m.bones;
+
+ memcpy(cur_val, m.cur_val, sizeof cur_val);
+
+ aabb = m.aabb;
+ aabb_valid = m.aabb_valid;
+ bsph = m.bsph;
+ bsph_valid = m.bsph_valid;
+
+ hitface = m.hitface;
+ hitvert = m.hitvert;
+
+ intersect_mode = m.intersect_mode;
+ vertex_sel_dist = m.vertex_sel_dist;
+ vis_vecsize = m.vis_vecsize;
+
+ return true;
+}
+
+void Mesh::set_name(const char *name)
+{
+ this->name = 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;
+}
+
+bool Mesh::is_indexed() const
+{
+ return ibo_valid || idata_valid;
+}
+
+void Mesh::clear()
+{
+ //bones.clear();
+
+ for(int i=0; i<NUM_MESH_ATTR; i++) {
+ vattr[i].nelem = 0;
+ vattr[i].vbo_valid = false;
+ vattr[i].data_valid = false;
+ //vattr[i].sdr_loc = -1;
+ vattr[i].data.clear();
+ }
+ ibo_valid = idata_valid = false;
+ idata.clear();
+
+ wire_ibo_valid = false;
+
+ nverts = nfaces = 0;
+
+ bsph_valid = false;
+ aabb_valid = false;
+}
+
+float *Mesh::set_attrib_data(int attrib, int nelem, unsigned int num, const float *data)
+{
+ if(attrib < 0 || attrib >= 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 Vec4 &v)
+{
+ float *data = get_attrib_data(attrib);
+ if(data) {
+ data += idx * vattr[attrib].nelem;
+ for(int i=0; i<vattr[attrib].nelem; i++) {
+ data[i] = v[i];
+ }
+ }
+}
+
+Vec4 Mesh::get_attrib(int attrib, int idx) const
+{
+ Vec4 v(0.0, 0.0, 0.0, 1.0);
+ const float *data = get_attrib_data(attrib);
+ if(data) {
+ data += idx * vattr[attrib].nelem;
+ for(int i=0; i<vattr[attrib].nelem; i++) {
+ v[i] = data[i];
+ }
+ }
+ return v;
+}
+
+int Mesh::get_attrib_count(int attrib) const
+{
+ return has_attrib(attrib) ? nverts : 0;
+}
+
+
+unsigned int *Mesh::set_index_data(int num, const unsigned int *indices)
+{
+ int nidx = nfaces * 3;
+ if(nidx && num != nidx) {
+ fprintf(stderr, "%s: index count mismatch (%d instead of %d)\n", __FUNCTION__, num, nidx);
+ return 0;
+ }
+ nfaces = num / 3;
+
+ idata.clear();
+ idata.resize(num);
+
+ if(indices) {
+ memcpy(&idata[0], indices, num * sizeof *indices);
+ }
+
+ idata_valid = true;
+ ibo_valid = false;
+
+ return &idata[0];
+}
+
+unsigned int *Mesh::get_index_data()
+{
+ ibo_valid = false;
+ return (unsigned int*)((const Mesh*)this)->get_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];
+}
+
+int Mesh::get_index_count() const
+{
+ return nfaces * 3;
+}
+
+void Mesh::append(const Mesh &mesh)
+{
+ unsigned int idxoffs = nverts;
+
+ if(!nverts) {
+ clone(mesh);
+ return;
+ }
+
+ nverts += mesh.nverts;
+ nfaces += mesh.nfaces;
+
+ for(int i=0; i<NUM_MESH_ATTR; i++) {
+ if(has_attrib(i) && mesh.has_attrib(i)) {
+ // force validating the data arrays
+ get_attrib_data(i);
+ mesh.get_attrib_data(i);
+
+ // append the mesh data
+ vattr[i].data.insert(vattr[i].data.end(), mesh.vattr[i].data.begin(), mesh.vattr[i].data.end());
+ }
+ }
+
+ if(ibo_valid || idata_valid) {
+ // make index arrays valid
+ get_index_data();
+ mesh.get_index_data();
+
+ size_t orig_sz = idata.size();
+
+ idata.insert(idata.end(), mesh.idata.begin(), mesh.idata.end());
+
+ // fixup all the new indices
+ for(size_t i=orig_sz; i<idata.size(); i++) {
+ idata[i] += idxoffs;
+ }
+ }
+
+ // fuck everything
+ wire_ibo_valid = false;
+ aabb_valid = false;
+ bsph_valid = false;
+}
+
+// assemble a complete vertex by adding all the useful attributes
+void Mesh::vertex(float x, float y, float z)
+{
+ cur_val[MESH_ATTR_VERTEX] = Vec4(x, y, z, 1.0f);
+ vattr[MESH_ATTR_VERTEX].data_valid = true;
+ vattr[MESH_ATTR_VERTEX].nelem = 3;
+
+ for(int i=0; i<NUM_MESH_ATTR; i++) {
+ if(vattr[i].data_valid) {
+ for(int j=0; j<vattr[MESH_ATTR_VERTEX].nelem; j++) {
+ vattr[i].data.push_back(cur_val[i][j]);
+ }
+ }
+ vattr[i].vbo_valid = false;
+ }
+
+ if(idata_valid) {
+ idata.clear();
+ }
+ ibo_valid = idata_valid = false;
+}
+
+void Mesh::normal(float nx, float ny, float nz)
+{
+ cur_val[MESH_ATTR_NORMAL] = Vec4(nx, ny, nz, 1.0f);
+ vattr[MESH_ATTR_NORMAL].data_valid = true;
+ vattr[MESH_ATTR_NORMAL].nelem = 3;
+}
+
+void Mesh::tangent(float tx, float ty, float tz)
+{
+ cur_val[MESH_ATTR_TANGENT] = Vec4(tx, ty, tz, 1.0f);
+ vattr[MESH_ATTR_TANGENT].data_valid = true;
+ vattr[MESH_ATTR_TANGENT].nelem = 3;
+}
+
+void Mesh::texcoord(float u, float v, float w)
+{
+ cur_val[MESH_ATTR_TEXCOORD] = Vec4(u, v, w, 1.0f);
+ vattr[MESH_ATTR_TEXCOORD].data_valid = true;
+ vattr[MESH_ATTR_TEXCOORD].nelem = 3;
+}
+
+void Mesh::boneweights(float w1, float w2, float w3, float w4)
+{
+ cur_val[MESH_ATTR_BONEWEIGHTS] = Vec4(w1, w2, w3, w4);
+ vattr[MESH_ATTR_BONEWEIGHTS].data_valid = true;
+ vattr[MESH_ATTR_BONEWEIGHTS].nelem = 4;
+}
+
+void Mesh::boneidx(int idx1, int idx2, int idx3, int idx4)
+{
+ cur_val[MESH_ATTR_BONEIDX] = Vec4(idx1, idx2, idx3, idx4);
+ vattr[MESH_ATTR_BONEIDX].data_valid = true;
+ vattr[MESH_ATTR_BONEIDX].nelem = 4;
+}
+
+int Mesh::get_poly_count() const
+{
+ if(nfaces) {
+ return nfaces;
+ }
+ if(nverts) {
+ return nverts / 3;
+ }
+ return 0;
+}
+
+/// static function
+void Mesh::set_attrib_location(int attr, int loc)
+{
+ if(attr < 0 || attr >= 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<NUM_MESH_ATTR; i++) {
+ Mesh::global_sdr_loc[i] = -1;
+ }
+}
+
+/// static function
+void Mesh::set_vis_vecsize(float sz)
+{
+ Mesh::vis_vecsize = sz;
+}
+
+float Mesh::get_vis_vecsize()
+{
+ return Mesh::vis_vecsize;
+}
+
+void Mesh::apply_xform(const Mat4 &xform)
+{
+ Mat4 dir_xform = xform.upper3x3();
+ apply_xform(xform, dir_xform);
+}
+
+void Mesh::apply_xform(const Mat4 &xform, const Mat4 &dir_xform)
+{
+ for(unsigned int i=0; i<nverts; i++) {
+ Vec4 v = get_attrib(MESH_ATTR_VERTEX, i);
+ set_attrib(MESH_ATTR_VERTEX, i, xform * v);
+
+ if(has_attrib(MESH_ATTR_NORMAL)) {
+ Vec3 n = Vec3(get_attrib(MESH_ATTR_NORMAL, i));
+ set_attrib(MESH_ATTR_NORMAL, i, Vec4(dir_xform * n));
+ }
+ if(has_attrib(MESH_ATTR_TANGENT)) {
+ Vec3 t = Vec3(get_attrib(MESH_ATTR_TANGENT, i));
+ set_attrib(MESH_ATTR_TANGENT, i, Vec4(dir_xform * t));
+ }
+ }
+}
+
+void Mesh::flip()
+{
+ flip_faces();
+ flip_normals();
+}
+
+void Mesh::flip_faces()
+{
+ if(is_indexed()) {
+ unsigned int *indices = get_index_data();
+ if(!indices) return;
+
+ int idxnum = get_index_count();
+ for(int i=0; i<idxnum; i+=3) {
+ unsigned int tmp = indices[i + 2];
+ indices[i + 2] = indices[i + 1];
+ indices[i + 1] = tmp;
+ }
+
+ } else {
+ Vec3 *verts = (Vec3*)get_attrib_data(MESH_ATTR_VERTEX);
+ if(!verts) return;
+
+ int vnum = get_attrib_count(MESH_ATTR_VERTEX);
+ for(int i=0; i<vnum; i+=3) {
+ Vec3 tmp = verts[i + 2];
+ verts[i + 2] = verts[i + 1];
+ verts[i + 1] = tmp;
+ }
+ }
+}
+
+void Mesh::flip_normals()
+{
+ Vec3 *normals = (Vec3*)get_attrib_data(MESH_ATTR_NORMAL);
+ if(!normals) return;
+
+ int num = get_attrib_count(MESH_ATTR_NORMAL);
+ for(int i=0; i<num; i++) {
+ normals[i] = -normals[i];
+ }
+}
+
+void Mesh::explode()
+{
+ if(!is_indexed()) return; // no vertex sharing is possible in unindexed meshes
+
+ unsigned int *indices = get_index_data();
+ assert(indices);
+
+ int idxnum = get_index_count();
+ int nnverts = idxnum;
+
+ nverts = nnverts;
+
+ for(int i=0; i<NUM_MESH_ATTR; i++) {
+ if(!has_attrib(i)) continue;
+
+ const float *srcbuf = get_attrib_data(i);
+
+ float *tmpbuf = new float[nnverts * vattr[i].nelem];
+ float *dstptr = tmpbuf;
+ for(int j=0; j<idxnum; j++) {
+ unsigned int idx = indices[j];
+ const float *srcptr = srcbuf + idx * vattr[i].nelem;
+
+ for(int k=0; k<vattr[i].nelem; k++) {
+ *dstptr++ = *srcptr++;
+ }
+ }
+ set_attrib_data(i, vattr[i].nelem, nnverts, tmpbuf);
+ delete [] tmpbuf;
+ }
+
+ ibo_valid = false;
+ idata_valid = false;
+ idata.clear();
+
+ nfaces = idxnum / 3;
+}
+
+void Mesh::calc_face_normals()
+{
+ const Vec3 *varr = (Vec3*)get_attrib_data(MESH_ATTR_VERTEX);
+ Vec3 *narr = (Vec3*)get_attrib_data(MESH_ATTR_NORMAL);
+ if(!varr) {
+ return;
+ }
+
+ if(is_indexed()) {
+ const unsigned int *idxarr = get_index_data();
+
+ for(unsigned int i=0; i<nfaces; i++) {
+ Triangle face(i, varr, idxarr);
+ face.calc_normal();
+
+ for(int j=0; j<3; j++) {
+ unsigned int idx = *idxarr++;
+ narr[idx] = face.normal;
+ }
+ }
+ } else {
+ // non-indexed
+ int nfaces = nverts / 3;
+
+ for(int i=0; i<nfaces; i++) {
+ Triangle face(varr[0], varr[1], varr[2]);
+ face.calc_normal();
+
+ narr[0] = narr[1] = narr[2] = face.normal;
+ varr += vattr[MESH_ATTR_VERTEX].nelem;
+ narr += vattr[MESH_ATTR_NORMAL].nelem;
+ }
+ }
+}
+
+/*
+int Mesh::add_bone(XFormNode *bone)
+{
+ int idx = bones.size();
+ bones.push_back(bone);
+ return idx;
+}
+
+const XFormNode *Mesh::get_bone(int idx) const
+{
+ if(idx < 0 || idx >= (int)bones.size()) {
+ return 0;
+ }
+ return bones[idx];
+}
+
+int Mesh::get_bones_count() const
+{
+ return (int)bones.size();
+}
+*/
+
+bool Mesh::pre_draw() const
+{
+ cur_sdr = 0;
+ glGetIntegerv(GL_CURRENT_PROGRAM, &cur_sdr);
+
+ ((Mesh*)this)->update_buffers();
+
+ if(!vattr[MESH_ATTR_VERTEX].vbo_valid) {
+ fprintf(stderr, "%s: invalid vertex buffer\n", __FUNCTION__);
+ return false;
+ }
+
+ if(cur_sdr && use_custom_sdr_attr) {
+ // rendering with shaders
+ if(global_sdr_loc[MESH_ATTR_VERTEX] == -1) {
+ fprintf(stderr, "%s: shader attribute location for vertices unset\n", __FUNCTION__);
+ return false;
+ }
+
+ for(int i=0; i<NUM_MESH_ATTR; i++) {
+ int loc = global_sdr_loc[i];
+ if(loc >= 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);
+
+ return true;
+}
+
+void Mesh::draw() const
+{
+ if(!pre_draw()) return;
+
+ 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);
+ }
+
+ post_draw();
+}
+
+void Mesh::post_draw() const
+{
+ if(cur_sdr && use_custom_sdr_attr) {
+ // rendered with shaders
+ for(int i=0; i<NUM_MESH_ATTR; i++) {
+ int loc = global_sdr_loc[i];
+ if(loc >= 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
+{
+ if(!pre_draw()) return;
+
+ ((Mesh*)this)->update_wire_ibo();
+
+ int num_faces = get_poly_count();
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, wire_ibo);
+ glDrawElements(GL_LINES, num_faces * 6, GL_UNSIGNED_INT, 0);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+
+ post_draw();
+}
+
+void Mesh::draw_vertices() const
+{
+ if(!pre_draw()) return;
+
+ glDrawArrays(GL_POINTS, 0, nverts);
+
+ post_draw();
+}
+
+void Mesh::draw_normals() const
+{
+#ifdef USE_OLDGL
+ int cur_sdr = 0;
+ glGetIntegerv(GL_CURRENT_PROGRAM, &cur_sdr);
+
+ Vec3 *varr = (Vec3*)get_attrib_data(MESH_ATTR_VERTEX);
+ Vec3 *norm = (Vec3*)get_attrib_data(MESH_ATTR_NORMAL);
+ if(!varr || !norm) {
+ return;
+ }
+
+ glBegin(GL_LINES);
+ if(cur_sdr && use_custom_sdr_attr) {
+ int vert_loc = global_sdr_loc[MESH_ATTR_VERTEX];
+ if(vert_loc < 0) {
+ glEnd();
+ return;
+ }
+
+ for(size_t i=0; i<nverts; i++) {
+ glVertexAttrib3f(vert_loc, varr[i].x, varr[i].y, varr[i].z);
+ Vec3 end = varr[i] + norm[i] * vis_vecsize;
+ glVertexAttrib3f(vert_loc, end.x, end.y, end.z);
+ }
+ } else {
+ for(size_t i=0; i<nverts; i++) {
+ glVertex3f(varr[i].x, varr[i].y, varr[i].z);
+ Vec3 end = varr[i] + norm[i] * vis_vecsize;
+ glVertex3f(end.x, end.y, end.z);
+ }
+ }
+ glEnd();
+#endif // USE_OLDGL
+}
+
+void Mesh::draw_tangents() const
+{
+#ifdef USE_OLDGL
+ int cur_sdr = 0;
+ glGetIntegerv(GL_CURRENT_PROGRAM, &cur_sdr);
+
+ Vec3 *varr = (Vec3*)get_attrib_data(MESH_ATTR_VERTEX);
+ Vec3 *tang = (Vec3*)get_attrib_data(MESH_ATTR_TANGENT);
+ if(!varr || !tang) {
+ return;
+ }
+
+ glBegin(GL_LINES);
+ if(cur_sdr && use_custom_sdr_attr) {
+ int vert_loc = global_sdr_loc[MESH_ATTR_VERTEX];
+ if(vert_loc < 0) {
+ glEnd();
+ return;
+ }
+
+ for(size_t i=0; i<nverts; i++) {
+ glVertexAttrib3f(vert_loc, varr[i].x, varr[i].y, varr[i].z);
+ Vec3 end = varr[i] + tang[i] * vis_vecsize;
+ glVertexAttrib3f(vert_loc, end.x, end.y, end.z);
+ }
+ } else {
+ for(size_t i=0; i<nverts; i++) {
+ glVertex3f(varr[i].x, varr[i].y, varr[i].z);
+ Vec3 end = varr[i] + tang[i] * vis_vecsize;
+ glVertex3f(end.x, end.y, end.z);
+ }
+ }
+ glEnd();
+#endif // USE_OLDGL
+}
+
+void Mesh::get_aabbox(Vec3 *vmin, Vec3 *vmax) const
+{
+ if(!aabb_valid) {
+ ((Mesh*)this)->calc_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(Vec3 *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 Vec3 *varr = (Vec3*)get_attrib_data(MESH_ATTR_VERTEX);
+ const Vec3 *narr = (Vec3*)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.obj = 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<nverts; i++) {
+
+ if((Mesh::intersect_mode & ISECT_FRONT) && dot(narr[i], ray.dir) > 0) {
+ continue;
+ }
+
+ // project the vertex onto the ray line
+ float t = dot(varr[i] - ray.origin, ray.dir);
+ Vec3 vproj = ray.origin + ray.dir * t;
+
+ float dist_sq = length_sq(vproj - varr[i]);
+ 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.obj = &hitvert;
+ }
+
+ } else {
+ // regular intersection test with polygons
+
+ for(unsigned int i=0; i<nfaces; i++) {
+ Triangle face(i, varr, idxarr);
+
+ // ignore back-facing polygons if the mode flags include ISECT_FRONT
+ if((Mesh::intersect_mode & ISECT_FRONT) && dot(face.get_normal(), ray.dir) > 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.obj) {
+ 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;
+ } else if(Mesh::intersect_mode & ISECT_VERTICES) {
+ hit->obj = &hitvert;
+ } else {
+ hit->obj = this;
+ }
+ }
+ return true;
+ }
+ return false;
+}
+
+
+// texture coordinate manipulation
+void Mesh::texcoord_apply_xform(const Mat4 &xform)
+{
+ if(!has_attrib(MESH_ATTR_TEXCOORD)) {
+ return;
+ }
+
+ for(unsigned int i=0; i<nverts; i++) {
+ Vec4 tc = get_attrib(MESH_ATTR_TEXCOORD, i);
+ set_attrib(MESH_ATTR_TEXCOORD, i, xform * tc);
+ }
+}
+
+void Mesh::texcoord_gen_plane(const Vec3 &norm, const Vec3 &tang)
+{
+ if(!nverts) return;
+
+ if(!has_attrib(MESH_ATTR_TEXCOORD)) {
+ // allocate texture coordinate attribute array
+ set_attrib_data(MESH_ATTR_TEXCOORD, 2, nverts);
+ }
+
+ Vec3 n = normalize(norm);
+ Vec3 b = normalize(cross(n, tang));
+ Vec3 t = cross(b, n);
+
+ for(unsigned int i=0; i<nverts; i++) {
+ Vec3 pos = Vec3(get_attrib(MESH_ATTR_VERTEX, i));
+
+ // distance along the tangent direction
+ float u = dot(pos, t);
+ // distance along the bitangent direction
+ float v = dot(pos, b);
+
+ set_attrib(MESH_ATTR_TEXCOORD, i, Vec4(u, v, 0, 1));
+ }
+}
+
+void Mesh::texcoord_gen_box()
+{
+ if(!nverts || !has_attrib(MESH_ATTR_NORMAL)) return;
+
+ if(!has_attrib(MESH_ATTR_TEXCOORD)) {
+ // allocate texture coordinate attribute array
+ set_attrib_data(MESH_ATTR_TEXCOORD, 2, nverts);
+ }
+
+ for(unsigned int i=0; i<nverts; i++) {
+ Vec3 pos = Vec3(get_attrib(MESH_ATTR_VERTEX, i)) * 0.5 + Vec3(0.5, 0.5, 0.5);
+ Vec3 norm = Vec3(get_attrib(MESH_ATTR_NORMAL, i));
+
+ float abs_nx = fabs(norm.x);
+ float abs_ny = fabs(norm.y);
+ float abs_nz = fabs(norm.z);
+ int dom = abs_nx > abs_ny && abs_nx > abs_nz ? 0 : (abs_ny > abs_nz ? 1 : 2);
+
+ float uv[2], *uvptr = uv;
+ for(int j=0; j<3; j++) {
+ if(j == dom) continue; // skip dominant axis
+
+ *uvptr++ = pos[j];
+ }
+ set_attrib(MESH_ATTR_TEXCOORD, i, Vec4(uv[0], uv[1], 0, 1));
+ }
+}
+
+void Mesh::texcoord_gen_cylinder()
+{
+ if(!nverts) return;
+
+ if(!has_attrib(MESH_ATTR_TEXCOORD)) {
+ // allocate texture coordinate attribute array
+ set_attrib_data(MESH_ATTR_TEXCOORD, 2, nverts);
+ }
+
+ for(unsigned int i=0; i<nverts; i++) {
+ Vec3 pos = Vec3(get_attrib(MESH_ATTR_VERTEX, i));
+
+ float rho = sqrt(pos.x * pos.x + pos.z * pos.z);
+ float theta = rho == 0.0 ? 0.0 : atan2(pos.z / rho, pos.x / rho);
+
+ float u = theta / (2.0 * M_PI) + 0.5;
+ float v = pos.y;
+
+ set_attrib(MESH_ATTR_TEXCOORD, i, Vec4(u, v, 0, 1));
+ }
+}
+
+
+bool Mesh::dump(const char *fname) const
+{
+ FILE *fp = fopen(fname, "wb");
+ if(fp) {
+ bool res = dump(fp);
+ fclose(fp);
+ return res;
+ }
+ return false;
+}
+
+bool Mesh::dump(FILE *fp) const
+{
+ if(!has_attrib(MESH_ATTR_VERTEX)) {
+ return false;
+ }
+
+ fprintf(fp, "VERTEX ATTRIBUTES\n");
+ static const char *label[] = { "pos", "nor", "tan", "tex", "col", "bw", "bid" };
+ static const char *elemfmt[] = { 0, " %s(%g)", " %s(%g, %g)", " %s(%g, %g, %g)", " %s(%g, %g, %g, %g)", 0 };
+
+ for(int i=0; i<(int)nverts; i++) {
+ fprintf(fp, "%5u:", i);
+ for(int j=0; j<NUM_MESH_ATTR; j++) {
+ if(has_attrib(j)) {
+ Vec4 v = get_attrib(j, i);
+ int nelem = vattr[j].nelem;
+ fprintf(fp, elemfmt[nelem], label[j], v.x, v.y, v.z, v.w);
+ }
+ }
+ fputc('\n', fp);
+ }
+
+ if(is_indexed()) {
+ const unsigned int *idx = get_index_data();
+ int numidx = get_index_count();
+ int numtri = numidx / 3;
+ assert(numidx % 3 == 0);
+
+ fprintf(fp, "FACES\n");
+
+ for(int i=0; i<numtri; i++) {
+ fprintf(fp, "%5d: %d %d %d\n", i, idx[0], idx[1], idx[2]);
+ idx += 3;
+ }
+ }
+ return true;
+}
+
+bool Mesh::dump_obj(const char *fname) const
+{
+ FILE *fp = fopen(fname, "wb");
+ if(fp) {
+ bool res = dump_obj(fp);
+ fclose(fp);
+ return res;
+ }
+ return false;
+}
+
+bool Mesh::dump_obj(FILE *fp) const
+{
+ if(!has_attrib(MESH_ATTR_VERTEX)) {
+ return false;
+ }
+
+ for(int i=0; i<(int)nverts; i++) {
+ Vec4 v = get_attrib(MESH_ATTR_VERTEX, i);
+ fprintf(fp, "v %g %g %g\n", v.x, v.y, v.z);
+ }
+
+ if(has_attrib(MESH_ATTR_NORMAL)) {
+ for(int i=0; i<(int)nverts; i++) {
+ Vec4 v = get_attrib(MESH_ATTR_NORMAL, i);
+ fprintf(fp, "vn %g %g %g\n", v.x, v.y, v.z);
+ }
+ }
+
+ if(has_attrib(MESH_ATTR_TEXCOORD)) {
+ for(int i=0; i<(int)nverts; i++) {
+ Vec4 v = get_attrib(MESH_ATTR_TEXCOORD, i);
+ fprintf(fp, "vt %g %g\n", v.x, v.y);
+ }
+ }
+
+ if(is_indexed()) {
+ const unsigned int *idxptr = get_index_data();
+ int numidx = get_index_count();
+ int numtri = numidx / 3;
+ assert(numidx % 3 == 0);
+
+ for(int i=0; i<numtri; i++) {
+ fputc('f', fp);
+ for(int j=0; j<3; j++) {
+ unsigned int idx = *idxptr++ + 1;
+ fprintf(fp, " %u/%u/%u", idx, idx, idx);
+ }
+ fputc('\n', fp);
+ }
+ } else {
+ int numtri = nverts / 3;
+ unsigned int idx = 1;
+ for(int i=0; i<numtri; i++) {
+ fputc('f', fp);
+ for(int j=0; j<3; j++) {
+ fprintf(fp, " %u/%u/%u", idx, idx, idx);
+ ++idx;
+ }
+ fputc('\n', fp);
+ }
+ }
+ return true;
+}
+
+// ------ 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 = Vec3(FLT_MAX, FLT_MAX, FLT_MAX);
+ aabb.max = -aabb.min;
+
+ for(unsigned int i=0; i<nverts; i++) {
+ Vec4 v = get_attrib(MESH_ATTR_VERTEX, i);
+ for(int j=0; j<3; j++) {
+ if(v[j] < aabb.min[j]) {
+ aabb.min[j] = v[j];
+ }
+ if(v[j] > 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;
+ }
+
+ Vec3 v;
+ bsph.center = Vec3(0, 0, 0);
+
+ // first find the center
+ for(unsigned int i=0; i<nverts; i++) {
+ v = Vec3(get_attrib(MESH_ATTR_VERTEX, i));
+ bsph.center += v;
+ }
+ bsph.center /= (float)nverts;
+
+ bsph.radius = 0.0f;
+ for(unsigned int i=0; i<nverts; i++) {
+ v = Vec3(get_attrib(MESH_ATTR_VERTEX, i));
+ float dist_sq = length_sq(v - bsph.center);
+ if(dist_sq > bsph.radius) {
+ bsph.radius = dist_sq;
+ }
+ }
+ bsph.radius = sqrt(bsph.radius);
+
+ bsph_valid = true;
+}
+
+void Mesh::update_buffers()
+{
+ for(int i=0; i<NUM_MESH_ATTR; i++) {
+ if(has_attrib(i) && !vattr[i].vbo_valid) {
+ glBindBuffer(GL_ARRAY_BUFFER, vattr[i].vbo);
+ glBufferData(GL_ARRAY_BUFFER, nverts * vattr[i].nelem * sizeof(float), &vattr[i].data[0], GL_STATIC_DRAW);
+ vattr[i].vbo_valid = true;
+ }
+ }
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+ if(idata_valid && !ibo_valid) {
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, nfaces * 3 * sizeof(unsigned int), &idata[0], GL_STATIC_DRAW);
+ ibo_valid = true;
+ }
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+}
+
+void Mesh::update_wire_ibo()
+{
+ update_buffers();
+
+ if(wire_ibo_valid) {
+ return;
+ }
+
+ if(!wire_ibo) {
+ glGenBuffers(1, &wire_ibo);
+ }
+
+ int num_faces = get_poly_count();
+
+ unsigned int *wire_idxarr = new unsigned int[num_faces * 6];
+ unsigned int *dest = wire_idxarr;
+
+ if(ibo_valid) {
+ // we're dealing with an indexed mesh
+ const unsigned int *idxarr = ((const Mesh*)this)->get_index_data();
+
+ for(int i=0; i<num_faces; i++) {
+ *dest++ = idxarr[0];
+ *dest++ = idxarr[1];
+ *dest++ = idxarr[1];
+ *dest++ = idxarr[2];
+ *dest++ = idxarr[2];
+ *dest++ = idxarr[0];
+ idxarr += 3;
+ }
+ } else {
+ // not an indexed mesh ...
+ for(int i=0; i<num_faces; i++) {
+ int vidx = i * 3;
+ *dest++ = vidx;
+ *dest++ = vidx + 1;
+ *dest++ = vidx + 1;
+ *dest++ = vidx + 2;
+ *dest++ = vidx + 2;
+ *dest++ = vidx;
+ }
+ }
+
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, wire_ibo);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, num_faces * 6 * sizeof(unsigned int), wire_idxarr, GL_STATIC_DRAW);
+ delete [] wire_idxarr;
+ wire_ibo_valid = true;
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+}
+
+
+// ------ class Triangle ------
+Triangle::Triangle()
+{
+ normal_valid = false;
+ id = -1;
+}
+
+Triangle::Triangle(const Vec3 &v0, const Vec3 &v1, const Vec3 &v2)
+{
+ v[0] = v0;
+ v[1] = v1;
+ v[2] = v2;
+ normal_valid = false;
+ id = -1;
+}
+
+Triangle::Triangle(int n, const Vec3 *varr, const unsigned int *idxarr)
+{
+ if(idxarr) {
+ v[0] = varr[idxarr[n * 3]];
+ v[1] = varr[idxarr[n * 3 + 1]];
+ v[2] = varr[idxarr[n * 3 + 2]];
+ } else {
+ v[0] = varr[n * 3];
+ v[1] = varr[n * 3 + 1];
+ v[2] = varr[n * 3 + 2];
+ }
+ normal_valid = false;
+ id = n;
+}
+
+void Triangle::calc_normal()
+{
+ normal = normalize(cross(v[1] - v[0], v[2] - v[0]));
+ normal_valid = true;
+}
+
+const Vec3 &Triangle::get_normal() const
+{
+ if(!normal_valid) {
+ ((Triangle*)this)->calc_normal();
+ }
+ return normal;
+}
+
+void Triangle::transform(const Mat4 &xform)
+{
+ v[0] = xform * v[0];
+ v[1] = xform * v[1];
+ v[2] = xform * v[2];
+ normal_valid = false;
+}
+
+void Triangle::draw() const
+{
+ Vec3 n[3];
+ n[0] = n[1] = 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);
+}
+
+Vec3 Triangle::calc_barycentric(const Vec3 &pos) const
+{
+ Vec3 norm = get_normal();
+
+ float area_sq = fabs(dot(cross(v[1] - v[0], v[2] - v[0]), norm));
+ if(area_sq < 1e-5) {
+ return Vec3(0, 0, 0);
+ }
+
+ float asq0 = fabs(dot(cross(v[1] - pos, v[2] - pos), norm));
+ float asq1 = fabs(dot(cross(v[2] - pos, v[0] - pos), norm));
+ float asq2 = fabs(dot(cross(v[0] - pos, v[1] - pos), norm));
+
+ return Vec3(asq0 / area_sq, asq1 / area_sq, asq2 / area_sq);
+}
+
+bool Triangle::intersect(const Ray &ray, HitPoint *hit) const
+{
+ Vec3 normal = get_normal();
+
+ float ndotdir = dot(ray.dir, normal);
+ if(fabs(ndotdir) < 1e-4) {
+ return false;
+ }
+
+ Vec3 vertdir = v[0] - ray.origin;
+ float t = dot(normal, vertdir) / ndotdir;
+
+ Vec3 pos = ray.origin + ray.dir * t;
+ Vec3 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->obj = this;
+ }
+ return true;
+}
--- /dev/null
+#ifndef MESH_H_
+#define MESH_H_
+
+#include <stdio.h>
+#include <string>
+#include <vector>
+#include "gmath/gmath.h"
+#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 XFormNode;
+
+
+class Triangle {
+public:
+ Vec3 v[3];
+ Vec3 normal;
+ bool normal_valid;
+ int id;
+
+ Triangle();
+ Triangle(const Vec3 &v0, const Vec3 &v1, const Vec3 &v2);
+ Triangle(int n, const Vec3 *varr, const unsigned int *idxarr = 0);
+
+ /// calculate normal (quite expensive)
+ void calc_normal();
+ const Vec3 &get_normal() const;
+
+ void transform(const Mat4 &xform);
+
+ void draw() const;
+ void draw_wire() const;
+
+ /// calculate barycentric coordinates of a point
+ Vec3 calc_barycentric(const Vec3 &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.
+ Vec4 cur_val[NUM_MESH_ATTR];
+
+ unsigned int buffer_objects[NUM_MESH_ATTR + 1];
+
+ // vertex attribute data and buffer objects
+ struct VertexAttrib {
+ int nelem; // number of elements per attribute range: [1, 4]
+ std::vector<float> 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<XFormNode*> bones; // bones affecting this mesh
+
+ // index data and buffer object
+ std::vector<unsigned int> 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 Vec3 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();
+
+ mutable int cur_sdr;
+ bool pre_draw() const;
+ void post_draw() const;
+
+
+public:
+ static bool use_custom_sdr_attr;
+
+ Mesh();
+ ~Mesh();
+
+ Mesh(const Mesh &rhs);
+ Mesh &operator =(const Mesh &rhs);
+ bool clone(const Mesh &m);
+
+ void set_name(const char *name);
+ const char *get_name() const;
+
+ bool has_attrib(int attr) const;
+ bool is_indexed() 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 Vec4 &v); // invalidates vbo
+ Vec4 get_attrib(int attrib, int idx) const;
+
+ int get_attrib_count(int attrib) 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;
+
+ int get_index_count() 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);
+
+ int get_poly_count() const;
+
+ /* apply a transformation to the vertices and its inverse-transpose
+ * to the normals and tangents.
+ */
+ void apply_xform(const Mat4 &xform);
+ void apply_xform(const Mat4 &xform, const Mat4 &dir_xform);
+
+ void flip(); // both faces and normals
+ void flip_faces();
+ void flip_normals();
+
+ void explode(); // undo all vertex sharing
+
+ void calc_face_normals(); // this is only guaranteed to work on an exploded mesh
+
+ // 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(Vec3 *vmin, Vec3 *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(Vec3 *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;
+
+ // texture coordinate manipulation
+ void texcoord_apply_xform(const Mat4 &xform);
+ void texcoord_gen_plane(const Vec3 &norm, const Vec3 &tang);
+ void texcoord_gen_box();
+ void texcoord_gen_cylinder();
+
+ bool dump(const char *fname) const;
+ bool dump(FILE *fp) const;
+ bool dump_obj(const char *fname) const;
+ bool dump_obj(FILE *fp) const;
+};
+
+#endif // MESH_H_
--- /dev/null
+#include <stdio.h>
+#include "meshgen.h"
+#include "mesh.h"
+
+// -------- sphere --------
+
+#define SURAD(u) ((u) * 2.0 * M_PI)
+#define SVRAD(v) ((v) * M_PI)
+
+static Vec3 sphvec(float theta, float phi)
+{
+ return Vec3(sin(theta) * sin(phi),
+ cos(phi),
+ cos(theta) * sin(phi));
+}
+
+void gen_sphere(Mesh *mesh, float rad, int usub, int vsub, float urange, float vrange)
+{
+ if(urange == 0.0 || vrange == 0.0) return;
+
+ if(usub < 4) usub = 4;
+ if(vsub < 2) vsub = 2;
+
+ int uverts = usub + 1;
+ int vverts = vsub + 1;
+
+ int num_verts = uverts * vverts;
+ int num_quads = usub * vsub;
+ int num_tri = num_quads * 2;
+
+ mesh->clear();
+ Vec3 *varr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_VERTEX, 3, num_verts, 0);
+ Vec3 *narr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_NORMAL, 3, num_verts, 0);
+ Vec3 *tarr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_TANGENT, 3, num_verts, 0);
+ Vec2 *uvarr = (Vec2*)mesh->set_attrib_data(MESH_ATTR_TEXCOORD, 2, num_verts, 0);
+ unsigned int *idxarr = mesh->set_index_data(num_tri * 3, 0);
+
+ float du = urange / (float)(uverts - 1);
+ float dv = vrange / (float)(vverts - 1);
+
+ float u = 0.0;
+ for(int i=0; i<uverts; i++) {
+ float theta = u * 2.0 * M_PI;
+
+ float v = 0.0;
+ for(int j=0; j<vverts; j++) {
+ float phi = v * M_PI;
+
+ Vec3 pos = sphvec(theta, phi);
+
+ *varr++ = pos * rad;
+ *narr++ = pos;
+ *tarr++ = normalize(sphvec(theta + 0.1f, (float)M_PI / 2.0f) - sphvec(theta - 0.1f, (float)M_PI / 2.0f));
+ *uvarr++ = Vec2(u / urange, v / vrange);
+
+ if(i < usub && j < vsub) {
+ int idx = i * vverts + j;
+ *idxarr++ = idx;
+ *idxarr++ = idx + 1;
+ *idxarr++ = idx + vverts + 1;
+
+ *idxarr++ = idx;
+ *idxarr++ = idx + vverts + 1;
+ *idxarr++ = idx + vverts;
+ }
+
+ v += dv;
+ }
+ u += du;
+ }
+}
+
+// ------ geosphere ------
+#define PHI 1.618034
+
+static Vec3 icosa_pt[] = {
+ Vec3(PHI, 1, 0),
+ Vec3(-PHI, 1, 0),
+ Vec3(PHI, -1, 0),
+ Vec3(-PHI, -1, 0),
+ Vec3(1, 0, PHI),
+ Vec3(1, 0, -PHI),
+ Vec3(-1, 0, PHI),
+ Vec3(-1, 0, -PHI),
+ Vec3(0, PHI, 1),
+ Vec3(0, -PHI, 1),
+ Vec3(0, PHI, -1),
+ Vec3(0, -PHI, -1)
+};
+enum { P11, P12, P13, P14, P21, P22, P23, P24, P31, P32, P33, P34 };
+static int icosa_idx[] = {
+ P11, P31, P21,
+ P11, P22, P33,
+ P13, P21, P32,
+ P13, P34, P22,
+ P12, P23, P31,
+ P12, P33, P24,
+ P14, P32, P23,
+ P14, P24, P34,
+
+ P11, P33, P31,
+ P12, P31, P33,
+ P13, P32, P34,
+ P14, P34, P32,
+
+ P21, P13, P11,
+ P22, P11, P13,
+ P23, P12, P14,
+ P24, P14, P12,
+
+ P31, P23, P21,
+ P32, P21, P23,
+ P33, P22, P24,
+ P34, P24, P22
+};
+
+static void geosphere(std::vector<Vec3> *verts, const Vec3 &v1, const Vec3 &v2, const Vec3 &v3, int iter)
+{
+ if(!iter) {
+ verts->push_back(v1);
+ verts->push_back(v2);
+ verts->push_back(v3);
+ return;
+ }
+
+ Vec3 v12 = normalize(v1 + v2);
+ Vec3 v23 = normalize(v2 + v3);
+ Vec3 v31 = normalize(v3 + v1);
+
+ geosphere(verts, v1, v12, v31, iter - 1);
+ geosphere(verts, v2, v23, v12, iter - 1);
+ geosphere(verts, v3, v31, v23, iter - 1);
+ geosphere(verts, v12, v23, v31, iter - 1);
+}
+
+void gen_geosphere(Mesh *mesh, float rad, int subdiv, bool hemi)
+{
+ int num_tri = (sizeof icosa_idx / sizeof *icosa_idx) / 3;
+
+ std::vector<Vec3> verts;
+ for(int i=0; i<num_tri; i++) {
+ Vec3 v[3];
+
+ for(int j=0; j<3; j++) {
+ int vidx = icosa_idx[i * 3 + j];
+ v[j] = normalize(icosa_pt[vidx]);
+ }
+
+ if(hemi && (v[0].y < 0.0 || v[1].y < 0.0 || v[2].y < 0.0)) {
+ continue;
+ }
+
+ geosphere(&verts, v[0], v[1], v[2], subdiv);
+ }
+
+ int num_verts = (int)verts.size();
+
+ mesh->clear();
+ Vec3 *varr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_VERTEX, 3, num_verts, 0);
+ Vec3 *narr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_NORMAL, 3, num_verts, 0);
+ Vec3 *tarr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_TANGENT, 3, num_verts, 0);
+ Vec2 *uvarr = (Vec2*)mesh->set_attrib_data(MESH_ATTR_TEXCOORD, 2, num_verts, 0);
+
+ for(int i=0; i<num_verts; i++) {
+ *varr++ = verts[i] * rad;
+ *narr++ = verts[i];
+
+ float theta = atan2(verts[i].z, verts[i].x);
+ float phi = acos(verts[i].y);
+
+ *tarr++ = normalize(sphvec(theta + 0.1f, (float)M_PI / 2.0f) - sphvec(theta - 0.1f, (float)M_PI / 2.0f));
+
+ float u = 0.5 * theta / M_PI + 0.5;
+ float v = phi / M_PI;
+ *uvarr++ = Vec2(u, v);
+ }
+}
+
+// -------- torus -----------
+static Vec3 torusvec(float theta, float phi, float mr, float rr)
+{
+ theta = -theta;
+
+ float rx = -cos(phi) * rr + mr;
+ float ry = sin(phi) * rr;
+ float rz = 0.0;
+
+ float x = rx * sin(theta) + rz * cos(theta);
+ float y = ry;
+ float z = -rx * cos(theta) + rz * sin(theta);
+
+ return Vec3(x, y, z);
+}
+
+void gen_torus(Mesh *mesh, float mainrad, float ringrad, int usub, int vsub, float urange, float vrange)
+{
+ if(usub < 4) usub = 4;
+ if(vsub < 2) vsub = 2;
+
+ int uverts = usub + 1;
+ int vverts = vsub + 1;
+
+ int num_verts = uverts * vverts;
+ int num_quads = usub * vsub;
+ int num_tri = num_quads * 2;
+
+ mesh->clear();
+ Vec3 *varr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_VERTEX, 3, num_verts, 0);
+ Vec3 *narr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_NORMAL, 3, num_verts, 0);
+ Vec3 *tarr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_TANGENT, 3, num_verts, 0);
+ Vec2 *uvarr = (Vec2*)mesh->set_attrib_data(MESH_ATTR_TEXCOORD, 2, num_verts, 0);
+ unsigned int *idxarr = mesh->set_index_data(num_tri * 3, 0);
+
+ float du = urange / (float)(uverts - 1);
+ float dv = vrange / (float)(vverts - 1);
+
+ float u = 0.0;
+ for(int i=0; i<uverts; i++) {
+ float theta = u * 2.0 * M_PI;
+
+ float v = 0.0;
+ for(int j=0; j<vverts; j++) {
+ float phi = v * 2.0 * M_PI;
+
+ Vec3 pos = torusvec(theta, phi, mainrad, ringrad);
+ Vec3 cent = torusvec(theta, phi, mainrad, 0.0);
+
+ *varr++ = pos;
+ *narr++ = (pos - cent) / ringrad;
+
+ Vec3 pprev = torusvec(theta - 0.1f, phi, mainrad, ringrad);
+ Vec3 pnext = torusvec(theta + 0.1f, phi, mainrad, ringrad);
+
+ *tarr++ = normalize(pnext - pprev);
+ *uvarr++ = Vec2(u * urange, v * vrange);
+
+ if(i < usub && j < vsub) {
+ int idx = i * vverts + j;
+ *idxarr++ = idx;
+ *idxarr++ = idx + 1;
+ *idxarr++ = idx + vverts + 1;
+
+ *idxarr++ = idx;
+ *idxarr++ = idx + vverts + 1;
+ *idxarr++ = idx + vverts;
+ }
+
+ v += dv;
+ }
+ u += du;
+ }
+}
+
+
+// -------- cylinder --------
+
+static Vec3 cylvec(float theta, float height)
+{
+ return Vec3(sin(theta), height, cos(theta));
+}
+
+void gen_cylinder(Mesh *mesh, float rad, float height, int usub, int vsub, int capsub, float urange, float vrange)
+{
+ if(usub < 4) usub = 4;
+ if(vsub < 1) vsub = 1;
+
+ int uverts = usub + 1;
+ int vverts = vsub + 1;
+
+ int num_body_verts = uverts * vverts;
+ int num_body_quads = usub * vsub;
+ int num_body_tri = num_body_quads * 2;
+
+ int capvverts = capsub ? capsub + 1 : 0;
+ int num_cap_verts = uverts * capvverts;
+ int num_cap_quads = usub * capsub;
+ int num_cap_tri = num_cap_quads * 2;
+
+ int num_verts = num_body_verts + num_cap_verts * 2;
+ int num_tri = num_body_tri + num_cap_tri * 2;
+
+ mesh->clear();
+ Vec3 *varr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_VERTEX, 3, num_verts, 0);
+ Vec3 *narr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_NORMAL, 3, num_verts, 0);
+ Vec3 *tarr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_TANGENT, 3, num_verts, 0);
+ Vec2 *uvarr = (Vec2*)mesh->set_attrib_data(MESH_ATTR_TEXCOORD, 2, num_verts, 0);
+ unsigned int *idxarr = mesh->set_index_data(num_tri * 3, 0);
+
+ float du = urange / (float)(uverts - 1);
+ float dv = vrange / (float)(vverts - 1);
+
+ float u = 0.0;
+ for(int i=0; i<uverts; i++) {
+ float theta = SURAD(u);
+
+ float v = 0.0;
+ for(int j=0; j<vverts; j++) {
+ float y = (v - 0.5) * height;
+ Vec3 pos = cylvec(theta, y);
+
+ *varr++ = Vec3(pos.x * rad, pos.y, pos.z * rad);
+ *narr++ = Vec3(pos.x, 0.0, pos.z);
+ *tarr++ = normalize(cylvec(theta + 0.1, 0.0) - cylvec(theta - 0.1, 0.0));
+ *uvarr++ = Vec2(u * urange, v * vrange);
+
+ if(i < usub && j < vsub) {
+ int idx = i * vverts + j;
+
+ *idxarr++ = idx;
+ *idxarr++ = idx + vverts + 1;
+ *idxarr++ = idx + 1;
+
+ *idxarr++ = idx;
+ *idxarr++ = idx + vverts;
+ *idxarr++ = idx + vverts + 1;
+ }
+
+ v += dv;
+ }
+ u += du;
+ }
+
+
+ // now the cap!
+ if(!capsub) {
+ return;
+ }
+
+ dv = 1.0 / (float)(capvverts - 1);
+
+ u = 0.0;
+ for(int i=0; i<uverts; i++) {
+ float theta = SURAD(u);
+
+ float v = 0.0;
+ for(int j=0; j<capvverts; j++) {
+ float r = v * rad;
+
+ Vec3 pos = cylvec(theta, height / 2.0) * r;
+ pos.y = height / 2.0;
+ Vec3 tang = normalize(cylvec(theta + 0.1, 0.0) - cylvec(theta - 0.1, 0.0));
+
+ *varr++ = pos;
+ *narr++ = Vec3(0, 1, 0);
+ *tarr++ = tang;
+ *uvarr++ = Vec2(u * urange, v);
+
+ pos.y = -height / 2.0;
+ *varr++ = pos;
+ *narr++ = Vec3(0, -1, 0);
+ *tarr++ = -tang;
+ *uvarr++ = Vec2(u * urange, v);
+
+ if(i < usub && j < capsub) {
+ unsigned int idx = num_body_verts + (i * capvverts + j) * 2;
+
+ unsigned int vidx[4] = {
+ idx,
+ idx + capvverts * 2,
+ idx + (capvverts + 1) * 2,
+ idx + 2
+ };
+
+ *idxarr++ = vidx[0];
+ *idxarr++ = vidx[2];
+ *idxarr++ = vidx[1];
+ *idxarr++ = vidx[0];
+ *idxarr++ = vidx[3];
+ *idxarr++ = vidx[2];
+
+ *idxarr++ = vidx[0] + 1;
+ *idxarr++ = vidx[1] + 1;
+ *idxarr++ = vidx[2] + 1;
+ *idxarr++ = vidx[0] + 1;
+ *idxarr++ = vidx[2] + 1;
+ *idxarr++ = vidx[3] + 1;
+ }
+
+ v += dv;
+ }
+ u += du;
+ }
+}
+
+// -------- cone --------
+
+static Vec3 conevec(float theta, float y, float height)
+{
+ float scale = 1.0 - y / height;
+ return Vec3(sin(theta) * scale, y, cos(theta) * scale);
+}
+
+void gen_cone(Mesh *mesh, float rad, float height, int usub, int vsub, int capsub, float urange, float vrange)
+{
+ if(usub < 4) usub = 4;
+ if(vsub < 1) vsub = 1;
+
+ int uverts = usub + 1;
+ int vverts = vsub + 1;
+
+ int num_body_verts = uverts * vverts;
+ int num_body_quads = usub * vsub;
+ int num_body_tri = num_body_quads * 2;
+
+ int capvverts = capsub ? capsub + 1 : 0;
+ int num_cap_verts = uverts * capvverts;
+ int num_cap_quads = usub * capsub;
+ int num_cap_tri = num_cap_quads * 2;
+
+ int num_verts = num_body_verts + num_cap_verts;
+ int num_tri = num_body_tri + num_cap_tri;
+
+ mesh->clear();
+ Vec3 *varr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_VERTEX, 3, num_verts, 0);
+ Vec3 *narr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_NORMAL, 3, num_verts, 0);
+ Vec3 *tarr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_TANGENT, 3, num_verts, 0);
+ Vec2 *uvarr = (Vec2*)mesh->set_attrib_data(MESH_ATTR_TEXCOORD, 2, num_verts, 0);
+ unsigned int *idxarr = mesh->set_index_data(num_tri * 3, 0);
+
+ float du = urange / (float)(uverts - 1);
+ float dv = vrange / (float)(vverts - 1);
+
+ float u = 0.0;
+ for(int i=0; i<uverts; i++) {
+ float theta = SURAD(u);
+
+ float v = 0.0;
+ for(int j=0; j<vverts; j++) {
+ float y = v * height;
+ Vec3 pos = conevec(theta, y, height);
+
+ Vec3 tang = normalize(conevec(theta + 0.1, 0.0, height) - conevec(theta - 0.1, 0.0, height));
+ Vec3 bitang = normalize(conevec(theta, y + 0.1, height) - pos);
+
+ *varr++ = Vec3(pos.x * rad, pos.y, pos.z * rad);
+ *narr++ = cross(tang, bitang);
+ *tarr++ = tang;
+ *uvarr++ = Vec2(u * urange, v * vrange);
+
+ if(i < usub && j < vsub) {
+ int idx = i * vverts + j;
+
+ *idxarr++ = idx;
+ *idxarr++ = idx + vverts + 1;
+ *idxarr++ = idx + 1;
+
+ *idxarr++ = idx;
+ *idxarr++ = idx + vverts;
+ *idxarr++ = idx + vverts + 1;
+ }
+
+ v += dv;
+ }
+ u += du;
+ }
+
+
+ // now the bottom cap!
+ if(!capsub) {
+ return;
+ }
+
+ dv = 1.0 / (float)(capvverts - 1);
+
+ u = 0.0;
+ for(int i=0; i<uverts; i++) {
+ float theta = SURAD(u);
+
+ float v = 0.0;
+ for(int j=0; j<capvverts; j++) {
+ float r = v * rad;
+
+ Vec3 pos = conevec(theta, 0.0, height) * r;
+ Vec3 tang = normalize(cylvec(theta + 0.1, 0.0) - cylvec(theta - 0.1, 0.0));
+
+ *varr++ = pos;
+ *narr++ = Vec3(0, -1, 0);
+ *tarr++ = tang;
+ *uvarr++ = Vec2(u * urange, v);
+
+ if(i < usub && j < capsub) {
+ unsigned int idx = num_body_verts + i * capvverts + j;
+
+ unsigned int vidx[4] = {
+ idx,
+ idx + capvverts,
+ idx + (capvverts + 1),
+ idx + 1
+ };
+
+ *idxarr++ = vidx[0];
+ *idxarr++ = vidx[1];
+ *idxarr++ = vidx[2];
+ *idxarr++ = vidx[0];
+ *idxarr++ = vidx[2];
+ *idxarr++ = vidx[3];
+ }
+
+ v += dv;
+ }
+ u += du;
+ }
+}
+
+
+// -------- plane --------
+
+void gen_plane(Mesh *mesh, float width, float height, int usub, int vsub)
+{
+ gen_heightmap(mesh, width, height, usub, vsub, 0);
+}
+
+
+// ----- heightmap ------
+
+void gen_heightmap(Mesh *mesh, float width, float height, int usub, int vsub, float (*hf)(float, float, void*), void *hfdata)
+{
+ if(usub < 1) usub = 1;
+ if(vsub < 1) vsub = 1;
+
+ mesh->clear();
+
+ int uverts = usub + 1;
+ int vverts = vsub + 1;
+ int num_verts = uverts * vverts;
+
+ int num_quads = usub * vsub;
+ int num_tri = num_quads * 2;
+
+ Vec3 *varr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_VERTEX, 3, num_verts, 0);
+ Vec3 *narr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_NORMAL, 3, num_verts, 0);
+ Vec3 *tarr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_TANGENT, 3, num_verts, 0);
+ Vec2 *uvarr = (Vec2*)mesh->set_attrib_data(MESH_ATTR_TEXCOORD, 2, num_verts, 0);
+ unsigned int *idxarr = mesh->set_index_data(num_tri * 3, 0);
+
+ float du = 1.0 / (float)usub;
+ float dv = 1.0 / (float)vsub;
+
+ float u = 0.0;
+ for(int i=0; i<uverts; i++) {
+ float v = 0.0;
+ for(int j=0; j<vverts; j++) {
+ float x = (u - 0.5) * width;
+ float y = (v - 0.5) * height;
+ float z = hf ? hf(u, v, hfdata) : 0.0;
+
+ Vec3 normal = Vec3(0, 0, 1);
+ if(hf) {
+ float u1z = hf(u + du, v, hfdata);
+ float v1z = hf(u, v + dv, hfdata);
+
+ Vec3 tang = Vec3(du * width, 0, u1z - z);
+ Vec3 bitan = Vec3(0, dv * height, v1z - z);
+ normal = normalize(cross(tang, bitan));
+ }
+
+ *varr++ = Vec3(x, y, z);
+ *narr++ = normal;
+ *tarr++ = Vec3(1, 0, 0);
+ *uvarr++ = Vec2(u, v);
+
+ if(i < usub && j < vsub) {
+ int idx = i * vverts + j;
+
+ *idxarr++ = idx;
+ *idxarr++ = idx + vverts + 1;
+ *idxarr++ = idx + 1;
+
+ *idxarr++ = idx;
+ *idxarr++ = idx + vverts;
+ *idxarr++ = idx + vverts + 1;
+ }
+
+ v += dv;
+ }
+ u += du;
+ }
+}
+
+// ----- box ------
+void gen_box(Mesh *mesh, float xsz, float ysz, float zsz, int usub, int vsub)
+{
+ static const float face_angles[][2] = {
+ {0, 0},
+ {M_PI / 2.0, 0},
+ {M_PI, 0},
+ {3.0 * M_PI / 2.0, 0},
+ {0, M_PI / 2.0},
+ {0, -M_PI / 2.0}
+ };
+
+ if(usub < 1) usub = 1;
+ if(vsub < 1) vsub = 1;
+
+ mesh->clear();
+
+ for(int i=0; i<6; i++) {
+ Mat4 xform, dir_xform;
+ Mesh m;
+
+ gen_plane(&m, 1, 1, usub, vsub);
+ xform.translate(Vec3(0, 0, 0.5));
+ xform.rotate(Vec3(face_angles[i][1], face_angles[i][0], 0));
+ dir_xform = xform;
+ m.apply_xform(xform, dir_xform);
+
+ mesh->append(m);
+ }
+
+ Mat4 scale;
+ scale.scaling(xsz, ysz, zsz);
+ mesh->apply_xform(scale, Mat4::identity);
+}
+
+/*
+void gen_box(Mesh *mesh, float xsz, float ysz, float zsz)
+{
+ mesh->clear();
+
+ const int num_faces = 6;
+ int num_verts = num_faces * 4;
+ int num_tri = num_faces * 2;
+
+ float x = xsz / 2.0;
+ float y = ysz / 2.0;
+ float z = zsz / 2.0;
+
+ Vec3 *varr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_VERTEX, 3, num_verts, 0);
+ Vec3 *narr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_NORMAL, 3, num_verts, 0);
+ Vec3 *tarr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_TANGENT, 3, num_verts, 0);
+ Vec2 *uvarr = (Vec2*)mesh->set_attrib_data(MESH_ATTR_TEXCOORD, 2, num_verts, 0);
+ unsigned int *idxarr = mesh->set_index_data(num_tri * 3, 0);
+
+ static const Vec2 uv[] = { Vec2(0, 0), Vec2(1, 0), Vec2(1, 1), Vec2(0, 1) };
+
+ // front
+ for(int i=0; i<4; i++) {
+ *narr++ = Vec3(0, 0, 1);
+ *tarr++ = Vec3(1, 0, 0);
+ *uvarr++ = uv[i];
+ }
+ *varr++ = Vec3(-x, -y, z);
+ *varr++ = Vec3(x, -y, z);
+ *varr++ = Vec3(x, y, z);
+ *varr++ = Vec3(-x, y, z);
+ // right
+ for(int i=0; i<4; i++) {
+ *narr++ = Vec3(1, 0, 0);
+ *tarr++ = Vec3(0, 0, -1);
+ *uvarr++ = uv[i];
+ }
+ *varr++ = Vec3(x, -y, z);
+ *varr++ = Vec3(x, -y, -z);
+ *varr++ = Vec3(x, y, -z);
+ *varr++ = Vec3(x, y, z);
+ // back
+ for(int i=0; i<4; i++) {
+ *narr++ = Vec3(0, 0, -1);
+ *tarr++ = Vec3(-1, 0, 0);
+ *uvarr++ = uv[i];
+ }
+ *varr++ = Vec3(x, -y, -z);
+ *varr++ = Vec3(-x, -y, -z);
+ *varr++ = Vec3(-x, y, -z);
+ *varr++ = Vec3(x, y, -z);
+ // left
+ for(int i=0; i<4; i++) {
+ *narr++ = Vec3(-1, 0, 0);
+ *tarr++ = Vec3(0, 0, 1);
+ *uvarr++ = uv[i];
+ }
+ *varr++ = Vec3(-x, -y, -z);
+ *varr++ = Vec3(-x, -y, z);
+ *varr++ = Vec3(-x, y, z);
+ *varr++ = Vec3(-x, y, -z);
+ // top
+ for(int i=0; i<4; i++) {
+ *narr++ = Vec3(0, 1, 0);
+ *tarr++ = Vec3(1, 0, 0);
+ *uvarr++ = uv[i];
+ }
+ *varr++ = Vec3(-x, y, z);
+ *varr++ = Vec3(x, y, z);
+ *varr++ = Vec3(x, y, -z);
+ *varr++ = Vec3(-x, y, -z);
+ // bottom
+ for(int i=0; i<4; i++) {
+ *narr++ = Vec3(0, -1, 0);
+ *tarr++ = Vec3(1, 0, 0);
+ *uvarr++ = uv[i];
+ }
+ *varr++ = Vec3(-x, -y, -z);
+ *varr++ = Vec3(x, -y, -z);
+ *varr++ = Vec3(x, -y, z);
+ *varr++ = Vec3(-x, -y, z);
+
+ // index array
+ static const int faceidx[] = {0, 1, 2, 0, 2, 3};
+ for(int i=0; i<num_faces; i++) {
+ for(int j=0; j<6; j++) {
+ *idxarr++ = faceidx[j] + i * 4;
+ }
+ }
+}
+*/
+
+static inline Vec3 rev_vert(float u, float v, Vec2 (*rf)(float, float, void*), void *cls)
+{
+ Vec2 pos = rf(u, v, cls);
+
+ float angle = u * 2.0 * M_PI;
+ float x = pos.x * cos(angle);
+ float y = pos.y;
+ float z = pos.x * sin(angle);
+
+ return Vec3(x, y, z);
+}
+
+// ------ surface of revolution -------
+void gen_revol(Mesh *mesh, int usub, int vsub, Vec2 (*rfunc)(float, float, void*), void *cls)
+{
+ gen_revol(mesh, usub, vsub, rfunc, 0, cls);
+}
+
+void gen_revol(Mesh *mesh, int usub, int vsub, Vec2 (*rfunc)(float, float, void*),
+ Vec2 (*nfunc)(float, float, void*), void *cls)
+{
+ if(!rfunc) return;
+ if(usub < 3) usub = 3;
+ if(vsub < 1) vsub = 1;
+
+ mesh->clear();
+
+ int uverts = usub + 1;
+ int vverts = vsub + 1;
+ int num_verts = uverts * vverts;
+
+ int num_quads = usub * vsub;
+ int num_tri = num_quads * 2;
+
+ Vec3 *varr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_VERTEX, 3, num_verts, 0);
+ Vec3 *narr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_NORMAL, 3, num_verts, 0);
+ Vec3 *tarr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_TANGENT, 3, num_verts, 0);
+ Vec2 *uvarr = (Vec2*)mesh->set_attrib_data(MESH_ATTR_TEXCOORD, 2, num_verts, 0);
+ unsigned int *idxarr = mesh->set_index_data(num_tri * 3, 0);
+
+ float du = 1.0 / (float)(uverts - 1);
+ float dv = 1.0 / (float)(vverts - 1);
+
+ float u = 0.0;
+ for(int i=0; i<uverts; i++) {
+ float v = 0.0;
+ for(int j=0; j<vverts; j++) {
+ Vec3 pos = rev_vert(u, v, rfunc, cls);
+
+ Vec3 nextu = rev_vert(fmod(u + du, 1.0), v, rfunc, cls);
+ Vec3 tang = nextu - pos;
+ if(length_sq(tang) < 1e-6) {
+ float new_v = v > 0.5 ? v - dv * 0.25 : v + dv * 0.25;
+ nextu = rev_vert(fmod(u + du, 1.0), new_v, rfunc, cls);
+ tang = nextu - pos;
+ }
+
+ Vec3 normal;
+ if(nfunc) {
+ normal = rev_vert(u, v, nfunc, cls);
+ } else {
+ Vec3 nextv = rev_vert(u, v + dv, rfunc, cls);
+ Vec3 bitan = nextv - pos;
+ if(length_sq(bitan) < 1e-6) {
+ nextv = rev_vert(u, v - dv, rfunc, cls);
+ bitan = pos - nextv;
+ }
+
+ normal = cross(tang, bitan);
+ }
+
+ *varr++ = pos;
+ *narr++ = normalize(normal);
+ *tarr++ = normalize(tang);
+ *uvarr++ = Vec2(u, v);
+
+ if(i < usub && j < vsub) {
+ int idx = i * vverts + j;
+
+ *idxarr++ = idx;
+ *idxarr++ = idx + vverts + 1;
+ *idxarr++ = idx + 1;
+
+ *idxarr++ = idx;
+ *idxarr++ = idx + vverts;
+ *idxarr++ = idx + vverts + 1;
+ }
+
+ v += dv;
+ }
+ u += du;
+ }
+}
+
+
+static inline Vec3 sweep_vert(float u, float v, float height, Vec2 (*sf)(float, float, void*), void *cls)
+{
+ Vec2 pos = sf(u, v, cls);
+
+ float x = pos.x;
+ float y = v * height;
+ float z = pos.y;
+
+ return Vec3(x, y, z);
+}
+
+// ---- sweep shape along a path ----
+void gen_sweep(Mesh *mesh, float height, int usub, int vsub, Vec2 (*sfunc)(float, float, void*), void *cls)
+{
+ if(!sfunc) return;
+ if(usub < 3) usub = 3;
+ if(vsub < 1) vsub = 1;
+
+ mesh->clear();
+
+ int uverts = usub + 1;
+ int vverts = vsub + 1;
+ int num_verts = uverts * vverts;
+
+ int num_quads = usub * vsub;
+ int num_tri = num_quads * 2;
+
+ Vec3 *varr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_VERTEX, 3, num_verts, 0);
+ Vec3 *narr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_NORMAL, 3, num_verts, 0);
+ Vec3 *tarr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_TANGENT, 3, num_verts, 0);
+ Vec2 *uvarr = (Vec2*)mesh->set_attrib_data(MESH_ATTR_TEXCOORD, 2, num_verts, 0);
+ unsigned int *idxarr = mesh->set_index_data(num_tri * 3, 0);
+
+ float du = 1.0 / (float)(uverts - 1);
+ float dv = 1.0 / (float)(vverts - 1);
+
+ float u = 0.0;
+ for(int i=0; i<uverts; i++) {
+ float v = 0.0;
+ for(int j=0; j<vverts; j++) {
+ Vec3 pos = sweep_vert(u, v, height, sfunc, cls);
+
+ Vec3 nextu = sweep_vert(fmod(u + du, 1.0), v, height, sfunc, cls);
+ Vec3 tang = nextu - pos;
+ if(length_sq(tang) < 1e-6) {
+ float new_v = v > 0.5 ? v - dv * 0.25 : v + dv * 0.25;
+ nextu = sweep_vert(fmod(u + du, 1.0), new_v, height, sfunc, cls);
+ tang = nextu - pos;
+ }
+
+ Vec3 normal;
+ Vec3 nextv = sweep_vert(u, v + dv, height, sfunc, cls);
+ Vec3 bitan = nextv - pos;
+ if(length_sq(bitan) < 1e-6) {
+ nextv = sweep_vert(u, v - dv, height, sfunc, cls);
+ bitan = pos - nextv;
+ }
+
+ normal = cross(tang, bitan);
+
+ *varr++ = pos;
+ *narr++ = normalize(normal);
+ *tarr++ = normalize(tang);
+ *uvarr++ = Vec2(u, v);
+
+ if(i < usub && j < vsub) {
+ int idx = i * vverts + j;
+
+ *idxarr++ = idx;
+ *idxarr++ = idx + vverts + 1;
+ *idxarr++ = idx + 1;
+
+ *idxarr++ = idx;
+ *idxarr++ = idx + vverts;
+ *idxarr++ = idx + vverts + 1;
+ }
+
+ v += dv;
+ }
+ u += du;
+ }
+}
--- /dev/null
+#ifndef MESHGEN_H_
+#define MESHGEN_H_
+
+#include "gmath/gmath.h"
+
+class Mesh;
+
+void gen_sphere(Mesh *mesh, float rad, int usub, int vsub, float urange = 1.0, float vrange = 1.0);
+void gen_geosphere(Mesh *mesh, float rad, int subdiv, bool hemi = false);
+void gen_torus(Mesh *mesh, float mainrad, float ringrad, int usub, int vsub, float urange = 1.0, float vrange = 1.0);
+void gen_cylinder(Mesh *mesh, float rad, float height, int usub, int vsub, int capsub = 0, float urange = 1.0, float vrange = 1.0);
+void gen_cone(Mesh *mesh, float rad, float height, int usub, int vsub, int capsub = 0, float urange = 1.0, float vrange = 1.0);
+void gen_plane(Mesh *mesh, float width, float height, int usub = 1, int vsub = 1);
+void gen_heightmap(Mesh *mesh, float width, float height, int usub, int vsub, float (*hf)(float, float, void*), void *hfdata = 0);
+void gen_box(Mesh *mesh, float xsz, float ysz, float zsz, int usub = 1, int vsub = 1);
+
+void gen_revol(Mesh *mesh, int usub, int vsub, Vec2 (*rfunc)(float, float, void*), void *cls = 0);
+void gen_revol(Mesh *mesh, int usub, int vsub, Vec2 (*rfunc)(float, float, void*), Vec2 (*nfunc)(float, float, void*), void *cls);
+
+/* callback args: (float u, float v, void *cls) -> Vec2 XZ offset u,v in [0, 1] */
+void gen_sweep(Mesh *mesh, float height, int usub, int vsub, Vec2 (*sfunc)(float, float, void*), void *cls = 0);
+
+#endif // MESHGEN_H_
--- /dev/null
+#ifndef OPENGL_H_
+#define OPENGL_H_
+
+#include <GL/glew.h>
+
+#endif // OPENGL_H_