|
|
@@ -1,8 +1,10 @@
|
|
|
#include "Base.h"
|
|
|
-#include "Game.h"
|
|
|
-#include "MeshPart.h"
|
|
|
#include "PhysicsController.h"
|
|
|
+#include "PhysicsRigidBody.h"
|
|
|
+#include "PhysicsCharacter.h"
|
|
|
#include "PhysicsMotionState.h"
|
|
|
+#include "Game.h"
|
|
|
+#include "MeshPart.h"
|
|
|
#include "Package.h"
|
|
|
|
|
|
#include "BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h"
|
|
|
@@ -42,16 +44,6 @@ void PhysicsController::addStatusListener(Listener* listener)
|
|
|
_listeners->push_back(listener);
|
|
|
}
|
|
|
|
|
|
-PhysicsCharacter* PhysicsController::createCharacter(Node* node, float radius, float height, const Vector3& center)
|
|
|
-{
|
|
|
- return new PhysicsCharacter(node, radius, height, center);
|
|
|
-}
|
|
|
-
|
|
|
-void PhysicsController::destroyCharacter(PhysicsCharacter* character)
|
|
|
-{
|
|
|
- SAFE_DELETE(character);
|
|
|
-}
|
|
|
-
|
|
|
PhysicsFixedConstraint* PhysicsController::createFixedConstraint(PhysicsRigidBody* a, PhysicsRigidBody* b)
|
|
|
{
|
|
|
checkConstraintRigidBodies(a, b);
|
|
|
@@ -163,7 +155,7 @@ PhysicsCollisionObject* PhysicsController::rayTest(const Ray& ray, float distanc
|
|
|
btScalar PhysicsController::addSingleResult(btManifoldPoint& cp, const btCollisionObject* a, int partIdA, int indexA,
|
|
|
const btCollisionObject* b, int partIdB, int indexB)
|
|
|
{
|
|
|
- // Get pointers to the PhysicsRigidBody objects.
|
|
|
+ // Get pointers to the PhysicsCollisionObject objects.
|
|
|
PhysicsCollisionObject* rbA = Game::getInstance()->getPhysicsController()->getCollisionObject(a);
|
|
|
PhysicsCollisionObject* rbB = Game::getInstance()->getPhysicsController()->getCollisionObject(b);
|
|
|
|
|
|
@@ -171,7 +163,7 @@ btScalar PhysicsController::addSingleResult(btManifoldPoint& cp, const btCollisi
|
|
|
// we notify the listeners only if the pair was not colliding
|
|
|
// during the previous frame. Otherwise, it's a new pair, so add a
|
|
|
// new entry to the cache with the appropriate listeners and notify them.
|
|
|
- PhysicsRigidBody::CollisionPair pair(rbA, rbB);
|
|
|
+ PhysicsCollisionObject::CollisionPair pair(rbA, rbB);
|
|
|
|
|
|
CollisionInfo* collisionInfo;
|
|
|
if (_collisionStatus.count(pair) > 0)
|
|
|
@@ -184,7 +176,7 @@ btScalar PhysicsController::addSingleResult(btManifoldPoint& cp, const btCollisi
|
|
|
collisionInfo = &_collisionStatus[pair];
|
|
|
|
|
|
// Add the appropriate listeners.
|
|
|
- PhysicsRigidBody::CollisionPair p1(pair.objectA, NULL);
|
|
|
+ PhysicsCollisionObject::CollisionPair p1(pair.objectA, NULL);
|
|
|
if (_collisionStatus.count(p1) > 0)
|
|
|
{
|
|
|
const CollisionInfo& ci = _collisionStatus[p1];
|
|
|
@@ -194,7 +186,7 @@ btScalar PhysicsController::addSingleResult(btManifoldPoint& cp, const btCollisi
|
|
|
collisionInfo->_listeners.push_back(*iter);
|
|
|
}
|
|
|
}
|
|
|
- PhysicsRigidBody::CollisionPair p2(pair.objectB, NULL);
|
|
|
+ PhysicsCollisionObject::CollisionPair p2(pair.objectB, NULL);
|
|
|
if (_collisionStatus.count(p2) > 0)
|
|
|
{
|
|
|
const CollisionInfo& ci = _collisionStatus[p2];
|
|
|
@@ -328,12 +320,12 @@ void PhysicsController::update(long elapsedTime)
|
|
|
// If an entry was marked for removal in the last frame, remove it now.
|
|
|
|
|
|
// Dirty the collision status cache entries.
|
|
|
- std::map<PhysicsRigidBody::CollisionPair, CollisionInfo>::iterator iter = _collisionStatus.begin();
|
|
|
+ std::map<PhysicsCollisionObject::CollisionPair, CollisionInfo>::iterator iter = _collisionStatus.begin();
|
|
|
for (; iter != _collisionStatus.end();)
|
|
|
{
|
|
|
if ((iter->second._status & REMOVE) != 0)
|
|
|
{
|
|
|
- std::map<PhysicsRigidBody::CollisionPair, CollisionInfo>::iterator eraseIter = iter;
|
|
|
+ std::map<PhysicsCollisionObject::CollisionPair, CollisionInfo>::iterator eraseIter = iter;
|
|
|
iter++;
|
|
|
_collisionStatus.erase(eraseIter);
|
|
|
}
|
|
|
@@ -410,15 +402,15 @@ void PhysicsController::addCollisionObject(PhysicsCollisionObject* object)
|
|
|
switch (object->getType())
|
|
|
{
|
|
|
case PhysicsCollisionObject::RIGID_BODY:
|
|
|
- _world->addRigidBody(static_cast<btRigidBody*>(object->getCollisionObject()));
|
|
|
+ _world->addRigidBody(static_cast<btRigidBody*>(object->getCollisionObject()));//, btBroadphaseProxy::DefaultFilter, btBroadphaseProxy::StaticFilter | btBroadphaseProxy::CharacterFilter | btBroadphaseProxy::AllFilter);
|
|
|
break;
|
|
|
|
|
|
case PhysicsCollisionObject::CHARACTER:
|
|
|
- _world->addCollisionObject(object->getCollisionObject(), btBroadphaseProxy::CharacterFilter, btBroadphaseProxy::StaticFilter | btBroadphaseProxy::CharacterFilter | btBroadphaseProxy::DefaultFilter);
|
|
|
+ _world->addCollisionObject(object->getCollisionObject());//, btBroadphaseProxy::DefaultFilter, btBroadphaseProxy::StaticFilter | btBroadphaseProxy::CharacterFilter | btBroadphaseProxy::AllFilter);//, btBroadphaseProxy::CharacterFilter, btBroadphaseProxy::CharacterFilter );// | btBroadphaseProxy::CharacterFilter | btBroadphaseProxy::DefaultFilter*/ 0);
|
|
|
break;
|
|
|
|
|
|
case PhysicsCollisionObject::GHOST_OBJECT:
|
|
|
- _world->addCollisionObject(object->getCollisionObject(), btBroadphaseProxy::DefaultFilter, btBroadphaseProxy::AllFilter);
|
|
|
+ _world->addCollisionObject(object->getCollisionObject());//, btBroadphaseProxy::DefaultFilter, btBroadphaseProxy::StaticFilter | btBroadphaseProxy::CharacterFilter | btBroadphaseProxy::AllFilter);//, btBroadphaseProxy::DefaultFilter, btBroadphaseProxy::DefaultFilter | btBroadphaseProxy::StaticFilter | btBroadphaseProxy::CharacterFilter);
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
@@ -449,28 +441,8 @@ void PhysicsController::removeCollisionObject(PhysicsCollisionObject* object)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // Release collision shape
|
|
|
- if (object->getCollisionShape())
|
|
|
- {
|
|
|
- PhysicsCollisionShape* shape = reinterpret_cast<PhysicsCollisionShape*>(object->getCollisionShape()->getUserPointer());
|
|
|
- if (shape)
|
|
|
- {
|
|
|
- if (shape->getRefCount() == 1)
|
|
|
- {
|
|
|
- std::vector<PhysicsCollisionShape*>::iterator shapeItr = std::find(_shapes.begin(), _shapes.end(), shape);
|
|
|
- shape->release();
|
|
|
- if (shapeItr != _shapes.end())
|
|
|
- _shapes.erase(shapeItr);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- shape->release();
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
// Find all references to the object in the collision status cache and mark them for removal.
|
|
|
- std::map<PhysicsRigidBody::CollisionPair, CollisionInfo>::iterator iter = _collisionStatus.begin();
|
|
|
+ std::map<PhysicsCollisionObject::CollisionPair, CollisionInfo>::iterator iter = _collisionStatus.begin();
|
|
|
for (; iter != _collisionStatus.end(); iter++)
|
|
|
{
|
|
|
if (iter->first.objectA == object || iter->first.objectB == object)
|
|
|
@@ -484,55 +456,207 @@ PhysicsCollisionObject* PhysicsController::getCollisionObject(const btCollisionO
|
|
|
return reinterpret_cast<PhysicsCollisionObject*>(collisionObject->getUserPointer());
|
|
|
}
|
|
|
|
|
|
-btCollisionShape* PhysicsController::createBox(const Vector3& min, const Vector3& max, const Vector3& scale)
|
|
|
+void getBoundingBox(Node* node, BoundingBox* out, bool merge = false)
|
|
|
{
|
|
|
- btVector3 halfExtents(scale.x * 0.5 * abs(max.x - min.x), scale.y * 0.5 * abs(max.y - min.y), scale.z * 0.5 * abs(max.z - min.z));
|
|
|
+ if (node->getModel())
|
|
|
+ {
|
|
|
+ if (merge)
|
|
|
+ out->merge(node->getModel()->getMesh()->getBoundingBox());
|
|
|
+ else
|
|
|
+ {
|
|
|
+ out->set(node->getModel()->getMesh()->getBoundingBox());
|
|
|
+ merge = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ Node* child = node->getFirstChild();
|
|
|
+ while (child)
|
|
|
+ {
|
|
|
+ getBoundingBox(child, out, merge);
|
|
|
+ child = child->getNextSibling();
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
- // Return the box shape from the cache if it already exists.
|
|
|
- for (unsigned int i = 0; i < _shapes.size(); i++)
|
|
|
+void getBoundingSphere(Node* node, BoundingSphere* out, bool merge = false)
|
|
|
+{
|
|
|
+ if (node->getModel())
|
|
|
+ {
|
|
|
+ if (merge)
|
|
|
+ out->merge(node->getModel()->getMesh()->getBoundingSphere());
|
|
|
+ else
|
|
|
+ {
|
|
|
+ out->set(node->getModel()->getMesh()->getBoundingSphere());
|
|
|
+ merge = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ Node* child = node->getFirstChild();
|
|
|
+ while (child)
|
|
|
+ {
|
|
|
+ getBoundingSphere(child, out, merge);
|
|
|
+ child = child->getNextSibling();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void computeCenterOfMass(const Vector3& center, const Vector3& scale, Vector3* centerOfMassOffset)
|
|
|
+{
|
|
|
+ // Update center of mass offset
|
|
|
+ *centerOfMassOffset = center;
|
|
|
+ centerOfMassOffset->x *= scale.x;
|
|
|
+ centerOfMassOffset->y *= scale.y;
|
|
|
+ centerOfMassOffset->z *= scale.z;
|
|
|
+ centerOfMassOffset->negate();
|
|
|
+}
|
|
|
+
|
|
|
+PhysicsCollisionShape* PhysicsController::createShape(Node* node, const PhysicsCollisionShape::Definition& shape, Vector3* centerOfMassOffset)
|
|
|
+{
|
|
|
+ PhysicsCollisionShape* collisionShape = NULL;
|
|
|
+
|
|
|
+ // Get the node's world scale (we need to apply this during creation since rigid bodies don't scale dynamically).
|
|
|
+ Vector3 scale;
|
|
|
+ node->getWorldMatrix().getScale(&scale);
|
|
|
+
|
|
|
+ switch (shape.type)
|
|
|
{
|
|
|
- if (_shapes[i]->_shape->getShapeType() == BOX_SHAPE_PROXYTYPE)
|
|
|
+ case PhysicsCollisionShape::SHAPE_BOX:
|
|
|
{
|
|
|
- btBoxShape* box = static_cast<btBoxShape*>(_shapes[i]->_shape);
|
|
|
- if (box->getHalfExtentsWithMargin() == halfExtents)
|
|
|
- {
|
|
|
- _shapes[i]->addRef();
|
|
|
- return box;
|
|
|
- }
|
|
|
+ if (shape.isExplicit)
|
|
|
+ {
|
|
|
+ // Use the passed in box information
|
|
|
+ collisionShape = createBox(shape.data.boxExtents, Vector3::one());
|
|
|
+
|
|
|
+ if (shape.centerAbsolute)
|
|
|
+ {
|
|
|
+ computeCenterOfMass(shape.data.boxCenter, scale, centerOfMassOffset);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ BoundingBox box;
|
|
|
+ getBoundingBox(node, &box);
|
|
|
+ computeCenterOfMass(box.getCenter() + shape.data.boxCenter, scale, centerOfMassOffset);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ // Automatically compute bounding box from mesh's bounding box
|
|
|
+ BoundingBox box;
|
|
|
+ getBoundingBox(node, &box);
|
|
|
+ collisionShape = createBox(Vector3(std::abs(box.max.x - box.min.x), std::abs(box.max.y - box.min.y), std::abs(box.max.z - box.min.z)), scale);
|
|
|
+
|
|
|
+ computeCenterOfMass(box.getCenter(), scale, centerOfMassOffset);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ break;
|
|
|
+
|
|
|
+ case PhysicsCollisionShape::SHAPE_SPHERE:
|
|
|
+ {
|
|
|
+ if (shape.isExplicit)
|
|
|
+ {
|
|
|
+ // Use the passed in sphere information
|
|
|
+ collisionShape = createSphere(shape.data.sphereRadius, Vector3::one());
|
|
|
+
|
|
|
+ if (shape.centerAbsolute)
|
|
|
+ {
|
|
|
+ computeCenterOfMass(shape.data.sphereCenter, scale, centerOfMassOffset);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ BoundingSphere sphere;
|
|
|
+ getBoundingSphere(node, &sphere);
|
|
|
+ computeCenterOfMass(sphere.center + shape.data.sphereCenter, scale, centerOfMassOffset);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ // Automatically compute bounding sphere from mesh's bounding sphere
|
|
|
+ BoundingSphere sphere;
|
|
|
+ getBoundingSphere(node, &sphere);
|
|
|
+ collisionShape = createSphere(sphere.radius, scale);
|
|
|
+
|
|
|
+ computeCenterOfMass(sphere.center, scale, centerOfMassOffset);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ break;
|
|
|
+
|
|
|
+ case PhysicsCollisionShape::SHAPE_CAPSULE:
|
|
|
+ {
|
|
|
+ if (shape.isExplicit)
|
|
|
+ {
|
|
|
+ // Use the passed in capsule information
|
|
|
+ collisionShape = createCapsule(shape.data.capsuleRadius, shape.data.capsuleHeight, Vector3::one());
|
|
|
+
|
|
|
+ if (shape.centerAbsolute)
|
|
|
+ {
|
|
|
+ computeCenterOfMass(shape.data.capsuleCenter, scale, centerOfMassOffset);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ BoundingBox box;
|
|
|
+ getBoundingBox(node, &box);
|
|
|
+ computeCenterOfMass(box.getCenter() + shape.data.capsuleCenter, scale, centerOfMassOffset);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ // Compute a capsule shape that roughly matches the bounding box of the mesh
|
|
|
+ BoundingBox box;
|
|
|
+ getBoundingBox(node, &box);
|
|
|
+ float radius = std::max((box.max.x - box.min.x) * 0.5f, (box.max.z - box.min.z) * 0.5f);
|
|
|
+ float height = (box.max.y - box.min.y) - radius * 2.0f;
|
|
|
+ collisionShape = createCapsule(radius, height, scale);
|
|
|
+
|
|
|
+ computeCenterOfMass(box.getCenter(), scale, centerOfMassOffset);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ break;
|
|
|
+
|
|
|
+ case PhysicsCollisionShape::SHAPE_HEIGHTFIELD:
|
|
|
+ {
|
|
|
+ // Build heightfield rigid body from the passed in shape
|
|
|
+ collisionShape = createHeightfield(node, shape.data.heightfield, centerOfMassOffset);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+
|
|
|
+ case PhysicsCollisionShape::SHAPE_MESH:
|
|
|
+ {
|
|
|
+ // Build mesh from passed in shape
|
|
|
+ collisionShape = createMesh(shape.data.mesh, scale);
|
|
|
}
|
|
|
+ break;
|
|
|
}
|
|
|
-
|
|
|
- // Create the box shape and add it to the cache.
|
|
|
- btBoxShape* box = bullet_new<btBoxShape>(halfExtents);
|
|
|
- _shapes.push_back(new PhysicsCollisionShape(box));
|
|
|
|
|
|
- return box;
|
|
|
+ return collisionShape;
|
|
|
}
|
|
|
|
|
|
-btCollisionShape* PhysicsController::createCapsule(float radius, float height)
|
|
|
+PhysicsCollisionShape* PhysicsController::createBox(const Vector3& extents, const Vector3& scale)
|
|
|
{
|
|
|
- // Return the capsule shape from the cache if it already exists.
|
|
|
- for (unsigned int i = 0; i < _shapes.size(); i++)
|
|
|
+ btVector3 halfExtents(scale.x * 0.5 * extents.x, scale.y * 0.5 * extents.y, scale.z * 0.5 * extents.z);
|
|
|
+
|
|
|
+ PhysicsCollisionShape* shape;
|
|
|
+
|
|
|
+ // Return the box shape from the cache if it already exists.
|
|
|
+ for (unsigned int i = 0; i < _shapes.size(); ++i)
|
|
|
{
|
|
|
- if (_shapes[i]->_shape->getShapeType() == CAPSULE_SHAPE_PROXYTYPE)
|
|
|
+ shape = _shapes[i];
|
|
|
+ if (shape->getType() == PhysicsCollisionShape::SHAPE_BOX)
|
|
|
{
|
|
|
- btCapsuleShape* capsule = static_cast<btCapsuleShape*>(_shapes[i]->_shape);
|
|
|
- if (capsule->getRadius() == radius && capsule->getHalfHeight() == 0.5f * height)
|
|
|
+ btBoxShape* box = static_cast<btBoxShape*>(shape->_shape);
|
|
|
+ if (box->getHalfExtentsWithMargin() == halfExtents)
|
|
|
{
|
|
|
- _shapes[i]->addRef();
|
|
|
- return capsule;
|
|
|
+ shape->addRef();
|
|
|
+ return shape;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- // Create the capsule shape and add it to the cache.
|
|
|
- btCapsuleShape* capsule = bullet_new<btCapsuleShape>(radius, height);
|
|
|
- _shapes.push_back(new PhysicsCollisionShape(capsule));
|
|
|
|
|
|
- return capsule;
|
|
|
+ // Create the box shape and add it to the cache.
|
|
|
+ shape = new PhysicsCollisionShape(PhysicsCollisionShape::SHAPE_BOX, bullet_new<btBoxShape>(halfExtents));
|
|
|
+ _shapes.push_back(shape);
|
|
|
+
|
|
|
+ return shape;
|
|
|
}
|
|
|
|
|
|
-btCollisionShape* PhysicsController::createSphere(float radius, const Vector3& scale)
|
|
|
+PhysicsCollisionShape* PhysicsController::createSphere(float radius, const Vector3& scale)
|
|
|
{
|
|
|
// Since sphere shapes depend only on the radius, the best we can do is take
|
|
|
// the largest dimension and apply that as the uniform scale to the rigid body.
|
|
|
@@ -541,48 +665,188 @@ btCollisionShape* PhysicsController::createSphere(float radius, const Vector3& s
|
|
|
uniformScale = scale.y;
|
|
|
if (uniformScale < scale.z)
|
|
|
uniformScale = scale.z;
|
|
|
-
|
|
|
+
|
|
|
+ float scaledRadius = radius * uniformScale;
|
|
|
+
|
|
|
+ PhysicsCollisionShape* shape;
|
|
|
+
|
|
|
// Return the sphere shape from the cache if it already exists.
|
|
|
- for (unsigned int i = 0; i < _shapes.size(); i++)
|
|
|
+ for (unsigned int i = 0; i < _shapes.size(); ++i)
|
|
|
{
|
|
|
- if (_shapes[i]->_shape->getShapeType() == SPHERE_SHAPE_PROXYTYPE)
|
|
|
+ shape = _shapes[i];
|
|
|
+ if (shape->getType() == PhysicsCollisionShape::SHAPE_SPHERE)
|
|
|
{
|
|
|
- btSphereShape* sphere = static_cast<btSphereShape*>(_shapes[i]->_shape);
|
|
|
- if (sphere->getRadius() == uniformScale * radius)
|
|
|
+ btSphereShape* sphere = static_cast<btSphereShape*>(shape->_shape);
|
|
|
+ if (sphere->getRadius() == scaledRadius)
|
|
|
{
|
|
|
- _shapes[i]->addRef();
|
|
|
- return sphere;
|
|
|
+ shape->addRef();
|
|
|
+ return shape;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// Create the sphere shape and add it to the cache.
|
|
|
- btSphereShape* sphere = bullet_new<btSphereShape>(uniformScale * radius);
|
|
|
- _shapes.push_back(new PhysicsCollisionShape(sphere));
|
|
|
+ shape = new PhysicsCollisionShape(PhysicsCollisionShape::SHAPE_SPHERE, bullet_new<btSphereShape>(scaledRadius));
|
|
|
+ _shapes.push_back(shape);
|
|
|
|
|
|
- return sphere;
|
|
|
+ return shape;
|
|
|
}
|
|
|
|
|
|
-btCollisionShape* PhysicsController::createMesh(PhysicsRigidBody* body, const Vector3& scale)
|
|
|
+PhysicsCollisionShape* PhysicsController::createCapsule(float radius, float height, const Vector3& scale)
|
|
|
{
|
|
|
- assert(body);
|
|
|
+ float girthScale = scale.x;
|
|
|
+ if (girthScale < scale.z)
|
|
|
+ girthScale = scale.z;
|
|
|
+ float scaledRadius = radius * girthScale;
|
|
|
+ float scaledHeight = height * scale.y;
|
|
|
|
|
|
- // Retrieve the mesh rigid body data from the node's mesh.
|
|
|
- Model* model = body->_node ? body->_node->getModel() : NULL;
|
|
|
- Mesh* mesh = model ? model->getMesh() : NULL;
|
|
|
- if (mesh == NULL)
|
|
|
+ PhysicsCollisionShape* shape;
|
|
|
+
|
|
|
+ // Return the capsule shape from the cache if it already exists.
|
|
|
+ for (unsigned int i = 0; i < _shapes.size(); i++)
|
|
|
{
|
|
|
- LOG_ERROR("Cannot create mesh rigid body for node without model/mesh.");
|
|
|
- return NULL;
|
|
|
+ shape = _shapes[i];
|
|
|
+ if (shape->getType() == PhysicsCollisionShape::SHAPE_CAPSULE)
|
|
|
+ {
|
|
|
+ btCapsuleShape* capsule = static_cast<btCapsuleShape*>(shape->_shape);
|
|
|
+ if (capsule->getRadius() == scaledRadius && capsule->getHalfHeight() == 0.5f * scaledHeight)
|
|
|
+ {
|
|
|
+ shape->addRef();
|
|
|
+ return shape;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Create the capsule shape and add it to the cache.
|
|
|
+ shape = new PhysicsCollisionShape(PhysicsCollisionShape::SHAPE_CAPSULE, bullet_new<btCapsuleShape>(scaledRadius, scaledHeight));
|
|
|
+ _shapes.push_back(shape);
|
|
|
+
|
|
|
+ return shape;
|
|
|
+}
|
|
|
+
|
|
|
+PhysicsCollisionShape* PhysicsController::createHeightfield(Node* node, Image* image, Vector3* centerOfMassOffset)
|
|
|
+{
|
|
|
+ // Get the dimensions of the heightfield.
|
|
|
+ // If the node has a mesh defined, use the dimensions of the bounding box for the mesh.
|
|
|
+ // Otherwise simply use the image dimensions (with a max height of 255).
|
|
|
+ float width, length, minHeight, maxHeight;
|
|
|
+ if (node->getModel() && node->getModel()->getMesh())
|
|
|
+ {
|
|
|
+ const BoundingBox& box = node->getModel()->getMesh()->getBoundingBox();
|
|
|
+ width = box.max.x - box.min.x;
|
|
|
+ length = box.max.z - box.min.z;
|
|
|
+ minHeight = box.min.y;
|
|
|
+ maxHeight = box.max.y;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ width = image->getWidth();
|
|
|
+ length = image->getHeight();
|
|
|
+ minHeight = 0.0f;
|
|
|
+ maxHeight = 255.0f;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Get the size in bytes of a pixel (we ensure that the image's
|
|
|
+ // pixel format is actually supported before calling this constructor).
|
|
|
+ unsigned int pixelSize = 0;
|
|
|
+ switch (image->getFormat())
|
|
|
+ {
|
|
|
+ case Image::RGB:
|
|
|
+ pixelSize = 3;
|
|
|
+ break;
|
|
|
+ case Image::RGBA:
|
|
|
+ pixelSize = 4;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ LOG_ERROR("Unsupported pixel format for heightmap image.");
|
|
|
+ return NULL;
|
|
|
}
|
|
|
|
|
|
- // Only support meshes with triangle list primitive types
|
|
|
- if (mesh->getPrimitiveType() != Mesh::TRIANGLES)
|
|
|
+ // Calculate the heights for each pixel.
|
|
|
+ float* heights = new float[image->getWidth() * image->getHeight()];
|
|
|
+ unsigned char* data = image->getData();
|
|
|
+ for (unsigned int x = 0, w = image->getWidth(); x < w; ++x)
|
|
|
{
|
|
|
- LOG_ERROR("Cannot create mesh rigid body for mesh without TRIANGLES primitive type.");
|
|
|
- return NULL;
|
|
|
+ for (unsigned int y = 0, h = image->getHeight(); y < h; ++y)
|
|
|
+ {
|
|
|
+ heights[x + y * w] = ((((float)data[(x + y * h) * pixelSize + 0]) +
|
|
|
+ ((float)data[(x + y * h) * pixelSize + 1]) +
|
|
|
+ ((float)data[(x + y * h) * pixelSize + 2])) / 768.0f) * (maxHeight - minHeight) + minHeight;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
+ PhysicsCollisionShape::HeightfieldData* heightfieldData = new PhysicsCollisionShape::HeightfieldData();
|
|
|
+ heightfieldData->heightData = NULL;
|
|
|
+ heightfieldData->inverseIsDirty = true;
|
|
|
+
|
|
|
+ // Generate the heightmap data needed for physics (one height per world unit).
|
|
|
+ unsigned int sizeWidth = width;
|
|
|
+ unsigned int sizeHeight = length;
|
|
|
+ heightfieldData->width = sizeWidth + 1;
|
|
|
+ heightfieldData->height = sizeHeight + 1;
|
|
|
+ heightfieldData->heightData = new float[heightfieldData->width * heightfieldData->height];
|
|
|
+ unsigned int heightIndex = 0;
|
|
|
+ float widthImageFactor = (float)(image->getWidth() - 1) / sizeWidth;
|
|
|
+ float heightImageFactor = (float)(image->getHeight() - 1) / sizeHeight;
|
|
|
+ float x = 0.0f;
|
|
|
+ float z = 0.0f;
|
|
|
+ for (unsigned int row = 0, z = 0.0f; row <= sizeHeight; row++, z += 1.0f)
|
|
|
+ {
|
|
|
+ for (unsigned int col = 0, x = 0.0f; col <= sizeWidth; col++, x += 1.0f)
|
|
|
+ {
|
|
|
+ heightIndex = row * heightfieldData->width + col;
|
|
|
+ heightfieldData->heightData[heightIndex] = calculateHeight(heights, image->getWidth(), image->getHeight(), x * widthImageFactor, (sizeHeight - z) * heightImageFactor);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ SAFE_DELETE_ARRAY(heights);
|
|
|
+
|
|
|
+ // Offset the heightmap's center of mass according to the way that Bullet calculates the origin
|
|
|
+ // of its heightfield collision shape; see documentation for the btHeightfieldTerrainShape for more info.
|
|
|
+ Vector3 s;
|
|
|
+ node->getWorldMatrix().getScale(&s);
|
|
|
+ centerOfMassOffset->set(0.0f, -(maxHeight - (0.5f * (maxHeight - minHeight))) / s.y, 0.0f);
|
|
|
+
|
|
|
+ // Create the bullet terrain shape
|
|
|
+ btHeightfieldTerrainShape* terrainShape = bullet_new<btHeightfieldTerrainShape>(
|
|
|
+ heightfieldData->width, heightfieldData->height, heightfieldData->heightData, 1.0f, minHeight, maxHeight, 1, PHY_FLOAT, false);
|
|
|
+
|
|
|
+ // Create our collision shape object and store heightfieldData in it
|
|
|
+ PhysicsCollisionShape* shape = new PhysicsCollisionShape(PhysicsCollisionShape::SHAPE_HEIGHTFIELD, terrainShape);
|
|
|
+ shape->_shapeData.heightfieldData = heightfieldData;
|
|
|
+
|
|
|
+ _shapes.push_back(shape);
|
|
|
+
|
|
|
+ return shape;
|
|
|
+}
|
|
|
+
|
|
|
+PhysicsCollisionShape* PhysicsController::createMesh(Mesh* mesh, const Vector3& scale)
|
|
|
+{
|
|
|
+ assert(mesh);
|
|
|
+
|
|
|
+ // Only support meshes with triangle list primitive types
|
|
|
+ bool triMesh = true;
|
|
|
+ if (mesh->getPartCount() > 0)
|
|
|
+ {
|
|
|
+ for (unsigned int i = 0; i < mesh->getPartCount(); ++i)
|
|
|
+ {
|
|
|
+ if (mesh->getPart(i)->getPrimitiveType() != Mesh::TRIANGLES)
|
|
|
+ {
|
|
|
+ triMesh = false;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ triMesh = mesh->getPrimitiveType() == Mesh::TRIANGLES;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!triMesh)
|
|
|
+ {
|
|
|
+ LOG_ERROR("Mesh rigid bodies are currently only supported on meshes with TRIANGLES primitive type.");
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+
|
|
|
// The mesh must have a valid URL (i.e. it must have been loaded from a Package)
|
|
|
// in order to fetch mesh data for computing mesh rigid body.
|
|
|
if (strlen(mesh->getUrl()) == 0)
|
|
|
@@ -597,11 +861,15 @@ btCollisionShape* PhysicsController::createMesh(PhysicsRigidBody* body, const Ve
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
+ // Create mesh data to be populated and store in returned collision shape
|
|
|
+ PhysicsCollisionShape::MeshData* shapeMeshData = new PhysicsCollisionShape::MeshData();
|
|
|
+ shapeMeshData->vertexData = NULL;
|
|
|
+
|
|
|
// Copy the scaled vertex position data to the rigid body's local buffer.
|
|
|
Matrix m;
|
|
|
Matrix::createScale(scale, &m);
|
|
|
unsigned int vertexCount = data->vertexCount;
|
|
|
- body->_vertexData = new float[vertexCount * 3];
|
|
|
+ shapeMeshData->vertexData = new float[vertexCount * 3];
|
|
|
Vector3 v;
|
|
|
int vertexStride = data->vertexFormat.getVertexSize();
|
|
|
for (unsigned int i = 0; i < data->vertexCount; i++)
|
|
|
@@ -610,7 +878,7 @@ btCollisionShape* PhysicsController::createMesh(PhysicsRigidBody* body, const Ve
|
|
|
*((float*)&data->vertexData[i * vertexStride + 1 * sizeof(float)]),
|
|
|
*((float*)&data->vertexData[i * vertexStride + 2 * sizeof(float)]));
|
|
|
v *= m;
|
|
|
- memcpy(&(body->_vertexData[i * 3]), &v, sizeof(float) * 3);
|
|
|
+ memcpy(&(shapeMeshData->vertexData[i * 3]), &v, sizeof(float) * 3);
|
|
|
}
|
|
|
|
|
|
btTriangleIndexVertexArray* meshInterface = bullet_new<btTriangleIndexVertexArray>();
|
|
|
@@ -643,7 +911,7 @@ btCollisionShape* PhysicsController::createMesh(PhysicsRigidBody* body, const Ve
|
|
|
|
|
|
// Move the index data into the rigid body's local buffer.
|
|
|
// Set it to NULL in the MeshPartData so it is not released when the data is freed.
|
|
|
- body->_indexData.push_back(meshPart->indexData);
|
|
|
+ shapeMeshData->indexData.push_back(meshPart->indexData);
|
|
|
meshPart->indexData = NULL;
|
|
|
|
|
|
// Create a btIndexedMesh object for the current mesh part.
|
|
|
@@ -651,9 +919,9 @@ btCollisionShape* PhysicsController::createMesh(PhysicsRigidBody* body, const Ve
|
|
|
indexedMesh.m_indexType = indexType;
|
|
|
indexedMesh.m_numTriangles = meshPart->indexCount / 3; // assume TRIANGLES primitive type
|
|
|
indexedMesh.m_numVertices = meshPart->indexCount;
|
|
|
- indexedMesh.m_triangleIndexBase = (const unsigned char*)body->_indexData[i];
|
|
|
+ indexedMesh.m_triangleIndexBase = (const unsigned char*)shapeMeshData->indexData[i];
|
|
|
indexedMesh.m_triangleIndexStride = indexStride*3;
|
|
|
- indexedMesh.m_vertexBase = (const unsigned char*)body->_vertexData;
|
|
|
+ indexedMesh.m_vertexBase = (const unsigned char*)shapeMeshData->vertexData;
|
|
|
indexedMesh.m_vertexStride = sizeof(float)*3;
|
|
|
indexedMesh.m_vertexType = PHY_FLOAT;
|
|
|
|
|
|
@@ -669,16 +937,16 @@ btCollisionShape* PhysicsController::createMesh(PhysicsRigidBody* body, const Ve
|
|
|
{
|
|
|
indexData[i] = i;
|
|
|
}
|
|
|
- body->_indexData.push_back((unsigned char*)indexData);
|
|
|
+ shapeMeshData->indexData.push_back((unsigned char*)indexData);
|
|
|
|
|
|
// Create a single btIndexedMesh object for the mesh interface.
|
|
|
btIndexedMesh indexedMesh;
|
|
|
indexedMesh.m_indexType = PHY_INTEGER;
|
|
|
indexedMesh.m_numTriangles = data->vertexCount / 3; // assume TRIANGLES primitive type
|
|
|
indexedMesh.m_numVertices = data->vertexCount;
|
|
|
- indexedMesh.m_triangleIndexBase = body->_indexData[0];
|
|
|
+ indexedMesh.m_triangleIndexBase = shapeMeshData->indexData[0];
|
|
|
indexedMesh.m_triangleIndexStride = sizeof(unsigned int);
|
|
|
- indexedMesh.m_vertexBase = (const unsigned char*)body->_vertexData;
|
|
|
+ indexedMesh.m_vertexBase = (const unsigned char*)shapeMeshData->vertexData;
|
|
|
indexedMesh.m_vertexStride = sizeof(float)*3;
|
|
|
indexedMesh.m_vertexType = PHY_FLOAT;
|
|
|
|
|
|
@@ -686,8 +954,11 @@ btCollisionShape* PhysicsController::createMesh(PhysicsRigidBody* body, const Ve
|
|
|
meshInterface->addIndexedMesh(indexedMesh, indexedMesh.m_indexType);
|
|
|
}
|
|
|
|
|
|
- btBvhTriangleMeshShape* shape = bullet_new<btBvhTriangleMeshShape>(meshInterface, true);
|
|
|
- _shapes.push_back(new PhysicsCollisionShape(shape));
|
|
|
+ // Create our collision shape object and store shapeMeshData in it
|
|
|
+ PhysicsCollisionShape* shape = new PhysicsCollisionShape(PhysicsCollisionShape::SHAPE_MESH, bullet_new<btBvhTriangleMeshShape>(meshInterface, true));
|
|
|
+ shape->_shapeData.meshData = shapeMeshData;
|
|
|
+
|
|
|
+ _shapes.push_back(shape);
|
|
|
|
|
|
// Free the temporary mesh data now that it's stored in physics system
|
|
|
SAFE_DELETE(data);
|
|
|
@@ -695,11 +966,52 @@ btCollisionShape* PhysicsController::createMesh(PhysicsRigidBody* body, const Ve
|
|
|
return shape;
|
|
|
}
|
|
|
|
|
|
-btCollisionShape* PhysicsController::createHeightfield(int width, int height, void* heightfieldData, float minHeight, float maxHeight)
|
|
|
+void PhysicsController::destroyShape(PhysicsCollisionShape* shape)
|
|
|
{
|
|
|
- btCollisionShape* shape = bullet_new<btHeightfieldTerrainShape>(width, height, heightfieldData, 1.0f, minHeight, maxHeight, 1, PHY_FLOAT, false);
|
|
|
- _shapes.push_back(new PhysicsCollisionShape(shape));
|
|
|
- return shape;
|
|
|
+ if (shape)
|
|
|
+ {
|
|
|
+ if (shape->getRefCount() == 1)
|
|
|
+ {
|
|
|
+ // Remove shape from shape cache
|
|
|
+ std::vector<PhysicsCollisionShape*>::iterator shapeItr = std::find(_shapes.begin(), _shapes.end(), shape);
|
|
|
+ if (shapeItr != _shapes.end())
|
|
|
+ _shapes.erase(shapeItr);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Release the shape
|
|
|
+ shape->release();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+float PhysicsController::calculateHeight(float* data, unsigned int width, unsigned int height, float x, float y)
|
|
|
+{
|
|
|
+ unsigned int x1 = x;
|
|
|
+ unsigned int y1 = y;
|
|
|
+ unsigned int x2 = x1 + 1;
|
|
|
+ unsigned int y2 = y1 + 1;
|
|
|
+ float tmp;
|
|
|
+ float xFactor = modf(x, &tmp);
|
|
|
+ float yFactor = modf(y, &tmp);
|
|
|
+ float xFactorI = 1.0f - xFactor;
|
|
|
+ float yFactorI = 1.0f - yFactor;
|
|
|
+
|
|
|
+ if (x2 >= width && y2 >= height)
|
|
|
+ {
|
|
|
+ return data[x1 + y1 * width];
|
|
|
+ }
|
|
|
+ else if (x2 >= width)
|
|
|
+ {
|
|
|
+ return data[x1 + y1 * width] * yFactorI + data[x1 + y2 * width] * yFactor;
|
|
|
+ }
|
|
|
+ else if (y2 >= height)
|
|
|
+ {
|
|
|
+ return data[x1 + y1 * width] * xFactorI + data[x2 + y1 * width] * xFactor;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ return data[x1 + y1 * width] * xFactorI * yFactorI + data[x1 + y2 * width] * xFactorI * yFactor +
|
|
|
+ data[x2 + y2 * width] * xFactor * yFactor + data[x2 + y1 * width] * xFactor * yFactorI;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
void PhysicsController::addConstraint(PhysicsRigidBody* a, PhysicsRigidBody* b, PhysicsConstraint* constraint)
|
|
|
@@ -743,7 +1055,7 @@ void PhysicsController::removeConstraint(PhysicsConstraint* constraint)
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
PhysicsController::DebugDrawer::DebugDrawer()
|
|
|
: _mode(btIDebugDraw::DBG_DrawAabb | btIDebugDraw::DBG_DrawConstraintLimits | btIDebugDraw::DBG_DrawConstraints |
|
|
|
btIDebugDraw::DBG_DrawContactPoints | btIDebugDraw::DBG_DrawWireframe), _viewProjection(NULL), _meshBatch(NULL)
|
|
|
@@ -760,7 +1072,7 @@ PhysicsController::DebugDrawer::DebugDrawer()
|
|
|
" gl_Position = u_viewProjectionMatrix * a_position;\n"
|
|
|
"}"
|
|
|
};
|
|
|
-
|
|
|
+
|
|
|
// Fragment shader for drawing colored lines.
|
|
|
const char* fs_str =
|
|
|
{
|
|
|
@@ -772,7 +1084,7 @@ PhysicsController::DebugDrawer::DebugDrawer()
|
|
|
" gl_FragColor = v_color;\n"
|
|
|
"}"
|
|
|
};
|
|
|
-
|
|
|
+
|
|
|
Effect* effect = Effect::createFromSource(vs_str, fs_str);
|
|
|
Material* material = Material::create(effect);
|
|
|
material->getStateBlock()->setDepthTest(true);
|
|
|
@@ -783,7 +1095,7 @@ PhysicsController::DebugDrawer::DebugDrawer()
|
|
|
VertexFormat::Element(VertexFormat::COLOR, 4),
|
|
|
};
|
|
|
_meshBatch = MeshBatch::create(VertexFormat(elements, 2), Mesh::LINES, material, false);
|
|
|
-
|
|
|
+
|
|
|
SAFE_RELEASE(material);
|
|
|
SAFE_RELEASE(effect);
|
|
|
}
|
|
|
@@ -809,7 +1121,7 @@ void PhysicsController::DebugDrawer::end()
|
|
|
void PhysicsController::DebugDrawer::drawLine(const btVector3& from, const btVector3& to, const btVector3& fromColor, const btVector3& toColor)
|
|
|
{
|
|
|
static DebugDrawer::DebugVertex fromVertex, toVertex;
|
|
|
-
|
|
|
+
|
|
|
fromVertex.x = from.getX();
|
|
|
fromVertex.y = from.getY();
|
|
|
fromVertex.z = from.getZ();
|
|
|
@@ -860,16 +1172,4 @@ int PhysicsController::DebugDrawer::getDebugMode() const
|
|
|
return _mode;
|
|
|
}
|
|
|
|
|
|
-PhysicsController::PhysicsCollisionShape::PhysicsCollisionShape(btCollisionShape* shape)
|
|
|
- : _shape(shape)
|
|
|
-{
|
|
|
- // Assign user pointer to allow efficient lookup of PhysicsCollisionShape from bullet object
|
|
|
- shape->setUserPointer(this);
|
|
|
-}
|
|
|
-
|
|
|
-PhysicsController::PhysicsCollisionShape::~PhysicsCollisionShape()
|
|
|
-{
|
|
|
- SAFE_DELETE(_shape);
|
|
|
-}
|
|
|
-
|
|
|
}
|