diff --git a/FileLoader/FileLoader.h b/FileLoader/FileLoader.h new file mode 100644 index 0000000..8a6cffb --- /dev/null +++ b/FileLoader/FileLoader.h @@ -0,0 +1,88 @@ + +#ifndef FILELOADER_H +#define FILELOADER_H FILELOADER_H + +#include +#include + +class FileLoader +{ + public: + class Path + { + public: + std::string fullPath; + std::string shortPath; + Path() { } + Path(const std::string & full_path, + const std::string & short_path) + : fullPath(full_path), shortPath(short_path) { } + std::string toString() const + { + std::string ret; + if (shortPath != "") + ret += shortPath + ","; + if (fullPath != "") + ret += fullPath; + return ret; + } + }; + + class Buffer + { + public: + char * data; + int size; + Buffer(int sz) + { + size = sz; + data = new char[size]; + _refcnt = new int; + *_refcnt = 1; + m_alloced = true; + } + Buffer(char * data, int sz) + { + size = sz; + this->data = data; + m_alloced = false; + } + void copy(const Buffer & other) + { + data = other.data; + size = other.size; + m_alloced = other.m_alloced; + if (m_alloced) + { + _refcnt = other._refcnt; + (*_refcnt)++; + } + } + Buffer(const Buffer & other) { copy(other); } + Buffer & operator=(const Buffer & other) + { + copy(other); + return *this; + } + ~Buffer() + { + if (m_alloced) + { + (*_refcnt)--; + if (*_refcnt < 1) + { + delete _refcnt; + delete data; + } + } + } + protected: + int * _refcnt; + bool m_alloced; + }; + + virtual int getSize(const Path & path) = 0; + virtual Buffer load(const Path & path) = 0; +}; + +#endif diff --git a/OdeWorld/Makefile b/OdeWorld/Makefile new file mode 100644 index 0000000..9674c30 --- /dev/null +++ b/OdeWorld/Makefile @@ -0,0 +1,12 @@ + +TARGET := OdeWorld.o +SOURCES := $(wildcard *.cc) +HEADERS := $(wildcard *.h) + +all: $(TARGET) + +$(TARGET): $(SOURCES) $(HEADERS) + $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCES) + +clean: + -rm -f *~ *.o diff --git a/OdeWorld/OdeWorld.cc b/OdeWorld/OdeWorld.cc new file mode 100644 index 0000000..2d53712 --- /dev/null +++ b/OdeWorld/OdeWorld.cc @@ -0,0 +1,660 @@ + +#include "OdeWorld.h" +#include +#include +#include +#include /* std::sort() */ +#include +using namespace std; + +#define WORLD_STEP 0.001 + +/* used by ODE to perform collision detection */ +void OdeWorld_collide_callback(void * data, dGeomID o1, dGeomID o2) +{ + const int maxNumContacts = 4; + static dContact contact[maxNumContacts]; + OdeWorld * ow = (OdeWorld *) data; + dBodyID b1 = dGeomGetBody(o1); + dBodyID b2 = dGeomGetBody(o2); + if ((b1 == b2) || (b1 && b2 && dAreConnected(b1, b2))) + return; + int num = dCollide(o1, o2, maxNumContacts, + &contact[0].geom, sizeof(contact[0])); + for (int i = 0; i < num; i++) + { + contact[i].surface.mode = + dContactSlip1 | dContactSlip2 | dContactBounce | + dContactSoftERP | dContactSoftCFM | dContactApprox1; + contact[i].surface.mu = 0.5; + contact[i].surface.slip1 = 0.0; + contact[i].surface.slip2 = 0.0; + contact[i].surface.soft_erp = 0.1; + contact[i].surface.soft_cfm = 0.01; + contact[i].surface.bounce = 0.0; + dJointID joint = dJointCreateContact(ow->m_world, + ow->m_contactJointGroup, contact + i); + dJointAttach(joint, b1, b2); + } +} + +void OdeWorld_pick_collide_callback(void * data, dGeomID o1, dGeomID o2) +{ + dContactGeom contact_geom; + OdeWorld * ow = (OdeWorld *) data; + int num = dCollide(o1, o2, 1, &contact_geom, sizeof(contact_geom)); + if (num > 0) + { + dGeomID other = o1 == ow->m_pick_ray ? o2 : o1; + ow->m_pick_points.push_back( + new OdeWorld::PickedObject(other, contact_geom)); + } +} + +OdeWorld::OdeWorld() +{ + m_world = dWorldCreate(); + dWorldSetAutoDisableFlag(m_world, 1); + m_space = dHashSpaceCreate(0); + m_contactJointGroup = dJointGroupCreate(0); + setGravity(0, 0, -9.81); +} + +OdeWorld::~OdeWorld() +{ + dJointGroupDestroy(m_contactJointGroup); + dSpaceDestroy(m_space); + dWorldDestroy(m_world); +} + +/* invokes ODE to do physics on our world */ +void OdeWorld::step() +{ + dSpaceCollide(m_space, this, OdeWorld_collide_callback); + dWorldQuickStep(m_world, WORLD_STEP); + dJointGroupEmpty(m_contactJointGroup); +} + +OdeWorld::Object * OdeWorld::createObject(bool is_static, float scale) +{ + return new Object(is_static, this, m_world, m_space, scale); +} + +/* push an OpenGL matrix onto the matrix stack for a given + * ODE body position and rotation */ +void OdeWorld::pushTransform(const dReal * pos, const dReal * R) +{ + GLfloat matrix[16]; + matrix[0] = R[0]; + matrix[1] = R[4]; + matrix[2] = R[8]; + matrix[3] = 0; + matrix[4] = R[1]; + matrix[5] = R[5]; + matrix[6] = R[9]; + matrix[7] = 0; + matrix[8] = R[2]; + matrix[9] = R[6]; + matrix[10] = R[10]; + matrix[11] = 0; + matrix[12] = pos[0]; + matrix[13] = pos[1]; + matrix[14] = pos[2]; + matrix[15] = 1; + glPushMatrix(); + glMultMatrixf(matrix); +} + +dBodyID OdeWorld::createBody() +{ + dBodyID id = dBodyCreate(m_world); + m_bodies[id] = 1; + return id; +} + +void OdeWorld::enableAllBodies() +{ + for (std::map::iterator it = m_bodies.begin(); + it != m_bodies.end(); + it++) + { + dBodyEnable(it->first); + } +} + +dJointID OdeWorld::createAMotor(dBodyID b1, dBodyID b2) +{ + dJointID j = dJointCreateAMotor(m_world, 0); + dJointAttach(j, b1, b2); + return j; +} + +dJointID OdeWorld::createHinge(dBodyID b1, dBodyID b2, + dReal anchor_x, dReal anchor_y, dReal anchor_z, + dReal axis_x, dReal axis_y, dReal axis_z) +{ + dJointID j = dJointCreateHinge(m_world, 0); + dJointAttach(j, b1, b2); + dJointSetHingeAnchor(j, anchor_x, anchor_y, anchor_z); + dJointSetHingeAxis(j, axis_x, axis_y, axis_z); + return j; +} + +static bool RPPickedObjectComparator(const refptr & one, + const refptr & two) +{ + return one->dist < two->dist; +} + +refptr< vector< OdeWorld::PickedObjectRef > > OdeWorld::pickObjects( + float start_x, float start_y, float start_z, + float dir_x, float dir_y, float dir_z) +{ + m_pick_ray = dCreateRay(0, dInfinity); + dGeomRaySet(m_pick_ray, start_x, start_y, start_z, dir_x, dir_y, dir_z); + m_pick_points.clear(); + dSpaceCollide2(m_pick_ray, (dGeomID) m_space, + this, OdeWorld_pick_collide_callback); + std::sort(m_pick_points.begin(), m_pick_points.end(), + RPPickedObjectComparator); + dGeomDestroy(m_pick_ray); + return new vector(m_pick_points); +} + +OdeWorld::PickedObjectRef OdeWorld::pickOne( + float start_x, float start_y, float start_z, + float dir_x, float dir_y, float dir_z, + OdeWorld::Object * o) +{ + dGeomID pick_ray = dCreateRay(0, dInfinity); + dGeomRaySet(pick_ray, start_x, start_y, start_z, dir_x, dir_y, dir_z); + + PickedObjectRef por; + bool found_one = false; + + dContactGeom contact_geom; + for (vector::iterator it = o->getGeoms().begin(); + it != o->getGeoms().end(); + it++) + { + dGeomID g = *it; + int num = dCollide(pick_ray, g, 1, &contact_geom, sizeof(contact_geom)); + if (num > 0 && (!found_one || contact_geom.depth < por->dist)) + { + por = new OdeWorld::PickedObject(g, contact_geom); + found_one = true; + } + } + dGeomDestroy(pick_ray); + return por; +} + +void OdeWorld::destroyBody(dBodyID body) +{ + m_bodies.erase(body); + dBodyDestroy(body); + enableAllBodies(); +} + +OdeWorld::Object::Object(bool is_static, OdeWorld * ode_world, + dWorldID world, dSpaceID space, float scale) +{ + m_is_static = is_static; + m_world = world; + m_space = space; + m_body = 0; + m_scale = scale; + m_ode_world = ode_world; + dMassSetZero(&m_mass); + for (int i = 0; i < 3; i++) + m_position[i] = 0.0; + for (int j = 0; j < 3; j++) + for (int i = 0; i < 4; i++) + m_rotation[j * 4 + i] = (j == i) ? 1.0 : 0.0; +} + +OdeWorld::Object::Object(const OdeWorld::Object & orig) +{ + m_is_static = orig.m_is_static; + m_world = orig.m_world; + m_space = orig.m_space; + m_scale = orig.m_scale; + m_ode_world = orig.m_ode_world; + for (int i = 0; i < 3; i++) + m_position[i] = orig.m_position[i]; + for (int i = 0; i < 12; i++) + m_rotation[i] = orig.m_rotation[i]; + /* make a copy of the ODE body */ + if (orig.m_body != 0) + { + m_body = m_ode_world->createBody(); + m_mass = orig.m_mass; + dBodySetMass(m_body, &m_mass); + const dReal * pos = dBodyGetPosition(orig.m_body); + dBodySetPosition(m_body, pos[0], pos[1], pos[2]); + const dReal * rot = dBodyGetRotation(orig.m_body); + dBodySetRotation(m_body, rot); + dBodySetAutoDisableFlag(m_body, + dBodyGetAutoDisableFlag(orig.m_body)); + dBodySetAutoDisableLinearThreshold(m_body, + dBodyGetAutoDisableLinearThreshold(orig.m_body)); + dBodySetAutoDisableAngularThreshold(m_body, + dBodyGetAutoDisableAngularThreshold(orig.m_body)); + dBodySetAutoDisableSteps(m_body, + dBodyGetAutoDisableSteps(orig.m_body)); + dBodySetAutoDisableTime(m_body, + dBodyGetAutoDisableTime(orig.m_body)); + int finite_rotation_mode = dBodyGetFiniteRotationMode(orig.m_body); + dBodySetFiniteRotationMode(m_body, finite_rotation_mode); + if (finite_rotation_mode) + { + dVector3 finite_rotation_axis; + dBodyGetFiniteRotationAxis(orig.m_body, finite_rotation_axis); + dBodySetFiniteRotationAxis(m_body, + finite_rotation_axis[0], + finite_rotation_axis[1], + finite_rotation_axis[2]); + } + dBodySetGravityMode(m_body, dBodyGetGravityMode(orig.m_body)); + } + else + { + m_body = 0; + } + /* make a copy of the ODE geoms */ + for (int i = 0, sz = orig.m_geoms.size(); i < sz; i++) + { + m_geoms.push_back(cloneGeom(orig.m_geoms[i], m_body)); + } +} + +OdeWorld::Object::~Object() +{ + for (int i = 0, sz = m_geoms.size(); i < sz; i++) + dGeomDestroy(m_geoms[i]); + if (m_body != 0) + m_ode_world->destroyBody(m_body); +} + +dGeomID OdeWorld::Object::cloneGeom(dGeomID geom, dBodyID body) +{ + dGeomID id = 0; + dSpaceID space = dGeomGetSpace(geom); + switch (dGeomGetClass(geom)) + { + case dSphereClass: + id = dCreateSphere(space, dGeomSphereGetRadius(geom)); + break; + case dBoxClass: + { + dVector3 size; + dGeomBoxGetLengths(geom, size); + id = dCreateBox(space, size[0], size[1], size[2]); + } + break; + case dCylinderClass: + { + dReal radius, length; + dGeomCylinderGetParams(geom, &radius, &length); + id = dCreateCylinder(space, radius, length); + } + break; + case dCapsuleClass: + { + dReal radius, length; + dGeomCapsuleGetParams(geom, &radius, &length); + id = dCreateCapsule(space, radius, length); + } + break; + case dPlaneClass: + { + dVector4 params; + dGeomPlaneGetParams(geom, params); + id = dCreatePlane(space, + params[0], params[1], params[2], params[3]); + } + break; + case dGeomTransformClass: + { + dGeomID old_inner_geom = dGeomTransformGetGeom(geom); + dGeomID new_inner_geom = cloneGeom(old_inner_geom, body); + if (new_inner_geom != 0) + { + id = dCreateGeomTransform(space); + dGeomTransformSetGeom(id, new_inner_geom); + dGeomTransformSetCleanup(id, + dGeomTransformGetCleanup(geom)); + dGeomTransformSetInfo(id, dGeomTransformGetInfo(geom)); + } + } + break; + case dRayClass: + case dTriMeshClass: + case dSimpleSpaceClass: + case dHashSpaceClass: + /* unsupported for cloning */ + break; + } + if (id != 0) + { + if (dGeomGetBody(geom) != 0) + { + /* if the original geom was attached to a body + * (i.e., a non-static geom), then attach it to the new body */ + dGeomSetBody(id, body); + } + else if (dGeomGetClass(id) != dPlaneClass) + { + /* if the original geom was static, then copy the + * position and rotation to the new geom */ + const dReal * pos = dGeomGetPosition(geom); + const dReal * rot = dGeomGetRotation(geom); + dGeomSetPosition(id, pos[0], pos[1], pos[2]); + dGeomSetRotation(id, rot); + } + dGeomSetData(id, dGeomGetData(geom)); + } + return id; +} + +bool OdeWorld::Object::addBox(refptr< std::vector > args) +{ + if (args->size() != 9) + return false; + dGeomID id = dCreateBox(0, + m_scale * (*args)[0], + m_scale * (*args)[1], + m_scale * (*args)[2]); + dGeomSetData(id, this); + dMass mass; + dMassSetBox(&mass, 1.0, + m_scale * (*args)[0], + m_scale * (*args)[1], + m_scale * (*args)[2]); + setupGeom(id, &mass, + (*args)[3], (*args)[4], (*args)[5], + (*args)[6], (*args)[7], (*args)[8]); + return true; +} + +bool OdeWorld::Object::addSphere(refptr< std::vector > args) +{ + if (args->size() != 4) + return false; + dGeomID id = dCreateSphere(0, m_scale * (*args)[0]); + dGeomSetData(id, this); + dMass mass; + dMassSetSphere(&mass, 1.0, m_scale * (*args)[0]); + setupGeom(id, &mass, + (*args)[1], (*args)[2], (*args)[3], + 0.0, 0.0, 0.0); + return true; +} + +bool OdeWorld::Object::addCylinder(refptr< std::vector > args) +{ + if (args->size() != 8) + return false; + dGeomID id = dCreateCylinder(0, m_scale * (*args)[0], m_scale * (*args)[1]); + dGeomSetData(id, this); + dMass mass; + dMassSetCylinder(&mass, 1.0, 3, m_scale * (*args)[0], m_scale * (*args)[1]); + setupGeom(id, &mass, + (*args)[2], (*args)[3], (*args)[4], + (*args)[5], (*args)[6], (*args)[7]); + return true; +} + +bool OdeWorld::Object::addCapsule(refptr< std::vector > args) +{ + if (args->size() != 8) + return false; + dGeomID id = dCreateCapsule(0, + m_scale * (*args)[0], m_scale * (*args)[1]); + dGeomSetData(id, this); + dMass mass; + dMassSetCapsule(&mass, 1.0, 3, m_scale * (*args)[0], + m_scale * (*args)[1]); + setupGeom(id, &mass, + (*args)[2], (*args)[3], (*args)[4], + (*args)[5], (*args)[6], (*args)[7]); + return true; +} + +bool OdeWorld::Object::addPlane(refptr< std::vector > args) +{ + float a, b, c, d; + if (args->size() == 6) + { + dMatrix3 r; + dRFromEulerAngles(r, (*args)[3], (*args)[4], (*args)[5]); + dVector3 default_plane_direction = {0, 0, 1, 0}; + dVector3 rotated_plane_direction; + dMultiply0(rotated_plane_direction, default_plane_direction, r, 1, 3, 3); + + a = rotated_plane_direction[0]; + b = rotated_plane_direction[1]; + c = rotated_plane_direction[2]; + d = m_scale * (a * (*args)[0] + b * (*args)[1] + c * (*args)[2]); + } + else if (args->size() == 4) + { + a = (*args)[0]; + b = (*args)[1]; + c = (*args)[2]; + d = (*args)[3]; + } + else + { + return false; + } + + dGeomID id = dCreatePlane(m_space, a, b, c, d); + dGeomSetData(id, this); + m_geoms.push_back(id); + return true; +} + +void OdeWorld::Object::setupGeom(dGeomID geom, dMass * mass, + float locx, float locy, float locz, + float rotx, float roty, float rotz) +{ + locx = m_scale * locx; + locy = m_scale * locy; + locz = m_scale * locz; + + dMatrix3 rot; + dRFromEulerAngles(rot, rotx, roty, rotz); + dGeomSetRotation(geom, rot); + dGeomSetPosition(geom, locx, locy, locz); + + dGeomID transform = dCreateGeomTransform(m_space); + dGeomTransformSetCleanup(transform, 1); + dGeomTransformSetInfo(transform, 1); + dGeomTransformSetGeom(transform, geom); + dGeomSetData(transform, this); + + m_geoms.push_back(transform); + + if (!m_is_static) + { + /* attach the geometry to the body */ + if (m_body == 0) + m_body = m_ode_world->createBody(); + + dGeomSetBody(transform, m_body); + + dMassRotate(mass, rot); + dMassTranslate(mass, locx, locy, locz); + dMassAdd(&m_mass, mass); + } +} + +const dReal * OdeWorld::Object::getPosition() +{ + if (m_body != 0) + { + return dBodyGetPosition(m_body); + } + else if (m_geoms.size() > 0) + { + if (dGeomGetClass(m_geoms[0]) != dPlaneClass) + return dGeomGetPosition(m_geoms[0]); + } + return m_position; +} + +const dReal * OdeWorld::Object::getRotation() +{ + if (m_body != 0) + { + return dBodyGetRotation(m_body); + } + else if (m_geoms.size() > 0) + { + if (dGeomGetClass(m_geoms[0]) != dPlaneClass) + return dGeomGetRotation(m_geoms[0]); + } + return m_rotation; +} + +void OdeWorld::Object::setPosition(double x, double y, double z) +{ + if (m_is_static) + { + for (int i = 0, sz = m_geoms.size(); i < sz; i++) + dGeomSetPosition(m_geoms[i], x, y, z); + } + else + { + if (m_body != 0) + { + dBodySetPosition(m_body, x, y, z); + enableBody(); + } + } + m_position[0] = x; + m_position[1] = y; + m_position[2] = z; +} + +void OdeWorld::Object::getPosition(double * x, double * y, double * z) +{ + const dReal * pos = NULL; + if (m_is_static) + { + if (m_geoms.size() > 0) + { + if (dGeomGetClass(m_geoms[0]) == dPlaneClass) + pos = &m_position[0]; + else + pos = dGeomGetPosition(m_geoms[0]); + } + } + else + { + if (m_body != 0) + pos = dBodyGetPosition(m_body); + } + + if (pos != NULL) + { + *x = pos[0]; + *y = pos[1]; + *z = pos[2]; + } + else + { + *x = m_position[0]; + *y = m_position[1]; + *z = m_position[2]; + } +} + +void OdeWorld::Object::setRotation(dReal x, dReal y, dReal z) +{ + dMatrix3 r; + dRFromEulerAngles(r, x, y, z); + if (m_is_static) + { + for (int i = 0, sz = m_geoms.size(); i < sz; i++) + dGeomSetRotation(m_geoms[i], r); + } + else + { + if (m_body != 0) + { + dBodySetRotation(m_body, r); + enableBody(); + } + } +} + +void OdeWorld::Object::addForce(dReal fx, dReal fy, dReal fz) +{ + if (m_body != 0) + { + dBodyAddForce(m_body, fx, fy, fz); + enableBody(); + } +} + +void OdeWorld::Object::addForceRel(dReal fx, dReal fy, dReal fz) +{ + if (m_body != 0) + { + dBodyAddRelForce(m_body, fx, fy, fz); + enableBody(); + } +} + +void OdeWorld::Object::addTorque(dReal fx, dReal fy, dReal fz) +{ + if (m_body != 0) + { + dBodyAddTorque(m_body, fx, fy, fz); + enableBody(); + } +} + +void OdeWorld::Object::addTorqueRel(dReal fx, dReal fy, dReal fz) +{ + if (m_body != 0) + { + dBodyAddRelTorque(m_body, fx, fy, fz); + enableBody(); + } +} + +void OdeWorld::Object::finalize() +{ + if (m_body != 0) + { +#if 0 + cerr << "Warning: translating mass by (" + << (-m_mass.c[0]) << ", " + << (-m_mass.c[1]) << ", " + << (-m_mass.c[2]) << ")" << endl; +#endif + dMassTranslate(&m_mass, -m_mass.c[0], -m_mass.c[1], -m_mass.c[2]); + dBodySetMass(m_body, &m_mass); + } +} + +void OdeWorld::Object::setMass(dReal newmass) +{ + if (m_body != 0) + { + dMassAdjust(&m_mass, newmass); + dBodySetMass(m_body, &m_mass); + enableBody(); + } +} + +void OdeWorld::Object::enableBody() +{ + if (m_body != 0) + { + dBodyEnable(m_body); + } +} diff --git a/OdeWorld/OdeWorld.h b/OdeWorld/OdeWorld.h new file mode 100644 index 0000000..de75333 --- /dev/null +++ b/OdeWorld/OdeWorld.h @@ -0,0 +1,204 @@ + +#ifndef ODEWORLD_H +#define ODEWORLD_H + +#include +#include +#include +#include +#include "refptr/refptr.h" +#ifdef DEBUG_AMOTOR +#include +using namespace std; +#endif + +class OdeWorld +{ + public: + enum GeomType { BOX, SPHERE, PLANE, CYLINDER, CAPSULE }; + + class Object + { + public: + Object(bool is_static, OdeWorld * ode_world, + dWorldID world, dSpaceID space, float scale = 1.0f); + Object(const Object & orig); + ~Object(); + + void setPosition(double x, double y, double z); + void getPosition(double * x, double * y, double * z); + void setRotation(dReal x, dReal y, dReal z); + const dReal * getPosition(); + const dReal * getRotation(); + bool addBox(refptr< std::vector > args); + bool addSphere(refptr< std::vector > args); + bool addCylinder(refptr< std::vector > args); + bool addCapsule(refptr< std::vector > args); + bool addPlane(refptr< std::vector > args); + void addForce(dReal fx, dReal fy, dReal fz); + void addForceRel(dReal fx, dReal fy, dReal fz); + void addTorque(dReal fx, dReal fy, dReal fz); + void addTorqueRel(dReal fx, dReal fy, dReal fz); + void finalize(); + dReal getMass() { return m_mass.mass; } + void setMass(dReal newmass); + void enableBody(); + dBodyID getBody() { return m_body; } + void setGravityMode(bool enabled) + { + if (m_body != 0) + { + dBodySetGravityMode(m_body, enabled); + } + } + void setUserData(void * user_data) + { + m_user_data = user_data; + } + void * getUserData() { return m_user_data; } + std::vector & getGeoms() { return m_geoms; } + + protected: + bool m_is_static; + dBodyID m_body; + dMass m_mass; + dWorldID m_world; + dSpaceID m_space; + std::vector m_geoms; + float m_scale; + dReal m_position[3]; + dReal m_rotation[12]; + OdeWorld * m_ode_world; + void * m_user_data; + + void setupGeom(dGeomID geom, dMass * mass, + float locx, float locy, float locz, + float rotx, float roty, float rotz); + dGeomID cloneGeom(dGeomID geom, dBodyID body); + }; + + class PickedObject + { + public: + Object * obj; + float dist; + float pos[3]; + float normal[3]; + + PickedObject(dGeomID geom, dContactGeom & contact) + { + obj = (Object *) dGeomGetData(geom); + dist = contact.depth; + pos[0] = contact.pos[0]; + pos[1] = contact.pos[1]; + pos[2] = contact.pos[2]; + normal[0] = contact.normal[0]; + normal[1] = contact.normal[1]; + normal[2] = contact.normal[2]; + } + }; + typedef refptr PickedObjectRef; + + OdeWorld(); + ~OdeWorld(); + + Object * createObject(bool is_static, float scale = 1.0f); + void setGravity(float x, float y, float z) + { + dWorldSetGravity(m_world, x, y, z); + enableAllBodies(); + } + void step(); + void enableAllBodies(); + dWorldID getWorldID() { return m_world; } + dJointID createAMotor(dBodyID b1, dBodyID b2); + dJointID createHinge(dBodyID b1, dBodyID b2, + dReal anchor_x, dReal anchor_y, dReal anchor_z, + dReal axis_x, dReal axis_y, dReal axis_z); + void setAMotorNumAxes(dJointID j, int num_axes) + { +#ifdef DEBUG_AMOTOR + cout << "num axes: " << num_axes << endl; +#endif + dJointSetAMotorNumAxes(j, num_axes); + } + void setAMotorAngle(dJointID j, int anum, dReal val) + { +#ifdef DEBUG_AMOTOR + cout << "angle: " << val << endl; +#endif + dJointSetAMotorAngle(j, anum, val); + } + void setAMotorAxis(dJointID j, int anum, int rel, + dReal x, dReal y, dReal z) + { +#ifdef DEBUG_AMOTOR + cout << "axis: " << anum << ", " << rel + << ", (" << x << ", " << y << ", " << z << ")" << endl; +#endif + dJointSetAMotorAxis(j, anum, rel, x, y, z); + } + void setAMotorLoStop(dJointID j, dReal val) + { +#ifdef DEBUG_AMOTOR + cout << "lo stop: " << val << endl; +#endif + dJointSetAMotorParam(j, dParamLoStop, val); + } + void setAMotorHiStop(dJointID j, dReal val) + { +#ifdef DEBUG_AMOTOR + cout << "hi stop: " << val << endl; +#endif + dJointSetAMotorParam(j, dParamHiStop, val); + } + void setAMotorVel(dJointID j, dReal val) + { +#ifdef DEBUG_AMOTOR + cout << "vel: " << val << endl; +#endif + dJointSetAMotorParam(j, dParamVel, val); + } + void setAMotorFMax(dJointID j, dReal val) + { +#ifdef DEBUG_AMOTOR + cout << "fmax: " << val << endl; +#endif + dJointSetAMotorParam(j, dParamFMax, val); + } + void setAMotorBounce(dJointID j, dReal val) + { +#ifdef DEBUG_AMOTOR + cout << "bounce: " << val << endl; +#endif + dJointSetAMotorParam(j, dParamBounce, val); + } + refptr< std::vector > pickObjects( + float start_x, float start_y, float start_z, + float dir_x, float dir_y, float dir_z); + PickedObjectRef pickOne( + float start_x, float start_y, float start_z, + float dir_x, float dir_y, float dir_z, + Object * o); + + friend void OdeWorld_collide_callback(void * data, + dGeomID o1, dGeomID o2); + + friend void OdeWorld_pick_collide_callback(void * data, + dGeomID o1, dGeomID o2); + + static void pushTransform(const dReal * pos, const dReal * R); + + protected: + dBodyID createBody(); + void destroyBody(dBodyID body); + + dWorldID m_world; + dSpaceID m_space; + dJointGroupID m_contactJointGroup; + std::map m_bodies; + dGeomID m_pick_ray; + std::vector< refptr > m_pick_points; +}; + +#endif diff --git a/PhyObj/Makefile b/PhyObj/Makefile new file mode 100644 index 0000000..839c2a8 --- /dev/null +++ b/PhyObj/Makefile @@ -0,0 +1,12 @@ + +TARGET := PhyObj.o +SOURCES := $(wildcard *.cc) +HEADERS := $(wildcard *.h) + +all: $(TARGET) + +$(TARGET): $(SOURCES) $(HEADERS) + $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCES) + +clean: + -rm -f *~ *.o diff --git a/PhyObj/PhyObj.cc b/PhyObj/PhyObj.cc new file mode 100644 index 0000000..7f1b41b --- /dev/null +++ b/PhyObj/PhyObj.cc @@ -0,0 +1,106 @@ + +#include "PhyObj.h" +#include +#include +#include +using namespace std; + +#define WHITESPACE " \t\r\n\f" + +/********** static utility functions **********/ +static string trim(const string & orig) +{ + string result = orig; + size_t pos = result.find_first_not_of(WHITESPACE); + if (pos == string::npos) + { + result = ""; + } + else + { + if (pos > 0) + result = result.substr(pos, result.length() - pos); + pos = result.find_last_not_of(WHITESPACE); + if (pos < result.length() - 1) + result = result.substr(0, pos + 1); + } + return result; +} + +/********** PhyObj::Geom functions **********/ +PhyObj::Geom::Geom() +{ + m_type = NONE; +} + +PhyObj::Geom::Geom(PhyObj::GeomType type, refptr< vector > args) +{ + m_type = type; + m_args = args; +} + + +/********** PhyObj functions **********/ +void PhyObj::load(FileLoader * fileLoader, const FileLoader::Path & path) +{ + FileLoader::Buffer buff = fileLoader->load(path); + if (buff.size <= 0) + return; + string str(buff.data, buff.size); + stringstream istr(str, ios_base::in); + while (!istr.eof()) + { + string line; + getline(istr, line); + line = trim(line); + if (line == "" || line[0] == '#') + continue; + size_t pos = line.find_first_of(WHITESPACE); + if (pos == string::npos) + continue; + string type = line.substr(0, pos); + pos = line.find("\"", pos); + if (pos == string::npos) + continue; + size_t pos2 = line.find("\"", pos + 1); + if (pos2 == string::npos) + continue; + string name = line.substr(pos + 1, pos2 - pos - 1); + pos = pos2 + 1; + refptr< vector > args = new vector(); + for (;;) + { + pos = line.find_first_not_of(WHITESPACE, pos); + if (pos == string::npos) + break; + pos2 = line.find_first_of(WHITESPACE, pos); + string n = line.substr(pos, pos2 - pos); + float f = atof(n.c_str()); + args->push_back(f); + if (pos2 == string::npos) + break; + pos = pos2 + 1; + } + GeomType geom_type = NONE; + if (type == "cube") + { + geom_type = BOX; + } + else if (type == "sphere") + { + geom_type = SPHERE; + } + else if (type == "capsule") + { + geom_type = CAPSULE; + } + else if (type == "plane") + { + geom_type = PLANE; + } + if (geom_type != NONE) + { + m_geoms.push_back(new Geom(geom_type, args)); + } + } +} diff --git a/PhyObj/PhyObj.h b/PhyObj/PhyObj.h new file mode 100644 index 0000000..2fe17ec --- /dev/null +++ b/PhyObj/PhyObj.h @@ -0,0 +1,40 @@ + +#ifndef PHYOBJ_H +#define PHYOBJ_H + +#include "refptr/refptr.h" +#include "FileLoader/FileLoader.h" +#include + +class PhyObj +{ + public: + /* Types */ + enum GeomType { NONE, BOX, SPHERE, PLANE, CAPSULE }; + + class Geom + { + public: + /* Constructors */ + Geom(); + Geom(GeomType type, refptr< std::vector > args); + + /* Methods */ + GeomType getType() { return m_type; } + refptr< std::vector > getArgs() { return m_args; } + + protected: + GeomType m_type; + refptr< std::vector > m_args; + }; + + /* Methods */ + void load(FileLoader * fileLoader, const FileLoader::Path & path); + size_t getNumGeoms() { return m_geoms.size(); } + refptr getGeom(int i) { return m_geoms[i]; } + + protected: + std::vector< refptr > m_geoms; +}; + +#endif diff --git a/TextureCache/Makefile b/TextureCache/Makefile new file mode 100644 index 0000000..bde2547 --- /dev/null +++ b/TextureCache/Makefile @@ -0,0 +1,12 @@ + +TARGET := TextureCache.o +SOURCES := $(wildcard *.cc) +HEADERS := $(wildcard *.h) + +all: $(TARGET) + +$(TARGET): $(SOURCES) $(HEADERS) + $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCES) + +clean: + -rm -f *~ *.o diff --git a/TextureCache/TextureCache.cc b/TextureCache/TextureCache.cc new file mode 100644 index 0000000..95b4142 --- /dev/null +++ b/TextureCache/TextureCache.cc @@ -0,0 +1,143 @@ + +#include +#include +#include +#include +#include +#include "TextureCache.h" +#include "TextureLoader/TextureLoader.h" +#include "FileLoader/FileLoader.h" +using namespace std; + +//#define DEBUG_GL_ERROR +#ifdef DEBUG_GL_ERROR +#define checkGLError() checkGLErrorLine(__FUNCTION__, __LINE__) +static void checkGLErrorLine(const char * function, int line) +{ + GLenum err = glGetError(); + if (err != 0) + { + cerr << "gl error in " << function + << ": " << err << " (0x" << hex << err << ") at line " + << dec << line << endl; + } +} +#else +#define checkGLError() +#endif + +TextureCache::~TextureCache() +{ +} + +GLuint TextureCache::load(const FileLoader::Path & path, + FileLoader & fileLoader, bool mipmaps, + int mode, int quality) +{ + string filename = path.shortPath; + if (filename == "") + filename = path.fullPath; + if (filename == "") + return 0; + map::iterator it = m_cache.find(filename); + if (it != m_cache.end()) + { + return it->second; + } + GLuint tex = loadTexture(path, fileLoader, mipmaps, mode, quality); + if (tex > 0) + { + m_cache[filename] = tex; + } + return tex; +} + +GLuint TextureCache::loadTexture(const FileLoader::Path & path, + FileLoader & fileLoader, bool mipmaps, + int mode, int quality) +{ + checkGLError(); + GLuint texture; + FileLoader::Buffer buff = fileLoader.load(path); + if (buff.size <= 0) + return 0; + SDL_RWops * ops = SDL_RWFromMem(buff.data, buff.size); + SDL_Surface * temp = IMG_Load_RW(ops, true); + if (!temp) + { + cerr << "Failed to load image ('" << path.fullPath << "', '" + << path.shortPath << "'): " << IMG_GetError() << endl; + return 0; + } + + SDL_PixelFormat fmt; + fmt.palette = NULL; + fmt.BitsPerPixel = 32; + fmt.BytesPerPixel = 4; + fmt.Rmask = 0x000000FF; + fmt.Gmask = 0x0000FF00; + fmt.Bmask = 0x00FF0000; + fmt.Amask = 0xFF000000; + fmt.Rshift = 0; + fmt.Gshift = 8; + fmt.Bshift = 16; + fmt.Ashift = 24; + + SDL_Surface * texsurf = SDL_ConvertSurface(temp, &fmt, SDL_SWSURFACE); + SDL_FreeSurface(temp); + if (!texsurf) + { + cerr << "Image was not converted properly!" << endl; + return 0; + } + unsigned int * pixels = new unsigned int[texsurf->w * texsurf->h]; + int y; + unsigned int dstOffset = texsurf->w * (texsurf->h - 1); + unsigned int srcOffset = 0; + for (y = 0; y < texsurf->h; y++) + { + memcpy(pixels + dstOffset, + ((unsigned int *)texsurf->pixels) + srcOffset, + texsurf->w << 2); + dstOffset -= texsurf->w; + srcOffset += texsurf->w; + } + glGenTextures(1, &texture); + checkGLError(); + glBindTexture(GL_TEXTURE_2D, texture); + checkGLError(); + glTexImage2D(GL_TEXTURE_2D, 0, 4, texsurf->w, texsurf->h, 0, + GL_RGBA, GL_UNSIGNED_BYTE, pixels); + + if (quality > 0) + { + checkGLError(); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, + mipmaps ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR); + checkGLError(); + } + else + { + checkGLError(); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, + mipmaps ? GL_NEAREST_MIPMAP_NEAREST : GL_NEAREST); + checkGLError(); + } + + checkGLError(); + + if (mipmaps) + { + checkGLError(); + gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA, + texsurf->w, texsurf->h, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + checkGLError(); + } + + SDL_FreeSurface(texsurf); + delete[] pixels; + checkGLError(); + return texture; +} diff --git a/TextureCache/TextureCache.h b/TextureCache/TextureCache.h new file mode 100644 index 0000000..f679ac8 --- /dev/null +++ b/TextureCache/TextureCache.h @@ -0,0 +1,30 @@ + +#ifndef TEXTURECACHE_H +#define TEXTURECACHE_H TEXTURECACHE_H + +#include +#include +#include +#include +#include "TextureLoader/TextureLoader.h" +#include "FileLoader/FileLoader.h" + +class TextureCache : public TextureLoader +{ +public: + virtual ~TextureCache(); + virtual GLuint load(const FileLoader::Path & path, + FileLoader & fileLoader, bool mipmaps = true, + int mode = GL_DECAL, int quality = 1); + +protected: + /* methods */ + GLuint loadTexture(const FileLoader::Path & path, + FileLoader & fileLoader, bool mipmaps, + int mode, int quality); + + /* data */ + std::map< std::string, GLuint > m_cache; +}; + +#endif diff --git a/TextureLoader/TextureLoader.h b/TextureLoader/TextureLoader.h new file mode 100644 index 0000000..75d8879 --- /dev/null +++ b/TextureLoader/TextureLoader.h @@ -0,0 +1,18 @@ + +#ifndef TEXTURELOADER_H +#define TEXTURELOADER_H TEXTURELOADER_H + +#include +#include +#include "FileLoader/FileLoader.h" + +class TextureLoader +{ + public: + virtual GLuint load( + const FileLoader::Path & path, + FileLoader & fileLoader, bool mipmaps = true, + int mode = GL_DECAL, int quality = 1) = 0; +}; + +#endif diff --git a/refptr/refptr.h b/refptr/refptr.h new file mode 100644 index 0000000..7335af5 --- /dev/null +++ b/refptr/refptr.h @@ -0,0 +1,99 @@ + +#ifndef REFPTR_H +#define REFPTR_H REFPTR_H + +/* Author: Josh Holtrop + * Purpose: Provide a reference-counting pointer-like first order + * C++ object that will free the object it is pointing to when + * all references to it have been destroyed. + * This implementation does not solve the circular reference problem. + * I was not concerned with that when developing this class. + */ +#include /* NULL */ + +template +class refptr +{ + public: + refptr(); + refptr(T * ptr); + refptr(const refptr & orig); + refptr & operator=(const refptr & orig); + refptr & operator=(T * ptr); + ~refptr(); + T & operator*() const { return *m_ptr; } + T * operator->() const { return m_ptr; } + bool isNull() const { return m_ptr == NULL; } + + private: + void cloneFrom(const refptr & orig); + void destroy(); + + T * m_ptr; + int * m_refCount; +}; + +template refptr::refptr() +{ + m_ptr = NULL; + m_refCount = NULL; +} + +template refptr::refptr(T * ptr) +{ + m_ptr = ptr; + m_refCount = new int; + *m_refCount = 1; +} + +template refptr::refptr(const refptr & orig) +{ + cloneFrom(orig); +} + +template refptr & refptr::operator=(const refptr & orig) +{ + destroy(); + cloneFrom(orig); + return *this; +} + +template refptr & refptr::operator=(T * ptr) +{ + destroy(); + m_ptr = ptr; + m_refCount = new int; + *m_refCount = 1; + return *this; +} + +template void refptr::cloneFrom(const refptr & orig) +{ + this->m_ptr = orig.m_ptr; + this->m_refCount = orig.m_refCount; + if (m_refCount != NULL) + (*m_refCount)++; +} + +template refptr::~refptr() +{ + destroy(); +} + +template void refptr::destroy() +{ + if (m_refCount != NULL) + { + if (*m_refCount <= 1) + { + delete m_ptr; + delete m_refCount; + } + else + { + (*m_refCount)--; + } + } +} + +#endif diff --git a/wfobj/Makefile b/wfobj/Makefile new file mode 100644 index 0000000..4813fa3 --- /dev/null +++ b/wfobj/Makefile @@ -0,0 +1,10 @@ + +TARGET := WFObj.o + +all: $(TARGET) + +%.o: %.cc + $(CXX) -c -o $@ $(CPPFLAGS) $(CXXFLAGS) $< + +clean: + -rm -f *~ *.o diff --git a/wfobj/Makefile.driver b/wfobj/Makefile.driver new file mode 100644 index 0000000..b6432bd --- /dev/null +++ b/wfobj/Makefile.driver @@ -0,0 +1,18 @@ + +CXX := g++ +CXXFLAGS := -O2 +SOURCE := WFObj.cc driver.cc +OBJS := $(SOURCE:.cc=.o) +LDFLAGS := -lGL +TARGET := driver + +all: $(TARGET) + +$(TARGET): $(OBJS) + $(CXX) -o $@ $^ $(LDFLAGS) + +%.o: %.cc %.hh + $(CXX) -c -o $@ $< $(CXXFLAGS) + +clean: + -rm -f *~ *.o $(TARGET) diff --git a/wfobj/WFObj.cc b/wfobj/WFObj.cc new file mode 100644 index 0000000..68b0ab6 --- /dev/null +++ b/wfobj/WFObj.cc @@ -0,0 +1,703 @@ + +#include +#include +#include +#include +#include // isspace() +#include // strlen() +#include +#include +#include +#include +#include +#include +#include +#include "WFObj.h" +using namespace std; + +#define WHITESPACE " \n\r\t\v" +//#define DEBUGGL + +/****** static functions ******/ + +static string trimString(string s) +{ + size_t lastpos = s.find_last_not_of(WHITESPACE); + if (lastpos == string::npos) + return ""; + s.erase(lastpos + 1); + s.erase(0, s.find_first_not_of(WHITESPACE)); + return s; +} + +static string stripFirstToken(string & input) +{ + size_t firstnonspace = input.find_first_not_of(WHITESPACE); + if (firstnonspace == string::npos) + return ""; + size_t spaceafter = input.find_first_of(WHITESPACE, firstnonspace); + string token = input.substr(firstnonspace, spaceafter - firstnonspace); + input.erase(0, spaceafter); + return token; +} + +static vector splitString(const string & str, char delim) +{ + vector ret; + string s = str; + size_t pos; + while ( (pos = s.find(delim)) != string::npos ) + { + string t = s.substr(0, pos); + ret.push_back(t); + s.erase(0, pos + 1); + } + if (s != "") + ret.push_back(s); + return ret; +} + +static string basePath(const string & str) +{ + string path = str; + size_t pos; + if ( (pos = path.find_last_of("/\\")) != string::npos ) + { + path.erase(pos + 1); + return path; + } + return ""; +} + +//#define DEBUG_GL_ERROR +#ifdef DEBUG_GL_ERROR +#define checkGLError() checkGLErrorLine(__FUNCTION__, __LINE__) +static void checkGLErrorLine(const char * function, int line) +{ + GLenum err = glGetError(); + if (err != 0) + { + cerr << "gl error in " << function + << ": " << err << " (0x" << hex << err << ") at line " + << dec << line << endl; + } +} +#else +#define checkGLError() +#endif + + +/****** WFObj functions ******/ + +WFObj::WFObj() +{ + init(); +} + +WFObj::WFObj(FileLoader & fileLoader) +{ + init(&fileLoader); +} + +WFObj::WFObj(TextureLoader & textureLoader) +{ + init(NULL, &textureLoader); +} + +WFObj::WFObj(FileLoader & fileLoader, TextureLoader & textureLoader) +{ + init(&fileLoader, &textureLoader); +} + +void WFObj::init (FileLoader * fileLoader, + TextureLoader * textureLoader) +{ + m_fileLoader = fileLoader; + if (m_fileLoader == NULL) + { + m_fileLoader = new WFFileLoader(); + m_iCreatedFileLoader = true; + } + else + { + m_iCreatedFileLoader = false; + } + m_textureLoader = textureLoader; +} + +WFObj::~WFObj() +{ + if (m_iCreatedFileLoader) + delete m_fileLoader; +} + +void WFObj::clear() +{ + m_data = std::vector< std::vector >(); + m_loadedVertex = false; +} + +bool WFObj::load(const FileLoader::Path & path) +{ + clear(); + + FileLoader::Buffer buff = m_fileLoader->load(path); + if (buff.size <= 0) + return false; + + m_path = path; + string str(buff.data, buff.size); + stringstream istr(str, ios_base::in); + load(istr, buff.size); + + return true; +} + +bool WFObj::load(std::istream & istr, unsigned int size) +{ + char buf[size+1]; + + string buildup; + while (istr.good()) + { + istr.getline(buf, size+1); + string input = trimString(buf); + int sz = input.size(); + if (sz == 0 || input[0] == '#') + continue; + if (input[sz-1] == '\\') + { + input[sz-1] = ' '; + buildup = input; + continue; + } + if (buildup != "") + input = buildup + input; + buildup = ""; + processInputLine(input); + } + return true; +} + +void WFObj::processInputLine(const std::string & input) +{ + string line = input; + vector lineParts; + for (;;) + { + string token = stripFirstToken(line); + if (token == "") + break; + lineParts.push_back(token); + } + if (lineParts.size() > 0) + m_data.push_back(lineParts); +} + + +GLuint WFObj::render(bool doTextureInfo, bool enableBlending) +{ + checkGLError(); + GLuint list = glGenLists(1); + glNewList(list, GL_COMPILE); + int len = m_data.size(); + enum { VERTEX, VERTEX_TEXTURE, VERTEX_NORMAL, VERTEX_TYPES }; + vector vertices[VERTEX_TYPES]; + int numVertsLast = 0; + bool inFace = false; + bool inMaterial = false; + string currentMaterialName; + WFMtl material(this); + for (int i = 0; i < len; i++) + { + string type = m_data[i][0]; + if (type == "v") + { + Vertex v = readVertex(m_data[i]); + updateAABB(v.getData()); + vertices[VERTEX].push_back(v); + } + else if (type == "vt") + vertices[VERTEX_TEXTURE].push_back(readVertex(m_data[i])); + else if (type == "vn") + vertices[VERTEX_NORMAL].push_back(readVertex(m_data[i])); + else if (type == "f") + { + int numVerts = m_data[i].size() - 1; + if (inFace && (numVerts != numVertsLast || numVertsLast > 4)) + { +#ifdef DEBUGGL + cout << "glEnd()" << endl; +#endif + glEnd(); + inFace = false; + } + if (!inFace) + { + if (numVerts == 3) + { +#ifdef DEBUGGL + cout << "glBegin(GL_TRIANGLES)" << endl; +#endif + glBegin(GL_TRIANGLES); + } + else if (numVerts == 4) + { +#ifdef DEBUGGL + cout << "glBegin(GL_QUADS)" << endl; +#endif + glBegin(GL_QUADS); + } + else + { +#ifdef DEBUGGL + cout << "glBegin(GL_POLYGON)" << endl; +#endif + glBegin(GL_POLYGON); + } + inFace = true; + } + for (int v = 1; v <= numVerts; v++) + { + int vertexIndices[3] = {0, 0, 0}; + parseVertexIndices(m_data[i][v], vertexIndices); + for (int j = 0; j < 3; j++) + { + if (vertexIndices[j] < 0) + vertexIndices[j] += vertices[j].size() + 1; + } + bool valid = true; + for (int j = 0; j < 3; j++) + { + if (vertexIndices[j] > (int) vertices[j].size()) + { + valid = false; + break; + } + } + if (vertexIndices[VERTEX] <= 0) + valid = false; + if (!valid) + continue; + if (vertexIndices[VERTEX_NORMAL] != 0) + { + /* a normal is present */ +#ifdef DEBUGGL + cout << " glNormal3f(" << + vertices[VERTEX_NORMAL][vertexIndices[VERTEX_NORMAL]-1] + [0] << ", " << + vertices[VERTEX_NORMAL][vertexIndices[VERTEX_NORMAL]-1] + [1] << ", " << + vertices[VERTEX_NORMAL][vertexIndices[VERTEX_NORMAL]-1] + [2] << ")" << endl; +#endif + glNormal3fv(vertices[VERTEX_NORMAL] + [vertexIndices[VERTEX_NORMAL]-1].getData()); + } + if (vertexIndices[VERTEX_TEXTURE] != 0) + { + /* a texture coordinate is present */ +#ifdef DEBUGGL + cout << " glTexCoord2f(" << + vertices[VERTEX_TEXTURE][vertexIndices[VERTEX_TEXTURE]-1] + [0] << ", " << + vertices[VERTEX_TEXTURE][vertexIndices[VERTEX_TEXTURE]-1] + [1] << ')' << endl; +#endif + glTexCoord2fv(vertices[VERTEX_TEXTURE] + [vertexIndices[VERTEX_TEXTURE]-1].getData()); + } +#ifdef DEBUGGL + cout << " glVertex3f(" << + vertices[VERTEX][vertexIndices[VERTEX]-1][0] << ", " << + vertices[VERTEX][vertexIndices[VERTEX]-1][1] << ", " << + vertices[VERTEX][vertexIndices[VERTEX]-1][2] << ")" << + " [" << vertexIndices[VERTEX] << "]" << endl; +#endif + glVertex3fv(vertices[VERTEX][vertexIndices[VERTEX]-1].getData()); + } + + numVertsLast = numVerts; + } + else if (type == "usemtl") + { + if (inFace) + { +#ifdef DEBUGGL + cout << "glEnd()" << endl; +#endif + glEnd(); + inFace = false; + } + if (inMaterial) + { + material.renderEnd(currentMaterialName, doTextureInfo, + enableBlending); + inMaterial = false; + } + if (m_data[i].size() >= 2) + { + currentMaterialName = m_data[i][1]; + material.renderBegin(currentMaterialName, doTextureInfo, + enableBlending); + inMaterial = true; + } + } + else if (type == "mtllib") + { + if (m_data[i].size() >= 2) + { + FileLoader::Path path( + basePath(m_path.fullPath) + m_data[i][1], + m_data[i][1]); + material.load(path); + } + } + } + if (inFace) + { +#ifdef DEBUGGL + cout << "glEnd()" << endl; +#endif + glEnd(); + inFace = false; + } + if (inMaterial) + { + material.renderEnd(currentMaterialName, doTextureInfo, enableBlending); + inMaterial = false; + } + glEndList(); + checkGLError(); + return list; +} + +void WFObj::updateAABB(const float * const vertex) +{ + if (m_loadedVertex) + { + if (vertex[0] < m_aabb[0]) + m_aabb[0] = vertex[0]; + else if (vertex[0] > m_aabb[3]) + m_aabb[3] = vertex[0]; + if (vertex[1] < m_aabb[1]) + m_aabb[1] = vertex[1]; + else if (vertex[1] > m_aabb[4]) + m_aabb[4] = vertex[1]; + if (vertex[2] < m_aabb[2]) + m_aabb[2] = vertex[2]; + else if (vertex[2] > m_aabb[5]) + m_aabb[5] = vertex[2]; + } + else + { + m_aabb[0] = m_aabb[3] = vertex[0]; + m_aabb[1] = m_aabb[4] = vertex[1]; + m_aabb[2] = m_aabb[5] = vertex[2]; + m_loadedVertex = true; + } +} + +WFObj::Vertex WFObj::readVertex(const vector & parts) +{ + int partslen = parts.size(); + Vertex v; + for (int i = 1; i < partslen && i <= 4; i++) + { + sscanf(parts[i].c_str(), "%f", &v[i - 1]); + } + return v; +} + +void WFObj::parseVertexIndices(const string & vtxref, int * ret) +{ + vector parts = splitString(vtxref, '/'); + int num = parts.size(); + for (int i = 0; i < num && i < 3; i++) + sscanf(parts[i].c_str(), "%d", ret + i); +} + + + + +/****** WFMtl functions ******/ + +void WFObj::WFMtl::clear() +{ + m_data = map< string, vector< vector > >(); + m_currentMaterialName = ""; + m_attributesPushed = false; +} + +bool WFObj::WFMtl::load(const FileLoader::Path & path) +{ + clear(); + + FileLoader::Buffer buff = m_obj->m_fileLoader->load(path); + if (buff.size <= 0) + return false; + + m_path = path; + string str(buff.data, buff.size); + stringstream istr(str, ios_base::in); + load(istr, buff.size); + + return true; +} + +bool WFObj::WFMtl::load(std::istream & istr, unsigned int size) +{ + char buf[size+1]; + + string buildup; + while (istr.good()) + { + istr.getline(buf, size+1); + string input = trimString(buf); + int sz = input.size(); + if (sz == 0 || input[0] == '#') + continue; + if (input[sz-1] == '\\') + { + input[sz-1] = ' '; + buildup = input; + continue; + } + if (buildup != "") + input = buildup + input; + buildup = ""; + processInputLine(input); + } + return true; +} + +void WFObj::WFMtl::processInputLine(const std::string & input) +{ + string line = input; + vector lineParts; + for (;;) + { + string token = stripFirstToken(line); + if (token == "") + break; + lineParts.push_back(token); + } + if (lineParts.size() > 0) + { + if ( (lineParts.size() >= 2) && (lineParts[0] == "newmtl") ) + { + m_currentMaterialName = lineParts[1]; + } + else if (m_currentMaterialName != "") + { + m_data[m_currentMaterialName].push_back(lineParts); + } + } +} + +void WFObj::WFMtl::renderBegin(const string & mtlname, bool doTextureInfo, + bool enableBlending) +{ + map< string, vector< vector > >::iterator it = m_data.find(mtlname); + if (it == m_data.end()) + return; + vector< vector > & stmts = it->second; + int num_stmts = stmts.size(); + bool foundTexture = false; + bool didSomething = false; + checkGLError(); + for (int i = 0; i < num_stmts; i++) + { + string & type = stmts[i][0]; + if (type == "Ka") /* set ambient color */ + { + if ( (stmts[i].size() == 4) && (stmts[i][1] != "spectral") ) + { + pushAttributes(); + float mat[4] = {0.0f, 0.0f, 0.0f, 1.0f}; + for (int j = 0; j < 3; j++) + sscanf(stmts[i][j+1].c_str(), "%f", &mat[j]); +#ifdef DEBUGGL + cout << " glMaterialfv(GL_FRONT, GL_AMBIENT, {"; + for (int j = 0; j < 4; j++) + cout << mat[j] << (j < 3 ? ", " : "})"); + cout << endl; +#endif + glMaterialfv(GL_FRONT, GL_AMBIENT, mat); + didSomething = true; + } + } + else if (type == "Kd") /* set diffuse color */ + { + if ( (stmts[i].size() == 4) && (stmts[i][1] != "spectral") ) + { + pushAttributes(); + float mat[4] = {0.0f, 0.0f, 0.0f, 1.0f}; + for (int j = 0; j < 3; j++) + sscanf(stmts[i][j+1].c_str(), "%f", &mat[j]); +#ifdef DEBUGGL + cout << " glMaterialfv(GL_FRONT, GL_DIFFUSE, {"; + for (int j = 0; j < 4; j++) + cout << mat[j] << (j < 3 ? ", " : "})"); + cout << endl; +#endif + glMaterialfv(GL_FRONT, GL_DIFFUSE, mat); + didSomething = true; + } + } + else if (type == "Ks") /* set specular color */ + { + if ( (stmts[i].size() == 4) && (stmts[i][1] != "spectral") ) + { + pushAttributes(); + float mat[4] = {0.0f, 0.0f, 0.0f, 1.0f}; + for (int j = 0; j < 3; j++) + sscanf(stmts[i][j+1].c_str(), "%f", &mat[j]); +#ifdef DEBUGGL + cout << " glMaterialfv(GL_FRONT, GL_SPECULAR, {"; + for (int j = 0; j < 4; j++) + cout << mat[j] << (j < 3 ? ", " : "})"); + cout << endl; +#endif + glMaterialfv(GL_FRONT, GL_SPECULAR, mat); + didSomething = true; + } + } + else if (type == "Ns") /* set shininess */ + { + if (stmts[i].size() == 2) + { + pushAttributes(); + GLfloat shininess = 0.0f; + sscanf(stmts[i][1].c_str(), "%f", &shininess); +#ifdef DEBUGGL + cout << " glMaterialf(GL_FRONT, GL_SHININESS, " + << shininess << ")" << endl; +#endif + glMaterialf(GL_FRONT, GL_SHININESS, shininess); + didSomething = true; + } + } + else if (type == "map_Kd") /* load a diffuse texture */ + { + if (doTextureInfo) + { + if (stmts[i].size() == 2) + { + if (m_obj->m_textureLoader != NULL) + { + FileLoader::Path path( + basePath(m_path.fullPath + stmts[i][1]), + stmts[i][1]); + GLuint tex = m_obj->m_textureLoader->load(path, + *m_obj->m_fileLoader); + if (tex > 0) + { + pushAttributes(); /* jh 2009-11-16 */ +#ifdef DEBUGGL + cout << " glBindTexture(GL_TEXTURE_2D, " << tex << ")" << endl; +#endif + glBindTexture(GL_TEXTURE_2D, tex); + foundTexture = true; + didSomething = true; + } + } + } + } + } + } + + checkGLError(); + if (didSomething) + { + pushAttributes(); + if (doTextureInfo) + { + if (enableBlending) + { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + } + if (foundTexture) + { +#ifdef DEBUGGL + cout << " glEnable(GL_TEXTURE_2D)" << endl; +#endif + glEnable(GL_TEXTURE_2D); + } + else + { +#ifdef DEBUGGL + cout << " glDisable(GL_TEXTURE_2D)" << endl; +#endif + glDisable(GL_TEXTURE_2D); + } + } + } + checkGLError(); +} + +void WFObj::WFMtl::pushAttributes() +{ + if (m_attributesPushed) + return; + m_attributesPushed = true; +#ifdef DEBUGGL + cout << " glPushAttrib(GL_LIGHTING_BIT | GL_TEXTURE_BIT | GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT)" + << endl; +#endif + checkGLError(); + glPushAttrib(GL_LIGHTING_BIT | GL_TEXTURE_BIT | GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT); + checkGLError(); +} + +void WFObj::WFMtl::renderEnd(const string & mtlname, bool doTextureInfo, + bool enableBlending) +{ + map< string, vector< vector > >::iterator it = m_data.find(mtlname); + if (it == m_data.end()) + return; + if (m_attributesPushed) + { +#ifdef DEBUGGL + cout << " glPopAttrib()" << endl; +#endif + checkGLError(); + glPopAttrib(); + checkGLError(); + m_attributesPushed = false; + } +} + + +/****** WFFileLoader functions ******/ + +int WFObj::WFFileLoader::getSize(const Path & path) +{ + struct stat st; + + if (path.fullPath == "") + return -1; + if (stat(path.fullPath.c_str(), &st)) + return -2; + return st.st_size; +} + +FileLoader::Buffer WFObj::WFFileLoader::load(const Path & path) +{ + int size = getSize(path); + if (size > 0) + { + int fd = open(path.fullPath.c_str(), O_RDONLY); + if (fd > 0) + { + Buffer buf(size); + int num_read = read(fd, buf.data, size); + close(fd); + if (num_read > 0) + return buf; + } + } + return Buffer(0); +} diff --git a/wfobj/WFObj.h b/wfobj/WFObj.h new file mode 100644 index 0000000..d8815ca --- /dev/null +++ b/wfobj/WFObj.h @@ -0,0 +1,92 @@ + +#ifndef WFOBJ_H +#define WFOBJ_H + +#include "FileLoader/FileLoader.h" +#include "TextureLoader/TextureLoader.h" +#include +#include +#include +#include + +class WFObj +{ +public: + WFObj(); + WFObj(FileLoader & fileLoader); + WFObj(TextureLoader & textureLoader); + WFObj(FileLoader & fileLoader, TextureLoader & textureLoader); + ~WFObj(); + + class WFMtl + { + public: + WFMtl(WFObj * obj) { m_obj = obj; } + bool load(const FileLoader::Path & path); + void renderBegin(const std::string & mtlname, + bool doTextureInfo = true, + bool enableBlending = false); + void renderEnd(const std::string & mtlname, + bool doTextureInfo = true, + bool enableBlending = false); + + protected: + /* methods */ + void clear(); + void processInputLine(const std::string & input); + void pushAttributes(); + bool load(std::istream & istr, unsigned int size); + + /* variables */ + std::map< std::string, std::vector< std::vector > > m_data; + std::string m_currentMaterialName; + bool m_attributesPushed; + FileLoader::Path m_path; + WFObj * m_obj; + }; + + class WFFileLoader : public FileLoader + { + public: + virtual int getSize(const Path & path); + virtual Buffer load(const Path & path); + }; + + bool load(const FileLoader::Path & path); + GLuint render(bool doTextureInfo = true, + bool enableBlending = false); + const float * const getAABB() { return m_aabb; } + +protected: + /* types */ + class Vertex + { + public: + float operator[](int idx) const { return data[idx]; } + float & operator[](int idx) { return data[idx]; } + float * getData() { return data; } + private: + float data[4]; + }; + + /* methods */ + void init(FileLoader * fileLoader = NULL, + TextureLoader * textureLoader = NULL); + void clear(); + void processInputLine(const std::string & input); + Vertex readVertex(const std::vector & parts); + void parseVertexIndices(const std::string & vtxref, int * ret); + void updateAABB(const float * const vertex); + bool load(std::istream & istr, unsigned int size); + + /* variables */ + std::vector< std::vector > m_data; + FileLoader::Path m_path; + float m_aabb[6]; + bool m_loadedVertex; + FileLoader * m_fileLoader; + TextureLoader * m_textureLoader; + bool m_iCreatedFileLoader; +}; + +#endif diff --git a/wfobj/driver.cc b/wfobj/driver.cc new file mode 100644 index 0000000..eeb8d54 --- /dev/null +++ b/wfobj/driver.cc @@ -0,0 +1,28 @@ + +#include +#include +#include "WFObj.hh" +using namespace std; + +GLuint loadTexture(const char * texture) +{ + cout << "got texture request: '" << texture << "'" << endl; + return 33; +} + +int main(int argc, char * argv[]) +{ + WFObj w; + if (argc < 2) + { + cout << "Usage: " << argv[0] << " " << endl; + return -2; + } + if (!w.load(argv[1], &loadTexture)) + { + cout << "Couldn't open '" << argv[1] << "'!" << endl; + return -1; + } + w.render(); + return 0; +}