Parcourir la source

- Working changes for physics development.

Chris Culy il y a 14 ans
Parent
commit
cced75c4bf

+ 6 - 0
gameplay/gameplay.vcxproj

@@ -104,9 +104,15 @@
     <ClInclude Include="src\Node.h" />
     <ClInclude Include="src\Package.h" />
     <ClInclude Include="src\ParticleEmitter.h" />
+    <ClInclude Include="src\PhysicsConstraint.h" />
     <ClInclude Include="src\PhysicsController.h" />
+    <ClInclude Include="src\PhysicsFixedConstraint.h" />
+    <ClInclude Include="src\PhysicsGenericConstraint.h" />
+    <ClInclude Include="src\PhysicsHingeConstraint.h" />
     <ClInclude Include="src\PhysicsMotionState.h" />
     <ClInclude Include="src\PhysicsRigidBody.h" />
+    <ClInclude Include="src\PhysicsSocketConstraint.h" />
+    <ClInclude Include="src\PhysicsSpringConstraint.h" />
     <ClInclude Include="src\Plane.h" />
     <ClInclude Include="src\Platform.h" />
     <ClInclude Include="src\Properties.h" />

+ 1 - 1
gameplay/src/Game.h

@@ -290,7 +290,7 @@ private:
     int _clearStencil;                          // The clear stencil value last used for clearing the stencil buffer.
     AnimationController _animationController;   // Controls the scheduling and running of animations.
     AudioController _audioController;           // Controls audio sources that are playing in the game.
-    PhysicsController _physicsController;     // Controls the simulation of a physics scene and entities.
+    PhysicsController _physicsController;       // Controls the simulation of a physics scene and entities.
 };
 
 }

+ 10 - 12
gameplay/src/Joint.cpp

@@ -39,12 +39,13 @@ void Joint::updateJointMatrix(const Matrix& bindShape, Vector4* matrixPalette)
 {
     const char* id = _id.c_str();
 
-    if (_jointMatrixDirty)
+    // Hack: this is so that a single skeleton/joint can be shared across multiple mesh skins.
+    //if (_jointMatrixDirty)
     {
         _jointMatrixDirty = false;
 
         Matrix t;
-        Matrix::multiply(getWorldMatrix(), getInverseBindPose(), &t);
+        Matrix::multiply(getJointMatrix(), getInverseBindPose(), &t);
         Matrix::multiply(t, bindShape, &t);
 
         matrixPalette[0].set(t.m[0], t.m[4], t.m[8], t.m[12]);
@@ -65,29 +66,26 @@ void Joint::setInverseBindPose(const Matrix& m)
 }
 
 const Matrix& Joint::getWorldMatrix() const
-{
-    return Node::getWorldMatrix();
-}
-
-const Matrix& Joint::getJointMatrix() const
 {
     // If this is the root joint, then we 
     // also apply the transform of the model
     // that the skin is attached to to get the
     // actual world matrix.
-    if (_parent == NULL)
+    if (_parent == NULL && _skin != NULL)
     {
-        if (_skin != NULL)
             Matrix::multiply(_skin->_model->getNode()->getWorldMatrix(), Node::getWorldMatrix(), &_jointWorld);
-        else if (_firstChild != NULL && ((Joint*)_firstChild)->_skin != NULL)
-            Matrix::multiply(((Joint*)_firstChild)->_skin->_model->getNode()->getWorldMatrix(), Node::getWorldMatrix(), &_jointWorld);
     }
     else
     {
-        memcpy((void*)_jointWorld.m, Node::getWorldMatrix().m, MATRIX_SIZE);
+        memcpy((void*)_jointWorld.m, Node::getWorldMatrix().m, sizeof(float) * 16);
     }
 
     return _jointWorld;
 }
 
+const Matrix& Joint::getJointMatrix() const
+{
+    return Node::getWorldMatrix();
+}
+
 }

+ 1 - 1
gameplay/src/Matrix.h

@@ -236,7 +236,7 @@ public:
 //     * @param plane The Plane about which to create a reflection.
 //     * @param dst A matrix to store the result in.
 //     */
-//    static void createRefelction(const Plane& plane, Matrix* dst);
+//    static void createReflection(const Plane& plane, Matrix* dst);
 
     /**
      * Creates a scale matrix.

+ 3 - 0
gameplay/src/MeshSkin.cpp

@@ -41,7 +41,10 @@ Joint* MeshSkin::getJoint(const char* id) const
     {
         Joint* j = _joints[i];
         if (j && j->getId() != NULL && strcmp(j->getId(), id) == 0)
+        {
             return j;
+        }
+    }
 
     return NULL;
 }

+ 1 - 1
gameplay/src/MeshSkin.h

@@ -63,7 +63,7 @@ public:
      */
     unsigned int getMatrixPaletteSize() const;
 
-//private:
+private:
 
     /**
      * Constructor.

+ 11 - 0
gameplay/src/Model.cpp

@@ -325,8 +325,19 @@ void Model::validatePartCount()
     }
 }
 
+<<<<<<< HEAD
 void Model::setMaterialNodeBinding(Material *material)
 {
+=======
+Model* Model::create(Mesh* mesh)
+{
+    mesh->addRef();
+    return new Model(mesh);
+}
+
+void Model::setMaterialNodeBinding(Material *material)
+{
+>>>>>>> ccc0fd7... - Working changes for physics development.
     if (_node)
     {
         material->setNodeBinding(_node);

+ 9 - 0
gameplay/src/Model.h

@@ -151,12 +151,21 @@ private:
      */
     ~Model();
 
+<<<<<<< HEAD
     /**
      * Sets the MeshSkin for this model.
      * 
      * @param skin The MeshSkin for this model.
      */
     void setSkin(MeshSkin* skin);
+=======
+    void validatePartCount();
+
+    /**
+     * Sets the specified materia's node binding to this model's node.
+     */
+    void setMaterialNodeBinding(Material *m);
+>>>>>>> ccc0fd7... - Working changes for physics development.
 
     /**
      * Sets the node that is associated with this model.

+ 5 - 2
gameplay/src/Node.cpp

@@ -15,7 +15,7 @@ namespace gameplay
 
 Node::Node(const char* id)
     : _scene(NULL), _firstChild(NULL), _nextSibling(NULL), _prevSibling(NULL), _parent(NULL), _childCount(NULL),
-    _camera(NULL), _light(NULL), _model(NULL), _audioSource(NULL), _particleEmitter(NULL), _physicsRigidBody(NULL),
+    _camera(NULL), _light(NULL), _model(NULL), _audioSource(NULL), _particleEmitter(NULL), _physicsRigidBody(NULL), 
     _dirtyBits(NODE_DIRTY_ALL), _notifyHierarchyChanged(true), _boundsType(NONE)
 {
     if (id)
@@ -288,7 +288,7 @@ const Matrix& Node::getWorldMatrix() const
         // If we have a parent, multiply our parent world transform by our local
         // transform to obtain our final resolved world transform.
         Node* parent = getParent();
-        if (parent)
+        if (parent && !_physicsRigidBody)
         {
             Matrix::multiply(parent->getWorldMatrix(), getMatrix(), &_world);
         }
@@ -400,6 +400,9 @@ const Matrix& Node::getWorldViewProjectionMatrix() const
 }
 
 Vector3 Node::getTranslationWorld() const
+}
+
+Vector3 Node::getWorldTranslation() const
 {
     Vector3 translation;
     getWorldMatrix().getTranslation(&translation);

+ 32 - 0
gameplay/src/PhysicsConstraint.h

@@ -0,0 +1,32 @@
+/*
+ * PhysicsConstraint.h
+ */
+
+#ifndef PHYSICSCONSTRAINT_H_
+#define PHYSICSCONSTRAINT_H_
+
+#include "PhysicsController.h"
+
+namespace gameplay
+{
+
+/**
+ * Base class for physics constraints.
+ */
+class PhysicsConstraint
+{
+    friend class PhysicsController;
+
+public:
+    /**
+     * Constructor.
+     */
+    PhysicsConstraint() : _constraint(NULL) {}
+
+protected:
+    btTypedConstraint* _constraint;
+};
+
+}
+
+#endif

+ 84 - 0
gameplay/src/PhysicsController.cpp

@@ -4,6 +4,7 @@
 
 #include "Base.h"
 #include "PhysicsController.h"
+#include "PhysicsMotionState.h"
 
 namespace gameplay
 {
@@ -31,6 +32,55 @@ void PhysicsController::setGravity(Vector3 gravity)
 	}
 }
 
+PhysicsFixedConstraint* PhysicsController::createFixedConstraint(PhysicsRigidBody* a, 
+    const Quaternion& rotationOffsetA, const Vector3& translationOffsetA, PhysicsRigidBody* b,
+    const Quaternion& rotationOffsetB, const Vector3& translationOffsetB)
+{
+    PhysicsFixedConstraint* constraint = new PhysicsFixedConstraint(a, rotationOffsetA, 
+        translationOffsetA, b, rotationOffsetB, translationOffsetB);
+    addConstraint(constraint);
+    return constraint;
+}
+
+PhysicsGenericConstraint* PhysicsController::createGenericConstraint(PhysicsRigidBody* a,
+    const Quaternion& rotationOffsetA, const Vector3& translationOffsetA, PhysicsRigidBody* b,
+    const Quaternion& rotationOffsetB, const Vector3& translationOffsetB)
+{
+    PhysicsGenericConstraint* constraint = new PhysicsGenericConstraint(a, rotationOffsetA, 
+        translationOffsetA, b, rotationOffsetB, translationOffsetB);
+    addConstraint(constraint);
+    return constraint;
+}
+
+PhysicsHingeConstraint* PhysicsController::createHingeConstraint(PhysicsRigidBody* a,
+    const Quaternion& rotationOffsetA, const Vector3& translationOffsetA, PhysicsRigidBody* b, 
+    const Quaternion& rotationOffsetB, const Vector3& translationOffsetB)
+{
+    PhysicsHingeConstraint* constraint = new PhysicsHingeConstraint(a, rotationOffsetA, 
+        translationOffsetA, b, rotationOffsetB, translationOffsetB);
+    addConstraint(constraint);
+    return constraint;
+}
+
+PhysicsSocketConstraint* PhysicsController::createSocketConstraint(PhysicsRigidBody* a,
+    const Vector3& translationOffsetA, PhysicsRigidBody* b, const Vector3& translationOffsetB)
+{
+    PhysicsSocketConstraint* constraint = new PhysicsSocketConstraint(a,
+        translationOffsetA, b, translationOffsetB);
+    addConstraint(constraint);
+    return constraint;
+}
+
+PhysicsSpringConstraint* PhysicsController::createSpringConstraint(PhysicsRigidBody* a,
+    const Quaternion& rotationOffsetA, const Vector3& translationOffsetA, PhysicsRigidBody* b, 
+    const Quaternion& rotationOffsetB, const Vector3& translationOffsetB)
+{
+    PhysicsSpringConstraint* constraint = new PhysicsSpringConstraint(a, rotationOffsetA, 
+        translationOffsetA, b, rotationOffsetB, translationOffsetB);
+    addConstraint(constraint);
+    return constraint;
+}
+
 void PhysicsController::initialize()
 {
 	// TODO: Should any of this be configurable?
@@ -46,6 +96,20 @@ void PhysicsController::initialize()
 
 void PhysicsController::finalize()
 {
+    // Remove the constraints from the world and delete them.
+	for (int i = _world->getNumConstraints() - 1; i >= 0; i--)
+	{
+		btTypedConstraint* constraint = _world->getConstraint(i);
+		_world->removeConstraint(constraint);
+		delete constraint;
+	}
+
+    // Delete the GamePlay physics constraint objects.
+    for (unsigned int i = 0; i < _constraints.size(); i++)
+    {
+        delete _constraints[i];
+    }
+
 	// Remove the rigid bodies from the world and delete them.
 	for (int i = _world->getNumCollisionObjects() - 1; i >= 0 ; i--)
 	{
@@ -103,6 +167,20 @@ void PhysicsController::update(long elapsedTime)
 	// Note that stepSimulation takes elapsed time in seconds
 	// so we divide by 1000 to convert from milliseconds.
 	_world->stepSimulation((float)elapsedTime * 0.001, 10);
+
+    // TODO!!
+    /*
+    // Update the motion states for each rigid body in the scene.
+    for (int i = _world->getNumCollisionObjects() - 1; i >= 0 ; i--)
+	{
+		btCollisionObject* obj = _world->getCollisionObjectArray()[i];
+		btRigidBody* body = btRigidBody::upcast(obj);
+		if (body && body->getMotionState())
+		{
+            ((PhysicsMotionState*)body->getMotionState())->update();
+        }
+    }
+    */
 }
 
 btCollisionShape* PhysicsController::getBox(const Vector3& min, const Vector3& max, const btVector3& scale)
@@ -140,4 +218,10 @@ btCollisionShape* PhysicsController::getHeightfield(void* data, int width, int h
 	return NULL;
 }
 
+void PhysicsController::addConstraint(PhysicsConstraint* constraint)
+{
+    _world->addConstraint(constraint->_constraint);
+    _constraints.push_back(constraint);
+}
+
 }

+ 49 - 6
gameplay/src/PhysicsController.h

@@ -5,6 +5,12 @@
 #ifndef PHYSICSCONTROLLER_H_
 #define PHYSICSCONTROLLER_H_
 
+#include "PhysicsConstraint.h"
+#include "PhysicsFixedConstraint.h"
+#include "PhysicsGenericConstraint.h"
+#include "PhysicsHingeConstraint.h"
+#include "PhysicsSocketConstraint.h"
+#include "PhysicsSpringConstraint.h"
 #include "PhysicsRigidBody.h"
 
 namespace gameplay
@@ -32,35 +38,68 @@ public:
 	 */
 	void setGravity(Vector3 gravity);
 
+    /**
+     * Creates a fixed constraint.
+     */
+    PhysicsFixedConstraint* createFixedConstraint(PhysicsRigidBody* a, const Quaternion& rotationOffsetA, 
+        const Vector3& translationOffsetA, PhysicsRigidBody* b = NULL, 
+        const Quaternion& rotationOffsetB = Quaternion(), const Vector3& translationOffsetB = Vector3());
+
+    /**
+     * Creates a generic constraint.
+     */
+    PhysicsGenericConstraint* createGenericConstraint(PhysicsRigidBody* a, const Quaternion& rotationOffsetA, 
+        const Vector3& translationOffsetA, PhysicsRigidBody* b = NULL, 
+        const Quaternion& rotationOffsetB = Quaternion(), const Vector3& translationOffsetB = Vector3());
+
+    /**
+     * Creates a hinge constraint.
+     */
+    PhysicsHingeConstraint* createHingeConstraint(PhysicsRigidBody* a, const Quaternion& rotationOffsetA, 
+        const Vector3& translationOffsetA, PhysicsRigidBody* b = NULL, 
+        const Quaternion& rotationOffsetB = Quaternion(), const Vector3& translationOffsetB = Vector3());
+
+    /**
+     * Creates a socket constraint.
+     */
+    PhysicsSocketConstraint* createSocketConstraint(PhysicsRigidBody* a, const Vector3& translationOffsetA,
+        PhysicsRigidBody* b = NULL, const Vector3& translationOffsetB = Vector3());
+
+    /**
+     * Creates a spring constraint.
+     */
+    PhysicsSpringConstraint* createSpringConstraint(PhysicsRigidBody* a, const Quaternion& rotationOffsetA, 
+        const Vector3& translationOffsetA, PhysicsRigidBody* b, const Quaternion& rotationOffsetB, const Vector3& translationOffsetB);
+
 private:
 	
 	/**
-	 * Constructor
+	 * Constructor.
 	 */
 	PhysicsController();
 
 	/**
-	 * Controller initialize
+	 * Controller initialize.
 	 */
 	void initialize();
 
 	/**
-	 * Controller finalize
+	 * Controller finalize.
 	 */
     void finalize();
 
 	/**
-	 * Controller pause
+	 * Controller pause.
 	 */
     void pause();
 
 	/**
-	 * Controller resume
+	 * Controller resume.
 	 */
     void resume();
 
 	/**
-	 * Controller update
+	 * Controller update.
 	 */
     void update(long elapsedTime);
 
@@ -69,6 +108,8 @@ private:
 	btCollisionShape* getTriangleMesh(float* vertexData, int vertexPositionStride, unsigned char* indexData, Mesh::IndexFormat indexFormat);
 	btCollisionShape* getHeightfield(void* data, int width, int height);
 
+    void addConstraint(PhysicsConstraint* constraint);
+
 	btVector3 _gravity;
 	btDefaultCollisionConfiguration* _collisionConfiguration;
 	btCollisionDispatcher* _dispatcher;
@@ -76,6 +117,8 @@ private:
 	btSequentialImpulseConstraintSolver* _solver;
 	btDynamicsWorld* _world;
 	btAlignedObjectArray<btCollisionShape*> _shapes;
+
+    std::vector<PhysicsConstraint*> _constraints;
 };
 
 }

+ 226 - 0
gameplay/src/PhysicsFixedConstraint.h

@@ -0,0 +1,226 @@
+/*
+ * PhysicsFixedConstraint.h
+ */
+
+#ifndef PHYSICSFIXEDCONSTRAINT_H_
+#define PHYSICSFIXEDCONSTRAINT_H_
+
+#include "PhysicsGenericConstraint.h"
+#include "Vector3.h"
+
+namespace gameplay
+{
+
+// TODO!
+/*
+// Taken from http://www.geometrictools.com/LibFoundation/Mathematics/Wm4Matrix3.inl.html.
+bool matrixToEulerXYZ(const Matrix& m, Vector3& xyz)
+{
+    if (m.m[8] < 1.0f)
+    {
+            if (m.m[8] > -1.0f)
+            {
+                    xyz.x = atan2f(-m.m[5],m.m[2]);
+                    xyz.y = asinf(m.m[8]);
+                    xyz.z = atan2f(-m.m[4],m.m[0]);
+                    return true;
+            }
+            else
+            {
+                    // WARNING.  Not unique.  XA - ZA = -atan2(r10,r11)
+                    xyz.x = -atan2f(m.m[12],m.m[1]);
+                    xyz.y = -MATH_PIOVER2;
+                    xyz.z = 0.0;
+                    return false;
+            }
+    }
+    else
+    {
+            // WARNING.  Not unique.  XAngle + ZAngle = atan2(r10,r11)
+            xyz.x = atan2f(m.m[12],m.m[1]);
+            xyz.y = MATH_PIOVER2;
+            xyz.z = 0.0;
+    }
+    return false;
+}
+*/
+
+/**
+ * Represents a constraint where two rigid bodies 
+ * (or one rigid body and the world) are bound together
+ * with a given orientation and translation offset and all
+ * movement is restricted.
+ *
+ * (This is similar in concept to parenting one node to another,
+ * but can be used in specific situations for a more appropriate effect
+ * (e.g. for implementing sticky projectiles, etc.).)
+ */
+class PhysicsFixedConstraint : public PhysicsGenericConstraint
+{
+    friend class PhysicsController;
+
+public:
+    /**
+     * Sets the angular limits along the constraint's local
+     * X, Y, and Z axes using the values in the given vector.
+     * 
+     * @param limits The angular limits along the local X, Y, and Z axes.
+     */
+    void setAngularLimit(const Vector3& limits)
+    {
+        setAngularLowerLimit(limits);
+        setAngularUpperLimit(limits);
+    }
+
+    /**
+     * Sets the linear limits along the constraint's local
+     * X, Y, and Z axes using the values in the given vector.
+     * 
+     * @param limits The linear limits along the local X, Y, and Z axes.
+     */
+    void setLinearLimit(const Vector3& limits)
+    {
+        setLinearLowerLimit(limits);
+        setLinearUpperLimit(limits);
+    }
+
+protected:
+
+    void initialize()
+    {
+        /*
+        const BoundingBox& box = joint10->getModel()->getMesh()->getBoundingBox();
+        float a = 0.5f * (box.max.x - box.min.x);
+    
+        const BoundingSphere& sphere = _basketball->getModel()->getMesh()->getBoundingSphere();
+        float b = -sphere.radius;
+    
+        Quaternion r1, r2;
+        joint10->getWorldMatrix().getRotation(&r1);
+        _basketball->getWorldMatrix().getRotation(&r2);
+
+        Vector3 bMin, bMax;
+        joint10->getWorldMatrix().transformPoint(box.min, &bMin);
+        joint10->getWorldMatrix().transformPoint(box.max, &bMax);
+        Vector3 c1(bMin, bMax);
+        c1.scale(0.5f);
+        c1.add(bMin);
+
+        Vector3 c2;
+        _basketball->getWorldMatrix().transformPoint(sphere.center, &c2);
+
+        Vector3 d(c1, c2);
+        d.scale(0.5f);
+
+        Quaternion q;
+        joint10->getWorldMatrix().getRotation(&q);
+        Vector3 v;
+        joint10->getWorldMatrix().getTranslation(&v);
+        Transform t(Vector3::one(), q, v);
+        Matrix w1(t.getMatrix());
+        Matrix w1i; w1.invert(&w1i);
+        Vector3 t1(d);
+        w1i.transformNormal(&t1);
+        t1.x *= 1.0f / joint10->getScaleX();
+        t1.y *= 1.0f / joint10->getScaleY();
+        t1.z *= 1.0f / joint10->getScaleZ();
+
+        _basketball->getWorldMatrix().getRotation(&q);
+        _basketball->getWorldMatrix().getTranslation(&v);
+        t.set(Vector3::one(), q, v);
+        Matrix w2(t.getMatrix());
+        Matrix w2i; w2.invert(&w2i);
+        Vector3 t2(d);
+        t2.negate();
+        w2i.transformNormal(&t2);
+        t2.x *= 1.0f / _basketball->getScaleX();
+        t2.y *= 1.0f / _basketball->getScaleY();
+        t2.z *= 1.0f / _basketball->getScaleZ();
+
+        Quaternion::createFromAxisAngle(Vector3(0.0f, 0.0f, 1.0f), MATH_DEG_TO_RAD(90.0f), &q);
+    
+        // TODO!
+        // Construct the correct rotation limits.
+        r1.inverse();
+        r2.inverse();
+        Matrix mm1;
+        Matrix::createRotation(r1, &mm1);
+        Matrix::multiply(w1, mm1, &mm1);
+        mm1.invert();
+        Matrix mm2;
+        Matrix::createRotation(r2, &mm2);
+        Matrix::multiply(w2, mm2, &mm2);
+        Matrix mm;
+        Matrix::multiply(mm1, mm2, &mm);
+        matrixToEulerXYZ(mm, euler);
+
+        PhysicsGenericConstraint* constraint = Game::getInstance()->getPhysicsController()->createGenericConstraint(joint10->getPhysicsRigidBody(),
+            Quaternion::identity(), t1, _basketball->getPhysicsRigidBody(), Quaternion::identity(), t2);
+
+        Vector3 rz90(0.0f, 0.0f, MATH_DEG_TO_RAD(90.0f));
+        //constraint->setAngularLowerLimit(Vector3(0.0f, 0.0f, 0.0f));
+        //constraint->setAngularUpperLimit(Vector3(0.0f, 0.0f, 0.0f));
+        //constraint->setAngularLowerLimit(rz90);
+        //constraint->setAngularUpperLimit(rz90);
+        constraint->setAngularLowerLimit(euler);
+        constraint->setAngularUpperLimit(euler);
+    
+        d.scale(2.0f);
+        d.x *= 1.0f / _basketball->getScaleX();
+        d.y *= 1.0f / _basketball->getScaleY();
+        d.z *= 1.0f / _basketball->getScaleZ();;
+        w1i.transformNormal(&d);
+        constraint->setLinearLowerLimit(d);
+        constraint->setLinearUpperLimit(d);
+        //*/
+    }
+
+    /**
+     * @see PhysicsGenericConstraint
+     */
+    PhysicsFixedConstraint(PhysicsRigidBody* a, const Quaternion& rotationOffsetA, const Vector3& translationOffsetA,
+        PhysicsRigidBody* b, const Quaternion& rotationOffsetB, const Vector3& translationOffsetB)
+        : PhysicsGenericConstraint(a, rotationOffsetA, translationOffsetA, b, rotationOffsetB, translationOffsetB)
+    {
+    }
+
+    // Note: We make these functions protected to prevent usage
+    // (these are public in the base class, PhysicsGenericConstraint).
+
+    /**
+     * Protected to prevent usage.
+     */
+    inline void setAngularLowerLimit(const Vector3& limit)
+    {
+        PhysicsGenericConstraint::setAngularLowerLimit(limit);
+    }
+    
+    /**
+     * Protected to prevent usage.
+     */
+    inline void setAngularUpperLimit(const Vector3& limit)
+    {
+        PhysicsGenericConstraint::setAngularUpperLimit(limit);
+    }
+    
+    /**
+     * Protected to prevent usage.
+     */
+    inline void setLinearLowerLimit(const Vector3& limit)
+    {
+        PhysicsGenericConstraint::setLinearLowerLimit(limit);
+    }
+    
+    /**
+     * Protected to prevent usage.
+     */
+    inline void setLinearUpperLimit(const Vector3& limit)
+    {
+        PhysicsGenericConstraint::setLinearUpperLimit(limit);
+    }
+
+};
+
+}
+
+#endif

+ 120 - 0
gameplay/src/PhysicsGenericConstraint.h

@@ -0,0 +1,120 @@
+/*
+ * PhysicsGenericConstraint.h
+ */
+
+#ifndef PHYSICSGENERICCONSTRAINT_H_
+#define PHYSICSGENERICCONSTRAINT_H_
+
+#include "PhysicsConstraint.h"
+#include "PhysicsRigidBody.h"
+#include "Quaternion.h"
+#include "Vector3.h"
+
+namespace gameplay
+{
+
+/**
+ * Represents a completely generic constraint between two
+ * rigid bodies (or one rigid body and the world) where the
+ * limits for all six degrees of freedom can be set individually.
+ */
+class PhysicsGenericConstraint : public PhysicsConstraint
+{
+    friend class PhysicsController;
+
+public:
+    /**
+     * Destructor.
+     */
+    ~PhysicsGenericConstraint()
+    {
+        if (_constraint)
+        {
+            delete _constraint;
+            _constraint = NULL;
+        }
+    }
+
+    /**
+     * Sets the lower angular limits along the constraint's local
+     * X, Y, and Z axes using the values in the given vector.
+     * 
+     * @param limits The lower angular limits along the local X, Y, and Z axes.
+     */
+    inline void setAngularLowerLimit(const Vector3& limits)
+    {
+        ((btGeneric6DofConstraint*)_constraint)->setAngularLowerLimit(btVector3(limits.x, limits.y, limits.z));
+    }
+
+    /**
+     * Sets the upper angular limits along the constraint's local
+     * X, Y, and Z axes using the values in the given vector.
+     * 
+     * @param limits The upper angular limits along the local X, Y, and Z axes.
+     */
+    inline void setAngularUpperLimit(const Vector3& limits)
+    {
+        ((btGeneric6DofConstraint*)_constraint)->setAngularUpperLimit(btVector3(limits.x, limits.y, limits.z));
+    }
+    
+    /**
+     * Sets the lower linear limits along the constraint's local
+     * X, Y, and Z axes using the values in the given vector.
+     * 
+     * @param limits The lower linear limits along the local X, Y, and Z axes.
+     */
+    inline void setLinearLowerLimit(const Vector3& limits)
+    {
+        ((btGeneric6DofConstraint*)_constraint)->setLinearLowerLimit(btVector3(limits.x, limits.y, limits.z));
+    }
+    
+    /**
+     * Sets the upper linear limits along the constraint's local
+     * X, Y, and Z axes using the values in the given vector.
+     * 
+     * @param limits The upper linear limits along the local X, Y, and Z axes.
+     */
+    inline void setLinearUpperLimit(const Vector3& limits)
+    {
+        ((btGeneric6DofConstraint*)_constraint)->setLinearUpperLimit(btVector3(limits.x, limits.y, limits.z));
+    }
+
+protected:
+    /**
+     * Creates a generic constraint.
+     * 
+     * @param a The first (possibly only) rigid body to constrain. If this is the only rigid
+     *      body specified the constraint applies between it and the global physics world object.
+     * @param rotationOffsetA The rotation offset for the first rigid body 
+     *      (in its local space) with respect to the constraint joint.
+     * @param translationOffsetA The translation offset for the first rigid body
+     *      (in its local space) with respect to the constraint joint.
+     * @param b The second rigid body to constrain (optional).
+     * @param rotationOffsetB The rotation offset for the second rigid body
+     *      (in its local space) with respect to the constraint joint (optional).
+     * @param translationOffsetB The translation offset for the second rigid body
+     *      (in its local space) with respect to the constraint joint (optional).
+     */
+    PhysicsGenericConstraint(PhysicsRigidBody* a, const Quaternion& rotationOffsetA, const Vector3& translationOffsetA,
+        PhysicsRigidBody* b, const Quaternion& rotationOffsetB, const Vector3& translationOffsetB)
+    {
+        if (b)
+        {
+            btTransform frameInA(btQuaternion(rotationOffsetA.x, rotationOffsetA.y, rotationOffsetA.z, rotationOffsetA.w), 
+                btVector3(translationOffsetA.x, translationOffsetA.y, translationOffsetA.z));
+            btTransform frameInB(btQuaternion(rotationOffsetB.x, rotationOffsetB.y, rotationOffsetB.z, rotationOffsetB.w), 
+                btVector3(translationOffsetB.x, translationOffsetB.y, translationOffsetB.z));
+            _constraint = new btGeneric6DofConstraint(*a->_body, *b->_body, frameInA, frameInB, true);
+        }
+        else
+        {
+            btTransform frameInA(btQuaternion(rotationOffsetA.x, rotationOffsetA.y, rotationOffsetA.z, rotationOffsetA.w), 
+                btVector3(translationOffsetA.x, translationOffsetA.y, translationOffsetA.z));
+            _constraint = new btGeneric6DofConstraint(*a->_body, frameInA, true);
+        }
+    }
+};
+
+}
+
+#endif

+ 91 - 0
gameplay/src/PhysicsHingeConstraint.h

@@ -0,0 +1,91 @@
+/*
+ * PhysicsHingeConstraint.h
+ */
+
+#ifndef PHYSICSHINGECONSTRAINT_H_
+#define PHYSICSHINGECONSTRAINT_H_
+
+#include "PhysicsConstraint.h"
+#include "PhysicsRigidBody.h"
+#include "Quaternion.h"
+#include "Vector3.h"
+
+namespace gameplay
+{
+
+/**
+ * Represents a hinge constraint between two rigid bodies
+ * (or one rigid body and the world) where movement is
+ * restricted to rotation about one axis.
+ */
+class PhysicsHingeConstraint : public PhysicsConstraint
+{
+    friend class PhysicsController;
+
+public:
+    /**
+     * Destructor.
+     */
+    ~PhysicsHingeConstraint()
+    {
+        if (_constraint)
+        {
+            delete _constraint;
+            _constraint = NULL;
+        }
+    }
+
+    /**
+     * Sets the limits (and optionally, some properties) for the hinge.
+     * 
+     * @param minAngle The minimum angle for the hinge.
+     * @param maxAngle The maximum angle for the hinge.
+     * @param softness The softness of the hinge (defaults to 0.9).
+     * @param biasFactor The bias factor for the hinge (defaults to 0.3).
+     * @param relaxationFactor The relaxation factor for the hinge (defaults to 1.0).
+     */
+    void setLimits(float minAngle, float maxAngle, float softness = 0.9f, 
+        float biasFactor = 0.3f, float relaxationFactor = 1.0f)
+    {
+        ((btHingeConstraint*)_constraint)->setLimit(minAngle, maxAngle, softness, biasFactor, relaxationFactor);
+    }
+
+private:
+    /**
+     * Creates a hinge constraint.
+     * 
+     * @param a The first (possibly only) rigid body to constrain. If this is the only rigid
+     *      body specified the constraint applies between it and the global physics world object.
+     * @param rotationOffsetA The rotation offset for the first rigid body 
+     *      (in its local space) with respect to the constraint joint.
+     * @param translationOffsetA The translation offset for the first rigid body
+     *      (in its local space) with respect to the constraint joint.
+     * @param b The second rigid body to constrain (optional).
+     * @param rotationOffsetB The rotation offset for the second rigid body
+     *      (in its local space) with respect to the constraint joint (optional).
+     * @param translationOffsetB The translation offset for the second rigid body
+     *      (in its local space) with respect to the constraint joint (optional).
+     */
+    PhysicsHingeConstraint(PhysicsRigidBody* a, const Quaternion& rotationOffsetA, const Vector3& translationOffsetA,
+        PhysicsRigidBody* b, const Quaternion& rotationOffsetB, const Vector3& translationOffsetB)
+    {
+        if (b)
+        {
+            btTransform frameInA(btQuaternion(rotationOffsetA.x, rotationOffsetA.y, rotationOffsetA.z, rotationOffsetA.w), 
+                btVector3(translationOffsetA.x, translationOffsetA.y, translationOffsetA.z));
+            btTransform frameInB(btQuaternion(rotationOffsetB.x, rotationOffsetB.y, rotationOffsetB.z, rotationOffsetB.w), 
+                btVector3(translationOffsetB.x, translationOffsetB.y, translationOffsetB.z));
+            _constraint = new btHingeConstraint(*a->_body, *b->_body, frameInA, frameInB);
+        }
+        else
+        {
+            btTransform frameInA(btQuaternion(rotationOffsetA.x, rotationOffsetA.y, rotationOffsetA.z, rotationOffsetA.w), 
+                btVector3(translationOffsetA.x, translationOffsetA.y, translationOffsetA.z));
+            _constraint = new btHingeConstraint(*a->_body, frameInA);
+        }
+    }
+};
+
+}
+
+#endif

+ 57 - 8
gameplay/src/PhysicsMotionState.h

@@ -16,14 +16,34 @@ class PhysicsMotionState : public btMotionState
 {
 	friend class PhysicsRigidBody;
 
-	PhysicsMotionState(Node* node) : _node(node)
+    PhysicsMotionState(Node* node, const Vector3* centerOfMassOffset = NULL) : _node(node),
+        _needsUpdate(false), _centerOfMassOffset(btTransform::getIdentity())
 	{
         // Store the initial world transform (minus the scale) for use by Bullet later on.
         Quaternion rotation;
         const Matrix& m = _node->getWorldMatrix();
         m.getRotation(&rotation);
-		_worldTransform = btTransform(btQuaternion(rotation.x, rotation.y, rotation.z, rotation.w), 
-			btVector3(m.m[12], m.m[13], m.m[14]));
+
+        if (centerOfMassOffset)
+        {
+            // Store the center of mass offset.
+            _centerOfMassOffset.setOrigin(btVector3(centerOfMassOffset->x, centerOfMassOffset->y, centerOfMassOffset->z));
+
+            // When there is a center of mass offset, we modify the initial world transformation
+            // so that when physics is initially applied, the object is in the correct location.
+            btQuaternion orientation(rotation.x, rotation.y, rotation.z, rotation.w);
+            btTransform offset = btTransform(orientation, btVector3(0.0f, 0.0f, 0.0f)) * _centerOfMassOffset.inverse();
+
+            btVector3 origin(m.m[12] + _centerOfMassOffset.getOrigin().getX() + offset.getOrigin().getX(),
+                m.m[13] + _centerOfMassOffset.getOrigin().getY() + offset.getOrigin().getY(), 
+                m.m[14] + _centerOfMassOffset.getOrigin().getZ() + offset.getOrigin().getZ());
+            _worldTransform = btTransform(orientation, origin);
+        }
+        else
+        {
+		    _worldTransform = btTransform(btQuaternion(rotation.x, rotation.y, rotation.z, rotation.w), 
+			    btVector3(m.m[12], m.m[13], m.m[14]));
+        }
 	}
 
 public:
@@ -34,15 +54,13 @@ public:
 
     virtual void getWorldTransform(btTransform &transform) const
 	{
-		transform = _worldTransform;
+		transform = _centerOfMassOffset.inverse() * _worldTransform;
     }
 
     virtual void setWorldTransform(const btTransform &transform)
 	{
-		// Calculate the actual world transform by 
-		// taking into account the center of mass offset.
-		_worldTransform = transform;
-
+        _worldTransform = transform * _centerOfMassOffset;
+        
         const btQuaternion& rot = _worldTransform.getRotation();
 		const btVector3& pos = _worldTransform.getOrigin();
 
@@ -50,9 +68,40 @@ public:
 		_node->setTranslation(pos.x(), pos.y(), pos.z());
     }
 
+    /*
+    void update()
+    {
+        static Quaternion r;
+        static Vector3 t;
+
+        if (_needsUpdate)
+        {
+            if (_node->getParent()->getPhysicsRigidBody())
+                _node->getParent()->getPhysicsRigidBody()->update();
+
+            // Use the inverse of the parent's world matrix to
+            // calculate the local transform change due to physics.
+            const Matrix& m = _node->getParent()->getWorldMatrix();
+            m.getRotation(&r);
+            m.getTranslation(&t);
+            btTransform parentWorldInverse = (btTransform(btQuaternion(r.x, r.y, r.z, r.w), btVector3(t.x, t.y, t.z))).inverse();
+            _worldTransform = parentWorldInverse * _worldTransform;
+
+            _needsUpdate = false;
+        }
+    }
+
+    const btTransform& getCenterOfMassOffset()
+    {
+        return _centerOfMassOffset;
+    }
+    */
+
 private:
 	Node* _node;
+    btTransform _centerOfMassOffset;
 	btTransform _worldTransform;
+    bool _needsUpdate;
 };
 
 }

+ 31 - 4
gameplay/src/PhysicsRigidBody.cpp

@@ -24,7 +24,17 @@ PhysicsRigidBody::PhysicsRigidBody(Node* node, PhysicsRigidBody::Type type, floa
             PhysicsController* physics = Game::getInstance()->getPhysicsController();
             btCollisionShape* shape = physics->getBox(box.min, box.max, btVector3(node->getScaleX(), node->getScaleY(), node->getScaleZ()));
 			
-			_body = createBulletRigidBody(shape, mass, node, friction, restitution, linearDamping, angularDamping);
+            // Use the center of the bounding box as the center of mass offset.
+            Vector3 c(box.min, box.max);
+            c.scale(0.5f);
+            c.add(box.min);
+            c.negate();
+
+            if (c.lengthSquared() > MATH_EPSILON)
+			    _body = createBulletRigidBody(shape, mass, node, friction, restitution, linearDamping, angularDamping, &c);
+            else
+                _body = createBulletRigidBody(shape, mass, node, friction, restitution, linearDamping, angularDamping);
+
 			break;
 		}
 		case PhysicsRigidBody::PHYSICS_SHAPE_SPHERE:
@@ -34,7 +44,15 @@ PhysicsRigidBody::PhysicsRigidBody(Node* node, PhysicsRigidBody::Type type, floa
 			PhysicsController* physics = Game::getInstance()->getPhysicsController();
 			btCollisionShape* shape = physics->getSphere(sphere.radius, btVector3(node->getScaleX(), node->getScaleY(), node->getScaleZ()));
 
-            _body = createBulletRigidBody(shape, mass, node, friction, restitution, linearDamping, angularDamping);
+            // Use the center of the bounding sphere as the center of mass offset.
+            Vector3 c(sphere.center);
+            c.negate();
+
+            if (c.lengthSquared() > MATH_EPSILON)
+                _body = createBulletRigidBody(shape, mass, node, friction, restitution, linearDamping, angularDamping, &c);
+            else
+                _body = createBulletRigidBody(shape, mass, node, friction, restitution, linearDamping, angularDamping);
+
 			break;
 		}
 		case PhysicsRigidBody::PHYSICS_SHAPE_TRIANGLE_MESH:
@@ -119,8 +137,17 @@ void PhysicsRigidBody::applyTorqueImpulse(const Vector3& torque)
     }
 }
 
+// TODO!!
+/*
+void PhysicsRigidBody::update()
+{
+    if (_body->getMotionState())
+        ((PhysicsMotionState*)_body->getMotionState())->update();
+}
+*/
+
 btRigidBody* PhysicsRigidBody::createBulletRigidBody(btCollisionShape* shape, float mass, Node* node,
-    float friction, float restitution, float linearDamping, float angularDamping)
+    float friction, float restitution, float linearDamping, float angularDamping, const Vector3* centerOfMassOffset)
 {
 	// If the mass is non-zero, then the object is dynamic
 	// and we need to calculate the local inertia.
@@ -129,7 +156,7 @@ btRigidBody* PhysicsRigidBody::createBulletRigidBody(btCollisionShape* shape, fl
 		shape->calculateLocalInertia(mass, localInertia);
 
 	// Create the Bullet physics rigid body object.
-	PhysicsMotionState* motionState = new PhysicsMotionState(node);
+	PhysicsMotionState* motionState = new PhysicsMotionState(node, centerOfMassOffset);
 	btRigidBody::btRigidBodyConstructionInfo rbInfo(mass, motionState, shape, localInertia);
     rbInfo.m_friction = friction;
     rbInfo.m_restitution = restitution;

+ 14 - 8
gameplay/src/PhysicsRigidBody.h

@@ -20,8 +20,15 @@ class Node;
  */
 class PhysicsRigidBody : public Ref
 {
-public:
+    friend class Node;
+	friend class PhysicsMotionState;
+    friend class PhysicsFixedConstraint;
+    friend class PhysicsGenericConstraint;
+    friend class PhysicsHingeConstraint;
+    friend class PhysicsSocketConstraint;
+    friend class PhysicsSpringConstraint;
 
+public:
 	enum Type
 	{
 		PHYSICS_SHAPE_BOX,
@@ -35,6 +42,8 @@ public:
 	void applyImpulse(const Vector3& impulse, const Vector3* relativePosition = NULL);
 	void applyTorque(const Vector3& torque);
     void applyTorqueImpulse(const Vector3& torque);
+    // TODO!!
+    //void update();
 
     inline void setFriction(float friction);
     inline void setRestitution(float restitution);
@@ -56,12 +65,8 @@ public:
 	inline float getAngularDamping();
 
 private:
-
-	friend class Node;
-	friend class PhysicsMotionState;
-
-    PhysicsRigidBody(Node* node, PhysicsRigidBody::Type type, float mass, 
-		float friction = 0.5, float restitution = 0.0, float linearDamping = 0.0, float angularDamping = 0.0);
+    PhysicsRigidBody(Node* node, PhysicsRigidBody::Type type, float mass, float friction = 0.5,
+        float restitution = 0.0, float linearDamping = 0.0, float angularDamping = 0.0);
     ~PhysicsRigidBody();
 
     /**
@@ -70,7 +75,8 @@ private:
     PhysicsRigidBody(const PhysicsRigidBody& body) {}
 
 	static btRigidBody* createBulletRigidBody(btCollisionShape* shape, float mass, Node* node,
-        float friction, float restitution, float linearDamping, float angularDamping);
+        float friction, float restitution, float linearDamping, float angularDamping,
+        const Vector3* centerOfMassOffset = NULL);
 
 	btRigidBody* _body;
     Node* _node;

+ 68 - 0
gameplay/src/PhysicsSocketConstraint.h

@@ -0,0 +1,68 @@
+/*
+ * PhysicsSocketConstraint.h
+ */
+
+#ifndef PHYSICSSOCKETCONSTRAINT_H_
+#define PHYSICSSOCKETCONSTRAINT_H_
+
+#include "PhysicsConstraint.h"
+#include "PhysicsRigidBody.h"
+#include "Vector3.h"
+
+namespace gameplay
+{
+
+/**
+ * Represents a ball-socket or point-to-point constraint
+ * between two rigid bodies (or one rigid body and the world)
+ * where rotation is unrestricted about the constraint joint (pivot point).
+ */
+class PhysicsSocketConstraint : public PhysicsConstraint
+{
+    friend class PhysicsController;
+
+public:
+    /**
+     * Destructor.
+     */
+    ~PhysicsSocketConstraint()
+    {
+        if (_constraint)
+        {
+            delete _constraint;
+            _constraint = NULL;
+        }
+    }
+
+private:
+    /**
+     * Creates a socket constraint.
+     * 
+     * @param a The first (possibly only) rigid body to constrain. If this is the only rigid
+     *      body specified the constraint applies between it and the global physics world object.
+     * @param translationOffsetA The translation offset for the first rigid body
+     *      (in its local space) with respect to the constraint joint.
+     * @param b The second rigid body to constrain (optional).
+     * @param translationOffsetB The translation offset for the second rigid body
+     *      (in its local space) with respect to the constraint joint (optional).
+     */
+    PhysicsSocketConstraint(PhysicsRigidBody* a, const Vector3& translationOffsetA, 
+        PhysicsRigidBody* b, const Vector3& translationOffsetB)
+    {
+        if (b)
+        {
+            _constraint = new btPoint2PointConstraint(*a->_body, *b->_body, 
+                btVector3(translationOffsetA.x, translationOffsetA.y, translationOffsetA.z),
+                btVector3(translationOffsetB.x, translationOffsetB.y, translationOffsetB.z));
+        }
+        else
+        {
+            _constraint = new btPoint2PointConstraint(*a->_body, 
+                btVector3(translationOffsetA.x, translationOffsetA.y, translationOffsetA.z));
+        }
+    }
+};
+
+}
+
+#endif

+ 236 - 0
gameplay/src/PhysicsSpringConstraint.h

@@ -0,0 +1,236 @@
+/*
+ * PhysicsSpringConstraint.h
+ */
+
+#ifndef PHYSICSSPRINGCONSTRAINT_H_
+#define PHYSICSSPRINGCONSTRAINT_H_
+
+#include "PhysicsGenericConstraint.h"
+
+namespace gameplay
+{
+
+/**
+ * Represents a generic spring constraint between two
+ * rigid bodies (or one rigid body and the world)
+ * where the spring strength and damping can be set
+ * for all six degrees of freedom.
+ */
+class PhysicsSpringConstraint : public PhysicsGenericConstraint
+{
+    friend class PhysicsController;
+
+public:
+    /**
+     * Sets the angular damping along the constraint's local X axis.
+     * 
+     * @param damping The angular damping value.
+     */
+    inline void setAngularDampingX(float damping)
+    {
+        setDamping(ANGULAR_X, damping);
+    }
+
+    /**
+     * Sets the angular damping along the constraint's local Y axis.
+     * 
+     * @param damping The angular damping value.
+     */
+    inline void setAngularDampingY(float damping)
+    {
+        setDamping(ANGULAR_Y, damping);
+    }
+
+    /**
+     * Sets the angular damping along the constraint's local Z axis.
+     * 
+     * @param damping The angular damping value.
+     */
+    inline void setAngularDampingZ(float damping)
+    {
+        setDamping(ANGULAR_Z, damping);
+    }
+
+    /**
+     * Sets the angular strength along the constraint's local X axis.
+     *
+     * Note: setting the strength to a non-zero value enables the
+     * spring for angular movement about the X axis (setting to zero disables it).
+     * 
+     * @param strength The angular strength value.
+     */
+    inline void setAngularStrengthX(float strength)
+    {
+        setStrength(ANGULAR_X, strength);
+    }
+
+    /**
+     * Sets the angular strength along the constraint's local Y axis.
+     *
+     * Note: setting the strength to a non-zero value enables the
+     * spring for angular movement about the Y axis (setting to zero disables it).
+     * 
+     * @param strength The angular strength value.
+     */
+    inline void setAngularStrengthY(float strength)
+    {
+        setStrength(ANGULAR_Y, strength);
+    }
+
+    /**
+     * Sets the angular strength along the constraint's local Z axis.
+     *
+     * Note: setting the strength to a non-zero value enables the
+     * spring for angular movement about the Z axis (setting to zero disables it).
+     * 
+     * @param strength The angular strength value.
+     */
+    inline void setAngularStrengthZ(float strength)
+    {
+        setStrength(ANGULAR_Z, strength);
+    }
+
+    /**
+     * Sets the linear damping along the constraint's local X axis.
+     * 
+     * @param damping The linear damping value.
+     */
+    inline void setLinearDampingX(float damping)
+    {
+        setDamping(LINEAR_X, damping);
+    }
+
+    /**
+     * Sets the linear damping along the constraint's local Y axis.
+     * 
+     * @param damping The linear damping value.
+     */
+    inline void setLinearDampingY(float damping)
+    {
+        setDamping(LINEAR_Y, damping);
+    }
+
+    /**
+     * Sets the linear damping along the constraint's local Z axis.
+     * 
+     * @param damping The linear damping value.
+     */
+    inline void setLinearDampingZ(float damping)
+    {
+        setDamping(LINEAR_Z, damping);
+    }
+
+    /**
+     * Sets the linear strength along the constraint's local X axis.
+     *
+     * Note: setting the strength to a non-zero value enables the
+     * spring for linear movement along the X axis (setting to zero disables it).
+     * 
+     * @param strength The linear strength value.
+     */
+    inline void setLinearStrengthX(float strength)
+    {
+        setStrength(LINEAR_X, strength);
+    }
+
+    /**
+     * Sets the linear strength along the constraint's local Y axis.
+     *
+     * Note: setting the strength to a non-zero value enables the
+     * spring for linear movement along the Y axis (setting to zero disables it).
+     * 
+     * @param strength The linear strength value.
+     */
+    inline void setLinearStrengthY(float strength)
+    {
+        setStrength(LINEAR_Y, strength);
+    }
+
+    /**
+     * Sets the linear strength along the constraint's local Z axis.
+     *
+     * Note: setting the strength to a non-zero value enables the
+     * spring for linear movement along the Z axis (setting to zero disables it).
+     * 
+     * @param strength The linear strength value.
+     */
+    inline void setLinearStrengthZ(float strength)
+    {
+        setStrength(LINEAR_Z, strength);
+    }
+
+private:
+    /**
+     * Creates a spring constraint.
+     * 
+     * @param a The first (possibly only) rigid body to constrain. If this is the only rigid
+     *      body specified the constraint applies between it and the global physics world object.
+     * @param rotationOffsetA The rotation offset for the first rigid body 
+     *      (in its local space) with respect to the constraint joint.
+     * @param translationOffsetA The translation offset for the first rigid body
+     *      (in its local space) with respect to the constraint joint.
+     * @param b The second rigid body to constrain (optional).
+     * @param rotationOffsetB The rotation offset for the second rigid body
+     *      (in its local space) with respect to the constraint joint (optional).
+     * @param translationOffsetB The translation offset for the second rigid body
+     *      (in its local space) with respect to the constraint joint (optional).
+     */
+    PhysicsSpringConstraint(PhysicsRigidBody* a, const Quaternion& rotationOffsetA, const Vector3& translationOffsetA,
+        PhysicsRigidBody* b, const Quaternion& rotationOffsetB, const Vector3& translationOffsetB)
+        : PhysicsGenericConstraint(a, rotationOffsetA, translationOffsetA, b, rotationOffsetB, translationOffsetB)
+    {
+        btTransform frameInA(btQuaternion(rotationOffsetA.x, rotationOffsetA.y, rotationOffsetA.z, rotationOffsetA.w), 
+            btVector3(translationOffsetA.x, translationOffsetA.y, translationOffsetA.z));
+        btTransform frameInB(btQuaternion(rotationOffsetB.x, rotationOffsetB.y, rotationOffsetB.z, rotationOffsetB.w), 
+            btVector3(translationOffsetB.x, translationOffsetB.y, translationOffsetB.z));
+        _constraint = new btGeneric6DofSpringConstraint(*a->_body, *b->_body, frameInA, frameInB, true);
+    }
+
+    // Represents the different properties that
+    // can be set on the spring constraint.
+    // 
+    // (Note: the values map to the index parameter
+    // used in the member functions of the Bullet
+    // class btGeneric6DofSpringConstraint.)
+    enum SpringProperty
+    {
+        LINEAR_X = 0,
+        LINEAR_Y,
+        LINEAR_Z,
+        ANGULAR_X,
+        ANGULAR_Y,
+        ANGULAR_Z
+    };
+
+    // Sets the strength for the given angular/linear 
+    // X/Y/Z axis combination determined by the given index.
+    // 
+    // See the Bullet class btGeneric6DofSpringConstraint
+    // for more information.
+    void setStrength(SpringProperty property, float strength)
+    {
+        if (strength < MATH_EPSILON)
+            ((btGeneric6DofSpringConstraint*)_constraint)->enableSpring(property, false);
+        else
+        {
+            ((btGeneric6DofSpringConstraint*)_constraint)->enableSpring(property, true);
+            ((btGeneric6DofSpringConstraint*)_constraint)->setStiffness(property, strength);
+            ((btGeneric6DofSpringConstraint*)_constraint)->setEquilibriumPoint(property);
+        }
+    }
+
+    // Sets the damping for the given angular/linear 
+    // X/Y/Z axis combination determined by the given index.
+    // 
+    // See the Bullet class btGeneric6DofSpringConstraint
+    // for more information.
+    void setDamping(SpringProperty property, float damping)
+    {
+        ((btGeneric6DofSpringConstraint*)_constraint)->setDamping(property, damping);
+        ((btGeneric6DofSpringConstraint*)_constraint)->setEquilibriumPoint(property);
+    }
+};
+
+}
+
+#endif

+ 11 - 0
gameplay/src/gameplay.h

@@ -58,3 +58,14 @@
 #include "AnimationValue.h"
 #include "Animation.h"
 #include "AnimationClip.h"
+
+// Physics
+#include "PhysicsConstraint.h"
+#include "PhysicsController.h"
+#include "PhysicsFixedConstraint.h"
+#include "PhysicsGenericConstraint.h"
+#include "PhysicsHingeConstraint.h"
+#include "PhysicsMotionState.h"
+#include "PhysicsRigidBody.h"
+#include "PhysicsSocketConstraint.h"
+#include "PhysicsSpringConstraint.h"