Imported Upstream version 0.26.0

This commit is contained in:
Bret Curtis 2013-10-17 16:37:22 +02:00
commit 9a2b6c69b6
1398 changed files with 212217 additions and 0 deletions

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,282 @@
/*
* =====================================================================================
*
* Filename: BtOgreExtras.h
*
* Description: Contains the Ogre Mesh to Bullet Shape converters.
*
* Version: 1.0
* Created: 27/12/2008 01:45:56 PM
*
* Author: Nikhilesh (nikki)
*
* =====================================================================================
*/
#ifndef _BtOgreShapes_H_
#define _BtOgreShapes_H_
#include "btBulletDynamicsCommon.h"
#include "OgreSimpleRenderable.h"
#include "OgreCamera.h"
#include "OgreHardwareBufferManager.h"
#include "OgreMaterialManager.h"
#include "OgreTechnique.h"
#include "OgrePass.h"
#include "OgreLogManager.h"
namespace BtOgre
{
typedef std::vector<Ogre::Vector3> Vector3Array;
//Converts from and to Bullet and Ogre stuff. Pretty self-explanatory.
class Convert
{
public:
Convert() {};
~Convert() {};
static btQuaternion toBullet(const Ogre::Quaternion &q)
{
return btQuaternion(q.x, q.y, q.z, q.w);
}
static btVector3 toBullet(const Ogre::Vector3 &v)
{
return btVector3(v.x, v.y, v.z);
}
static Ogre::Quaternion toOgre(const btQuaternion &q)
{
return Ogre::Quaternion(q.w(), q.x(), q.y(), q.z());
}
static Ogre::Vector3 toOgre(const btVector3 &v)
{
return Ogre::Vector3(v.x(), v.y(), v.z());
}
};
//From here on its debug-drawing stuff. ------------------------------------------------------------------
class DynamicRenderable : public Ogre::SimpleRenderable
{
public:
/// Constructor
DynamicRenderable();
/// Virtual destructor
virtual ~DynamicRenderable();
/** Initializes the dynamic renderable.
@remarks
This function should only be called once. It initializes the
render operation, and calls the abstract function
createVertexDeclaration().
@param operationType The type of render operation to perform.
@param useIndices Specifies whether to use indices to determine the
vertices to use as input. */
void initialize(Ogre::RenderOperation::OperationType operationType,
bool useIndices);
/// Implementation of Ogre::SimpleRenderable
virtual Ogre::Real getBoundingRadius(void) const;
/// Implementation of Ogre::SimpleRenderable
virtual Ogre::Real getSquaredViewDepth(const Ogre::Camera* cam) const;
protected:
/// Maximum capacity of the currently allocated vertex buffer.
size_t mVertexBufferCapacity;
/// Maximum capacity of the currently allocated index buffer.
size_t mIndexBufferCapacity;
/** Creates the vertex declaration.
@remarks
Override and set mRenderOp.vertexData->vertexDeclaration here.
mRenderOp.vertexData will be created for you before this method
is called. */
virtual void createVertexDeclaration() = 0;
/** Prepares the hardware buffers for the requested vertex and index counts.
@remarks
This function must be called before locking the buffers in
fillHardwareBuffers(). It guarantees that the hardware buffers
are large enough to hold at least the requested number of
vertices and indices (if using indices). The buffers are
possibly reallocated to achieve this.
@par
The vertex and index count in the render operation are set to
the values of vertexCount and indexCount respectively.
@param vertexCount The number of vertices the buffer must hold.
@param indexCount The number of indices the buffer must hold. This
parameter is ignored if not using indices. */
void prepareHardwareBuffers(size_t vertexCount, size_t indexCount);
/** Fills the hardware vertex and index buffers with data.
@remarks
This function must call prepareHardwareBuffers() before locking
the buffers to ensure the they are large enough for the data to
be written. Afterwards the vertex and index buffers (if using
indices) can be locked, and data can be written to them. */
virtual void fillHardwareBuffers() = 0;
};
class DynamicLines : public DynamicRenderable
{
typedef Ogre::Vector3 Vector3;
typedef Ogre::Quaternion Quaternion;
typedef Ogre::Camera Camera;
typedef Ogre::Real Real;
typedef Ogre::RenderOperation::OperationType OperationType;
public:
/// Constructor - see setOperationType() for description of argument.
DynamicLines(OperationType opType=Ogre::RenderOperation::OT_LINE_STRIP);
virtual ~DynamicLines();
/// Add a point to the point list
void addPoint(const Ogre::Vector3 &p);
/// Add a point to the point list
void addPoint(Real x, Real y, Real z);
/// Change the location of an existing point in the point list
void setPoint(unsigned short index, const Vector3 &value);
/// Return the location of an existing point in the point list
const Vector3& getPoint(unsigned short index) const;
/// Return the total number of points in the point list
unsigned short getNumPoints(void) const;
/// Remove all points from the point list
void clear();
/// Call this to update the hardware buffer after making changes.
void update();
/** Set the type of operation to draw with.
* @param opType Can be one of
* - RenderOperation::OT_LINE_STRIP
* - RenderOperation::OT_LINE_LIST
* - RenderOperation::OT_POINT_LIST
* - RenderOperation::OT_TRIANGLE_LIST
* - RenderOperation::OT_TRIANGLE_STRIP
* - RenderOperation::OT_TRIANGLE_FAN
* The default is OT_LINE_STRIP.
*/
void setOperationType(OperationType opType);
OperationType getOperationType() const;
protected:
/// Implementation DynamicRenderable, creates a simple vertex-only decl
virtual void createVertexDeclaration();
/// Implementation DynamicRenderable, pushes point list out to hardware memory
virtual void fillHardwareBuffers();
private:
std::vector<Vector3> mPoints;
bool mDirty;
};
class DebugDrawer : public btIDebugDraw
{
protected:
Ogre::SceneNode *mNode;
btDynamicsWorld *mWorld;
DynamicLines *mLineDrawer;
bool mDebugOn;
public:
DebugDrawer(Ogre::SceneNode *node, btDynamicsWorld *world)
: mNode(node),
mWorld(world),
mDebugOn(true)
{
mLineDrawer = new DynamicLines(Ogre::RenderOperation::OT_LINE_LIST);
mNode->attachObject(mLineDrawer);
if (!Ogre::ResourceGroupManager::getSingleton().resourceGroupExists("BtOgre"))
Ogre::ResourceGroupManager::getSingleton().createResourceGroup("BtOgre");
if (!Ogre::MaterialManager::getSingleton().resourceExists("BtOgre/DebugLines"))
{
Ogre::MaterialPtr mat = Ogre::MaterialManager::getSingleton().create("BtOgre/DebugLines", "BtOgre");
mat->setReceiveShadows(false);
mat->setSelfIllumination(1,1,1);
}
mLineDrawer->setMaterial("BtOgre/DebugLines");
//mLineDrawer->setVisibilityFlags (1024);
}
~DebugDrawer()
{
Ogre::MaterialManager::getSingleton().remove("BtOgre/DebugLines");
Ogre::ResourceGroupManager::getSingleton().destroyResourceGroup("BtOgre");
delete mLineDrawer;
}
void step()
{
if (mDebugOn)
{
mWorld->debugDrawWorld();
mLineDrawer->update();
mNode->needUpdate();
mLineDrawer->clear();
}
else
{
mLineDrawer->clear();
mLineDrawer->update();
mNode->needUpdate();
}
}
void drawLine(const btVector3& from,const btVector3& to,const btVector3& color)
{
mLineDrawer->addPoint(Convert::toOgre(from));
mLineDrawer->addPoint(Convert::toOgre(to));
}
void drawContactPoint(const btVector3& PointOnB,const btVector3& normalOnB,btScalar distance,int lifeTime,const btVector3& color)
{
mLineDrawer->addPoint(Convert::toOgre(PointOnB));
mLineDrawer->addPoint(Convert::toOgre(PointOnB) + (Convert::toOgre(normalOnB) * distance * 20));
}
void reportErrorWarning(const char* warningString)
{
Ogre::LogManager::getSingleton().logMessage(warningString);
}
void draw3dText(const btVector3& location,const char* textString)
{
}
//0 for off, anything else for on.
void setDebugMode(int isOn)
{
mDebugOn = (isOn == 0) ? false : true;
if (!mDebugOn)
mLineDrawer->clear();
}
//0 for off, anything else for on.
int getDebugMode() const
{
return mDebugOn;
}
};
}
#endif

View file

@ -0,0 +1,145 @@
/*
* =====================================================================================
*
* Filename: BtOgreGP.h
*
* Description: The part of BtOgre that handles information transfer from Ogre to
* Bullet (like mesh data for making trimeshes).
*
* Version: 1.0
* Created: 27/12/2008 03:29:56 AM
*
* Author: Nikhilesh (nikki)
*
* =====================================================================================
*/
#ifndef _BtOgrePG_H_
#define _BtOgrePG_H_
#include "btBulletDynamicsCommon.h"
#include "BtOgreExtras.h"
#include <OgreMatrix4.h>
#include <OgreMesh.h>
#include <OgreVector3.h>
namespace BtOgre {
typedef std::map<unsigned char, Vector3Array*> BoneIndex;
typedef std::pair<unsigned short, Vector3Array*> BoneKeyIndex;
class VertexIndexToShape
{
public:
VertexIndexToShape(const Ogre::Matrix4 &transform = Ogre::Matrix4::IDENTITY);
~VertexIndexToShape();
Ogre::Real getRadius();
Ogre::Vector3 getSize();
btSphereShape* createSphere();
btBoxShape* createBox();
btBvhTriangleMeshShape* createTrimesh();
btCylinderShape* createCylinder();
btConvexHullShape* createConvex();
const Ogre::Vector3* getVertices();
unsigned int getVertexCount();
const unsigned int* getIndices();
unsigned int getIndexCount();
protected:
void addStaticVertexData(const Ogre::VertexData *vertex_data);
void addAnimatedVertexData(const Ogre::VertexData *vertex_data,
const Ogre::VertexData *blended_data,
const Ogre::Mesh::IndexMap *indexMap);
void addIndexData(Ogre::IndexData *data, const unsigned int offset = 0);
protected:
Ogre::Vector3* mVertexBuffer;
unsigned int* mIndexBuffer;
unsigned int mVertexCount;
unsigned int mIndexCount;
Ogre::Matrix4 mTransform;
Ogre::Real mBoundRadius;
Ogre::Vector3 mBounds;
BoneIndex *mBoneIndex;
Ogre::Vector3 mScale;
};
//For static (non-animated) meshes.
class StaticMeshToShapeConverter : public VertexIndexToShape
{
public:
StaticMeshToShapeConverter(Ogre::Renderable *rend, const Ogre::Matrix4 &transform = Ogre::Matrix4::IDENTITY);
StaticMeshToShapeConverter(Ogre::Entity *entity, const Ogre::Matrix4 &transform = Ogre::Matrix4::IDENTITY);
StaticMeshToShapeConverter();
~StaticMeshToShapeConverter();
void addEntity(Ogre::Entity *entity,const Ogre::Matrix4 &transform = Ogre::Matrix4::IDENTITY);
void addMesh(const Ogre::MeshPtr &mesh, const Ogre::Matrix4 &transform = Ogre::Matrix4::IDENTITY);
protected:
Ogre::Entity* mEntity;
Ogre::SceneNode* mNode;
};
//For animated meshes.
class AnimatedMeshToShapeConverter : public VertexIndexToShape
{
public:
AnimatedMeshToShapeConverter(Ogre::Entity *entity, const Ogre::Matrix4 &transform = Ogre::Matrix4::IDENTITY);
AnimatedMeshToShapeConverter();
~AnimatedMeshToShapeConverter();
void addEntity(Ogre::Entity *entity,const Ogre::Matrix4 &transform = Ogre::Matrix4::IDENTITY);
void addMesh(const Ogre::MeshPtr &mesh, const Ogre::Matrix4 &transform);
btBoxShape* createAlignedBox(unsigned char bone,
const Ogre::Vector3 &bonePosition,
const Ogre::Quaternion &boneOrientation);
btBoxShape* createOrientedBox(unsigned char bone,
const Ogre::Vector3 &bonePosition,
const Ogre::Quaternion &boneOrientation);
protected:
bool getBoneVertices(unsigned char bone,
unsigned int &vertex_count,
Ogre::Vector3* &vertices,
const Ogre::Vector3 &bonePosition);
bool getOrientedBox(unsigned char bone,
const Ogre::Vector3 &bonePosition,
const Ogre::Quaternion &boneOrientation,
Ogre::Vector3 &extents,
Ogre::Vector3 *axis,
Ogre::Vector3 &center);
Ogre::Entity* mEntity;
Ogre::SceneNode* mNode;
Ogre::Vector3 *mTransformedVerticesTemp;
size_t mTransformedVerticesTempSize;
};
}
#endif

View file

@ -0,0 +1,81 @@
/*
* =====================================================================================
*
* Filename: BtOgrePG.h
*
* Description: The part of BtOgre that handles information transfer from Bullet to
* Ogre (like updating graphics object positions).
*
* Version: 1.0
* Created: 27/12/2008 03:40:56 AM
*
* Author: Nikhilesh (nikki)
*
* =====================================================================================
*/
#ifndef _BtOgreGP_H_
#define _BtOgreGP_H_
#include "btBulletDynamicsCommon.h"
#include "OgreSceneNode.h"
#include "BtOgreExtras.h"
namespace BtOgre {
//A MotionState is Bullet's way of informing you about updates to an object.
//Pass this MotionState to a btRigidBody to have your SceneNode updated automaticaly.
class RigidBodyState : public btMotionState
{
protected:
btTransform mTransform;
btTransform mCenterOfMassOffset;
Ogre::SceneNode *mNode;
public:
RigidBodyState(Ogre::SceneNode *node, const btTransform &transform, const btTransform &offset = btTransform::getIdentity())
: mTransform(transform),
mCenterOfMassOffset(offset),
mNode(node)
{
}
RigidBodyState(Ogre::SceneNode *node)
: mTransform(((node != NULL) ? BtOgre::Convert::toBullet(node->getOrientation()) : btQuaternion(0,0,0,1)),
((node != NULL) ? BtOgre::Convert::toBullet(node->getPosition()) : btVector3(0,0,0))),
mCenterOfMassOffset(btTransform::getIdentity()),
mNode(node)
{
}
virtual void getWorldTransform(btTransform &ret) const
{
ret = mCenterOfMassOffset.inverse() * mTransform;
}
virtual void setWorldTransform(const btTransform &in)
{
if (mNode == NULL)
return;
mTransform = in;
btTransform transform = in * mCenterOfMassOffset;
btQuaternion rot = transform.getRotation();
btVector3 pos = transform.getOrigin();
mNode->setOrientation(rot.w(), rot.x(), rot.y(), rot.z());
mNode->setPosition(pos.x(), pos.y(), pos.z());
}
void setNode(Ogre::SceneNode *node)
{
mNode = node;
}
};
//Softbody-Ogre connection goes here!
}
#endif

View file

@ -0,0 +1,153 @@
#include "BulletShapeLoader.h"
namespace OEngine {
namespace Physic
{
BulletShape::BulletShape(Ogre::ResourceManager* creator, const Ogre::String &name,
Ogre::ResourceHandle handle, const Ogre::String &group, bool isManual,
Ogre::ManualResourceLoader *loader) :
Ogre::Resource(creator, name, handle, group, isManual, loader)
{
/* If you were storing a pointer to an object, then you would set that pointer to NULL here.
*/
/* For consistency with StringInterface, but we don't add any parameters here
That's because the Resource implementation of StringInterface is to
list all the options that need to be set before loading, of which
we have none as such. Full details can be set through scripts.
*/
mCollisionShape = NULL;
mRaycastingShape = NULL;
mHasCollisionNode = false;
mCollide = true;
createParamDictionary("BulletShape");
}
BulletShape::~BulletShape()
{
deleteShape(mCollisionShape);
deleteShape(mRaycastingShape);
}
// farm out to BulletShapeLoader
void BulletShape::loadImpl()
{
mLoader->loadResource(this);
}
void BulletShape::deleteShape(btCollisionShape* shape)
{
if(shape!=NULL)
{
if(shape->isCompound())
{
btCompoundShape* ms = static_cast<btCompoundShape*>(mCollisionShape);
int a = ms->getNumChildShapes();
for(int i=0; i <a;i++)
{
deleteShape(ms->getChildShape(i));
}
}
delete shape;
}
shape = NULL;
}
void BulletShape::unloadImpl()
{
deleteShape(mCollisionShape);
deleteShape(mRaycastingShape);
}
//TODO:change this?
size_t BulletShape::calculateSize() const
{
return 1;
}
//=============================================================================================================
BulletShapeManager *BulletShapeManager::sThis = 0;
BulletShapeManager *BulletShapeManager::getSingletonPtr()
{
return sThis;
}
BulletShapeManager &BulletShapeManager::getSingleton()
{
assert(sThis);
return(*sThis);
}
BulletShapeManager::BulletShapeManager()
{
assert(!sThis);
sThis = this;
mResourceType = "BulletShape";
// low, because it will likely reference other resources
mLoadOrder = 30.0f;
// this is how we register the ResourceManager with OGRE
Ogre::ResourceGroupManager::getSingleton()._registerResourceManager(mResourceType, this);
}
BulletShapeManager::~BulletShapeManager()
{
// and this is how we unregister it
Ogre::ResourceGroupManager::getSingleton()._unregisterResourceManager(mResourceType);
sThis = 0;
}
#if (OGRE_VERSION >= ((1 << 16) | (9 << 8) | 0))
BulletShapePtr BulletShapeManager::getByName(const Ogre::String& name, const Ogre::String& groupName)
{
return getResourceByName(name, groupName).staticCast<BulletShape>();
}
BulletShapePtr BulletShapeManager::create (const Ogre::String& name, const Ogre::String& group,
bool isManual, Ogre::ManualResourceLoader* loader,
const Ogre::NameValuePairList* createParams)
{
return createResource(name,group,isManual,loader,createParams).staticCast<BulletShape>();
}
#endif
BulletShapePtr BulletShapeManager::load(const Ogre::String &name, const Ogre::String &group)
{
BulletShapePtr textf = getByName(name);
if (textf.isNull())
textf = create(name, group);
textf->load();
return textf;
}
Ogre::Resource *BulletShapeManager::createImpl(const Ogre::String &name, Ogre::ResourceHandle handle,
const Ogre::String &group, bool isManual, Ogre::ManualResourceLoader *loader,
const Ogre::NameValuePairList *createParams)
{
BulletShape* res = new BulletShape(this, name, handle, group, isManual, loader);
//if(isManual)
//{
//loader->loadResource(res);
//}
return res;
}
//====================================================================
void BulletShapeLoader::loadResource(Ogre::Resource *resource)
{}
void BulletShapeLoader::load(const std::string &name,const std::string &group)
{}
}
}

View file

@ -0,0 +1,176 @@
#ifndef _BULLET_SHAPE_LOADER_H_
#define _BULLET_SHAPE_LOADER_H_
#include <OgreResource.h>
#include <OgreResourceManager.h>
#include <btBulletCollisionCommon.h>
#include <OgreVector3.h>
namespace OEngine {
namespace Physic
{
/**
*Define a new resource which describe a Shape usable by bullet.See BulletShapeManager for how to get/use them.
*/
class BulletShape : public Ogre::Resource
{
Ogre::String mString;
protected:
void loadImpl();
void unloadImpl();
size_t calculateSize() const;
void deleteShape(btCollisionShape* shape);
public:
BulletShape(Ogre::ResourceManager *creator, const Ogre::String &name,
Ogre::ResourceHandle handle, const Ogre::String &group, bool isManual = false,
Ogre::ManualResourceLoader *loader = 0);
virtual ~BulletShape();
btCollisionShape* mCollisionShape;
btCollisionShape* mRaycastingShape;
// Whether or not a NiRootCollisionNode was present in the .nif. If there is none, the collision behaviour
// depends on object type, so we need to expose this variable.
bool mHasCollisionNode;
Ogre::Vector3 mBoxTranslation;
Ogre::Quaternion mBoxRotation;
//this flag indicate if the shape is used for collision or if it's for raycasting only.
bool mCollide;
};
/**
*
*/
#if (OGRE_VERSION < ((1 << 16) | (9 << 8) | 0))
class BulletShapePtr : public Ogre::SharedPtr<BulletShape>
{
public:
BulletShapePtr() : Ogre::SharedPtr<BulletShape>() {}
explicit BulletShapePtr(BulletShape *rep) : Ogre::SharedPtr<BulletShape>(rep) {}
BulletShapePtr(const BulletShapePtr &r) : Ogre::SharedPtr<BulletShape>(r) {}
BulletShapePtr(const Ogre::ResourcePtr &r) : Ogre::SharedPtr<BulletShape>()
{
if( r.isNull() )
return;
// lock & copy other mutex pointer
OGRE_LOCK_MUTEX(*r.OGRE_AUTO_MUTEX_NAME)
OGRE_COPY_AUTO_SHARED_MUTEX(r.OGRE_AUTO_MUTEX_NAME)
pRep = static_cast<BulletShape*>(r.getPointer());
pUseCount = r.useCountPointer();
useFreeMethod = r.freeMethod();
if (pUseCount)
{
++(*pUseCount);
}
}
/// Operator used to convert a ResourcePtr to a BulletShapePtr
BulletShapePtr& operator=(const Ogre::ResourcePtr& r)
{
if(pRep == static_cast<BulletShape*>(r.getPointer()))
return *this;
release();
if( r.isNull() )
return *this; // resource ptr is null, so the call to release above has done all we need to do.
// lock & copy other mutex pointer
OGRE_LOCK_MUTEX(*r.OGRE_AUTO_MUTEX_NAME)
OGRE_COPY_AUTO_SHARED_MUTEX(r.OGRE_AUTO_MUTEX_NAME)
pRep = static_cast<BulletShape*>(r.getPointer());
pUseCount = r.useCountPointer();
useFreeMethod = r.freeMethod();
if (pUseCount)
{
++(*pUseCount);
}
return *this;
}
};
#else
typedef Ogre::SharedPtr<BulletShape> BulletShapePtr;
#endif
/**
*Hold any BulletShape that was created by the ManualBulletShapeLoader.
*
*To get a bulletShape, you must load it first.
*First, create a manualBulletShapeLoader. Then call ManualBulletShapeManager->load(). This create an "empty" resource.
*Then use BulletShapeManager->load(). This will fill the resource with the required info.
*To get the resource,use BulletShapeManager::getByName.
*When you use the resource no more, just use BulletShapeManager->unload(). It won't completly delete the resource, but it will
*"empty" it.This allow a better management of memory: when you are leaving a cell, just unload every useless shape.
*
*Alternatively, you can call BulletShape->load() in order to actually load the resource.
*When you are finished with it, just call BulletShape->unload().
*
*IMO: prefere the first methode, i am not completly sure about the 2nd.
*
*Important Note: i have no idea of what happen if you try to load two time the same resource without unloading.
*It won't crash, but it might lead to memory leaks(I don't know how Ogre handle this). So don't do it!
*/
class BulletShapeManager : public Ogre::ResourceManager
{
protected:
// must implement this from ResourceManager's interface
Ogre::Resource *createImpl(const Ogre::String &name, Ogre::ResourceHandle handle,
const Ogre::String &group, bool isManual, Ogre::ManualResourceLoader *loader,
const Ogre::NameValuePairList *createParams);
static BulletShapeManager *sThis;
private:
/** \brief Explicit private copy constructor. This is a forbidden operation.*/
BulletShapeManager(const BulletShapeManager &);
/** \brief Private operator= . This is a forbidden operation. */
BulletShapeManager& operator=(const BulletShapeManager &);
public:
BulletShapeManager();
virtual ~BulletShapeManager();
#if (OGRE_VERSION >= ((1 << 16) | (9 << 8) | 0))
/// Get a resource by name
/// @see ResourceManager::getByName
BulletShapePtr getByName(const Ogre::String& name, const Ogre::String& groupName = Ogre::ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME);
/// Create a new shape
/// @see ResourceManager::createResource
BulletShapePtr create (const Ogre::String& name, const Ogre::String& group,
bool isManual = false, Ogre::ManualResourceLoader* loader = 0,
const Ogre::NameValuePairList* createParams = 0);
#endif
virtual BulletShapePtr load(const Ogre::String &name, const Ogre::String &group);
static BulletShapeManager &getSingleton();
static BulletShapeManager *getSingletonPtr();
};
class BulletShapeLoader : public Ogre::ManualResourceLoader
{
public:
BulletShapeLoader(){};
virtual ~BulletShapeLoader() {}
virtual void loadResource(Ogre::Resource *resource);
virtual void load(const std::string &name,const std::string &group);
};
}
}
#endif

View file

@ -0,0 +1,46 @@
#include "CMotionState.h"
#include "physic.hpp"
#include <btBulletDynamicsCommon.h>
#include <btBulletCollisionCommon.h>
#include <components/nifbullet/bulletnifloader.hpp>
namespace OEngine {
namespace Physic
{
CMotionState::CMotionState(PhysicEngine* eng,std::string name)
: isPC(false)
, isNPC(true)
{
pEng = eng;
tr.setIdentity();
pName = name;
}
void CMotionState::getWorldTransform(btTransform &worldTrans) const
{
worldTrans = tr;
}
void CMotionState::setWorldTransform(const btTransform &worldTrans)
{
tr = worldTrans;
PhysicEvent evt;
evt.isNPC = isNPC;
evt.isPC = isPC;
evt.newTransform = tr;
evt.RigidBodyName = pName;
if(isPC)
{
pEng->PEventList.push_back(evt);
}
else
{
pEng->NPEventList.push_back(evt);
}
}
}}

View file

@ -0,0 +1,52 @@
#ifndef OENGINE_CMOTIONSTATE_H
#define OENGINE_CMOTIONSTATE_H
#include <BulletDynamics/Dynamics/btRigidBody.h>
#include <string>
namespace OEngine {
namespace Physic
{
class PhysicEngine;
/**
* A CMotionState is associated with a single RigidBody.
* When the RigidBody is moved by bullet, bullet will call the function setWorldTransform.
* for more info, see the bullet Wiki at btMotionState.
*/
class CMotionState:public btMotionState
{
public:
CMotionState(PhysicEngine* eng,std::string name);
/**
* Return the position of the RigidBody.
*/
virtual void getWorldTransform(btTransform &worldTrans) const;
/**
* Function called by bullet when the RigidBody is moved.
* It add an event to the EventList of the PhysicEngine class.
*/
virtual void setWorldTransform(const btTransform &worldTrans);
protected:
PhysicEngine* pEng;
btTransform tr;
bool isNPC;
bool isPC;
std::string pName;
};
struct PhysicEvent
{
bool isNPC;
bool isPC;
btTransform newTransform;
std::string RigidBodyName;
};
}}
#endif

View file

@ -0,0 +1,643 @@
/*
Bullet Continuous Collision Detection and Physics Library
Copyright (c) 2003-2008 Erwin Coumans http://bulletphysics.com
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it freely,
subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "LinearMath/btIDebugDraw.h"
#include "BulletCollision/CollisionDispatch/btGhostObject.h"
#include "BulletCollision/CollisionShapes/btMultiSphereShape.h"
#include "BulletCollision/BroadphaseCollision/btOverlappingPairCache.h"
#include "BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h"
#include "BulletCollision/CollisionDispatch/btCollisionWorld.h"
#include "LinearMath/btDefaultMotionState.h"
#include "btKinematicCharacterController.h"
///@todo Interact with dynamic objects,
///Ride kinematicly animated platforms properly
///Support ducking
class btKinematicClosestNotMeRayResultCallback : public btCollisionWorld::ClosestRayResultCallback
{
public:
btKinematicClosestNotMeRayResultCallback (btCollisionObject* me) : btCollisionWorld::ClosestRayResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0))
{
m_me[0] = me;
count = 1;
}
btKinematicClosestNotMeRayResultCallback (btCollisionObject* me[], int count_) : btCollisionWorld::ClosestRayResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0))
{
count = count_;
for(int i = 0; i < count; i++)
m_me[i] = me[i];
}
virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult,bool normalInWorldSpace)
{
for(int i = 0; i < count; i++)
if (rayResult.m_collisionObject == m_me[i])
return 1.0;
return ClosestRayResultCallback::addSingleResult (rayResult, normalInWorldSpace);
}
protected:
btCollisionObject* m_me[10];
int count;
};
class btKinematicClosestNotMeConvexResultCallback : public btCollisionWorld::ClosestConvexResultCallback
{
public:
btKinematicClosestNotMeConvexResultCallback( btCollisionObject* me, const btVector3& up, btScalar minSlopeDot )
: btCollisionWorld::ClosestConvexResultCallback( btVector3( 0.0, 0.0, 0.0 ), btVector3( 0.0, 0.0, 0.0 ) ),
m_me( me ), m_up( up ), m_minSlopeDot( minSlopeDot )
{
}
virtual btScalar addSingleResult(btCollisionWorld::LocalConvexResult& convexResult,bool normalInWorldSpace)
{
if( convexResult.m_hitCollisionObject == m_me )
return btScalar( 1 );
btVector3 hitNormalWorld;
if( normalInWorldSpace )
{
hitNormalWorld = convexResult.m_hitNormalLocal;
}
else
{
///need to transform normal into worldspace
hitNormalWorld = m_hitCollisionObject->getWorldTransform().getBasis()*convexResult.m_hitNormalLocal;
}
// NOTE : m_hitNormalLocal is not always vertical on the ground with a capsule or a box...
btScalar dotUp = m_up.dot(hitNormalWorld);
if( dotUp < m_minSlopeDot )
return btScalar( 1 );
return ClosestConvexResultCallback::addSingleResult (convexResult, normalInWorldSpace);
}
protected:
btCollisionObject* m_me;
const btVector3 m_up;
btScalar m_minSlopeDot;
};
btKinematicCharacterController::btKinematicCharacterController( btPairCachingGhostObject* externalGhostObject_,
btPairCachingGhostObject* internalGhostObject_,
btScalar stepHeight,
btScalar constantScale,
btScalar gravity,
btScalar fallVelocity,
btScalar jumpVelocity,
btScalar recoveringFactor )
{
m_upAxis = btKinematicCharacterController::Y_AXIS;
m_walkDirection.setValue( btScalar( 0 ), btScalar( 0 ), btScalar( 0 ) );
m_useGhostObjectSweepTest = true;
externalGhostObject = externalGhostObject_;
internalGhostObject = internalGhostObject_;
m_recoveringFactor = recoveringFactor;
m_stepHeight = stepHeight;
m_useWalkDirection = true; // use walk direction by default, legacy behavior
m_velocityTimeInterval = btScalar( 0 );
m_verticalVelocity = btScalar( 0 );
m_verticalOffset = btScalar( 0 );
m_gravity = constantScale * gravity;
m_fallSpeed = constantScale * fallVelocity; // Terminal velocity of a sky diver in m/s.
m_jumpSpeed = constantScale * jumpVelocity; // ?
m_wasJumping = false;
setMaxSlope( btRadians( 45.0 ) );
mCollision = true;
}
btKinematicCharacterController::~btKinematicCharacterController ()
{
}
void btKinematicCharacterController::setVerticalVelocity(float z)
{
m_verticalVelocity = z;
}
bool btKinematicCharacterController::recoverFromPenetration( btCollisionWorld* collisionWorld )
{
bool penetration = false;
if(!mCollision) return penetration;
collisionWorld->getDispatcher()->dispatchAllCollisionPairs( internalGhostObject->getOverlappingPairCache(),
collisionWorld->getDispatchInfo(),
collisionWorld->getDispatcher() );
btVector3 currentPosition = internalGhostObject->getWorldTransform().getOrigin();
btScalar maxPen = btScalar( 0 );
for( int i = 0; i < internalGhostObject->getOverlappingPairCache()->getNumOverlappingPairs(); i++ )
{
m_manifoldArray.resize(0);
btBroadphasePair* collisionPair = &internalGhostObject->getOverlappingPairCache()->getOverlappingPairArray()[i];
if( collisionPair->m_algorithm )
collisionPair->m_algorithm->getAllContactManifolds( m_manifoldArray );
for( int j = 0; j < m_manifoldArray.size(); j++ )
{
btPersistentManifold* manifold = m_manifoldArray[j];
btScalar directionSign = manifold->getBody0() == internalGhostObject ? btScalar( -1.0 ) : btScalar( 1.0 );
for( int p = 0; p < manifold->getNumContacts(); p++ )
{
const btManifoldPoint&pt = manifold->getContactPoint( p );
if( (manifold->getBody1() == externalGhostObject && manifold->getBody0() == internalGhostObject)
||(manifold->getBody0() == externalGhostObject && manifold->getBody1() == internalGhostObject) )
{
}
else
{
btScalar dist = pt.getDistance();
if( dist < 0.0 )
{
if( dist < maxPen )
maxPen = dist;
// NOTE : btScalar affects the stairs but the parkinson...
// 0.0 , the capsule can break the walls...
currentPosition += pt.m_normalWorldOnB * directionSign * dist * m_recoveringFactor;
penetration = true;
}
}
}
// ???
//manifold->clearManifold();
}
}
btTransform transform = internalGhostObject->getWorldTransform();
transform.setOrigin( currentPosition );
internalGhostObject->setWorldTransform( transform );
externalGhostObject->setWorldTransform( transform );
return penetration;
}
btVector3 btKinematicCharacterController::stepUp( btCollisionWorld* world, const btVector3& currentPosition, btScalar& currentStepOffset )
{
btVector3 targetPosition = currentPosition + getUpAxisDirections()[ m_upAxis ] * ( m_stepHeight + ( m_verticalOffset > btScalar( 0.0 ) ? m_verticalOffset : 0.0 ) );
//if the no collisions mode is on, no need to go any further
if(!mCollision)
{
currentStepOffset = m_stepHeight;
return targetPosition;
}
// Retrieve the collision shape
//
btCollisionShape* collisionShape = externalGhostObject->getCollisionShape();
btAssert( collisionShape->isConvex() );
btConvexShape* convexShape = ( btConvexShape* )collisionShape;
// FIXME: Handle penetration properly
//
btTransform start;
start.setIdentity();
start.setOrigin( currentPosition + getUpAxisDirections()[ m_upAxis ] * ( convexShape->getMargin() ) );
btTransform end;
end.setIdentity();
end.setOrigin( targetPosition );
btKinematicClosestNotMeConvexResultCallback callback( externalGhostObject, -getUpAxisDirections()[ m_upAxis ], m_maxSlopeCosine );
callback.m_collisionFilterGroup = externalGhostObject->getBroadphaseHandle()->m_collisionFilterGroup;
callback.m_collisionFilterMask = externalGhostObject->getBroadphaseHandle()->m_collisionFilterMask;
// Sweep test
//
if( m_useGhostObjectSweepTest )
externalGhostObject->convexSweepTest( convexShape, start, end, callback, world->getDispatchInfo().m_allowedCcdPenetration );
else
world->convexSweepTest( convexShape, start, end, callback );
if( callback.hasHit() )
{
// Only modify the position if the hit was a slope and not a wall or ceiling.
//
if( callback.m_hitNormalWorld.dot(getUpAxisDirections()[m_upAxis]) > btScalar( 0.0 ) )
{
// We moved up only a fraction of the step height
//
currentStepOffset = m_stepHeight * callback.m_closestHitFraction;
return currentPosition.lerp( targetPosition, callback.m_closestHitFraction );
}
m_verticalVelocity = btScalar( 0.0 );
m_verticalOffset = btScalar( 0.0 );
return currentPosition;
}
else
{
currentStepOffset = m_stepHeight;
return targetPosition;
}
}
///Reflect the vector d around the vector r
inline btVector3 reflect( const btVector3& d, const btVector3& r )
{
return d - ( btScalar( 2.0 ) * d.dot( r ) ) * r;
}
///Project a vector u on another vector v
inline btVector3 project( const btVector3& u, const btVector3& v )
{
return v * u.dot( v );
}
///Helper for computing the character sliding
inline btVector3 slide( const btVector3& direction, const btVector3& planeNormal )
{
return direction - project( direction, planeNormal );
}
btVector3 slideOnCollision( const btVector3& fromPosition, const btVector3& toPosition, const btVector3& hitNormal )
{
btVector3 moveDirection = toPosition - fromPosition;
btScalar moveLength = moveDirection.length();
if( moveLength <= btScalar( SIMD_EPSILON ) )
return toPosition;
moveDirection.normalize();
btVector3 reflectDir = reflect( moveDirection, hitNormal );
reflectDir.normalize();
return fromPosition + slide( reflectDir, hitNormal ) * moveLength;
}
btVector3 btKinematicCharacterController::stepForwardAndStrafe( btCollisionWorld* collisionWorld, const btVector3& currentPosition, const btVector3& walkMove )
{
// We go to !
//
btVector3 targetPosition = currentPosition + walkMove;
//if the no collisions mode is on, no need to go any further
if(!mCollision) return targetPosition;
// Retrieve the collision shape
//
btCollisionShape* collisionShape = externalGhostObject->getCollisionShape();
btAssert( collisionShape->isConvex() );
btConvexShape* convexShape = ( btConvexShape* )collisionShape;
btTransform start;
start.setIdentity();
btTransform end;
end.setIdentity();
btScalar fraction = btScalar( 1.0 );
// This optimization scheme suffers in the corners.
// It basically jumps from a wall to another, then fails to find a new
// position (after 4 iterations here) and finally don't move at all.
//
// The stepping algorithm adds some problems with stairs. It seems
// the treads create some fake corner using capsules for collisions.
//
for( int i = 0; i < 4 && fraction > btScalar( 0.01 ); i++ )
{
start.setOrigin( currentPosition );
end.setOrigin( targetPosition );
btVector3 sweepDirNegative = currentPosition - targetPosition;
btKinematicClosestNotMeConvexResultCallback callback( externalGhostObject, sweepDirNegative, btScalar( 0.0 ) );
callback.m_collisionFilterGroup = externalGhostObject->getBroadphaseHandle()->m_collisionFilterGroup;
callback.m_collisionFilterMask = externalGhostObject->getBroadphaseHandle()->m_collisionFilterMask;
if( m_useGhostObjectSweepTest )
externalGhostObject->convexSweepTest( convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration );
else
collisionWorld->convexSweepTest( convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration );
if( callback.hasHit() )
{
// Try another target position
//
targetPosition = slideOnCollision( currentPosition, targetPosition, callback.m_hitNormalWorld );
fraction = callback.m_closestHitFraction;
}
else
// Move to the valid target position
//
return targetPosition;
}
// Don't move if you can't find a valid target position...
// It prevents some flickering.
//
return currentPosition;
}
///Handle the gravity
btScalar btKinematicCharacterController::addFallOffset( bool wasOnGround, btScalar currentStepOffset, btScalar dt )
{
btScalar downVelocity = ( m_verticalVelocity < 0.0 ? -m_verticalVelocity : btScalar( 0.0 ) ) * dt;
if( downVelocity > btScalar( 0.0 ) && downVelocity < m_stepHeight && ( wasOnGround || !m_wasJumping ) )
downVelocity = m_stepHeight;
return currentStepOffset + downVelocity;
}
btVector3 btKinematicCharacterController::stepDown( btCollisionWorld* collisionWorld, const btVector3& currentPosition, btScalar currentStepOffset )
{
btVector3 stepDrop = getUpAxisDirections()[ m_upAxis ] * currentStepOffset;
// Be sure we are falling from the last m_currentPosition
// It prevents some flickering
//
btVector3 targetPosition = currentPosition - stepDrop;
//if the no collisions mode is on, no need to go any further
if(!mCollision) return targetPosition;
btTransform start;
start.setIdentity();
start.setOrigin( currentPosition );
btTransform end;
end.setIdentity();
end.setOrigin( targetPosition );
btKinematicClosestNotMeConvexResultCallback callback( internalGhostObject, getUpAxisDirections()[ m_upAxis ], m_maxSlopeCosine );
callback.m_collisionFilterGroup = internalGhostObject->getBroadphaseHandle()->m_collisionFilterGroup;
callback.m_collisionFilterMask = internalGhostObject->getBroadphaseHandle()->m_collisionFilterMask;
// Retrieve the collision shape
//
btCollisionShape* collisionShape = internalGhostObject->getCollisionShape();
btAssert( collisionShape->isConvex() );
btConvexShape* convexShape = ( btConvexShape* )collisionShape;
if( m_useGhostObjectSweepTest )
externalGhostObject->convexSweepTest( convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration );
else
collisionWorld->convexSweepTest( convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration );
if( callback.hasHit() )
{
m_verticalVelocity = btScalar( 0.0 );
m_verticalOffset = btScalar( 0.0 );
m_wasJumping = false;
// We dropped a fraction of the height -> hit floor
//
return currentPosition.lerp( targetPosition, callback.m_closestHitFraction );
}
else
// We dropped the full height
//
return targetPosition;
}
void btKinematicCharacterController::setWalkDirection( const btVector3& walkDirection )
{
m_useWalkDirection = true;
m_walkDirection = walkDirection;
}
void btKinematicCharacterController::setVelocityForTimeInterval( const btVector3& velocity, btScalar timeInterval )
{
m_useWalkDirection = false;
m_walkDirection = velocity;
m_velocityTimeInterval = timeInterval;
}
void btKinematicCharacterController::reset()
{
}
void btKinematicCharacterController::warp( const btVector3& origin )
{
btTransform transform;
transform.setIdentity();
transform.setOrigin( -origin );
externalGhostObject->setWorldTransform( transform );
internalGhostObject->setWorldTransform( transform );
}
void btKinematicCharacterController::preStep( btCollisionWorld* collisionWorld )
{
BT_PROFILE( "preStep" );
for( int i = 0; i < 4 && recoverFromPenetration ( collisionWorld ); i++ );
}
void btKinematicCharacterController::playerStep( btCollisionWorld* collisionWorld, btScalar dt )
{
BT_PROFILE( "playerStep" );
if( !m_useWalkDirection && m_velocityTimeInterval <= btScalar( 0.0 ) )
return;
bool wasOnGround = onGround();
// Handle the gravity
//
m_verticalVelocity -= m_gravity * dt;
if( m_verticalVelocity > 0.0 && m_verticalVelocity > m_jumpSpeed )
m_verticalVelocity = m_jumpSpeed;
if( m_verticalVelocity < 0.0 && btFabs( m_verticalVelocity ) > btFabs( m_fallSpeed ) )
m_verticalVelocity = -btFabs( m_fallSpeed );
m_verticalOffset = m_verticalVelocity * dt;
// This forced stepping up can cause problems when the character
// walks (jump in fact...) under too low ceilings.
//
btVector3 currentPosition = externalGhostObject->getWorldTransform().getOrigin();
btScalar currentStepOffset;
currentPosition = stepUp( collisionWorld, currentPosition, currentStepOffset );
// Move in the air and slide against the walls ignoring the stair steps.
//
if( m_useWalkDirection )
currentPosition = stepForwardAndStrafe( collisionWorld, currentPosition, m_walkDirection );
else
{
btScalar dtMoving = ( dt < m_velocityTimeInterval ) ? dt : m_velocityTimeInterval;
m_velocityTimeInterval -= dt;
// How far will we move while we are moving ?
//
btVector3 moveDirection = m_walkDirection * dtMoving;
currentPosition = stepForwardAndStrafe( collisionWorld, currentPosition, moveDirection );
}
// Finally find the ground.
//
currentStepOffset = addFallOffset( wasOnGround, currentStepOffset, dt );
currentPosition = stepDown( collisionWorld, currentPosition, currentStepOffset );
// Apply the new position to the collision objects.
//
btTransform tranform;
tranform = externalGhostObject->getWorldTransform();
tranform.setOrigin( currentPosition );
externalGhostObject->setWorldTransform( tranform );
internalGhostObject->setWorldTransform( tranform );
}
void btKinematicCharacterController::setFallSpeed( btScalar fallSpeed )
{
m_fallSpeed = fallSpeed;
}
void btKinematicCharacterController::setJumpSpeed( btScalar jumpSpeed )
{
m_jumpSpeed = jumpSpeed;
}
void btKinematicCharacterController::setMaxJumpHeight( btScalar maxJumpHeight )
{
m_maxJumpHeight = maxJumpHeight;
}
bool btKinematicCharacterController::canJump() const
{
return onGround();
}
void btKinematicCharacterController::jump()
{
if( !canJump() )
return;
m_verticalVelocity = m_jumpSpeed;
m_wasJumping = true;
}
void btKinematicCharacterController::setGravity( btScalar gravity )
{
m_gravity = gravity;
}
btScalar btKinematicCharacterController::getGravity() const
{
return m_gravity;
}
void btKinematicCharacterController::setMaxSlope( btScalar slopeRadians )
{
m_maxSlopeRadians = slopeRadians;
m_maxSlopeCosine = btCos( slopeRadians );
}
btScalar btKinematicCharacterController::getMaxSlope() const
{
return m_maxSlopeRadians;
}
bool btKinematicCharacterController::onGround() const
{
return btFabs( m_verticalVelocity ) < btScalar( SIMD_EPSILON ) &&
btFabs( m_verticalOffset ) < btScalar( SIMD_EPSILON );
}
btVector3* btKinematicCharacterController::getUpAxisDirections()
{
static btVector3 sUpAxisDirection[] =
{
btVector3( btScalar( 0.0 ), btScalar( 0.0 ), btScalar( 0.0 ) ),
btVector3( btScalar( 0.0 ), btScalar( 1.0 ), btScalar( 0.0 ) ),
btVector3( btScalar( 0.0 ), btScalar( 0.0 ), btScalar( 1.0 ) )
};
return sUpAxisDirection;
}
void btKinematicCharacterController::debugDraw( btIDebugDraw* debugDrawer )
{
}

View file

@ -0,0 +1,168 @@
/*
Bullet Continuous Collision Detection and Physics Library
Copyright (c) 2003-2008 Erwin Coumans http://bulletphysics.com
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it freely,
subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#ifndef KINEMATIC_CHARACTER_CONTROLLER_H
#define KINEMATIC_CHARACTER_CONTROLLER_H
#include "LinearMath/btVector3.h"
#include "LinearMath/btQuickprof.h"
#include "BulletDynamics/Character/btCharacterControllerInterface.h"
#include "BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h"
class btCollisionShape;
class btRigidBody;
class btCollisionWorld;
class btCollisionDispatcher;
class btPairCachingGhostObject;
///btKinematicCharacterController is an object that supports a sliding motion in a world.
///It uses a ghost object and convex sweep test to test for upcoming collisions. This is combined with discrete collision detection to recover from penetrations.
///Interaction between btKinematicCharacterController and dynamic rigid bodies needs to be explicity implemented by the user.
class btKinematicCharacterController : public btCharacterControllerInterface
{
public:
enum UpAxis
{
X_AXIS = 0,
Y_AXIS = 1,
Z_AXIS = 2
};
private:
btPairCachingGhostObject* externalGhostObject; // use this for querying collisions for sliding and move
btPairCachingGhostObject* internalGhostObject; // and this for recoreving from penetrations
btScalar m_verticalVelocity;
btScalar m_verticalOffset;
btScalar m_fallSpeed;
btScalar m_jumpSpeed;
btScalar m_maxJumpHeight;
btScalar m_maxSlopeRadians; // Slope angle that is set (used for returning the exact value)
btScalar m_maxSlopeCosine; // Cosine equivalent of m_maxSlopeRadians (calculated once when set, for optimization)
btScalar m_gravity;
btScalar m_recoveringFactor;
btScalar m_stepHeight;
///this is the desired walk direction, set by the user
btVector3 m_walkDirection;
///keep track of the contact manifolds
btManifoldArray m_manifoldArray;
///Gravity attributes
bool m_wasJumping;
bool m_useGhostObjectSweepTest;
bool m_useWalkDirection;
btScalar m_velocityTimeInterval;
UpAxis m_upAxis;
static btVector3* getUpAxisDirections();
bool recoverFromPenetration ( btCollisionWorld* collisionWorld );
btVector3 stepUp( btCollisionWorld* collisionWorld, const btVector3& currentPosition, btScalar& currentStepOffset );
btVector3 stepForwardAndStrafe( btCollisionWorld* collisionWorld, const btVector3& currentPosition, const btVector3& walkMove );
btScalar addFallOffset( bool wasJumping, btScalar currentStepOffset, btScalar dt );
btVector3 stepDown( btCollisionWorld* collisionWorld, const btVector3& currentPosition, btScalar currentStepOffset );
public:
/// externalGhostObject is used for querying the collisions for sliding along the wall,
/// and internalGhostObject is used for querying the collisions for recovering from large penetrations.
/// These parameters can point on the same object.
/// Using a smaller internalGhostObject can help for removing some flickering but create some
/// stopping artefacts when sliding along stairs or small walls.
/// Don't forget to scale gravity and fallSpeed if you scale the world.
btKinematicCharacterController( btPairCachingGhostObject* externalGhostObject,
btPairCachingGhostObject* internalGhostObject,
btScalar stepHeight,
btScalar constantScale = btScalar( 1.0 ),
btScalar gravity = btScalar( 9.8 ),
btScalar fallVelocity = btScalar( 55.0 ),
btScalar jumpVelocity = btScalar( 9.8 ),
btScalar recoveringFactor = btScalar( 0.2 ) );
~btKinematicCharacterController ();
void setVerticalVelocity(float z);
///btActionInterface interface
virtual void updateAction( btCollisionWorld* collisionWorld, btScalar deltaTime )
{
preStep( collisionWorld );
playerStep( collisionWorld, deltaTime );
}
///btActionInterface interface
void debugDraw( btIDebugDraw* debugDrawer );
void setUpAxis( UpAxis axis )
{
m_upAxis = axis;
}
/// This should probably be called setPositionIncrementPerSimulatorStep.
/// This is neither a direction nor a velocity, but the amount to
/// increment the position each simulation iteration, regardless
/// of dt.
/// This call will reset any velocity set by setVelocityForTimeInterval().
virtual void setWalkDirection(const btVector3& walkDirection);
/// Caller provides a velocity with which the character should move for
/// the given time period. After the time period, velocity is reset
/// to zero.
/// This call will reset any walk direction set by setWalkDirection().
/// Negative time intervals will result in no motion.
virtual void setVelocityForTimeInterval(const btVector3& velocity,
btScalar timeInterval);
void reset();
void warp( const btVector3& origin );
void preStep( btCollisionWorld* collisionWorld );
void playerStep( btCollisionWorld* collisionWorld, btScalar dt );
void setFallSpeed( btScalar fallSpeed );
void setJumpSpeed( btScalar jumpSpeed );
void setMaxJumpHeight( btScalar maxJumpHeight );
bool canJump() const;
void jump();
void setGravity( btScalar gravity );
btScalar getGravity() const;
/// The max slope determines the maximum angle that the controller can walk up.
/// The slope angle is measured in radians.
void setMaxSlope( btScalar slopeRadians );
btScalar getMaxSlope() const;
void setUseGhostSweepTest( bool useGhostObjectSweepTest )
{
m_useGhostObjectSweepTest = useGhostObjectSweepTest;
}
bool onGround() const;
//if set to false, there will be no collision.
bool mCollision;
};
#endif // KINEMATIC_CHARACTER_CONTROLLER_H

View file

@ -0,0 +1,799 @@
#include "physic.hpp"
#include <btBulletDynamicsCommon.h>
#include <btBulletCollisionCommon.h>
#include <BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h>
#include <components/nifbullet/bulletnifloader.hpp>
#include "CMotionState.h"
#include "OgreRoot.h"
#include "btKinematicCharacterController.h"
#include "BtOgrePG.h"
#include "BtOgreGP.h"
#include "BtOgreExtras.h"
#include <boost/lexical_cast.hpp>
#include <boost/format.hpp>
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)
: mName(name), mEngine(engine), mMesh(mesh), mBoxScaledTranslation(0,0,0), mBoxRotationInverse(0,0,0,0)
, mBody(0), mRaycastingBody(0), mOnGround(false), mCollisionMode(true), mBoxRotation(0,0,0,0)
, mForce(0.0f)
{
mBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, position, rotation, &mBoxScaledTranslation, &mBoxRotation);
mRaycastingBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, position, rotation, &mBoxScaledTranslation, &mBoxRotation, true);
Ogre::Quaternion inverse = mBoxRotation.Inverse();
mBoxRotationInverse = btQuaternion(inverse.x, inverse.y, inverse.z,inverse.w);
mEngine->addRigidBody(mBody, false, mRaycastingBody,true); //Add rigid body to dynamics world, but do not add to object map
}
PhysicActor::~PhysicActor()
{
if(mBody)
{
mEngine->dynamicsWorld->removeRigidBody(mBody);
delete mBody;
}
if(mRaycastingBody)
{
mEngine->dynamicsWorld->removeRigidBody(mRaycastingBody);
delete mRaycastingBody;
}
}
void PhysicActor::enableCollisions(bool collision)
{
assert(mBody);
if(collision && !mCollisionMode) enableCollisionBody();
if(!collision && mCollisionMode) disableCollisionBody();
mCollisionMode = collision;
}
void PhysicActor::setPosition(const Ogre::Vector3 &pos)
{
assert(mBody);
if(pos != getPosition())
{
mEngine->adjustRigidBody(mBody, pos, getRotation(), mBoxScaledTranslation, mBoxRotation);
mEngine->adjustRigidBody(mRaycastingBody, pos, getRotation(), mBoxScaledTranslation, mBoxRotation);
}
}
void PhysicActor::setRotation(const Ogre::Quaternion &quat)
{
assert(mBody);
if(!quat.equals(getRotation(), Ogre::Radian(0))){
mEngine->adjustRigidBody(mBody, getPosition(), quat, mBoxScaledTranslation, mBoxRotation);
mEngine->adjustRigidBody(mRaycastingBody, getPosition(), quat, mBoxScaledTranslation, mBoxRotation);
}
}
Ogre::Vector3 PhysicActor::getPosition()
{
assert(mBody);
btVector3 vec = mBody->getWorldTransform().getOrigin();
Ogre::Quaternion rotation = Ogre::Quaternion(mBody->getWorldTransform().getRotation().getW(), mBody->getWorldTransform().getRotation().getX(),
mBody->getWorldTransform().getRotation().getY(), mBody->getWorldTransform().getRotation().getZ());
Ogre::Vector3 transrot = rotation * mBoxScaledTranslation;
Ogre::Vector3 visualPosition = Ogre::Vector3(vec.getX(), vec.getY(), vec.getZ()) - transrot;
return visualPosition;
}
Ogre::Quaternion PhysicActor::getRotation()
{
assert(mBody);
btQuaternion quat = mBody->getWorldTransform().getRotation() * mBoxRotationInverse;
return Ogre::Quaternion(quat.getW(), quat.getX(), quat.getY(), quat.getZ());
}
void PhysicActor::setScale(float scale){
//We only need to change the scaled box translation, box rotations remain the same.
assert(mBody);
mBoxScaledTranslation = mBoxScaledTranslation / mBody->getCollisionShape()->getLocalScaling().getX();
mBoxScaledTranslation *= scale;
Ogre::Vector3 pos = getPosition();
Ogre::Quaternion rot = getRotation();
if(mBody){
mEngine->dynamicsWorld->removeRigidBody(mBody);
mEngine->dynamicsWorld->removeRigidBody(mRaycastingBody);
delete mBody;
delete mRaycastingBody;
}
//Create the newly scaled rigid body
mBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, pos, rot);
mRaycastingBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, pos, rot, 0, 0, true);
mEngine->addRigidBody(mBody, false, mRaycastingBody,true); //Add rigid body to dynamics world, but do not add to object map
}
Ogre::Vector3 PhysicActor::getHalfExtents() const
{
if(mBody)
{
btBoxShape *box = static_cast<btBoxShape*>(mBody->getCollisionShape());
if(box != NULL)
{
btVector3 size = box->getHalfExtentsWithMargin();
return Ogre::Vector3(size.getX(), size.getY(), size.getZ());
}
}
return Ogre::Vector3(0.0f);
}
void PhysicActor::setInertialForce(const Ogre::Vector3 &force)
{
mForce = force;
}
void PhysicActor::setOnGround(bool grounded)
{
mOnGround = grounded;
}
void PhysicActor::disableCollisionBody()
{
mEngine->dynamicsWorld->removeRigidBody(mBody);
}
void PhysicActor::enableCollisionBody()
{
mEngine->dynamicsWorld->addRigidBody(mBody,CollisionType_Actor,CollisionType_World|CollisionType_HeightMap);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
RigidBody::RigidBody(btRigidBody::btRigidBodyConstructionInfo& CI,std::string name)
: btRigidBody(CI)
, mName(name)
, mPlaceable(false)
{
}
RigidBody::~RigidBody()
{
delete getMotionState();
}
///////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////
PhysicEngine::PhysicEngine(BulletShapeLoader* shapeLoader) :
mDebugActive(0)
, mSceneMgr(NULL)
{
// Set up the collision configuration and dispatcher
collisionConfiguration = new btDefaultCollisionConfiguration();
dispatcher = new btCollisionDispatcher(collisionConfiguration);
// The actual physics solver
solver = new btSequentialImpulseConstraintSolver;
//btOverlappingPairCache* pairCache = new btSortedOverlappingPairCache();
pairCache = new btSortedOverlappingPairCache();
//pairCache->setInternalGhostPairCallback( new btGhostPairCallback() );
broadphase = new btDbvtBroadphase();
// The world.
dynamicsWorld = new btDiscreteDynamicsWorld(dispatcher,broadphase,solver,collisionConfiguration);
dynamicsWorld->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, dynamicsWorld);
dynamicsWorld->setDebugDrawer(mDebugDrawer);
isDebugCreated = true;
dynamicsWorld->debugDrawWorld();
}
}
void PhysicEngine::setDebugRenderingMode(int mode)
{
if(!isDebugCreated)
{
createDebugRendering();
}
mDebugDrawer->setDebugMode(mode);
mDebugActive = mode;
}
bool PhysicEngine::toggleDebugRendering()
{
setDebugRenderingMode(!mDebugActive);
return mDebugActive;
}
void PhysicEngine::setSceneManager(Ogre::SceneManager* sceneMgr)
{
mSceneMgr = sceneMgr;
}
PhysicEngine::~PhysicEngine()
{
HeightFieldContainer::iterator hf_it = mHeightFieldMap.begin();
for (; hf_it != mHeightFieldMap.end(); ++hf_it)
{
dynamicsWorld->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)
{
dynamicsWorld->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)
{
dynamicsWorld->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 dynamicsWorld;
delete solver;
delete collisionConfiguration;
delete dispatcher;
delete broadphase;
delete pairCache;
delete mShapeLoader;
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(
sqrtVerts, sqrtVerts, heights, 1,
minh, maxh, 2,
PHY_FLOAT,true);
hfShape->setUseDiamondSubdivision(true);
btVector3 scl(triSize, triSize, 1);
hfShape->setLocalScaling(scl);
CMotionState* newMotionState = new CMotionState(this,name);
btRigidBody::btRigidBodyConstructionInfo CI = btRigidBody::btRigidBodyConstructionInfo(0,newMotionState,hfShape);
RigidBody* body = new RigidBody(CI,name);
body->getWorldTransform().setOrigin(btVector3( (x+0.5)*triSize*(sqrtVerts-1), (y+0.5)*triSize*(sqrtVerts-1), (maxh+minh)/2.f));
HeightField hf;
hf.mBody = body;
hf.mShape = hfShape;
mHeightFieldMap [name] = hf;
dynamicsWorld->addRigidBody(body,CollisionType_HeightMap|CollisionType_Raycasting,
CollisionType_World|CollisionType_Actor|CollisionType_Raycasting);
}
void PhysicEngine::removeHeightField(int x, int y)
{
const std::string name = "HeightField_"
+ boost::lexical_cast<std::string>(x) + "_"
+ boost::lexical_cast<std::string>(y);
HeightField hf = mHeightFieldMap [name];
dynamicsWorld->removeRigidBody(hf.mBody);
delete hf.mShape;
delete hf.mBody;
mHeightFieldMap.erase(name);
}
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;
//std::cout << "The string" << outputstring << "\n";
//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");
if (placeable && !raycasting && shape->mCollisionShape && !shape->mHasCollisionNode)
return NULL;
if (!shape->mCollisionShape && !raycasting)
return NULL;
if (!shape->mRaycastingShape && raycasting)
return NULL;
if (!raycasting)
shape->mCollisionShape->setLocalScaling( btVector3(scale,scale,scale));
else
shape->mRaycastingShape->setLocalScaling( btVector3(scale,scale,scale));
//create the motionState
CMotionState* newMotionState = new CMotionState(this,name);
//create the real body
btRigidBody::btRigidBodyConstructionInfo CI = btRigidBody::btRigidBodyConstructionInfo
(0,newMotionState, raycasting ? shape->mRaycastingShape : shape->mCollisionShape);
RigidBody* body = new RigidBody(CI,name);
body->mPlaceable = placeable;
if(scaledBoxTranslation != 0)
*scaledBoxTranslation = shape->mBoxTranslation * scale;
if(boxRotation != 0)
*boxRotation = shape->mBoxRotation;
adjustRigidBody(body, position, rotation, shape->mBoxTranslation * scale, shape->mBoxRotation);
return body;
}
void PhysicEngine::addRigidBody(RigidBody* body, bool addToMap, RigidBody* raycastingBody,bool actor)
{
if(!body && !raycastingBody)
return; // nothing to do
const std::string& name = (body ? body->mName : raycastingBody->mName);
if (body){
if(actor) dynamicsWorld->addRigidBody(body,CollisionType_Actor,CollisionType_World|CollisionType_HeightMap);
else dynamicsWorld->addRigidBody(body,CollisionType_World,CollisionType_World|CollisionType_Actor|CollisionType_HeightMap);
}
if (raycastingBody)
dynamicsWorld->addRigidBody(raycastingBody,CollisionType_Raycasting,CollisionType_Raycasting|CollisionType_World);
if(addToMap){
removeRigidBody(name);
deleteRigidBody(name);
if (body)
mCollisionObjectMap[name] = body;
if (raycastingBody)
mRaycastingObjectMap[name] = raycastingBody;
}
}
void PhysicEngine::removeRigidBody(const std::string &name)
{
RigidBodyContainer::iterator it = mCollisionObjectMap.find(name);
if (it != mCollisionObjectMap.end() )
{
RigidBody* body = it->second;
if(body != NULL)
{
dynamicsWorld->removeRigidBody(body);
}
}
it = mRaycastingObjectMap.find(name);
if (it != mRaycastingObjectMap.end() )
{
RigidBody* body = it->second;
if(body != NULL)
{
dynamicsWorld->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)
{
delete body;
}
mCollisionObjectMap.erase(it);
}
it = mRaycastingObjectMap.find(name);
if (it != mRaycastingObjectMap.end() )
{
RigidBody* body = it->second;
if(body != NULL)
{
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)
{
RigidBody* body = getRigidBody(name);
ContactTestResultCallback callback;
dynamicsWorld->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);
dynamicsWorld->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
dynamicsWorld->stepSimulation(deltaT,10, 1/60.0);
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);
//dynamicsWorld->addAction( newActor->mCharacter );
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;
}
}
void PhysicEngine::emptyEventLists(void)
{
}
std::pair<std::string,float> PhysicEngine::rayTest(btVector3& from,btVector3& to,bool raycastingObjectOnly,bool ignoreHeightMap)
{
std::string name = "";
float d = -1;
btCollisionWorld::ClosestRayResultCallback resultCallback1(from, to);
if(raycastingObjectOnly)
resultCallback1.m_collisionFilterMask = CollisionType_Raycasting;
else
resultCallback1.m_collisionFilterMask = CollisionType_World;
if(!ignoreHeightMap)
resultCallback1.m_collisionFilterMask = resultCallback1.m_collisionFilterMask | CollisionType_HeightMap;
dynamicsWorld->rayTest(from, to, resultCallback1);
if (resultCallback1.hasHit())
{
name = static_cast<const RigidBody&>(*resultCallback1.m_collisionObject).mName;
d = resultCallback1.m_closestHitFraction;;
}
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_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);
dynamicsWorld->convexSweepTest(&shape, from_, to_, callback);
if (callback.hasHit())
return std::make_pair(true, callback.m_closestHitFraction);
else
return std::make_pair(false, 1);
}
std::vector< std::pair<float, std::string> > PhysicEngine::rayTest2(btVector3& from, btVector3& to)
{
MyRayResultCallback resultCallback1;
resultCallback1.m_collisionFilterMask = CollisionType_Raycasting;
dynamicsWorld->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);
}
}
bool PhysicEngine::isAnyActorStandingOn (const std::string& objectName)
{
for (PhysicActorContainer::iterator it = mActorMap.begin(); it != mActorMap.end(); ++it)
{
if (!it->second->getOnGround())
continue;
Ogre::Vector3 pos = it->second->getPosition();
btVector3 from (pos.x, pos.y, pos.z);
btVector3 to = from - btVector3(0,0,5);
std::pair<std::string, float> result = rayTest(from, to);
if (result.first == objectName)
return true;
}
return false;
}
}
}

View file

@ -0,0 +1,389 @@
#ifndef OENGINE_BULLET_PHYSIC_H
#define OENGINE_BULLET_PHYSIC_H
#include <BulletDynamics/Dynamics/btRigidBody.h>
#include "BulletCollision/CollisionDispatch/btGhostObject.h"
#include <string>
#include <list>
#include <map>
#include "BulletShapeLoader.h"
#include "BulletCollision/CollisionShapes/btScaledBvhTriangleMeshShape.h"
class btRigidBody;
class btBroadphaseInterface;
class btDefaultCollisionConfiguration;
class btSequentialImpulseConstraintSolver;
class btCollisionDispatcher;
class btDiscreteDynamicsWorld;
class btHeightfieldTerrainShape;
namespace BtOgre
{
class DebugDrawer;
}
namespace Ogre
{
class SceneManager;
}
namespace MWWorld
{
class World;
}
namespace OEngine {
namespace Physic
{
class CMotionState;
struct PhysicEvent;
class PhysicEngine;
class RigidBody;
enum CollisionType {
CollisionType_Nothing = 0, //<Collide with nothing
CollisionType_World = 1<<0, //<Collide with world objects
CollisionType_Actor = 1<<1, //<Collide sith actors
CollisionType_HeightMap = 1<<2, //<collide with heightmap
CollisionType_Raycasting = 1<<3 //Still used?
};
/**
*This is just used to be able to name objects.
*/
class PairCachingGhostObject : public btPairCachingGhostObject
{
public:
PairCachingGhostObject(std::string name)
:btPairCachingGhostObject(),mName(name)
{
}
virtual ~PairCachingGhostObject(){}
std::string mName;
};
/**
*This class is just an extension of normal btRigidBody in order to add extra info.
*When bullet give back a btRigidBody, you can just do a static_cast to RigidBody,
*so one never should use btRigidBody directly!
*/
class RigidBody: public btRigidBody
{
public:
RigidBody(btRigidBody::btRigidBodyConstructionInfo& CI,std::string name);
virtual ~RigidBody();
std::string mName;
bool mPlaceable;
};
/**
* A physic actor uses a rigid body based on box shapes.
* Pmove is used to move the physic actor around the dynamic world.
*/
class PhysicActor
{
public:
PhysicActor(const std::string &name, const std::string &mesh, PhysicEngine *engine, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation, float scale);
~PhysicActor();
void setPosition(const Ogre::Vector3 &pos);
/**
* This adjusts the rotation of a PhysicActor
* If we have any problems with this (getting stuck in pmove) we should change it
* from setting the visual orientation to setting the orientation of the rigid body directly.
*/
void setRotation(const Ogre::Quaternion &quat);
void enableCollisions(bool collision);
bool getCollisionMode() const
{
return mCollisionMode;
}
/**
* This returns the visual position of the PhysicActor (used to position a scenenode).
* Note - this is different from the position of the contained mBody.
*/
Ogre::Vector3 getPosition();
/**
* Returns the visual orientation of the PhysicActor
*/
Ogre::Quaternion getRotation();
/**
* Sets the scale of the PhysicActor
*/
void setScale(float scale);
/**
* Returns the half extents for this PhysiActor
*/
Ogre::Vector3 getHalfExtents() const;
/**
* Sets the current amount of inertial force (incl. gravity) affecting this physic actor
*/
void setInertialForce(const Ogre::Vector3 &force);
/**
* Gets the current amount of inertial force (incl. gravity) affecting this physic actor
*/
const Ogre::Vector3 &getInertialForce() const
{
return mForce;
}
void setOnGround(bool grounded);
bool getOnGround() const
{
return mCollisionMode && mOnGround;
}
btCollisionObject *getCollisionBody() const
{
return mBody;
}
private:
void disableCollisionBody();
void enableCollisionBody();
public:
//HACK: in Visual Studio 2010 and presumably above, this structures alignment
// must be 16, but the built in operator new & delete don't properly
// perform this alignment.
#if _MSC_VER >= 1600
void * operator new (size_t Size) { return _aligned_malloc (Size, 16); }
void operator delete (void * Data) { _aligned_free (Data); }
#endif
private:
OEngine::Physic::RigidBody* mBody;
OEngine::Physic::RigidBody* mRaycastingBody;
Ogre::Vector3 mBoxScaledTranslation;
Ogre::Quaternion mBoxRotation;
btQuaternion mBoxRotationInverse;
Ogre::Vector3 mForce;
bool mOnGround;
bool mCollisionMode;
std::string mMesh;
std::string mName;
PhysicEngine *mEngine;
};
struct HeightField
{
btHeightfieldTerrainShape* mShape;
RigidBody* mBody;
};
/**
* The PhysicEngine class contain everything which is needed for Physic.
* It's needed that Ogre Resources are set up before the PhysicEngine is created.
* Note:deleting it WILL NOT delete the RigidBody!
* TODO:unload unused resources?
*/
class PhysicEngine
{
public:
/**
* Note that the shapeLoader IS destroyed by the phyic Engine!!
*/
PhysicEngine(BulletShapeLoader* shapeLoader);
/**
* It DOES destroy the shape loader!
*/
~PhysicEngine();
/**
* Creates a RigidBody. It does not add it to the simulation.
* After created, the body is set to the correct rotation, position, and scale
*/
RigidBody* createAndAdjustRigidBody(const std::string &mesh, const std::string &name,
float scale, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation,
Ogre::Vector3* scaledBoxTranslation = 0, Ogre::Quaternion* boxRotation = 0, bool raycasting=false, bool placeable=false);
/**
* Adjusts a rigid body to the right position and rotation
*/
void adjustRigidBody(RigidBody* body, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation,
const Ogre::Vector3 &scaledBoxTranslation = Ogre::Vector3::ZERO,
const Ogre::Quaternion &boxRotation = Ogre::Quaternion::IDENTITY);
/**
Mainly used to (but not limited to) adjust rigid bodies based on box shapes to the right position and rotation.
*/
void boxAdjustExternal(const std::string &mesh, RigidBody* body, float scale, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation);
/**
* Add a HeightField to the simulation
*/
void addHeightField(float* heights,
int x, int y, float yoffset,
float triSize, float sqrtVerts);
/**
* Remove a HeightField from the simulation
*/
void removeHeightField(int x, int y);
/**
* Add a RigidBody to the simulation
*/
void addRigidBody(RigidBody* body, bool addToMap = true, RigidBody* raycastingBody = NULL,bool actor = false);
/**
* Remove a RigidBody from the simulation. It does not delete it, and does not remove it from the RigidBodyMap.
*/
void removeRigidBody(const std::string &name);
/**
* Delete a RigidBody, and remove it from RigidBodyMap.
*/
void deleteRigidBody(const std::string &name);
/**
* Return a pointer to a given rigid body.
*/
RigidBody* getRigidBody(const std::string &name, bool raycasting=false);
/**
* Create and add a character to the scene, and add it to the ActorMap.
*/
void addCharacter(const std::string &name, const std::string &mesh,
const Ogre::Vector3 &position, float scale, const Ogre::Quaternion &rotation);
/**
* Remove a character from the scene. TODO:delete it! for now, a small memory leak^^ done?
*/
void removeCharacter(const std::string &name);
/**
* Return a pointer to a character
* TODO:check if the actor exist...
*/
PhysicActor* getCharacter(const std::string &name);
/**
* This step the simulation of a given time.
*/
void stepSimulation(double deltaT);
/**
* Empty events lists
*/
void emptyEventLists(void);
/**
* Create a debug rendering. It is called by setDebgRenderingMode if it's not created yet.
* Important Note: this will crash if the Render is not yet initialise!
*/
void createDebugRendering();
/**
* Set the debug rendering mode. 0 to turn it off.
* Important Note: this will crash if the Render is not yet initialise!
*/
void setDebugRenderingMode(int mode);
bool toggleDebugRendering();
void getObjectAABB(const std::string &mesh, float scale, btVector3 &min, btVector3 &max);
void setSceneManager(Ogre::SceneManager* sceneMgr);
bool isAnyActorStandingOn (const std::string& objectName);
/**
* Return the closest object hit by a ray. If there are no objects, it will return ("",-1).
*/
std::pair<std::string,float> rayTest(btVector3& from,btVector3& to,bool raycastingObjectOnly = true,bool ignoreHeightMap = false);
/**
* Return all objects hit by a ray.
*/
std::vector< std::pair<float, std::string> > rayTest2(btVector3& from, btVector3& to);
std::pair<bool, float> sphereCast (float radius, btVector3& from, btVector3& to);
///< @return (hit, relative distance)
std::vector<std::string> getCollisions(const std::string& name);
// Get the nearest object that's inside the given object, filtering out objects of the
// provided name
std::pair<const RigidBody*,btVector3> getFilteredContact(const std::string &filter,
const btVector3 &origin,
btCollisionObject *object);
//event list of non player object
std::list<PhysicEvent> NPEventList;
//event list affecting the player
std::list<PhysicEvent> PEventList;
//Bullet Stuff
btOverlappingPairCache* pairCache;
btBroadphaseInterface* broadphase;
btDefaultCollisionConfiguration* collisionConfiguration;
btSequentialImpulseConstraintSolver* solver;
btCollisionDispatcher* dispatcher;
btDiscreteDynamicsWorld* dynamicsWorld;
//the NIF file loader.
BulletShapeLoader* mShapeLoader;
typedef std::map<std::string, HeightField> HeightFieldContainer;
HeightFieldContainer mHeightFieldMap;
typedef std::map<std::string,RigidBody*> RigidBodyContainer;
RigidBodyContainer mCollisionObjectMap;
RigidBodyContainer mRaycastingObjectMap;
typedef std::map<std::string, PhysicActor*> PhysicActorContainer;
PhysicActorContainer mActorMap;
Ogre::SceneManager* mSceneMgr;
//debug rendering
BtOgre::DebugDrawer* mDebugDrawer;
bool isDebugCreated;
bool mDebugActive;
};
struct MyRayResultCallback : public btCollisionWorld::RayResultCallback
{
virtual btScalar addSingleResult( btCollisionWorld::LocalRayResult& rayResult, bool bNormalInWorldSpace)
{
results.push_back( std::make_pair(rayResult.m_hitFraction, rayResult.m_collisionObject) );
return rayResult.m_hitFraction;
}
static bool cmp( const std::pair<float, std::string>& i, const std::pair<float, std::string>& j )
{
if( i.first > j.first ) return false;
if( j.first > i.first ) return true;
return false;
}
std::vector < std::pair<float, const btCollisionObject*> > results;
};
}}
#endif

View file

@ -0,0 +1,130 @@
#include "trace.h"
#include <map>
#include <btBulletDynamicsCommon.h>
#include <btBulletCollisionCommon.h>
#include "physic.hpp"
namespace OEngine
{
namespace Physic
{
class ClosestNotMeConvexResultCallback : public btCollisionWorld::ClosestConvexResultCallback
{
public:
ClosestNotMeConvexResultCallback(btCollisionObject *me, const btVector3 &up, btScalar minSlopeDot)
: btCollisionWorld::ClosestConvexResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0)),
mMe(me), mUp(up), mMinSlopeDot(minSlopeDot)
{
}
virtual btScalar addSingleResult(btCollisionWorld::LocalConvexResult& convexResult,bool normalInWorldSpace)
{
if(convexResult.m_hitCollisionObject == mMe)
return btScalar( 1 );
btVector3 hitNormalWorld;
if(normalInWorldSpace)
hitNormalWorld = convexResult.m_hitNormalLocal;
else
{
///need to transform normal into worldspace
hitNormalWorld = convexResult.m_hitCollisionObject->getWorldTransform().getBasis()*convexResult.m_hitNormalLocal;
}
// NOTE : m_hitNormalLocal is not always vertical on the ground with a capsule or a box...
btScalar dotUp = mUp.dot(hitNormalWorld);
if(dotUp < mMinSlopeDot)
return btScalar(1);
return ClosestConvexResultCallback::addSingleResult(convexResult, normalInWorldSpace);
}
protected:
btCollisionObject *mMe;
const btVector3 mUp;
const btScalar mMinSlopeDot;
};
void ActorTracer::doTrace(btCollisionObject *actor, const Ogre::Vector3 &start, const Ogre::Vector3 &end, const PhysicEngine *enginePass)
{
const btVector3 btstart(start.x, start.y, start.z);
const btVector3 btend(end.x, end.y, end.z);
const btTransform &trans = actor->getWorldTransform();
btTransform from(trans);
btTransform to(trans);
from.setOrigin(btstart);
to.setOrigin(btend);
ClosestNotMeConvexResultCallback newTraceCallback(actor, btstart-btend, btScalar(0.0));
newTraceCallback.m_collisionFilterMask = CollisionType_World | CollisionType_HeightMap |
CollisionType_Actor;
btCollisionShape *shape = actor->getCollisionShape();
assert(shape->isConvex());
enginePass->dynamicsWorld->convexSweepTest(static_cast<btConvexShape*>(shape),
from, to, newTraceCallback);
// Copy the hit data over to our trace results struct:
if(newTraceCallback.hasHit())
{
const btVector3& tracehitnormal = newTraceCallback.m_hitNormalWorld;
mFraction = newTraceCallback.m_closestHitFraction;
mPlaneNormal = Ogre::Vector3(tracehitnormal.x(), tracehitnormal.y(), tracehitnormal.z());
mEndPos = (end-start)*mFraction + start;
}
else
{
mEndPos = end;
mPlaneNormal = Ogre::Vector3(0.0f, 0.0f, 1.0f);
mFraction = 1.0f;
}
}
void ActorTracer::findGround(btCollisionObject *actor, const Ogre::Vector3 &start, const Ogre::Vector3 &end, const PhysicEngine *enginePass)
{
const btVector3 btstart(start.x, start.y, start.z+1.0f);
const btVector3 btend(end.x, end.y, end.z+1.0f);
const btTransform &trans = actor->getWorldTransform();
btTransform from(trans.getBasis(), btstart);
btTransform to(trans.getBasis(), btend);
ClosestNotMeConvexResultCallback newTraceCallback(actor, btstart-btend, btScalar(0.0));
newTraceCallback.m_collisionFilterMask = CollisionType_World | CollisionType_HeightMap |
CollisionType_Actor;
const btBoxShape *shape = dynamic_cast<btBoxShape*>(actor->getCollisionShape());
assert(shape);
btVector3 halfExtents = shape->getHalfExtentsWithMargin();
halfExtents[2] = 1.0f;
btBoxShape box(halfExtents);
enginePass->dynamicsWorld->convexSweepTest(&box, from, to, newTraceCallback);
if(newTraceCallback.hasHit())
{
const btVector3& tracehitnormal = newTraceCallback.m_hitNormalWorld;
mFraction = newTraceCallback.m_closestHitFraction;
mPlaneNormal = Ogre::Vector3(tracehitnormal.x(), tracehitnormal.y(), tracehitnormal.z());
mEndPos = (end-start)*mFraction + start;
mEndPos[2] -= 1.0f;
}
else
{
mEndPos = end;
mPlaneNormal = Ogre::Vector3(0.0f, 0.0f, 1.0f);
mFraction = 1.0f;
}
}
}
}

View file

@ -0,0 +1,31 @@
#ifndef OENGINE_BULLET_TRACE_H
#define OENGINE_BULLET_TRACE_H
#include <OgreVector3.h>
class btCollisionObject;
namespace OEngine
{
namespace Physic
{
class PhysicEngine;
struct ActorTracer
{
Ogre::Vector3 mEndPos;
Ogre::Vector3 mPlaneNormal;
float mFraction;
void doTrace(btCollisionObject *actor, const Ogre::Vector3 &start, const Ogre::Vector3 &end,
const PhysicEngine *enginePass);
void findGround(btCollisionObject *actor, const Ogre::Vector3 &start, const Ogre::Vector3 &end,
const PhysicEngine *enginePass);
};
}
}
#endif