mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-04-30 13:57:59 +03:00
Imported Upstream version 0.26.0
This commit is contained in:
commit
9a2b6c69b6
1398 changed files with 212217 additions and 0 deletions
1094
libs/openengine/bullet/BtOgre.cpp
Normal file
1094
libs/openengine/bullet/BtOgre.cpp
Normal file
File diff suppressed because it is too large
Load diff
282
libs/openengine/bullet/BtOgreExtras.h
Normal file
282
libs/openengine/bullet/BtOgreExtras.h
Normal 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
|
||||
|
||||
|
||||
|
||||
|
||||
|
145
libs/openengine/bullet/BtOgreGP.h
Normal file
145
libs/openengine/bullet/BtOgreGP.h
Normal 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 ¢er);
|
||||
|
||||
Ogre::Entity* mEntity;
|
||||
Ogre::SceneNode* mNode;
|
||||
|
||||
Ogre::Vector3 *mTransformedVerticesTemp;
|
||||
size_t mTransformedVerticesTempSize;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
81
libs/openengine/bullet/BtOgrePG.h
Normal file
81
libs/openengine/bullet/BtOgrePG.h
Normal 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
|
153
libs/openengine/bullet/BulletShapeLoader.cpp
Normal file
153
libs/openengine/bullet/BulletShapeLoader.cpp
Normal 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)
|
||||
{}
|
||||
|
||||
}
|
||||
}
|
176
libs/openengine/bullet/BulletShapeLoader.h
Normal file
176
libs/openengine/bullet/BulletShapeLoader.h
Normal 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
|
46
libs/openengine/bullet/CMotionState.cpp
Normal file
46
libs/openengine/bullet/CMotionState.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
}}
|
52
libs/openengine/bullet/CMotionState.h
Normal file
52
libs/openengine/bullet/CMotionState.h
Normal 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
|
643
libs/openengine/bullet/btKinematicCharacterController.cpp
Normal file
643
libs/openengine/bullet/btKinematicCharacterController.cpp
Normal 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 )
|
||||
{
|
||||
}
|
168
libs/openengine/bullet/btKinematicCharacterController.h
Normal file
168
libs/openengine/bullet/btKinematicCharacterController.h
Normal 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
|
799
libs/openengine/bullet/physic.cpp
Normal file
799
libs/openengine/bullet/physic.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
389
libs/openengine/bullet/physic.hpp
Normal file
389
libs/openengine/bullet/physic.hpp
Normal 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
|
130
libs/openengine/bullet/trace.cpp
Normal file
130
libs/openengine/bullet/trace.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
31
libs/openengine/bullet/trace.h
Normal file
31
libs/openengine/bullet/trace.h
Normal 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
|
Loading…
Add table
Add a link
Reference in a new issue