Merged openengine into openmw

actorid
Nikolay Kasyanov 13 years ago
commit b4343431c6

3
.gitmodules vendored

@ -1,3 +0,0 @@
[submodule "libs/openengine"]
path = libs/openengine
url = git://github.com/zinnschlag/OpenEngine

@ -1 +0,0 @@
Subproject commit 8f98718315fe11af359740c4a025fd1ca52a9157

@ -0,0 +1,3 @@
*~
*.o
*_test

@ -0,0 +1,12 @@
OpenEngine README
=================
OpenEngine is a bunch of stand-alone game engine modules collected from the OpenMW project (see http://github.com/korslund/openmw or http://openmw.com ) and from certain other projects.
It is currently a very early work in progress, and development will follow OpenMW closely for a while forward.
OpenEngine will depend heavily on Mangle ( http://github.com/korslund/mangle/ ) and will thus aim to be backend agnostic. When finished it should work with a variety for free and commercial middleware libraries as backends for graphics, sound, physics, input and so on.
All questions can be directed to Nicolay Korslund at korslund@gmail.com
- Nicolay

File diff suppressed because it is too large Load Diff

@ -0,0 +1,280 @@
/*
* =====================================================================================
*
* 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");
}
~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

@ -0,0 +1,142 @@
/*
* =====================================================================================
*
* 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 "Ogre.h"
namespace BtOgre {
typedef std::map<unsigned char, Vector3Array*> BoneIndex;
typedef std::pair<unsigned short, Vector3Array*> BoneKeyIndex;
class VertexIndexToShape
{
public:
VertexIndexToShape(const Ogre::Matrix4 &transform = Ogre::Matrix4::IDENTITY);
~VertexIndexToShape();
Ogre::Real getRadius();
Ogre::Vector3 getSize();
btSphereShape* createSphere();
btBoxShape* createBox();
btBvhTriangleMeshShape* createTrimesh();
btCylinderShape* createCylinder();
btConvexHullShape* createConvex();
const Ogre::Vector3* getVertices();
unsigned int getVertexCount();
const unsigned int* getIndices();
unsigned int getIndexCount();
protected:
void addStaticVertexData(const Ogre::VertexData *vertex_data);
void addAnimatedVertexData(const Ogre::VertexData *vertex_data,
const Ogre::VertexData *blended_data,
const Ogre::Mesh::IndexMap *indexMap);
void addIndexData(Ogre::IndexData *data, const unsigned int offset = 0);
protected:
Ogre::Vector3* mVertexBuffer;
unsigned int* mIndexBuffer;
unsigned int mVertexCount;
unsigned int mIndexCount;
Ogre::Matrix4 mTransform;
Ogre::Real mBoundRadius;
Ogre::Vector3 mBounds;
BoneIndex *mBoneIndex;
Ogre::Vector3 mScale;
};
//For static (non-animated) meshes.
class StaticMeshToShapeConverter : public VertexIndexToShape
{
public:
StaticMeshToShapeConverter(Ogre::Renderable *rend, const Ogre::Matrix4 &transform = Ogre::Matrix4::IDENTITY);
StaticMeshToShapeConverter(Ogre::Entity *entity, const Ogre::Matrix4 &transform = Ogre::Matrix4::IDENTITY);
StaticMeshToShapeConverter();
~StaticMeshToShapeConverter();
void addEntity(Ogre::Entity *entity,const Ogre::Matrix4 &transform = Ogre::Matrix4::IDENTITY);
void addMesh(const Ogre::MeshPtr &mesh, const Ogre::Matrix4 &transform = Ogre::Matrix4::IDENTITY);
protected:
Ogre::Entity* mEntity;
Ogre::SceneNode* mNode;
};
//For animated meshes.
class AnimatedMeshToShapeConverter : public VertexIndexToShape
{
public:
AnimatedMeshToShapeConverter(Ogre::Entity *entity, const Ogre::Matrix4 &transform = Ogre::Matrix4::IDENTITY);
AnimatedMeshToShapeConverter();
~AnimatedMeshToShapeConverter();
void addEntity(Ogre::Entity *entity,const Ogre::Matrix4 &transform = Ogre::Matrix4::IDENTITY);
void addMesh(const Ogre::MeshPtr &mesh, const Ogre::Matrix4 &transform);
btBoxShape* createAlignedBox(unsigned char bone,
const Ogre::Vector3 &bonePosition,
const Ogre::Quaternion &boneOrientation);
btBoxShape* createOrientedBox(unsigned char bone,
const Ogre::Vector3 &bonePosition,
const Ogre::Quaternion &boneOrientation);
protected:
bool getBoneVertices(unsigned char bone,
unsigned int &vertex_count,
Ogre::Vector3* &vertices,
const Ogre::Vector3 &bonePosition);
bool getOrientedBox(unsigned char bone,
const Ogre::Vector3 &bonePosition,
const Ogre::Quaternion &boneOrientation,
Ogre::Vector3 &extents,
Ogre::Vector3 *axis,
Ogre::Vector3 &center);
Ogre::Entity* mEntity;
Ogre::SceneNode* mNode;
Ogre::Vector3 *mTransformedVerticesTemp;
size_t mTransformedVerticesTempSize;
};
}
#endif

@ -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

@ -0,0 +1,124 @@
#include "BulletShapeLoader.h"
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.
*/
Shape = NULL;
collide = true;
createParamDictionary("BulletShape");
}
BulletShape::~BulletShape()
{
}
// farm out to BulletShapeLoader
void BulletShape::loadImpl()
{
mLoader->loadResource(this);
}
void BulletShape::deleteShape(btCollisionShape* mShape)
{
if(mShape!=NULL)
{
if(mShape->isCompound())
{
btCompoundShape* ms = static_cast<btCompoundShape*>(Shape);
int a = ms->getNumChildShapes();
for(int i=0; i <a;i++)
{
deleteShape(ms->getChildShape(i));
}
}
delete mShape;
}
mShape = NULL;
}
void BulletShape::unloadImpl()
{
deleteShape(Shape);
}
//TODO:change this?
size_t BulletShape::calculateSize() const
{
return 1;
}
//=============================================================================================================
template<> BulletShapeManager *Ogre::Singleton<BulletShapeManager>::ms_Singleton = 0;
BulletShapeManager *BulletShapeManager::getSingletonPtr()
{
return ms_Singleton;
}
BulletShapeManager &BulletShapeManager::getSingleton()
{
assert(ms_Singleton);
return(*ms_Singleton);
}
BulletShapeManager::BulletShapeManager()
{
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);
}
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)
{}

@ -0,0 +1,138 @@
#ifndef _BULLET_SHAPE_LOADER_H_
#define _BULLET_SHAPE_LOADER_H_
#include <OgreResource.h>
#include <OgreResourceManager.h>
#include <btBulletCollisionCommon.h>
//For some reason, Ogre Singleton cannot be used in another namespace, that's why there is no namespace here.
//But the risk of name collision seems pretty low here.
/**
*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* mShape);
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* Shape;
//this flag indicate if the shape is used for collision or if it's for raycasting only.
bool collide;
};
/**
*
*/
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;
}
};
/**
*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, public Ogre::Singleton<BulletShapeManager>
{
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);
public:
BulletShapeManager();
virtual ~BulletShapeManager();
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

@ -0,0 +1,45 @@
#include "CMotionState.h"
#include "physic.hpp"
#include <btBulletDynamicsCommon.h>
#include <btBulletCollisionCommon.h>
#include <components/nifbullet/bullet_nif_loader.hpp>
//#include <apps\openmw\mwworld\world.hpp>
namespace OEngine {
namespace Physic
{
CMotionState::CMotionState(PhysicEngine* eng,std::string name)
{
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);
}
}
}}

@ -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

@ -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 )
{
}

@ -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

@ -0,0 +1,375 @@
#include "physic.hpp"
#include <btBulletDynamicsCommon.h>
#include <btBulletCollisionCommon.h>
#include <components/nifbullet/bullet_nif_loader.hpp>
//#include <apps\openmw\mwworld\world.hpp>
#include "CMotionState.h"
#include "OgreRoot.h"
#include "btKinematicCharacterController.h"
#include "BtOgrePG.h"
#include "BtOgreGP.h"
#include "BtOgreExtras.h"
#define BIT(x) (1<<(x))
namespace OEngine {
namespace Physic
{
enum collisiontypes {
COL_NOTHING = 0, //<Collide with nothing
COL_WORLD = BIT(0), //<Collide with world objects
COL_ACTOR_INTERNAL = BIT(1), //<Collide internal capsule
COL_ACTOR_EXTERNAL = BIT(2) //<collide with external capsule
};
PhysicActor::PhysicActor(std::string name)
{
mName = name;
// The capsule is at the origin
btTransform transform;
transform.setIdentity();
// External capsule
externalGhostObject = new PairCachingGhostObject(name);
externalGhostObject->setWorldTransform( transform );
btScalar externalCapsuleHeight = 120;
btScalar externalCapsuleWidth = 19;
externalCollisionShape = new btCapsuleShapeZ( externalCapsuleWidth, externalCapsuleHeight );
externalCollisionShape->setMargin( 0.1 );
externalGhostObject->setCollisionShape( externalCollisionShape );
externalGhostObject->setCollisionFlags( btCollisionObject::CF_CHARACTER_OBJECT );
// Internal capsule
internalGhostObject = new PairCachingGhostObject(name);
internalGhostObject->setWorldTransform( transform );
//internalGhostObject->getBroadphaseHandle()->s
btScalar internalCapsuleHeight = 110;
btScalar internalCapsuleWidth = 17;
internalCollisionShape = new btCapsuleShapeZ( internalCapsuleWidth, internalCapsuleHeight );
internalCollisionShape->setMargin( 0.1 );
internalGhostObject->setCollisionShape( internalCollisionShape );
internalGhostObject->setCollisionFlags( btCollisionObject::CF_CHARACTER_OBJECT );
mCharacter = new btKinematicCharacterController( externalGhostObject,internalGhostObject,btScalar( 40 ),1,4,20,9.8,0.2 );
mCharacter->setUpAxis(btKinematicCharacterController::Z_AXIS);
mCharacter->setUseGhostSweepTest(false);
mCharacter->mCollision = false;
setGravity(0);
mTranslation = btVector3(0,0,70);
}
PhysicActor::~PhysicActor()
{
delete mCharacter;
delete internalGhostObject;
delete internalCollisionShape;
delete externalGhostObject;
delete externalCollisionShape;
}
void PhysicActor::setGravity(float gravity)
{
mCharacter->setGravity(gravity);
//mCharacter->
}
void PhysicActor::enableCollisions(bool collision)
{
mCharacter->mCollision = collision;
}
void PhysicActor::setVerticalVelocity(float z)
{
mCharacter->setVerticalVelocity(z);
}
bool PhysicActor::getCollisionMode()
{
return mCharacter->mCollision;
}
void PhysicActor::setWalkDirection(const btVector3& mvt)
{
mCharacter->setWalkDirection( mvt );
}
void PhysicActor::Rotate(const btQuaternion& quat)
{
externalGhostObject->getWorldTransform().setRotation( externalGhostObject->getWorldTransform().getRotation() * quat );
internalGhostObject->getWorldTransform().setRotation( internalGhostObject->getWorldTransform().getRotation() * quat );
}
void PhysicActor::setRotation(const btQuaternion& quat)
{
externalGhostObject->getWorldTransform().setRotation( quat );
internalGhostObject->getWorldTransform().setRotation( quat );
}
btVector3 PhysicActor::getPosition(void)
{
return internalGhostObject->getWorldTransform().getOrigin() -mTranslation;
}
btQuaternion PhysicActor::getRotation(void)
{
return internalGhostObject->getWorldTransform().getRotation();
}
void PhysicActor::setPosition(const btVector3& pos)
{
internalGhostObject->getWorldTransform().setOrigin(pos+mTranslation);
externalGhostObject->getWorldTransform().setOrigin(pos+mTranslation);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
RigidBody::RigidBody(btRigidBody::btRigidBodyConstructionInfo& CI,std::string name)
:btRigidBody(CI),mName(name)
{
};
///////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////
PhysicEngine::PhysicEngine(BulletShapeLoader* shapeLoader)
{
// Set up the collision configuration and dispatcher
collisionConfiguration = new btDefaultCollisionConfiguration();
dispatcher = new btCollisionDispatcher(collisionConfiguration);
// The actual physics solver
solver = new btSequentialImpulseConstraintSolver;
//TODO: memory leak?
btOverlappingPairCache* pairCache = new btSortedOverlappingPairCache();
//pairCache->setInternalGhostPairCallback( new btGhostPairCallback() );
broadphase = new btDbvtBroadphase(pairCache);
// 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;
}
void PhysicEngine::createDebugRendering()
{
if(!isDebugCreated)
{
Ogre::SceneManagerEnumerator::SceneManagerIterator iter = Ogre::Root::getSingleton().getSceneManagerIterator();
iter.begin();
Ogre::SceneManager* scn = iter.getNext();
Ogre::SceneNode* node = scn->getRootSceneNode()->createChildSceneNode();
node->pitch(Ogre::Degree(-90));
mDebugDrawer = new BtOgre::DebugDrawer(node, dynamicsWorld);
dynamicsWorld->setDebugDrawer(mDebugDrawer);
isDebugCreated = true;
dynamicsWorld->debugDrawWorld();
}
}
void PhysicEngine::setDebugRenderingMode(int mode)
{
if(!isDebugCreated)
{
createDebugRendering();
}
mDebugDrawer->setDebugMode(mode);
}
PhysicEngine::~PhysicEngine()
{
delete dynamicsWorld;
delete solver;
delete collisionConfiguration;
delete dispatcher;
delete broadphase;
delete mShapeLoader;
}
RigidBody* PhysicEngine::createRigidBody(std::string mesh,std::string name,float scale)
{
//get the shape from the .nif
mShapeLoader->load(mesh,"General");
BulletShapeManager::getSingletonPtr()->load(mesh,"General");
BulletShapePtr shape = BulletShapeManager::getSingleton().getByName(mesh,"General");
shape->Shape->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,shape->Shape);
RigidBody* body = new RigidBody(CI,name);
body->collide = shape->collide;
return body;
}
void PhysicEngine::addRigidBody(RigidBody* body)
{
if(body->collide)
{
dynamicsWorld->addRigidBody(body,COL_WORLD,COL_WORLD|COL_ACTOR_INTERNAL|COL_ACTOR_EXTERNAL);
}
else
{
dynamicsWorld->addRigidBody(body,COL_WORLD,COL_NOTHING);
}
body->setActivationState(DISABLE_DEACTIVATION);
RigidBodyMap[body->mName] = body;
}
void PhysicEngine::removeRigidBody(std::string name)
{
std::map<std::string,RigidBody*>::iterator it = RigidBodyMap.find(name);
if (it != RigidBodyMap.end() )
{
RigidBody* body = it->second;
if(body != NULL)
{
// broadphase->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(body->getBroadphaseProxy(),dispatcher);
/*std::map<std::string,PhysicActor*>::iterator it2 = PhysicActorMap.begin();
for(;it2!=PhysicActorMap.end();it++)
{
it2->second->internalGhostObject->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(body->getBroadphaseProxy(),dispatcher);
it2->second->externalGhostObject->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(body->getBroadphaseProxy(),dispatcher);
}*/
dynamicsWorld->removeRigidBody(RigidBodyMap[name]);
}
}
}
void PhysicEngine::deleteRigidBody(std::string name)
{
std::map<std::string,RigidBody*>::iterator it = RigidBodyMap.find(name);
if (it != RigidBodyMap.end() )
{
RigidBody* body = it->second;
if(body != NULL)
{
delete body;
}
RigidBodyMap.erase(it);
}
}
RigidBody* PhysicEngine::getRigidBody(std::string name)
{
RigidBody* body = RigidBodyMap[name];
return body;
}
void PhysicEngine::stepSimulation(double deltaT)
{
dynamicsWorld->stepSimulation(deltaT,1,1/50.);
if(isDebugCreated)
{
mDebugDrawer->step();
}
}
void PhysicEngine::addCharacter(std::string name)
{
PhysicActor* newActor = new PhysicActor(name);
dynamicsWorld->addCollisionObject( newActor->externalGhostObject, COL_ACTOR_EXTERNAL, COL_WORLD |COL_ACTOR_EXTERNAL );
dynamicsWorld->addCollisionObject( newActor->internalGhostObject, COL_ACTOR_INTERNAL, COL_WORLD |COL_ACTOR_INTERNAL );
dynamicsWorld->addAction( newActor->mCharacter );
PhysicActorMap[name] = newActor;
}
void PhysicEngine::removeCharacter(std::string name)
{
//std::cout << "remove";
std::map<std::string,PhysicActor*>::iterator it = PhysicActorMap.find(name);
if (it != PhysicActorMap.end() )
{
PhysicActor* act = it->second;
if(act != NULL)
{
/*broadphase->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(act->externalGhostObject->getBroadphaseHandle(),dispatcher);
broadphase->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(act->internalGhostObject->getBroadphaseHandle(),dispatcher);
std::map<std::string,PhysicActor*>::iterator it2 = PhysicActorMap.begin();
for(;it2!=PhysicActorMap.end();it++)
{
it->second->internalGhostObject->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(act->externalGhostObject->getBroadphaseHandle(),dispatcher);
it->second->externalGhostObject->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(act->externalGhostObject->getBroadphaseHandle(),dispatcher);
it->second->internalGhostObject->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(act->internalGhostObject->getBroadphaseHandle(),dispatcher);
it->second->externalGhostObject->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(act->internalGhostObject->getBroadphaseHandle(),dispatcher);
}*/
//act->externalGhostObject->
dynamicsWorld->removeCollisionObject(act->externalGhostObject);
dynamicsWorld->removeCollisionObject(act->internalGhostObject);
dynamicsWorld->removeAction(act->mCharacter);
delete act;
}
PhysicActorMap.erase(it);
}
//std::cout << "ok";
}
PhysicActor* PhysicEngine::getCharacter(std::string name)
{
PhysicActor* act = PhysicActorMap[name];
return act;
}
void PhysicEngine::emptyEventLists(void)
{
}
std::pair<std::string,float> PhysicEngine::rayTest(btVector3& from,btVector3& to)
{
std::string name = "";
float d = -1;
float d1 = 10000.;
btCollisionWorld::ClosestRayResultCallback resultCallback1(from, to);
resultCallback1.m_collisionFilterMask = COL_WORLD;
dynamicsWorld->rayTest(from, to, resultCallback1);
if (resultCallback1.hasHit())
{
name = static_cast<RigidBody&>(*resultCallback1.m_collisionObject).mName;
d1 = resultCallback1.m_closestHitFraction;
d = d1;
}
btCollisionWorld::ClosestRayResultCallback resultCallback2(from, to);
resultCallback2.m_collisionFilterMask = COL_ACTOR_INTERNAL|COL_ACTOR_EXTERNAL;
dynamicsWorld->rayTest(from, to, resultCallback2);
float d2 = 10000.;
if (resultCallback2.hasHit())
{
d2 = resultCallback1.m_closestHitFraction;
if(d2<=d1)
{
name = static_cast<PairCachingGhostObject&>(*resultCallback2.m_collisionObject).mName;
d = d2;
}
}
return std::pair<std::string,float>(name,d);
}
}};

@ -0,0 +1,230 @@
#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"
class btRigidBody;
class btBroadphaseInterface;
class btDefaultCollisionConfiguration;
class btSequentialImpulseConstraintSolver;
class btCollisionDispatcher;
class btDiscreteDynamicsWorld;
class btKinematicCharacterController;
namespace BtOgre
{
class DebugDrawer;
}
namespace MWWorld
{
class World;
}
namespace OEngine {
namespace Physic
{
class CMotionState;
struct PhysicEvent;
/**
*This is just used to be able to name objects.
*/
class PairCachingGhostObject : public btPairCachingGhostObject
{
public:
PairCachingGhostObject(std::string name)
:btPairCachingGhostObject(),mName(name)
{
}
std::string mName;
};
/**
* A physic Actor use a modifed KinematicCharacterController taken in the bullet forum.
*/
class PhysicActor
{
public:
PhysicActor(std::string name);
~PhysicActor();
/**
* This function set the walkDirection. This is not relative to the actor orientation.
* I think it's also needed to take time into account. A typical call should look like this:
* setWalkDirection( mvt * orientation * dt)
*/
void setWalkDirection(const btVector3& mvt);
void Rotate(const btQuaternion& quat);
void setRotation(const btQuaternion& quat);
void setGravity(float gravity);
void setVerticalVelocity(float z);
void enableCollisions(bool collision);
bool getCollisionMode();
btVector3 getPosition(void);
btQuaternion getRotation(void);
void setPosition(const btVector3& pos);
btKinematicCharacterController* mCharacter;
PairCachingGhostObject* internalGhostObject;
btCollisionShape* internalCollisionShape;
PairCachingGhostObject* externalGhostObject;
btCollisionShape* externalCollisionShape;
std::string mName;
/**
*NPC scenenode is located on there feet, and you can't simply translate a btShape, so this vector is used
*each time get/setposition is called.
*/
btVector3 mTranslation;
};
/**
*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);
std::string mName;
//is this body used for raycasting only?
bool collide;
};
/**
* 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();
/**
* Create a RigidBody.It does not add it to the simulation, but it does add it to the rigidBody Map,
* so you can get it with the getRigidBody function.
*/
RigidBody* createRigidBody(std::string mesh,std::string name,float scale);
/**
* Add a RigidBody to the simulation
*/
void addRigidBody(RigidBody* body);
/**
* Remove a RigidBody from the simulation. It does not delete it, and does not remove it from the RigidBodyMap.
*/
void removeRigidBody(std::string name);
/**
* Delete a RigidBody, and remove it from RigidBodyMap.
*/
void deleteRigidBody(std::string name);
/**
* Return a pointer to a given rigid body.
* TODO:check if exist
*/
RigidBody* getRigidBody(std::string name);
/**
* Create and add a character to the scene, and add it to the ActorMap.
*/
void addCharacter(std::string name);
/**
* Remove a character from the scene. TODO:delete it! for now, a small memory leak^^ done?
*/
void removeCharacter(std::string name);
/**
* Return a pointer to a character
* TODO:check if the actor exist...
*/
PhysicActor* getCharacter(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);
/**
* 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);
//event list of non player object
std::list<PhysicEvent> NPEventList;
//event list affecting the player
std::list<PhysicEvent> PEventList;
//Bullet Stuff
btBroadphaseInterface* broadphase;
btDefaultCollisionConfiguration* collisionConfiguration;
btSequentialImpulseConstraintSolver* solver;
btCollisionDispatcher* dispatcher;
btDiscreteDynamicsWorld* dynamicsWorld;
//the NIF file loader.
BulletShapeLoader* mShapeLoader;
std::map<std::string,RigidBody*> RigidBodyMap;
std::map<std::string,PhysicActor*> PhysicActorMap;
//debug rendering
BtOgre::DebugDrawer* mDebugDrawer;
bool isDebugCreated;
};
}}
#endif

@ -0,0 +1,80 @@
#include <MyGUI.h>
#include <OIS/OIS.h>
#include <assert.h>
#include "events.hpp"
using namespace OIS;
using namespace OEngine::GUI;
EventInjector::EventInjector(MyGUI::Gui *g)
: gui(g), mouseX(0), mouseY(0), enabled(true)
{
assert(gui);
maxX = gui->getViewSize().width;
maxY = gui->getViewSize().height;
}
template <typename X>
void setRange(X &x, X min, X max)
{
if(x < min) x = min;
else if(x > max) x = max;
}
void EventInjector::event(Type type, int index, const void *p)
{
if(!enabled) return;
if(type & EV_Keyboard)
{
KeyEvent *key = (KeyEvent*)p;
MyGUI::KeyCode code = MyGUI::KeyCode::Enum(key->key);
if(type == EV_KeyDown)
{
/*
This is just a first approximation. Apparently, OIS is
unable to provide reliable unicode characters on all
platforms. At least that's what I surmise from the amount
of workaround that the MyGUI folks have put in place for
this. See Common/Input/OIS/InputManager.cpp in the MyGUI
sources for details.
If the work they have done there is indeed necessary (I
haven't tested that it is, although I have had dubious
experinces with OIS events in the past), then we should
probably adapt all that code here. Or even better,
directly into the OIS input manager in Mangle.
Note that all this only affects the 'text' field, and
should thus only affect typed text in input boxes (which
is still pretty significant.)
*/
MyGUI::Char text = (MyGUI::Char)key->text;
gui->injectKeyPress(code,text);
}
else
{
gui->injectKeyRelease(code);
}
}
else if(type & EV_Mouse)
{
MouseEvent *mouse = (MouseEvent*)p;
MyGUI::MouseButton id = MyGUI::MouseButton::Enum(index);
// Update mouse position
mouseX += mouse->state.X.rel;
mouseY += mouse->state.Y.rel;
setRange(mouseX,0,maxX);
setRange(mouseY,0,maxY);
if(type == EV_MouseDown)
gui->injectMousePress(mouseX, mouseY, id);
else if(type == EV_MouseUp)
gui->injectMouseRelease(mouseX, mouseY, id);
else
gui->injectMouseMove(mouseX, mouseY, mouse->state.Z.abs);
}
}

@ -0,0 +1,31 @@
#ifndef OENGINE_MYGUI_EVENTS_H
#define OENGINE_MYGUI_EVENTS_H
#include <mangle/input/event.hpp>
namespace MyGUI
{
class Gui;
}
namespace OEngine {
namespace GUI
{
/** Event handler that injects OIS events into MyGUI
*/
class EventInjector : public Mangle::Input::Event
{
MyGUI::Gui *gui;
int mouseX, mouseY;
int maxX, maxY;
public:
bool enabled;
EventInjector(MyGUI::Gui *g);
void event(Type type, int index, const void *p);
};
typedef boost::shared_ptr<EventInjector> EventInjectorPtr;
}}
#endif

@ -0,0 +1,119 @@
#ifndef OENGINE_MYGUI_LAYOUT_H
#define OENGINE_MYGUI_LAYOUT_H
#include <assert.h>
#include <MyGUI.h>
namespace OEngine {
namespace GUI
{
/** The Layout class is an utility class used to load MyGUI layouts
from xml files, and to manipulate member widgets.
*/
class Layout
{
public:
Layout(const std::string & _layout, MyGUI::WidgetPtr _parent = nullptr)
: mMainWidget(nullptr)
{ initialise(_layout, _parent); }
virtual ~Layout() { shutdown(); }
template <typename T>
void getWidget(T * & _widget, const std::string & _name, bool _throw = true)
{
_widget = nullptr;
for (MyGUI::VectorWidgetPtr::iterator iter=mListWindowRoot.begin();
iter!=mListWindowRoot.end(); ++iter)
{
MyGUI::WidgetPtr find = (*iter)->findWidget(mPrefix + _name);
if (nullptr != find)
{
T * cast = find->castType<T>(false);
if (nullptr != cast)
_widget = cast;
else if (_throw)
{
MYGUI_EXCEPT("Error cast : dest type = '" << T::getClassTypeName()
<< "' source name = '" << find->getName()
<< "' source type = '" << find->getTypeName() << "' in layout '" << mLayoutName << "'");
}
return;
}
}
MYGUI_ASSERT( ! _throw, "widget name '" << _name << "' in layout '" << mLayoutName << "' not found.");
}
void initialise(const std::string & _layout,
MyGUI::WidgetPtr _parent = nullptr)
{
const std::string MAIN_WINDOW = "_Main";
mLayoutName = _layout;
if (mLayoutName.empty())
mMainWidget = _parent;
else
{
mPrefix = MyGUI::utility::toString(this, "_");
mListWindowRoot = MyGUI::LayoutManager::getInstance().loadLayout(mLayoutName, mPrefix, _parent);
const std::string main_name = mPrefix + MAIN_WINDOW;
for (MyGUI::VectorWidgetPtr::iterator iter=mListWindowRoot.begin(); iter!=mListWindowRoot.end(); ++iter)
{
if ((*iter)->getName() == main_name)
{
mMainWidget = (*iter);
break;
}
}
MYGUI_ASSERT(mMainWidget, "root widget name '" << MAIN_WINDOW << "' in layout '" << mLayoutName << "' not found.");
}
}
void shutdown()
{
MyGUI::LayoutManager::getInstance().unloadLayout(mListWindowRoot);
mListWindowRoot.clear();
}
void setCoord(int x, int y, int w, int h)
{
mMainWidget->setCoord(x,y,w,h);
}
void setVisible(bool b)
{
mMainWidget->setVisible(b);
}
void setText(const std::string& name, const std::string& caption)
{
MyGUI::WidgetPtr pt;
getWidget(pt, name);
pt->setCaption(caption);
}
void setTextColor(const std::string& name, float r, float g, float b)
{
MyGUI::WidgetPtr pt;
getWidget(pt, name);
MyGUI::StaticText *st = dynamic_cast<MyGUI::StaticText*>(pt);
if(st != NULL)
st->setTextColour(MyGUI::Colour(b,g,r));
}
void setImage(const std::string& name, const std::string& imgName)
{
MyGUI::StaticImagePtr pt;
getWidget(pt, name);
pt->setImageTexture(imgName);
}
protected:
MyGUI::WidgetPtr mMainWidget;
std::string mPrefix;
std::string mLayoutName;
MyGUI::VectorWidgetPtr mListWindowRoot;
};
}}
#endif

@ -0,0 +1,47 @@
#include <MyGUI.h>
#include <MyGUI_OgrePlatform.h>
#include <assert.h>
#include "manager.hpp"
using namespace OEngine::GUI;
void MyGUIManager::setup(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool logging, const std::string& logDir)
{
assert(wnd);
assert(mgr);
using namespace MyGUI;
// Enable/disable MyGUI logging to stdout. (Logging to MyGUI.log is
// still enabled.) In order to do this we have to initialize the log
// manager before the main gui system itself, otherwise the main
// object will get the chance to spit out a few messages before we
// can able to disable it.
LogManager::initialise();
LogManager::setSTDOutputEnabled(logging);
std::string theLogFile = std::string(MYGUI_PLATFORM_LOG_FILENAME);
if(!logDir.empty())
theLogFile.insert(0, logDir);
// Set up OGRE platform. We might make this more generic later.
mPlatform = new OgrePlatform();
mPlatform->initialise(wnd, mgr, "General", theLogFile);
// Create GUI
mGui = new Gui();
mGui->initialise("core.xml", theLogFile);
}
void MyGUIManager::shutdown()
{
if(mGui) delete mGui;
if(mPlatform)
{
mPlatform->shutdown();
delete mPlatform;
}
mGui = NULL;
mPlatform = NULL;
}

@ -0,0 +1,36 @@
#ifndef OENGINE_MYGUI_MANAGER_H
#define OENGINE_MYGUI_MANAGER_H
namespace MyGUI
{
class OgrePlatform;
class Gui;
}
namespace Ogre
{
class RenderWindow;
class SceneManager;
}
namespace OEngine {
namespace GUI
{
class MyGUIManager
{
MyGUI::OgrePlatform *mPlatform;
MyGUI::Gui *mGui;
public:
MyGUIManager() : mPlatform(NULL), mGui(NULL) {}
MyGUIManager(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool logging=false, const std::string& logDir = std::string(""))
{ setup(wnd,mgr,logging, logDir); }
~MyGUIManager() { shutdown(); }
void setup(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool logging=false, const std::string& logDir = std::string(""));
void shutdown();
MyGUI::Gui *getGui() { return mGui; }
};
}}
#endif

@ -0,0 +1,75 @@
#ifndef OENGINE_INPUT_DISPATCHMAP_H
#define OENGINE_INPUT_DISPATCHMAP_H
#include <set>
#include <map>
#include <assert.h>
namespace OEngine {
namespace Input {
/**
DispatchMap is a simple connection system that connects incomming
signals with outgoing signals.
The signals can be connected one-to-one, many-to-one, one-to-many
or many-to-many.
The dispatch map is completely system agnostic. It is a pure data
structure and all signals are just integer indices. It does not
delegate any actions, but used together with Dispatcher it can be
used to build an event system.
*/
struct DispatchMap
{
typedef std::set<int> OutList;
typedef std::map<int, OutList> InMap;
typedef OutList::iterator Oit;
typedef InMap::iterator Iit;
InMap map;
void bind(int in, int out)
{
map[in].insert(out);
}
void unbind(int in, int out)
{
Iit it = map.find(in);
if(it != map.end())
{
it->second.erase(out);
// If there are no more elements, then remove the entire list
if(it->second.empty())
map.erase(it);
}
}
/// Check if a given input is bound to anything
bool isBound(int in) const
{
return map.find(in) != map.end();
}
/**
Get the list of outputs bound to the given input. Only call this
on inputs that you know are bound to something.
The returned set is only intended for immediate iteration. Do not
store references to it.
*/
const OutList &getList(int in) const
{
assert(isBound(in));
InMap::const_iterator it = map.find(in);
assert(it != map.end());
const OutList &out = it->second;
assert(!out.empty());
return out;
}
};
}}
#endif

@ -0,0 +1,78 @@
#ifndef OENGINE_INPUT_DISPATCHER_H
#define OENGINE_INPUT_DISPATCHER_H
#include <cassert>
#include <iostream>
#include <mangle/input/event.hpp>
#include "dispatch_map.hpp"
#include "func_binder.hpp"
namespace OEngine {
namespace Input {
struct Dispatcher : Mangle::Input::Event
{
DispatchMap map;
FuncBinder funcs;
/**
Constructor. Takes the number of actions and passes it to
FuncBinder.
*/
Dispatcher(int actions) : funcs(actions) {}
void bind(unsigned int action, int key)
{
assert(action < funcs.getSize());
map.bind(key, action);
}
void unbind(unsigned int action, int key)
{
assert(action < funcs.getSize());
map.unbind(key, action);
}
bool isBound(int key) const { return map.isBound(key); }
/**
Instigate an event. It is translated through the dispatch map and
sent to the function bindings.
*/
typedef DispatchMap::OutList _O;
void event(Type type, int index, const void* p)
{
// No bindings, nothing happens
if(!isBound(index))
return;
// Get the mapped actions and execute them
const _O &list = map.getList(index);
_O::const_iterator it;
for(it = list.begin(); it != list.end(); it++)
{
//catch exceptions thrown in the input handlers so that pressing a key
//doesn't cause OpenMw to crash
try
{
funcs.call(*it, p);
}
catch(const std::exception& e)
{
std::cerr << "Exception in input handler: " << e.what() << std::endl;
}
catch(...)
{
std::cerr << "Unknown exception in input handler" << std::endl;
}
}
}
};
// This helps us play nice with Mangle's EventPtr, but it should
// really be defined for all the classes in OEngine.
typedef boost::shared_ptr<Dispatcher> DispatcherPtr;
}}
#endif

@ -0,0 +1,106 @@
#ifndef OENGINE_INPUT_FUNCBINDER_H
#define OENGINE_INPUT_FUNCBINDER_H
#include <string>
#include <vector>
#include <boost/function.hpp>
#include <assert.h>
namespace OEngine {
namespace Input {
/**
An Action defines the user defined action corresponding to a
binding.
The first parameter is the action index that invoked this call. You
can assign the same function to multiple actions, and this can help
you keep track of which action was invoked.
The second parameter is an optional user-defined parameter,
represented by a void pointer. In many cases it is practical to
point this to temporaries (stack values), so make sure not to store
permanent references to it unless you've planning for this on the
calling side as well.
*/
typedef boost::function<void(int,const void*)> Action;
/**
The FuncBinder is a simple struct that binds user-defined indices
to functions. It is useful for binding eg. keyboard events to
specific actions in your program, but can potentially have many
other uses as well.
*/
class FuncBinder
{
struct FuncBinding
{
std::string name;
Action action;
};
std::vector<FuncBinding> bindings;
public:
/**
Constructor. Initialize the struct by telling it how many action
indices you intend to bind.
The indices you use should be 0 <= i < number.
*/
FuncBinder(int number) : bindings(number) {}
unsigned int getSize() { return bindings.size(); }
/**
Bind an action to an index.
*/
void bind(int index, Action action, const std::string &name="")
{
assert(index >= 0 && index < (int)bindings.size());
FuncBinding &fb = bindings[index];
fb.action = action;
fb.name = name;
}
/**
Unbind an index, reverting a previous bind().
*/
void unbind(int index)
{
assert(index >= 0 && index < (int)bindings.size());
bindings[index] = FuncBinding();
}
/**
Call a specific action. Takes an optional parameter that is
passed to the action.
*/
void call(int index, const void *p=NULL) const
{
assert(index >= 0 && index < (int)bindings.size());
const FuncBinding &fb = bindings[index];
if(fb.action) fb.action(index, p);
}
/// Check if a given index is bound to anything
bool isBound(int index) const
{
assert(index >= 0 && index < (int)bindings.size());
return !bindings[index].action.empty();
}
/// Return the name associated with an action (empty if not bound)
const std::string &getName(int index) const
{
assert(index >= 0 && index < (int)bindings.size());
return bindings[index].name;
}
};
}}
#endif

@ -0,0 +1,46 @@
#ifndef OENGINE_INPUT_POLLER_H
#define OENGINE_INPUT_POLLER_H
#include "dispatch_map.hpp"
#include <mangle/input/driver.hpp>
namespace OEngine {
namespace Input {
/** The poller is used to check (poll) for keys rather than waiting
for events. */
struct Poller
{
DispatchMap map;
Mangle::Input::Driver &input;
Poller(Mangle::Input::Driver &drv)
: input(drv) {}
/** Bind or unbind a given action with a key. The action is the first
parameter, the key is the second.
*/
void bind(int in, int out) { map.bind(in, out); }
void unbind(int in, int out) { map.unbind(in, out); }
bool isBound(int in) const { return map.isBound(in); }
/// Check whether a given action button is currently pressed.
typedef DispatchMap::OutList _O;
bool isDown(int index) const
{
// No bindings, no action
if(!isBound(index))
return false;
// Get all the keys bound to this action, and check them.
const _O &list = map.getList(index);
_O::const_iterator it;
for(it = list.begin(); it != list.end(); it++)
// If there's any match, we're good to go.
if(input.isDown(*it)) return true;
return false;
}
};
}}
#endif

@ -0,0 +1,18 @@
GCC=g++
all: funcbind_test dispatch_map_test sdl_driver_test sdl_binder_test
funcbind_test: funcbind_test.cpp ../func_binder.hpp
$(GCC) $< -o $@
dispatch_map_test: dispatch_map_test.cpp ../dispatch_map.hpp
$(GCC) $< -o $@
sdl_driver_test: sdl_driver_test.cpp
$(GCC) $< ../../mangle/input/servers/sdl_driver.cpp -o $@ -I/usr/include/SDL/ -lSDL -I../../
sdl_binder_test: sdl_binder_test.cpp
$(GCC) $< ../../mangle/input/servers/sdl_driver.cpp -o $@ -I/usr/include/SDL/ -lSDL -I../../
clean:
rm *_test

@ -0,0 +1,54 @@
#include <iostream>
using namespace std;
#include "../dispatch_map.hpp"
using namespace OEngine::Input;
typedef DispatchMap::OutList OutList;
typedef OutList::const_iterator Cit;
void showList(const DispatchMap::OutList &out)
{
for(Cit it = out.begin();
it != out.end(); it++)
{
cout << " " << *it << endl;
}
}
void showAll(DispatchMap &map)
{
cout << "\nPrinting everything:\n";
for(DispatchMap::Iit it = map.map.begin();
it != map.map.end(); it++)
{
cout << it->first << ":\n";
showList(map.getList(it->first));
}
}
int main()
{
cout << "Testing the dispatch map\n";
DispatchMap dsp;
dsp.bind(1,9);
dsp.bind(2,-5);
dsp.bind(2,9);
dsp.bind(3,10);
dsp.bind(3,12);
dsp.bind(3,10);
showAll(dsp);
dsp.unbind(1,9);
dsp.unbind(5,8);
dsp.unbind(3,11);
dsp.unbind(3,12);
dsp.unbind(3,12);
showAll(dsp);
return 0;
}

@ -0,0 +1,47 @@
#include <iostream>
using namespace std;
#include "../func_binder.hpp"
void f1(int i, const void *p)
{
cout << " F1 i=" << i << endl;
if(p)
cout << " Got a nice gift: "
<< *((const float*)p) << endl;
}
void f2(int i, const void *p)
{
cout << " F2 i=" << i << endl;
}
using namespace OEngine::Input;
int main()
{
cout << "This will test the function binding system\n";
FuncBinder bnd(5);
bnd.bind(0, &f1, "This is action 1");
bnd.bind(1, &f2);
bnd.bind(2, &f1, "This is action 3");
bnd.bind(3, &f2, "This is action 4");
bnd.unbind(2);
for(int i=0; i<5; i++)
{
cout << "Calling " << i << ": '" << bnd.getName(i) << "'\n";
bnd.call(i);
if(!bnd.isBound(i)) cout << " (not bound)\n";
}
cout << "\nCalling with parameter:\n";
float f = 3.1415;
bnd.call(0, &f);
return 0;
}

@ -0,0 +1,18 @@
Testing the dispatch map
Printing everything:
1:
9
2:
-5
9
3:
10
12
Printing everything:
2:
-5
9
3:
10

@ -0,0 +1,15 @@
This will test the function binding system
Calling 0: 'This is action 1'
F1 i=0
Calling 1: ''
F2 i=1
Calling 2: ''
(not bound)
Calling 3: 'This is action 4'
F2 i=3
Calling 4: ''
(not bound)
Calling with parameter:
F1 i=0
Got a nice gift: 3.1415

@ -0,0 +1,4 @@
Hold the Q key to quit:
You are running in script mode, aborting. Run this test with a parameter (any at all) to test the input loop properly
Bye bye!

@ -0,0 +1,4 @@
Hold the Q key to quit:
You are running in script mode, aborting. Run this test with a parameter (any at all) to test the input loop properly
Bye bye!

@ -0,0 +1,71 @@
#include <iostream>
#include <mangle/input/servers/sdl_driver.hpp>
#include <SDL.h>
#include "../dispatcher.hpp"
#include "../poller.hpp"
using namespace std;
using namespace Mangle::Input;
using namespace OEngine::Input;
enum Actions
{
A_Quit,
A_Left,
A_Right,
A_LAST
};
bool quit=false;
void doExit(int,const void*)
{
quit = true;
}
void goLeft(int,const void*)
{
cout << "Going left\n";
}
int main(int argc, char** argv)
{
SDL_Init(SDL_INIT_VIDEO);
SDL_SetVideoMode(640, 480, 0, SDL_SWSURFACE);
SDLDriver input;
Dispatcher *disp = new Dispatcher(A_LAST);
Poller poll(input);
input.setEvent(EventPtr(disp));
disp->funcs.bind(A_Quit, &doExit);
disp->funcs.bind(A_Left, &goLeft);
disp->bind(A_Quit, SDLK_q);
disp->bind(A_Left, SDLK_a);
disp->bind(A_Left, SDLK_LEFT);
poll.bind(A_Right, SDLK_d);
poll.bind(A_Right, SDLK_RIGHT);
cout << "Hold the Q key to quit:\n";
//input->setEvent(&mycb);
while(!quit)
{
input.capture();
if(poll.isDown(A_Right))
cout << "We're going right!\n";
SDL_Delay(20);
if(argc == 1)
{
cout << "You are running in script mode, aborting. Run this test with a parameter (any at all) to test the input loop properly\n";
break;
}
}
cout << "\nBye bye!\n";
SDL_Quit();
return 0;
}

@ -0,0 +1,31 @@
#include <iostream>
#include <mangle/input/servers/sdl_driver.hpp>
#include <SDL.h>
using namespace std;
using namespace Mangle::Input;
int main(int argc, char** argv)
{
SDL_Init(SDL_INIT_VIDEO);
SDL_SetVideoMode(640, 480, 0, SDL_SWSURFACE);
SDLDriver input;
cout << "Hold the Q key to quit:\n";
//input->setEvent(&mycb);
while(!input.isDown(SDLK_q))
{
input.capture();
SDL_Delay(20);
if(argc == 1)
{
cout << "You are running in script mode, aborting. Run this test with a parameter (any at all) to test the input loop properly\n";
break;
}
}
cout << "\nBye bye!\n";
SDL_Quit();
return 0;
}

@ -0,0 +1,18 @@
#!/bin/bash
make || exit
mkdir -p output
PROGS=*_test
for a in $PROGS; do
if [ -f "output/$a.out" ]; then
echo "Running $a:"
./$a | diff output/$a.out -
else
echo "Creating $a.out"
./$a > "output/$a.out"
git add "output/$a.out"
fi
done

@ -0,0 +1,178 @@
#ifndef MISC_LIST_H
#define MISC_LIST_H
#include <assert.h>
namespace Misc{
/*
A simple and completely allocation-less doubly linked list. The
class only manages pointers to and between elements. It leaving all
memory management to the user.
*/
template <typename Elem>
struct List
{
List() : head(0), tail(0), totalNum(0) {}
// Empty the list.
void reset()
{
head = 0;
tail = 0;
totalNum = 0;
}
// Insert an element at the end of the list. The element cannot be
// part of any other list when this is called.
void insert(Elem *p)
{
if(tail)
{
// There are existing elements. Insert the node at the end of
// the list.
assert(head && totalNum > 0);
tail->next = p;
}
else
{
// This is the first element
assert(head == 0 && totalNum == 0);
head = p;
}
// These have to be done in either case
p->prev = tail;
p->next = 0;
tail = p;
totalNum++;
}
// Remove element from the list. The element MUST be part of the
// list when this is called.
void remove(Elem *p)
{
assert(totalNum > 0);
if(p->next)
{
// There's an element following us. Set it up correctly.
p->next->prev = p->prev;
assert(tail && tail != p);
}
else
{
// We're the tail
assert(tail == p);
tail = p->prev;
}
// Now do exactly the same for the previous element
if(p->prev)
{
p->prev->next = p->next;
assert(head && head != p);
}
else
{
assert(head == p);
head = p->next;
}
totalNum--;
}
// Pop the first element off the list
Elem *pop()
{
Elem *res = getHead();
if(res) remove(res);
return res;
}
// Swap the contents of this list with another of the same type
void swap(List &other)
{
Elem *tmp;
tmp = head;
head = other.head;
other.head = tmp;
tmp = tail;
tail = other.tail;
other.tail = tmp;
unsigned int tmp2 = totalNum;
totalNum = other.totalNum;
other.totalNum = tmp2;
}
/* Absorb the contents of another list. All the elements from the
list are moved to the end of this list, and the other list is
cleared.
*/
void absorb(List &other)
{
assert(&other != this);
if(other.totalNum)
{
absorb(other.head, other.tail, other.totalNum);
other.reset();
}
assert(other.totalNum == 0);
}
/* Absorb a range of elements, endpoints included. The elements are
assumed NOT to belong to any list, but they ARE assumed to be
connected with a chain between them.
The connection MUST run all the way from 'first' to 'last'
through the ->next pointers, and vice versa through ->prev
pointers.
The parameter 'num' must give the exact number of elements in the
chain.
Passing first == last, num == 1 is allowed and is equivalent to
calling insert().
*/
void absorb(Elem* first, Elem *last, int num)
{
assert(first && last && num>=1);
if(tail)
{
// There are existing elements. Insert the first node at the
// end of the list.
assert(head && totalNum > 0);
tail->next = first;
}
else
{
// This is the first element
assert(head == 0 && totalNum == 0);
head = first;
}
// These have to be done in either case
first->prev = tail;
last->next = 0;
tail = last;
totalNum += num;
}
Elem* getHead() const { return head; }
Elem* getTail() const { return tail; }
unsigned int getNum() const { return totalNum; }
private:
Elem *head;
Elem *tail;
unsigned int totalNum;
};
}
#endif

@ -0,0 +1,37 @@
#ifndef OENGINE_OGRE_EXITLISTEN_H
#define OENGINE_OGRE_EXITLISTEN_H
/*
This FrameListener simply exits the rendering loop when the window
is closed. You can also tell it to exit manually by setting the exit
member to true;
*/
#include <OgreFrameListener.h>
#include <OgreRenderWindow.h>
namespace OEngine {
namespace Render
{
struct ExitListener : Ogre::FrameListener
{
Ogre::RenderWindow *window;
bool exit;
ExitListener(Ogre::RenderWindow *wnd)
: window(wnd), exit(false) {}
bool frameStarted(const Ogre::FrameEvent &evt)
{
if(window->isClosed())
exit = true;
return !exit;
}
// Function equivalent of setting exit=true. Handy when you need a
// delegate to bind to an event.
void exitNow() { exit = true; }
};
}}
#endif

@ -0,0 +1,136 @@
#include "fader.hpp"
#include <OgreOverlayManager.h>
#include <OgreOverlayContainer.h>
#include <OgreOverlay.h>
#include <OgreMaterial.h>
#include <OgreTechnique.h>
#include <OgreMaterialManager.h>
#include <OgreResourceGroupManager.h>
#include <assert.h>
#define FADE_OVERLAY_NAME "FadeInOutOverlay"
#define FADE_OVERLAY_PANEL_NAME "FadeInOutOverlayPanel"
#define FADE_MATERIAL_NAME "FadeInOutMaterial"
using namespace Ogre;
using namespace OEngine::Render;
Fader::Fader() :
mMode(FadingMode_In),
mRemainingTime(0.f),
mTargetTime(0.f),
mTargetAlpha(0.f),
mCurrentAlpha(0.f),
mStartAlpha(0.f)
{
// Create the fading material
MaterialPtr material = MaterialManager::getSingleton().create( FADE_MATERIAL_NAME, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME );
Pass* pass = material->getTechnique(0)->getPass(0);
pass->setSceneBlending(SBT_TRANSPARENT_ALPHA);
mFadeTextureUnit = pass->createTextureUnitState();
mFadeTextureUnit->setColourOperationEx(LBX_SOURCE1, LBS_MANUAL, LBS_CURRENT, ColourValue(0.f, 0.f, 0.f)); // always black colour
// Create the overlay
OverlayManager& ovm = OverlayManager::getSingleton();
mOverlay = ovm.create( FADE_OVERLAY_NAME );
OverlayContainer* overlay_panel;
overlay_panel = (OverlayContainer*)ovm.createOverlayElement("Panel", FADE_OVERLAY_PANEL_NAME);
// position it over the whole screen
overlay_panel->_setPosition(0, 0);
overlay_panel->_setDimensions(1, 1);
overlay_panel->setMaterialName( FADE_MATERIAL_NAME );
overlay_panel->show();
mOverlay->add2D(overlay_panel);
mOverlay->hide();
}
void Fader::update(float dt)
{
if (mRemainingTime > 0)
{
if (mMode == FadingMode_In)
{
mCurrentAlpha -= dt/mTargetTime * (mStartAlpha-mTargetAlpha);
if (mCurrentAlpha < mTargetAlpha) mCurrentAlpha = mTargetAlpha;
}
else if (mMode == FadingMode_Out)
{
mCurrentAlpha += dt/mTargetTime * (mTargetAlpha-mStartAlpha);
if (mCurrentAlpha > mTargetAlpha) mCurrentAlpha = mTargetAlpha;
}
applyAlpha();
mRemainingTime -= dt;
}
if (mCurrentAlpha == 0.f) mOverlay->hide();
}
void Fader::applyAlpha()
{
mOverlay->show();
mFadeTextureUnit->setAlphaOperation(LBX_SOURCE1, LBS_MANUAL, LBS_CURRENT, mCurrentAlpha);
}
void Fader::fadeIn(float time)
{
if (time<0.f) return;
if (time==0.f)
{
mCurrentAlpha = 0.f;
applyAlpha();
return;
}
mStartAlpha = mCurrentAlpha;
mTargetAlpha = 0.f;
mMode = FadingMode_In;
mTargetTime = time;
mRemainingTime = time;
}
void Fader::fadeOut(const float time)
{
if (time<0.f) return;
if (time==0.f)
{
mCurrentAlpha = 1.f;
applyAlpha();
return;
}
mStartAlpha = mCurrentAlpha;
mTargetAlpha = 1.f;
mMode = FadingMode_Out;
mTargetTime = time;
mRemainingTime = time;
}
void Fader::fadeTo(const int percent, const float time)
{
if (time<0.f) return;
if (time==0.f)
{
mCurrentAlpha = percent/100.f;
applyAlpha();
return;
}
mStartAlpha = mCurrentAlpha;
mTargetAlpha = percent/100.f;
if (mTargetAlpha == mStartAlpha) return;
else if (mTargetAlpha > mStartAlpha) mMode = FadingMode_Out;
else mMode = FadingMode_In;
mTargetTime = time;
mRemainingTime = time;
}

@ -0,0 +1,57 @@
#ifndef OENGINE_OGRE_FADE_H
#define OENGINE_OGRE_FADE_H
/*
A class that handles fading in the screen from black or fading it out to black.
To achieve this, it uses a full-screen Ogre::Overlay
inspired by http://www.ogre3d.org/tikiwiki/FadeEffectOverlay (heavily adjusted)
*/
#include <OgreFrameListener.h>
namespace Ogre
{
class TextureUnitState;
class Overlay;
}
namespace OEngine {
namespace Render
{
class Fader
{
public:
Fader();
void update(float dt);
void fadeIn(const float time);
void fadeOut(const float time);
void fadeTo(const int percent, const float time);
private:
enum FadingMode
{
FadingMode_In,
FadingMode_Out
};
void applyAlpha();
Ogre::TextureUnitState* mFadeTextureUnit;
Ogre::Overlay* mOverlay;
FadingMode mMode;
float mRemainingTime;
float mTargetTime;
float mTargetAlpha;
float mCurrentAlpha;
float mStartAlpha;
protected:
};
}}
#endif

@ -0,0 +1,57 @@
#include "mouselook.hpp"
#include <OIS/OIS.h>
#include <OgreCamera.h>
#include <OgreSceneNode.h>
using namespace OIS;
using namespace Ogre;
using namespace OEngine::Render;
void MouseLookEvent::event(Type type, int index, const void *p)
{
if(type != EV_MouseMove || camera == NULL) return;
MouseEvent *arg = (MouseEvent*)(p);
float x = arg->state.X.rel * sensX;
float y = arg->state.Y.rel * sensY;
camera->getParentSceneNode()->getParentSceneNode()->yaw(Degree(-x));
camera->getParentSceneNode()->pitch(Degree(-y));
if(flipProt)
{
// The camera before pitching
/*Quaternion nopitch = camera->getParentSceneNode()->getOrientation();
camera->getParentSceneNode()->pitch(Degree(-y));
// Apply some failsafe measures against the camera flipping
// upside down. Is the camera close to pointing straight up or
// down?
if(Ogre::Vector3(camera->getParentSceneNode()->getOrientation()*Ogre::Vector3::UNIT_Y)[1] <= 0.1)
// If so, undo the last pitch
camera->getParentSceneNode()->setOrientation(nopitch);*/
//camera->getU
// Angle of rotation around the X-axis.
float pitchAngle = (2 * Ogre::Degree(Ogre::Math::ACos(camera->getParentSceneNode()->getOrientation().w)).valueDegrees());
// Just to determine the sign of the angle we pick up above, the
// value itself does not interest us.
float pitchAngleSign = camera->getParentSceneNode()->getOrientation().x;
// Limit the pitch between -90 degress and +90 degrees, Quake3-style.
if (pitchAngle > 90.0f)
{
if (pitchAngleSign > 0)
// Set orientation to 90 degrees on X-axis.
camera->getParentSceneNode()->setOrientation(Ogre::Quaternion(Ogre::Math::Sqrt(0.5f),
Ogre::Math::Sqrt(0.5f), 0, 0));
else if (pitchAngleSign < 0)
// Sets orientation to -90 degrees on X-axis.
camera->getParentSceneNode()->setOrientation(Ogre::Quaternion(Ogre::Math::Sqrt(0.5f),
-Ogre::Math::Sqrt(0.5f), 0, 0));
}
}
}

@ -0,0 +1,56 @@
#ifndef OENGINE_OGRE_MOUSELOOK_H
#define OENGINE_OGRE_MOUSELOOK_H
/*
A mouse-look class for Ogre. Accepts input events from Mangle::Input
and translates them.
You can adjust the mouse sensibility and switch to a different
camera. The mouselook class also has an optional wrap protection
that keeps the camera from flipping upside down.
You can disable the mouse looker at any time by calling
setCamera(NULL), and reenable it by setting the camera back.
NOTE: The current implementation will ONLY work for native OIS
events.
*/
#include <mangle/input/event.hpp>
namespace Ogre
{
class Camera;
}
namespace OEngine {
namespace Render
{
class MouseLookEvent : public Mangle::Input::Event
{
Ogre::Camera* camera;
float sensX, sensY; // Mouse sensibility
bool flipProt; // Flip protection
public:
MouseLookEvent(Ogre::Camera *cam=NULL,
float sX=0.2, float sY=0.2,
bool prot=true)
: camera(cam)
, sensX(sX)
, sensY(sY)
, flipProt(prot)
{}
void setCamera(Ogre::Camera *cam)
{ camera = cam; }
void setSens(float sX, float sY)
{ sensX = sX; sensY = sY; }
void setProt(bool p) { flipProt = p; }
void event(Type type, int index, const void *p);
};
typedef boost::shared_ptr<MouseLookEvent> MouseLookEventPtr;
}}
#endif

@ -0,0 +1,118 @@
#include "renderer.hpp"
#include "fader.hpp"
#include "OgreRoot.h"
#include "OgreRenderWindow.h"
#include "OgreLogManager.h"
#include "OgreLog.h"
#include <assert.h>
using namespace Ogre;
using namespace OEngine::Render;
void OgreRenderer::cleanup()
{
if (mFader)
delete mFader;
if(mRoot)
delete mRoot;
mRoot = NULL;
}
void OgreRenderer::start()
{
mRoot->startRendering();
}
void OgreRenderer::update(float dt)
{
mFader->update(dt);
}
void OgreRenderer::screenshot(const std::string &file)
{
mWindow->writeContentsToFile(file);
}
float OgreRenderer::getFPS()
{
return mWindow->getLastFPS();
}
bool OgreRenderer::configure(bool showConfig,
const std::string &cfgPath,
const std::string &logPath,
const std::string &pluginCfg,
bool _logging)
{
// Set up logging first
new LogManager;
Log *log = LogManager::getSingleton().createLog(logPath + std::string("Ogre.log"));
logging = _logging;
if(logging)
// Full log detail
log->setLogDetail(LL_BOREME);
else
// Disable logging
log->setDebugOutputEnabled(false);
mRoot = new Root(pluginCfg, cfgPath, "");
// Show the configuration dialog and initialise the system, if the
// showConfig parameter is specified. The settings are stored in
// ogre.cfg. If showConfig is false, the settings are assumed to
// already exist in ogre.cfg.
int result;
if(showConfig)
result = mRoot->showConfigDialog();
else
result = mRoot->restoreConfig();
return !result;
}
bool OgreRenderer::configure(bool showConfig,
const std::string &cfgPath,
const std::string &pluginCfg,
bool _logging)
{
return configure(showConfig, cfgPath, cfgPath, pluginCfg, _logging);
}
bool OgreRenderer::configure(bool showConfig,
const std::string &pluginCfg,
bool _logging)
{
return configure(showConfig, "", pluginCfg, _logging);
}
void OgreRenderer::createWindow(const std::string &title)
{
assert(mRoot);
// Initialize OGRE window
mWindow = mRoot->initialise(true, title, "");
}
void OgreRenderer::createScene(const std::string camName, float fov, float nearClip)
{
assert(mRoot);
assert(mWindow);
// Get the SceneManager, in this case a generic one
mScene = mRoot->createSceneManager(ST_GENERIC);
// Create the camera
mCamera = mScene->createCamera(camName);
mCamera->setNearClipDistance(nearClip);
mCamera->setFOVy(Degree(fov));
// Create one viewport, entire window
mView = mWindow->addViewport(mCamera);
// Alter the camera aspect ratio to match the viewport
mCamera->setAspectRatio(Real(mView->getActualWidth()) / Real(mView->getActualHeight()));
mFader = new Fader();
}

@ -0,0 +1,98 @@
#ifndef OENGINE_OGRE_RENDERER_H
#define OENGINE_OGRE_RENDERER_H
/*
Ogre renderer class
*/
#include <string>
namespace Ogre
{
class Root;
class RenderWindow;
class SceneManager;
class Camera;
class Viewport;
}
namespace OEngine {
namespace Render
{
class Fader;
class OgreRenderer
{
Ogre::Root *mRoot;
Ogre::RenderWindow *mWindow;
Ogre::SceneManager *mScene;
Ogre::Camera *mCamera;
Ogre::Viewport *mView;
Fader* mFader;
bool logging;
public:
OgreRenderer()
: mRoot(NULL), mWindow(NULL), mScene(NULL), mFader(NULL) {}
~OgreRenderer() { cleanup(); }
/** Configure the renderer. This will load configuration files and
set up the Root and logging classes. */
bool configure(bool showConfig, // Show config dialog box?
const std::string &cfgPath, // Path to directory where to store config files
const std::string &logPath, // Path to directory where to store log files
const std::string &pluginCfg, // plugin.cfg file
bool _logging); // Enable or disable logging
bool configure(bool showConfig, // Show config dialog box?
const std::string &cfgPath, // Path to directory where to store config files
const std::string &pluginCfg, // plugin.cfg file
bool _logging); // Enable or disable logging
/** Configure the renderer. This will load configuration files and
set up the Root and logging classes. */
bool configure(bool showConfig, // Show config dialog box?
const std::string &pluginCfg, // plugin.cfg file
bool _logging); // Enable or disable logging
/// Create a window with the given title
void createWindow(const std::string &title);
/// Set up the scene manager, camera and viewport
void createScene(const std::string camName="Camera",// Camera name
float fov=55, // Field of view angle
float nearClip=5 // Near clip distance
);
/// Kill the renderer.
void cleanup();
/// Start the main rendering loop
void start();
void update(float dt);
/// Write a screenshot to file
void screenshot(const std::string &file);
float getFPS();
/// Get the Root
Ogre::Root *getRoot() { return mRoot; }
/// Get the rendering window
Ogre::RenderWindow *getWindow() { return mWindow; }
/// Get the scene manager
Ogre::SceneManager *getScene() { return mScene; }
/// Get the screen colour fader
Fader *getFader() { return mFader; }
/// Camera
Ogre::Camera *getCamera() { return mCamera; }
/// Viewport
Ogre::Viewport *getViewport() { return mView; }
};
}}
#endif

@ -0,0 +1,219 @@
#include "sndmanager.hpp"
#include "../misc/list.hpp"
#include <boost/weak_ptr.hpp>
using namespace OEngine::Sound;
using namespace Mangle::Sound;
/** This is our own internal implementation of the
Mangle::Sound::Sound interface. This class links a SoundPtr to
itself and prevents itself from being deleted as long as the sound
is playing.
*/
struct OEngine::Sound::ManagedSound : SoundFilter
{
private:
/** Who's your daddy? This is set if and only if we are listed
internally in the given SoundManager.
It may be NULL if the manager has been deleted but the user
keeps their own SoundPtrs to the object.
*/
SoundManager *mgr;
/** Keep a weak pointer to ourselves, which we convert into a
'strong' pointer when we are playing. When 'self' is pointing to
ourselves, the object will never be deleted.
This is used to make sure the sound is not deleted while
playing, unless it is explicitly ordered to do so by the
manager.
TODO: This kind of construct is useful. If we need it elsewhere
later, template it. It would be generally useful in any system
where we poll to check if a resource is still needed, but where
manual references are allowed.
*/
WSoundPtr weak;
SoundPtr self;
// Keep this object from being deleted
void lock()
{
self = SoundPtr(weak);
}
// Release the lock. This may or may not delete the object. Never do
// anything after calling unlock()!
void unlock()
{
self.reset();
}
public:
// Used for putting ourselves in linked lists
ManagedSound *next, *prev;
/** Detach this sound from its manager. This means that the manager
will no longer know we exist. Typically only called when either
the sound or the manager is about to get deleted.
Since this means update() will no longer be called, we also have
to unlock the sound manually since it will no longer be able to
do that itself. This means that the sound may be deleted, even
if it is still playing, when the manager is deleted.
However, you are still allowed to keep and manage your own
SoundPtr references, but the lock/unlock system is disabled
after the manager is gone.
*/
void detach()
{
if(mgr)
{
mgr->detach(this);
mgr = NULL;
}
// Unlock must be last command. Object may get deleted at this
// point.
unlock();
}
ManagedSound(SoundPtr snd, SoundManager *mg)
: SoundFilter(snd), mgr(mg)
{}
~ManagedSound() { detach(); }
// Needed to set up the weak pointer
void setup(SoundPtr self)
{
weak = WSoundPtr(self);
}
// Override play() to mark the object as locked
void play()
{
SoundFilter::play();
// Lock the object so that it is not deleted while playing. Only
// do this if we have a manager, otherwise the object will never
// get unlocked.
if(mgr) lock();
}
// Called regularly by the manager
void update()
{
// If we're no longer playing, don't force object retention.
if(!isPlaying())
unlock();
// unlock() may delete the object, so don't do anything below this
// point.
}
SoundPtr clone()
{
// Cloning only works when we have a manager.
assert(mgr);
return mgr->wrap(client->clone());
}
};
struct SoundManager::SoundManagerList
{
private:
// A linked list of ManagedSound objects.
typedef Misc::List<ManagedSound> SoundList;
SoundList list;
public:
// Add a new sound to the list
void addNew(ManagedSound* snd)
{
list.insert(snd);
}
// Remove a sound from the list
void remove(ManagedSound *snd)
{
list.remove(snd);
}
// Number of sounds in the list
int numSounds() { return list.getNum(); }
// Update all sounds
void updateAll()
{
ManagedSound *s = list.getHead();
while(s)
{
ManagedSound *cur = s;
// Propagate first, since update() may delete object
s = s->next;
cur->update();
}
}
// Detach and unlock all sounds
void detachAll()
{
ManagedSound *s = list.getHead();
while(s)
{
ManagedSound *cur = s;
s = s->next;
cur->detach();
}
}
};
SoundManager::SoundManager(SoundFactoryPtr fact)
: FactoryFilter(fact)
{
needsUpdate = true;
list = new SoundManagerList;
}
SoundManager::~SoundManager()
{
// Detach all sounds
list->detachAll();
}
SoundPtr SoundManager::wrap(SoundPtr client)
{
// Create and set up the sound wrapper
ManagedSound *snd = new ManagedSound(client,this);
SoundPtr ptr(snd);
snd->setup(ptr);
// Add ourselves to the list of all sounds
list->addNew(snd);
return ptr;
}
// Remove the sound from this manager.
void SoundManager::detach(ManagedSound *sound)
{
list->remove(sound);
}
int SoundManager::numSounds()
{
return list->numSounds();
}
void SoundManager::update()
{
// Update all the sounds we own
list->updateAll();
// Update the source if it needs it
if(client->needsUpdate)
client->update();
}

@ -0,0 +1,93 @@
#ifndef OENGINE_SOUND_MANAGER_H
#define OENGINE_SOUND_MANAGER_H
#include <mangle/sound/filters/pure_filter.hpp>
namespace OEngine
{
namespace Sound
{
using namespace Mangle::Sound;
class ManagedSound;
/** A manager of Mangle::Sounds.
The sound manager is a wrapper around the more low-level
SoundFactory - although it is also itself an implementation of
SoundFactory. It will:
- keep a list of all created sounds
- let you iterate the list
- keep references to playing sounds so you don't have to
- auto-release references to sounds that are finished playing
(ie. deleting them if you're not referencing them)
*/
class SoundManager : public FactoryFilter
{
// Shove the implementation details into the cpp file.
struct SoundManagerList;
SoundManagerList *list;
// Create a new sound wrapper based on the given source sound.
SoundPtr wrap(SoundPtr snd);
/** Internal function. Will completely disconnect the given
sound from this manager. Called from ManagedSound.
*/
friend class ManagedSound;
void detach(ManagedSound *sound);
public:
SoundManager(SoundFactoryPtr fact);
~SoundManager();
void update();
/// Get number of sounds currently managed by this manager.
int numSounds();
SoundPtr loadRaw(SampleSourcePtr input)
{ return wrap(client->loadRaw(input)); }
SoundPtr load(Mangle::Stream::StreamPtr input)
{ return wrap(client->load(input)); }
SoundPtr load(const std::string &file)
{ return wrap(client->load(file)); }
// Play a sound immediately, and release when done unless you
// keep the returned SoundPtr.
SoundPtr play(Mangle::Stream::StreamPtr sound)
{
SoundPtr snd = load(sound);
snd->play();
return snd;
}
SoundPtr play(const std::string &sound)
{
SoundPtr snd = load(sound);
snd->play();
return snd;
}
// Ditto for 3D sounds
SoundPtr play3D(Mangle::Stream::StreamPtr sound, float x, float y, float z)
{
SoundPtr snd = load(sound);
snd->setPos(x,y,z);
snd->play();
return snd;
}
SoundPtr play3D(const std::string &sound, float x, float y, float z)
{
SoundPtr snd = load(sound);
snd->setPos(x,y,z);
snd->play();
return snd;
}
};
typedef boost::shared_ptr<SoundManager> SoundManagerPtr;
}
}
#endif

@ -0,0 +1,16 @@
GCC=g++ -I../
all: sound_manager_test sound_3d_test
L_FFMPEG=$(shell pkg-config --libs libavcodec libavformat)
L_OPENAL=$(shell pkg-config --libs openal)
L_AUDIERE=-laudiere
sound_manager_test: sound_manager_test.cpp ../../mangle/sound/sources/audiere_source.cpp ../../mangle/sound/outputs/openal_out.cpp ../../mangle/stream/clients/audiere_file.cpp ../sndmanager.cpp
$(GCC) $^ -o $@ $(L_AUDIERE) $(L_OPENAL) -I../..
sound_3d_test: sound_3d_test.cpp ../../mangle/sound/sources/audiere_source.cpp ../../mangle/sound/outputs/openal_out.cpp ../../mangle/stream/clients/audiere_file.cpp ../sndmanager.cpp
$(GCC) $^ -o $@ $(L_AUDIERE) $(L_OPENAL) -I../..
clean:
rm *_test

@ -0,0 +1,3 @@
Playing at 0,0,0
Playing at 1,1,0
Playing at -1,0,0

@ -0,0 +1,5 @@
Playing ../../mangle/sound/tests/cow.wav
Replaying
pause
restart
Done playing.

@ -0,0 +1,46 @@
#include <iostream>
#include <exception>
#include <assert.h>
#include <mangle/stream/servers/file_stream.hpp>
#include <mangle/sound/filters/openal_audiere.hpp>
#include <sound/sndmanager.hpp>
using namespace std;
using namespace Mangle::Stream;
using namespace Mangle::Sound;
using namespace OEngine::Sound;
const std::string sound = "../../mangle/sound/tests/cow.wav";
SoundManagerPtr m;
// Play and wait for finish
void play(float x, float y, float z)
{
cout << "Playing at " << x << "," << y << "," << z << endl;
SoundPtr snd = m->play3D(sound,x,y,z);
while(snd->isPlaying())
{
usleep(10000);
m->update();
}
}
int main()
{
SoundFactoryPtr oaf(new OpenAL_Audiere_Factory);
SoundManagerPtr mg(new SoundManager(oaf));
m = mg;
mg->setListenerPos(0,0,0,0,1,0,0,0,1);
play(0,0,0);
play(1,1,0);
play(-1,0,0);
return 0;
}

@ -0,0 +1,73 @@
#include <iostream>
#include <exception>
#include <assert.h>
#include <mangle/stream/servers/file_stream.hpp>
#include <mangle/sound/filters/openal_audiere.hpp>
#include <sound/sndmanager.hpp>
using namespace std;
using namespace Mangle::Stream;
using namespace Mangle::Sound;
using namespace OEngine::Sound;
const std::string sound = "../../mangle/sound/tests/cow.wav";
int main()
{
SoundFactoryPtr oaf(new OpenAL_Audiere_Factory);
SoundManagerPtr mg(new SoundManager(oaf));
cout << "Playing " << sound << "\n";
assert(mg->numSounds() == 0);
/** Start the sound playing, and then let the pointer go out of
scope. Lower-level players (like 'oaf' above) will immediately
delete the sound. SoundManager OTOH will keep it until it's
finished.
*/
mg->play(sound);
assert(mg->numSounds() == 1);
// Loop while there are still sounds to manage
while(mg->numSounds() != 0)
{
assert(mg->numSounds() == 1);
usleep(10000);
if(mg->needsUpdate)
mg->update();
}
SoundPtr snd = mg->play(sound);
cout << "Replaying\n";
int i = 0;
while(mg->numSounds() != 0)
{
assert(mg->numSounds() == 1);
usleep(10000);
if(mg->needsUpdate)
mg->update();
if(i++ == 70)
{
cout << "pause\n";
snd->pause();
}
if(i == 130)
{
cout << "restart\n";
snd->play();
// Let the sound go out of scope
snd.reset();
}
}
cout << "Done playing.\n";
assert(mg->numSounds() == 0);
return 0;
}

@ -0,0 +1,18 @@
#!/bin/bash
make || exit
mkdir -p output
PROGS=*_test
for a in $PROGS; do
if [ -f "output/$a.out" ]; then
echo "Running $a:"
./$a | diff output/$a.out -
else
echo "Creating $a.out"
./$a > "output/$a.out"
git add "output/$a.out"
fi
done

@ -0,0 +1,11 @@
#!/bin/bash
function run()
{
echo "TESTING $1"
cd "$1/tests/"
./test.sh
cd ../../
}
run input
Loading…
Cancel
Save