You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
openmw-tes3mp/libs/openengine/bullet/physic.cpp

950 lines
33 KiB
C++

#include "physic.hpp"
#include <btBulletDynamicsCommon.h>
#include <btBulletCollisionCommon.h>
#include <BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h>
#include <boost/lexical_cast.hpp>
#include <boost/format.hpp>
#include <OgreSceneManager.h>
#include <components/nifbullet/bulletnifloader.hpp>
#include <components/misc/stringops.hpp>
#include "BtOgrePG.h"
#include "BtOgreGP.h"
#include "BtOgreExtras.h"
namespace
{
// Create a copy of the given collision shape (responsibility of user to delete the returned shape).
btCollisionShape *duplicateCollisionShape(btCollisionShape *shape)
{
if(shape->isCompound())
{
btCompoundShape *comp = static_cast<btCompoundShape*>(shape);
btCompoundShape *newShape = new btCompoundShape;
int numShapes = comp->getNumChildShapes();
for(int i = 0;i < numShapes;i++)
{
btCollisionShape *child = duplicateCollisionShape(comp->getChildShape(i));
btTransform trans = comp->getChildTransform(i);
newShape->addChildShape(trans, child);
}
return newShape;
}
if(btBvhTriangleMeshShape *trishape = dynamic_cast<btBvhTriangleMeshShape*>(shape))
{
btTriangleMesh* oldMesh = static_cast<btTriangleMesh*>(trishape->getMeshInterface());
btTriangleMesh* newMesh = new btTriangleMesh(*oldMesh);
NifBullet::TriangleMeshShape *newShape = new NifBullet::TriangleMeshShape(newMesh, true);
return newShape;
}
throw std::logic_error(std::string("Unhandled Bullet shape duplication: ")+shape->getName());
}
void deleteShape(btCollisionShape* shape)
{
if(shape!=NULL)
{
if(shape->isCompound())
{
btCompoundShape* ms = static_cast<btCompoundShape*>(shape);
int a = ms->getNumChildShapes();
for(int i=0; i <a;i++)
{
deleteShape(ms->getChildShape(i));
}
}
delete shape;
}
}
}
namespace OEngine {
namespace Physic
{
PhysicActor::PhysicActor(const std::string &name, const std::string &mesh, PhysicEngine *engine, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation, float scale)
: mCanWaterWalk(false), mWalkingOnWater(false)
, mBody(0), mScale(scale), mForce(0.0f), mOnGround(false)
, mInternalCollisionMode(true)
, mExternalCollisionMode(true)
, mMesh(mesh), mName(name), mEngine(engine)
{
if (!NifBullet::getBoundingBox(mMesh, mHalfExtents, mMeshTranslation, mMeshOrientation))
{
mHalfExtents = Ogre::Vector3(0.f);
mMeshTranslation = Ogre::Vector3(0.f);
mMeshOrientation = Ogre::Quaternion::IDENTITY;
}
// Use capsule shape only if base is square (nonuniform scaling apparently doesn't work on it)
if (std::abs(mHalfExtents.x-mHalfExtents.y)<mHalfExtents.x*0.05 && mHalfExtents.z >= mHalfExtents.x)
{
// Could also be btCapsuleShapeZ, but the movement solver seems to have issues with it (jumping on slopes doesn't work)
mShape.reset(new btCylinderShapeZ(BtOgre::Convert::toBullet(mHalfExtents)));
}
else
mShape.reset(new btBoxShape(BtOgre::Convert::toBullet(mHalfExtents)));
mShape->setLocalScaling(btVector3(scale,scale,scale));
btRigidBody::btRigidBodyConstructionInfo CI = btRigidBody::btRigidBodyConstructionInfo
(0,0, mShape.get());
mBody = new RigidBody(CI, name);
mBody->mPlaceable = false;
mBody->setCollisionFlags(btCollisionObject::CF_KINEMATIC_OBJECT);
mBody->setActivationState(DISABLE_DEACTIVATION);
setPosition(position);
setRotation(rotation);
updateCollisionMask();
}
PhysicActor::~PhysicActor()
{
if(mBody)
{
mEngine->mDynamicsWorld->removeRigidBody(mBody);
delete mBody;
}
}
void PhysicActor::enableCollisionMode(bool collision)
{
mInternalCollisionMode = collision;
}
void PhysicActor::enableCollisionBody(bool collision)
{
if (mExternalCollisionMode != collision)
{
mExternalCollisionMode = collision;
updateCollisionMask();
}
}
void PhysicActor::updateCollisionMask()
{
mEngine->mDynamicsWorld->removeRigidBody(mBody);
int collisionMask = CollisionType_World | CollisionType_HeightMap;
if (mExternalCollisionMode)
collisionMask |= CollisionType_Actor | CollisionType_Projectile;
if (mCanWaterWalk)
collisionMask |= CollisionType_Water;
mEngine->mDynamicsWorld->addRigidBody(mBody, CollisionType_Actor, collisionMask);
}
const Ogre::Vector3& PhysicActor::getPosition() const
{
return mPosition;
}
void PhysicActor::setPosition(const Ogre::Vector3 &position)
{
assert(mBody);
mPosition = position;
btTransform tr = mBody->getWorldTransform();
Ogre::Quaternion meshrot = mMeshOrientation;
Ogre::Vector3 transrot = meshrot * (mMeshTranslation * mScale);
Ogre::Vector3 newPosition = transrot + position;
tr.setOrigin(BtOgre::Convert::toBullet(newPosition));
mBody->setWorldTransform(tr);
}
void PhysicActor::setRotation (const Ogre::Quaternion& rotation)
{
btTransform tr = mBody->getWorldTransform();
tr.setRotation(BtOgre::Convert::toBullet(mMeshOrientation * rotation));
mBody->setWorldTransform(tr);
}
void PhysicActor::setScale(float scale)
{
mScale = scale;
mShape->setLocalScaling(btVector3(scale,scale,scale));
setPosition(mPosition);
}
Ogre::Vector3 PhysicActor::getHalfExtents() const
{
return mHalfExtents * mScale;
}
void PhysicActor::setInertialForce(const Ogre::Vector3 &force)
{
mForce = force;
}
void PhysicActor::setOnGround(bool grounded)
{
mOnGround = grounded;
}
bool PhysicActor::isWalkingOnWater() const
{
return mWalkingOnWater;
}
void PhysicActor::setWalkingOnWater(bool walkingOnWater)
{
mWalkingOnWater = walkingOnWater;
}
void PhysicActor::setCanWaterWalk(bool waterWalk)
{
if (waterWalk != mCanWaterWalk)
{
mCanWaterWalk = waterWalk;
updateCollisionMask();
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
RigidBody::RigidBody(btRigidBody::btRigidBodyConstructionInfo& CI,std::string name)
: btRigidBody(CI)
, mName(name)
, mPlaceable(false)
{
}
RigidBody::~RigidBody()
{
delete getMotionState();
}
///////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////
PhysicEngine::PhysicEngine(BulletShapeLoader* shapeLoader) :
mSceneMgr(NULL)
, mDebugActive(0)
{
// Set up the collision configuration and dispatcher
collisionConfiguration = new btDefaultCollisionConfiguration();
dispatcher = new btCollisionDispatcher(collisionConfiguration);
// The actual physics solver
solver = new btSequentialImpulseConstraintSolver;
broadphase = new btDbvtBroadphase();
// The world.
mDynamicsWorld = new btDiscreteDynamicsWorld(dispatcher,broadphase,solver,collisionConfiguration);
// Don't update AABBs of all objects every frame. Most objects in MW are static, so we don't need this.
// Should a "static" object ever be moved, we have to update its AABB manually using DynamicsWorld::updateSingleAabb.
mDynamicsWorld->setForceUpdateAllAabbs(false);
mDynamicsWorld->setGravity(btVector3(0,0,-10));
if(BulletShapeManager::getSingletonPtr() == NULL)
{
new BulletShapeManager();
}
//TODO:singleton?
mShapeLoader = shapeLoader;
isDebugCreated = false;
mDebugDrawer = NULL;
}
void PhysicEngine::createDebugRendering()
{
if(!isDebugCreated)
{
Ogre::SceneNode* node = mSceneMgr->getRootSceneNode()->createChildSceneNode();
mDebugDrawer = new BtOgre::DebugDrawer(node, mDynamicsWorld);
mDynamicsWorld->setDebugDrawer(mDebugDrawer);
isDebugCreated = true;
mDynamicsWorld->debugDrawWorld();
}
}
void PhysicEngine::setDebugRenderingMode(bool isDebug)
{
if(!isDebugCreated)
{
createDebugRendering();
}
mDebugDrawer->setDebugMode(isDebug);
mDebugActive = isDebug;
}
bool PhysicEngine::toggleDebugRendering()
{
setDebugRenderingMode(!mDebugActive);
return mDebugActive;
}
void PhysicEngine::setSceneManager(Ogre::SceneManager* sceneMgr)
{
mSceneMgr = sceneMgr;
}
PhysicEngine::~PhysicEngine()
{
for (std::map<RigidBody*, AnimatedShapeInstance>::iterator it = mAnimatedShapes.begin(); it != mAnimatedShapes.end(); ++it)
deleteShape(it->second.mCompound);
for (std::map<RigidBody*, AnimatedShapeInstance>::iterator it = mAnimatedRaycastingShapes.begin(); it != mAnimatedRaycastingShapes.end(); ++it)
deleteShape(it->second.mCompound);
HeightFieldContainer::iterator hf_it = mHeightFieldMap.begin();
for (; hf_it != mHeightFieldMap.end(); ++hf_it)
{
mDynamicsWorld->removeRigidBody(hf_it->second.mBody);
delete hf_it->second.mShape;
delete hf_it->second.mBody;
}
RigidBodyContainer::iterator rb_it = mCollisionObjectMap.begin();
for (; rb_it != mCollisionObjectMap.end(); ++rb_it)
{
if (rb_it->second != NULL)
{
mDynamicsWorld->removeRigidBody(rb_it->second);
delete rb_it->second;
rb_it->second = NULL;
}
}
rb_it = mRaycastingObjectMap.begin();
for (; rb_it != mRaycastingObjectMap.end(); ++rb_it)
{
if (rb_it->second != NULL)
{
mDynamicsWorld->removeRigidBody(rb_it->second);
delete rb_it->second;
rb_it->second = NULL;
}
}
PhysicActorContainer::iterator pa_it = mActorMap.begin();
for (; pa_it != mActorMap.end(); ++pa_it)
{
if (pa_it->second != NULL)
{
delete pa_it->second;
pa_it->second = NULL;
}
}
delete mDebugDrawer;
delete mDynamicsWorld;
delete solver;
delete collisionConfiguration;
delete dispatcher;
delete broadphase;
delete mShapeLoader;
// Moved the cleanup to mwworld/physicssystem
//delete BulletShapeManager::getSingletonPtr();
}
void PhysicEngine::addHeightField(float* heights,
int x, int y, float yoffset,
float triSize, float sqrtVerts)
{
const std::string name = "HeightField_"
+ boost::lexical_cast<std::string>(x) + "_"
+ boost::lexical_cast<std::string>(y);
// find the minimum and maximum heights (needed for bullet)
float minh = heights[0];
float maxh = heights[0];
for (int i=0; i<sqrtVerts*sqrtVerts; ++i)
{
float h = heights[i];
if (h>maxh) maxh = h;
if (h<minh) minh = h;
}
btHeightfieldTerrainShape* hfShape = new btHeightfieldTerrainShape(
static_cast<int>(sqrtVerts), static_cast<int>(sqrtVerts), heights, 1,
minh, maxh, 2,
PHY_FLOAT,true);
hfShape->setUseDiamondSubdivision(true);
btVector3 scl(triSize, triSize, 1);
hfShape->setLocalScaling(scl);
btRigidBody::btRigidBodyConstructionInfo CI = btRigidBody::btRigidBodyConstructionInfo(0,0,hfShape);
RigidBody* body = new RigidBody(CI,name);
body->getWorldTransform().setOrigin(btVector3( (x+0.5f)*triSize*(sqrtVerts-1), (y+0.5f)*triSize*(sqrtVerts-1), (maxh+minh)/2.f));
HeightField hf;
hf.mBody = body;
hf.mShape = hfShape;
mHeightFieldMap [name] = hf;
mDynamicsWorld->addRigidBody(body,CollisionType_HeightMap,
CollisionType_Actor|CollisionType_Raycasting|CollisionType_Projectile);
}
void PhysicEngine::removeHeightField(int x, int y)
{
const std::string name = "HeightField_"
+ boost::lexical_cast<std::string>(x) + "_"
+ boost::lexical_cast<std::string>(y);
HeightFieldContainer::iterator it = mHeightFieldMap.find(name);
if (it == mHeightFieldMap.end())
return;
const HeightField& hf = it->second;
mDynamicsWorld->removeRigidBody(hf.mBody);
delete hf.mShape;
delete hf.mBody;
mHeightFieldMap.erase(it);
}
void PhysicEngine::adjustRigidBody(RigidBody* body, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation,
const Ogre::Vector3 &scaledBoxTranslation, const Ogre::Quaternion &boxRotation)
{
btTransform tr;
Ogre::Quaternion boxrot = rotation * boxRotation;
Ogre::Vector3 transrot = boxrot * scaledBoxTranslation;
Ogre::Vector3 newPosition = transrot + position;
tr.setOrigin(btVector3(newPosition.x, newPosition.y, newPosition.z));
tr.setRotation(btQuaternion(boxrot.x,boxrot.y,boxrot.z,boxrot.w));
body->setWorldTransform(tr);
}
void PhysicEngine::boxAdjustExternal(const std::string &mesh, RigidBody* body,
float scale, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation)
{
std::string sid = (boost::format("%07.3f") % scale).str();
std::string outputstring = mesh + sid;
//get the shape from the .nif
mShapeLoader->load(outputstring,"General");
BulletShapeManager::getSingletonPtr()->load(outputstring,"General");
BulletShapePtr shape = BulletShapeManager::getSingleton().getByName(outputstring,"General");
adjustRigidBody(body, position, rotation, shape->mBoxTranslation * scale, shape->mBoxRotation);
}
RigidBody* PhysicEngine::createAndAdjustRigidBody(const std::string &mesh, const std::string &name,
float scale, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation,
Ogre::Vector3* scaledBoxTranslation, Ogre::Quaternion* boxRotation, bool raycasting, bool placeable)
{
std::string sid = (boost::format("%07.3f") % scale).str();
std::string outputstring = mesh + sid;
//get the shape from the .nif
mShapeLoader->load(outputstring,"General");
BulletShapeManager::getSingletonPtr()->load(outputstring,"General");
BulletShapePtr shape = BulletShapeManager::getSingleton().getByName(outputstring,"General");
// TODO: add option somewhere to enable collision for placeable meshes
if (placeable && !raycasting && shape->mCollisionShape)
return NULL;
if (!shape->mCollisionShape && !raycasting)
return NULL;
if (!shape->mRaycastingShape && raycasting)
return NULL;
btCollisionShape* collisionShape = raycasting ? shape->mRaycastingShape : shape->mCollisionShape;
// If this is an animated compound shape, we must duplicate it so we can animate
// multiple instances independently.
if (!raycasting && !shape->mAnimatedShapes.empty())
collisionShape = duplicateCollisionShape(collisionShape);
if (raycasting && !shape->mAnimatedRaycastingShapes.empty())
collisionShape = duplicateCollisionShape(collisionShape);
collisionShape->setLocalScaling( btVector3(scale,scale,scale));
//create the real body
btRigidBody::btRigidBodyConstructionInfo CI = btRigidBody::btRigidBodyConstructionInfo
(0,0, collisionShape);
RigidBody* body = new RigidBody(CI,name);
body->mPlaceable = placeable;
if (!raycasting && !shape->mAnimatedShapes.empty())
{
AnimatedShapeInstance instance;
instance.mAnimatedShapes = shape->mAnimatedShapes;
instance.mCompound = collisionShape;
mAnimatedShapes[body] = instance;
}
if (raycasting && !shape->mAnimatedRaycastingShapes.empty())
{
AnimatedShapeInstance instance;
instance.mAnimatedShapes = shape->mAnimatedRaycastingShapes;
instance.mCompound = collisionShape;
mAnimatedRaycastingShapes[body] = instance;
}
if(scaledBoxTranslation != 0)
*scaledBoxTranslation = shape->mBoxTranslation * scale;
if(boxRotation != 0)
*boxRotation = shape->mBoxRotation;
adjustRigidBody(body, position, rotation, shape->mBoxTranslation * scale, shape->mBoxRotation);
if (!raycasting)
{
assert (mCollisionObjectMap.find(name) == mCollisionObjectMap.end());
mCollisionObjectMap[name] = body;
mDynamicsWorld->addRigidBody(body,CollisionType_World,CollisionType_Actor|CollisionType_HeightMap);
}
else
{
assert (mRaycastingObjectMap.find(name) == mRaycastingObjectMap.end());
mRaycastingObjectMap[name] = body;
mDynamicsWorld->addRigidBody(body,CollisionType_Raycasting,CollisionType_Raycasting|CollisionType_Projectile);
body->setCollisionFlags(body->getCollisionFlags() | btCollisionObject::CF_DISABLE_VISUALIZE_OBJECT);
}
return body;
}
void PhysicEngine::removeRigidBody(const std::string &name)
{
RigidBodyContainer::iterator it = mCollisionObjectMap.find(name);
if (it != mCollisionObjectMap.end() )
{
RigidBody* body = it->second;
if(body != NULL)
{
mDynamicsWorld->removeRigidBody(body);
}
}
it = mRaycastingObjectMap.find(name);
if (it != mRaycastingObjectMap.end() )
{
RigidBody* body = it->second;
if(body != NULL)
{
mDynamicsWorld->removeRigidBody(body);
}
}
}
void PhysicEngine::deleteRigidBody(const std::string &name)
{
RigidBodyContainer::iterator it = mCollisionObjectMap.find(name);
if (it != mCollisionObjectMap.end() )
{
RigidBody* body = it->second;
if(body != NULL)
{
if (mAnimatedShapes.find(body) != mAnimatedShapes.end())
deleteShape(mAnimatedShapes[body].mCompound);
mAnimatedShapes.erase(body);
delete body;
}
mCollisionObjectMap.erase(it);
}
it = mRaycastingObjectMap.find(name);
if (it != mRaycastingObjectMap.end() )
{
RigidBody* body = it->second;
if(body != NULL)
{
if (mAnimatedRaycastingShapes.find(body) != mAnimatedRaycastingShapes.end())
deleteShape(mAnimatedRaycastingShapes[body].mCompound);
mAnimatedRaycastingShapes.erase(body);
delete body;
}
mRaycastingObjectMap.erase(it);
}
}
RigidBody* PhysicEngine::getRigidBody(const std::string &name, bool raycasting)
{
RigidBodyContainer* map = raycasting ? &mRaycastingObjectMap : &mCollisionObjectMap;
RigidBodyContainer::iterator it = map->find(name);
if (it != map->end() )
{
RigidBody* body = (*map)[name];
return body;
}
else
{
return NULL;
}
}
class ContactTestResultCallback : public btCollisionWorld::ContactResultCallback
{
public:
std::vector<std::string> mResult;
// added in bullet 2.81
// this is just a quick hack, as there does not seem to be a BULLET_VERSION macro?
#if defined(BT_COLLISION_OBJECT_WRAPPER_H)
virtual btScalar addSingleResult(btManifoldPoint& cp,
const btCollisionObjectWrapper* colObj0Wrap,int partId0,int index0,
const btCollisionObjectWrapper* colObj1Wrap,int partId1,int index1)
{
const RigidBody* body = dynamic_cast<const RigidBody*>(colObj0Wrap->m_collisionObject);
if (body && !(colObj0Wrap->m_collisionObject->getBroadphaseHandle()->m_collisionFilterGroup
& CollisionType_Raycasting))
mResult.push_back(body->mName);
return 0.f;
}
#else
virtual btScalar addSingleResult(btManifoldPoint& cp, const btCollisionObject* col0, int partId0, int index0,
const btCollisionObject* col1, int partId1, int index1)
{
const RigidBody* body = dynamic_cast<const RigidBody*>(col0);
if (body && !(col0->getBroadphaseHandle()->m_collisionFilterGroup
& CollisionType_Raycasting))
mResult.push_back(body->mName);
return 0.f;
}
#endif
};
class DeepestNotMeContactTestResultCallback : public btCollisionWorld::ContactResultCallback
{
const std::string &mFilter;
// Store the real origin, since the shape's origin is its center
btVector3 mOrigin;
public:
const RigidBody *mObject;
btVector3 mContactPoint;
btScalar mLeastDistSqr;
DeepestNotMeContactTestResultCallback(const std::string &filter, const btVector3 &origin)
: mFilter(filter), mOrigin(origin), mObject(0), mContactPoint(0,0,0),
mLeastDistSqr(std::numeric_limits<float>::max())
{ }
#if defined(BT_COLLISION_OBJECT_WRAPPER_H)
virtual btScalar addSingleResult(btManifoldPoint& cp,
const btCollisionObjectWrapper* col0Wrap,int partId0,int index0,
const btCollisionObjectWrapper* col1Wrap,int partId1,int index1)
{
const RigidBody* body = dynamic_cast<const RigidBody*>(col1Wrap->m_collisionObject);
if(body && body->mName != mFilter)
{
btScalar distsqr = mOrigin.distance2(cp.getPositionWorldOnA());
if(!mObject || distsqr < mLeastDistSqr)
{
mObject = body;
mLeastDistSqr = distsqr;
mContactPoint = cp.getPositionWorldOnA();
}
}
return 0.f;
}
#else
virtual btScalar addSingleResult(btManifoldPoint& cp,
const btCollisionObject* col0, int partId0, int index0,
const btCollisionObject* col1, int partId1, int index1)
{
const RigidBody* body = dynamic_cast<const RigidBody*>(col1);
if(body && body->mName != mFilter)
{
btScalar distsqr = mOrigin.distance2(cp.getPositionWorldOnA());
if(!mObject || distsqr < mLeastDistSqr)
{
mObject = body;
mLeastDistSqr = distsqr;
mContactPoint = cp.getPositionWorldOnA();
}
}
return 0.f;
}
#endif
};
std::vector<std::string> PhysicEngine::getCollisions(const std::string& name, int collisionGroup, int collisionMask)
{
RigidBody* body = getRigidBody(name);
if (!body) // fall back to raycasting body if there is no collision body
body = getRigidBody(name, true);
ContactTestResultCallback callback;
callback.m_collisionFilterGroup = collisionGroup;
callback.m_collisionFilterMask = collisionMask;
mDynamicsWorld->contactTest(body, callback);
return callback.mResult;
}
std::pair<const RigidBody*,btVector3> PhysicEngine::getFilteredContact(const std::string &filter,
const btVector3 &origin,
btCollisionObject *object)
{
DeepestNotMeContactTestResultCallback callback(filter, origin);
callback.m_collisionFilterGroup = CollisionType_Actor;
callback.m_collisionFilterMask = CollisionType_World | CollisionType_HeightMap | CollisionType_Actor;
mDynamicsWorld->contactTest(object, callback);
return std::make_pair(callback.mObject, callback.mContactPoint);
}
void PhysicEngine::stepSimulation(double deltaT)
{
// This seems to be needed for character controller objects
mDynamicsWorld->stepSimulation(static_cast<btScalar>(deltaT), 10, 1 / 60.0f);
if(isDebugCreated)
{
mDebugDrawer->step();
}
}
void PhysicEngine::addCharacter(const std::string &name, const std::string &mesh,
const Ogre::Vector3 &position, float scale, const Ogre::Quaternion &rotation)
{
// Remove character with given name, so we don't make memory
// leak when character would be added twice
removeCharacter(name);
PhysicActor* newActor = new PhysicActor(name, mesh, this, position, rotation, scale);
mActorMap[name] = newActor;
}
void PhysicEngine::removeCharacter(const std::string &name)
{
PhysicActorContainer::iterator it = mActorMap.find(name);
if (it != mActorMap.end() )
{
PhysicActor* act = it->second;
if(act != NULL)
{
delete act;
}
mActorMap.erase(it);
}
}
PhysicActor* PhysicEngine::getCharacter(const std::string &name)
{
PhysicActorContainer::iterator it = mActorMap.find(name);
if (it != mActorMap.end() )
{
PhysicActor* act = mActorMap[name];
return act;
}
else
{
return 0;
}
}
std::pair<std::string,float> PhysicEngine::rayTest(const btVector3 &from, const btVector3 &to, bool raycastingObjectOnly, bool ignoreHeightMap, Ogre::Vector3* normal)
{
std::string name = "";
float d = -1;
btCollisionWorld::ClosestRayResultCallback resultCallback1(from, to);
resultCallback1.m_collisionFilterGroup = 0xff;
if(raycastingObjectOnly)
resultCallback1.m_collisionFilterMask = CollisionType_Raycasting|CollisionType_Actor;
else
resultCallback1.m_collisionFilterMask = CollisionType_World;
if(!ignoreHeightMap)
resultCallback1.m_collisionFilterMask = resultCallback1.m_collisionFilterMask | CollisionType_HeightMap;
mDynamicsWorld->rayTest(from, to, resultCallback1);
if (resultCallback1.hasHit())
{
name = static_cast<const RigidBody&>(*resultCallback1.m_collisionObject).mName;
d = resultCallback1.m_closestHitFraction;
if (normal)
*normal = Ogre::Vector3(resultCallback1.m_hitNormalWorld.x(),
resultCallback1.m_hitNormalWorld.y(),
resultCallback1.m_hitNormalWorld.z());
}
return std::pair<std::string,float>(name,d);
}
// callback that ignores player in results
struct OurClosestConvexResultCallback : public btCollisionWorld::ClosestConvexResultCallback
{
public:
OurClosestConvexResultCallback(const btVector3& convexFromWorld,const btVector3& convexToWorld)
: btCollisionWorld::ClosestConvexResultCallback(convexFromWorld, convexToWorld) {}
virtual btScalar addSingleResult(btCollisionWorld::LocalConvexResult& convexResult,bool normalInWorldSpace)
{
if (const RigidBody* body = dynamic_cast<const RigidBody*>(convexResult.m_hitCollisionObject))
if (body->mName == "player")
return 0;
return btCollisionWorld::ClosestConvexResultCallback::addSingleResult(convexResult, normalInWorldSpace);
}
};
std::pair<bool, float> PhysicEngine::sphereCast (float radius, btVector3& from, btVector3& to)
{
OurClosestConvexResultCallback callback(from, to);
callback.m_collisionFilterGroup = 0xff;
callback.m_collisionFilterMask = OEngine::Physic::CollisionType_World|OEngine::Physic::CollisionType_HeightMap;
btSphereShape shape(radius);
const btQuaternion btrot(0.0f, 0.0f, 0.0f);
btTransform from_ (btrot, from);
btTransform to_ (btrot, to);
mDynamicsWorld->convexSweepTest(&shape, from_, to_, callback);
if (callback.hasHit())
return std::make_pair(true, callback.m_closestHitFraction);
else
return std::make_pair(false, 1.0f);
}
std::vector< std::pair<float, std::string> > PhysicEngine::rayTest2(const btVector3& from, const btVector3& to, int filterGroup)
{
MyRayResultCallback resultCallback1;
resultCallback1.m_collisionFilterGroup = filterGroup;
resultCallback1.m_collisionFilterMask = CollisionType_Raycasting|CollisionType_Actor|CollisionType_HeightMap;
mDynamicsWorld->rayTest(from, to, resultCallback1);
std::vector< std::pair<float, const btCollisionObject*> > results = resultCallback1.results;
std::vector< std::pair<float, std::string> > results2;
for (std::vector< std::pair<float, const btCollisionObject*> >::iterator it=results.begin();
it != results.end(); ++it)
{
results2.push_back( std::make_pair( (*it).first, static_cast<const RigidBody&>(*(*it).second).mName ) );
}
std::sort(results2.begin(), results2.end(), MyRayResultCallback::cmp);
return results2;
}
void PhysicEngine::getObjectAABB(const std::string &mesh, float scale, btVector3 &min, btVector3 &max)
{
std::string sid = (boost::format("%07.3f") % scale).str();
std::string outputstring = mesh + sid;
mShapeLoader->load(outputstring, "General");
BulletShapeManager::getSingletonPtr()->load(outputstring, "General");
BulletShapePtr shape =
BulletShapeManager::getSingleton().getByName(outputstring, "General");
btTransform trans;
trans.setIdentity();
if (shape->mRaycastingShape)
shape->mRaycastingShape->getAabb(trans, min, max);
else if (shape->mCollisionShape)
shape->mCollisionShape->getAabb(trans, min, max);
else
{
min = btVector3(0,0,0);
max = btVector3(0,0,0);
}
}
int PhysicEngine::toggleDebugRendering(Ogre::SceneManager *sceneMgr)
{
if(!sceneMgr)
return 0;
std::map<Ogre::SceneManager *, BtOgre::DebugDrawer *>::iterator iter =
mDebugDrawers.find(sceneMgr);
if(iter != mDebugDrawers.end()) // found scene manager
{
if((*iter).second)
{
// set a new drawer each time (maybe with a different scene manager)
mDynamicsWorld->setDebugDrawer(mDebugDrawers[sceneMgr]);
if(!mDebugDrawers[sceneMgr]->getDebugMode())
mDebugDrawers[sceneMgr]->setDebugMode(1 /*mDebugDrawFlags*/);
else
mDebugDrawers[sceneMgr]->setDebugMode(0);
mDynamicsWorld->debugDrawWorld();
return mDebugDrawers[sceneMgr]->getDebugMode();
}
}
return 0;
}
void PhysicEngine::stepDebug(Ogre::SceneManager *sceneMgr)
{
if(!sceneMgr)
return;
std::map<Ogre::SceneManager *, BtOgre::DebugDrawer *>::iterator iter =
mDebugDrawers.find(sceneMgr);
if(iter != mDebugDrawers.end()) // found scene manager
{
if((*iter).second)
(*iter).second->step();
else
return;
}
}
void PhysicEngine::createDebugDraw(Ogre::SceneManager *sceneMgr)
{
if(mDebugDrawers.find(sceneMgr) == mDebugDrawers.end())
{
mDebugSceneNodes[sceneMgr] = sceneMgr->getRootSceneNode()->createChildSceneNode();
mDebugDrawers[sceneMgr] = new BtOgre::DebugDrawer(mDebugSceneNodes[sceneMgr], mDynamicsWorld);
mDebugDrawers[sceneMgr]->setDebugMode(0);
}
}
void PhysicEngine::removeDebugDraw(Ogre::SceneManager *sceneMgr)
{
std::map<Ogre::SceneManager *, BtOgre::DebugDrawer *>::iterator iter =
mDebugDrawers.find(sceneMgr);
if(iter != mDebugDrawers.end())
{
delete (*iter).second;
mDebugDrawers.erase(iter);
}
std::map<Ogre::SceneManager *, Ogre::SceneNode *>::iterator it =
mDebugSceneNodes.find(sceneMgr);
if(it != mDebugSceneNodes.end())
{
std::string sceneNodeName = (*it).second->getName();
if(sceneMgr->hasSceneNode(sceneNodeName))
sceneMgr->destroySceneNode(sceneNodeName);
}
}
}
}