Imported Upstream version 0.26.0

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

12
libs/openengine/README Normal file
View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,799 @@
#include "physic.hpp"
#include <btBulletDynamicsCommon.h>
#include <btBulletCollisionCommon.h>
#include <BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h>
#include <components/nifbullet/bulletnifloader.hpp>
#include "CMotionState.h"
#include "OgreRoot.h"
#include "btKinematicCharacterController.h"
#include "BtOgrePG.h"
#include "BtOgreGP.h"
#include "BtOgreExtras.h"
#include <boost/lexical_cast.hpp>
#include <boost/format.hpp>
namespace OEngine {
namespace Physic
{
PhysicActor::PhysicActor(const std::string &name, const std::string &mesh, PhysicEngine *engine, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation, float scale)
: mName(name), mEngine(engine), mMesh(mesh), mBoxScaledTranslation(0,0,0), mBoxRotationInverse(0,0,0,0)
, mBody(0), mRaycastingBody(0), mOnGround(false), mCollisionMode(true), mBoxRotation(0,0,0,0)
, mForce(0.0f)
{
mBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, position, rotation, &mBoxScaledTranslation, &mBoxRotation);
mRaycastingBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, position, rotation, &mBoxScaledTranslation, &mBoxRotation, true);
Ogre::Quaternion inverse = mBoxRotation.Inverse();
mBoxRotationInverse = btQuaternion(inverse.x, inverse.y, inverse.z,inverse.w);
mEngine->addRigidBody(mBody, false, mRaycastingBody,true); //Add rigid body to dynamics world, but do not add to object map
}
PhysicActor::~PhysicActor()
{
if(mBody)
{
mEngine->dynamicsWorld->removeRigidBody(mBody);
delete mBody;
}
if(mRaycastingBody)
{
mEngine->dynamicsWorld->removeRigidBody(mRaycastingBody);
delete mRaycastingBody;
}
}
void PhysicActor::enableCollisions(bool collision)
{
assert(mBody);
if(collision && !mCollisionMode) enableCollisionBody();
if(!collision && mCollisionMode) disableCollisionBody();
mCollisionMode = collision;
}
void PhysicActor::setPosition(const Ogre::Vector3 &pos)
{
assert(mBody);
if(pos != getPosition())
{
mEngine->adjustRigidBody(mBody, pos, getRotation(), mBoxScaledTranslation, mBoxRotation);
mEngine->adjustRigidBody(mRaycastingBody, pos, getRotation(), mBoxScaledTranslation, mBoxRotation);
}
}
void PhysicActor::setRotation(const Ogre::Quaternion &quat)
{
assert(mBody);
if(!quat.equals(getRotation(), Ogre::Radian(0))){
mEngine->adjustRigidBody(mBody, getPosition(), quat, mBoxScaledTranslation, mBoxRotation);
mEngine->adjustRigidBody(mRaycastingBody, getPosition(), quat, mBoxScaledTranslation, mBoxRotation);
}
}
Ogre::Vector3 PhysicActor::getPosition()
{
assert(mBody);
btVector3 vec = mBody->getWorldTransform().getOrigin();
Ogre::Quaternion rotation = Ogre::Quaternion(mBody->getWorldTransform().getRotation().getW(), mBody->getWorldTransform().getRotation().getX(),
mBody->getWorldTransform().getRotation().getY(), mBody->getWorldTransform().getRotation().getZ());
Ogre::Vector3 transrot = rotation * mBoxScaledTranslation;
Ogre::Vector3 visualPosition = Ogre::Vector3(vec.getX(), vec.getY(), vec.getZ()) - transrot;
return visualPosition;
}
Ogre::Quaternion PhysicActor::getRotation()
{
assert(mBody);
btQuaternion quat = mBody->getWorldTransform().getRotation() * mBoxRotationInverse;
return Ogre::Quaternion(quat.getW(), quat.getX(), quat.getY(), quat.getZ());
}
void PhysicActor::setScale(float scale){
//We only need to change the scaled box translation, box rotations remain the same.
assert(mBody);
mBoxScaledTranslation = mBoxScaledTranslation / mBody->getCollisionShape()->getLocalScaling().getX();
mBoxScaledTranslation *= scale;
Ogre::Vector3 pos = getPosition();
Ogre::Quaternion rot = getRotation();
if(mBody){
mEngine->dynamicsWorld->removeRigidBody(mBody);
mEngine->dynamicsWorld->removeRigidBody(mRaycastingBody);
delete mBody;
delete mRaycastingBody;
}
//Create the newly scaled rigid body
mBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, pos, rot);
mRaycastingBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, pos, rot, 0, 0, true);
mEngine->addRigidBody(mBody, false, mRaycastingBody,true); //Add rigid body to dynamics world, but do not add to object map
}
Ogre::Vector3 PhysicActor::getHalfExtents() const
{
if(mBody)
{
btBoxShape *box = static_cast<btBoxShape*>(mBody->getCollisionShape());
if(box != NULL)
{
btVector3 size = box->getHalfExtentsWithMargin();
return Ogre::Vector3(size.getX(), size.getY(), size.getZ());
}
}
return Ogre::Vector3(0.0f);
}
void PhysicActor::setInertialForce(const Ogre::Vector3 &force)
{
mForce = force;
}
void PhysicActor::setOnGround(bool grounded)
{
mOnGround = grounded;
}
void PhysicActor::disableCollisionBody()
{
mEngine->dynamicsWorld->removeRigidBody(mBody);
}
void PhysicActor::enableCollisionBody()
{
mEngine->dynamicsWorld->addRigidBody(mBody,CollisionType_Actor,CollisionType_World|CollisionType_HeightMap);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
RigidBody::RigidBody(btRigidBody::btRigidBodyConstructionInfo& CI,std::string name)
: btRigidBody(CI)
, mName(name)
, mPlaceable(false)
{
}
RigidBody::~RigidBody()
{
delete getMotionState();
}
///////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////
PhysicEngine::PhysicEngine(BulletShapeLoader* shapeLoader) :
mDebugActive(0)
, mSceneMgr(NULL)
{
// Set up the collision configuration and dispatcher
collisionConfiguration = new btDefaultCollisionConfiguration();
dispatcher = new btCollisionDispatcher(collisionConfiguration);
// The actual physics solver
solver = new btSequentialImpulseConstraintSolver;
//btOverlappingPairCache* pairCache = new btSortedOverlappingPairCache();
pairCache = new btSortedOverlappingPairCache();
//pairCache->setInternalGhostPairCallback( new btGhostPairCallback() );
broadphase = new btDbvtBroadphase();
// The world.
dynamicsWorld = new btDiscreteDynamicsWorld(dispatcher,broadphase,solver,collisionConfiguration);
dynamicsWorld->setGravity(btVector3(0,0,-10));
if(BulletShapeManager::getSingletonPtr() == NULL)
{
new BulletShapeManager();
}
//TODO:singleton?
mShapeLoader = shapeLoader;
isDebugCreated = false;
mDebugDrawer = NULL;
}
void PhysicEngine::createDebugRendering()
{
if(!isDebugCreated)
{
Ogre::SceneNode* node = mSceneMgr->getRootSceneNode()->createChildSceneNode();
mDebugDrawer = new BtOgre::DebugDrawer(node, dynamicsWorld);
dynamicsWorld->setDebugDrawer(mDebugDrawer);
isDebugCreated = true;
dynamicsWorld->debugDrawWorld();
}
}
void PhysicEngine::setDebugRenderingMode(int mode)
{
if(!isDebugCreated)
{
createDebugRendering();
}
mDebugDrawer->setDebugMode(mode);
mDebugActive = mode;
}
bool PhysicEngine::toggleDebugRendering()
{
setDebugRenderingMode(!mDebugActive);
return mDebugActive;
}
void PhysicEngine::setSceneManager(Ogre::SceneManager* sceneMgr)
{
mSceneMgr = sceneMgr;
}
PhysicEngine::~PhysicEngine()
{
HeightFieldContainer::iterator hf_it = mHeightFieldMap.begin();
for (; hf_it != mHeightFieldMap.end(); ++hf_it)
{
dynamicsWorld->removeRigidBody(hf_it->second.mBody);
delete hf_it->second.mShape;
delete hf_it->second.mBody;
}
RigidBodyContainer::iterator rb_it = mCollisionObjectMap.begin();
for (; rb_it != mCollisionObjectMap.end(); ++rb_it)
{
if (rb_it->second != NULL)
{
dynamicsWorld->removeRigidBody(rb_it->second);
delete rb_it->second;
rb_it->second = NULL;
}
}
rb_it = mRaycastingObjectMap.begin();
for (; rb_it != mRaycastingObjectMap.end(); ++rb_it)
{
if (rb_it->second != NULL)
{
dynamicsWorld->removeRigidBody(rb_it->second);
delete rb_it->second;
rb_it->second = NULL;
}
}
PhysicActorContainer::iterator pa_it = mActorMap.begin();
for (; pa_it != mActorMap.end(); ++pa_it)
{
if (pa_it->second != NULL)
{
delete pa_it->second;
pa_it->second = NULL;
}
}
delete mDebugDrawer;
delete dynamicsWorld;
delete solver;
delete collisionConfiguration;
delete dispatcher;
delete broadphase;
delete pairCache;
delete mShapeLoader;
delete BulletShapeManager::getSingletonPtr();
}
void PhysicEngine::addHeightField(float* heights,
int x, int y, float yoffset,
float triSize, float sqrtVerts)
{
const std::string name = "HeightField_"
+ boost::lexical_cast<std::string>(x) + "_"
+ boost::lexical_cast<std::string>(y);
// find the minimum and maximum heights (needed for bullet)
float minh = heights[0];
float maxh = heights[0];
for (int i=0; i<sqrtVerts*sqrtVerts; ++i)
{
float h = heights[i];
if (h>maxh) maxh = h;
if (h<minh) minh = h;
}
btHeightfieldTerrainShape* hfShape = new btHeightfieldTerrainShape(
sqrtVerts, sqrtVerts, heights, 1,
minh, maxh, 2,
PHY_FLOAT,true);
hfShape->setUseDiamondSubdivision(true);
btVector3 scl(triSize, triSize, 1);
hfShape->setLocalScaling(scl);
CMotionState* newMotionState = new CMotionState(this,name);
btRigidBody::btRigidBodyConstructionInfo CI = btRigidBody::btRigidBodyConstructionInfo(0,newMotionState,hfShape);
RigidBody* body = new RigidBody(CI,name);
body->getWorldTransform().setOrigin(btVector3( (x+0.5)*triSize*(sqrtVerts-1), (y+0.5)*triSize*(sqrtVerts-1), (maxh+minh)/2.f));
HeightField hf;
hf.mBody = body;
hf.mShape = hfShape;
mHeightFieldMap [name] = hf;
dynamicsWorld->addRigidBody(body,CollisionType_HeightMap|CollisionType_Raycasting,
CollisionType_World|CollisionType_Actor|CollisionType_Raycasting);
}
void PhysicEngine::removeHeightField(int x, int y)
{
const std::string name = "HeightField_"
+ boost::lexical_cast<std::string>(x) + "_"
+ boost::lexical_cast<std::string>(y);
HeightField hf = mHeightFieldMap [name];
dynamicsWorld->removeRigidBody(hf.mBody);
delete hf.mShape;
delete hf.mBody;
mHeightFieldMap.erase(name);
}
void PhysicEngine::adjustRigidBody(RigidBody* body, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation,
const Ogre::Vector3 &scaledBoxTranslation, const Ogre::Quaternion &boxRotation)
{
btTransform tr;
Ogre::Quaternion boxrot = rotation * boxRotation;
Ogre::Vector3 transrot = boxrot * scaledBoxTranslation;
Ogre::Vector3 newPosition = transrot + position;
tr.setOrigin(btVector3(newPosition.x, newPosition.y, newPosition.z));
tr.setRotation(btQuaternion(boxrot.x,boxrot.y,boxrot.z,boxrot.w));
body->setWorldTransform(tr);
}
void PhysicEngine::boxAdjustExternal(const std::string &mesh, RigidBody* body,
float scale, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation)
{
std::string sid = (boost::format("%07.3f") % scale).str();
std::string outputstring = mesh + sid;
//std::cout << "The string" << outputstring << "\n";
//get the shape from the .nif
mShapeLoader->load(outputstring,"General");
BulletShapeManager::getSingletonPtr()->load(outputstring,"General");
BulletShapePtr shape = BulletShapeManager::getSingleton().getByName(outputstring,"General");
adjustRigidBody(body, position, rotation, shape->mBoxTranslation * scale, shape->mBoxRotation);
}
RigidBody* PhysicEngine::createAndAdjustRigidBody(const std::string &mesh, const std::string &name,
float scale, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation,
Ogre::Vector3* scaledBoxTranslation, Ogre::Quaternion* boxRotation, bool raycasting, bool placeable)
{
std::string sid = (boost::format("%07.3f") % scale).str();
std::string outputstring = mesh + sid;
//get the shape from the .nif
mShapeLoader->load(outputstring,"General");
BulletShapeManager::getSingletonPtr()->load(outputstring,"General");
BulletShapePtr shape = BulletShapeManager::getSingleton().getByName(outputstring,"General");
if (placeable && !raycasting && shape->mCollisionShape && !shape->mHasCollisionNode)
return NULL;
if (!shape->mCollisionShape && !raycasting)
return NULL;
if (!shape->mRaycastingShape && raycasting)
return NULL;
if (!raycasting)
shape->mCollisionShape->setLocalScaling( btVector3(scale,scale,scale));
else
shape->mRaycastingShape->setLocalScaling( btVector3(scale,scale,scale));
//create the motionState
CMotionState* newMotionState = new CMotionState(this,name);
//create the real body
btRigidBody::btRigidBodyConstructionInfo CI = btRigidBody::btRigidBodyConstructionInfo
(0,newMotionState, raycasting ? shape->mRaycastingShape : shape->mCollisionShape);
RigidBody* body = new RigidBody(CI,name);
body->mPlaceable = placeable;
if(scaledBoxTranslation != 0)
*scaledBoxTranslation = shape->mBoxTranslation * scale;
if(boxRotation != 0)
*boxRotation = shape->mBoxRotation;
adjustRigidBody(body, position, rotation, shape->mBoxTranslation * scale, shape->mBoxRotation);
return body;
}
void PhysicEngine::addRigidBody(RigidBody* body, bool addToMap, RigidBody* raycastingBody,bool actor)
{
if(!body && !raycastingBody)
return; // nothing to do
const std::string& name = (body ? body->mName : raycastingBody->mName);
if (body){
if(actor) dynamicsWorld->addRigidBody(body,CollisionType_Actor,CollisionType_World|CollisionType_HeightMap);
else dynamicsWorld->addRigidBody(body,CollisionType_World,CollisionType_World|CollisionType_Actor|CollisionType_HeightMap);
}
if (raycastingBody)
dynamicsWorld->addRigidBody(raycastingBody,CollisionType_Raycasting,CollisionType_Raycasting|CollisionType_World);
if(addToMap){
removeRigidBody(name);
deleteRigidBody(name);
if (body)
mCollisionObjectMap[name] = body;
if (raycastingBody)
mRaycastingObjectMap[name] = raycastingBody;
}
}
void PhysicEngine::removeRigidBody(const std::string &name)
{
RigidBodyContainer::iterator it = mCollisionObjectMap.find(name);
if (it != mCollisionObjectMap.end() )
{
RigidBody* body = it->second;
if(body != NULL)
{
dynamicsWorld->removeRigidBody(body);
}
}
it = mRaycastingObjectMap.find(name);
if (it != mRaycastingObjectMap.end() )
{
RigidBody* body = it->second;
if(body != NULL)
{
dynamicsWorld->removeRigidBody(body);
}
}
}
void PhysicEngine::deleteRigidBody(const std::string &name)
{
RigidBodyContainer::iterator it = mCollisionObjectMap.find(name);
if (it != mCollisionObjectMap.end() )
{
RigidBody* body = it->second;
if(body != NULL)
{
delete body;
}
mCollisionObjectMap.erase(it);
}
it = mRaycastingObjectMap.find(name);
if (it != mRaycastingObjectMap.end() )
{
RigidBody* body = it->second;
if(body != NULL)
{
delete body;
}
mRaycastingObjectMap.erase(it);
}
}
RigidBody* PhysicEngine::getRigidBody(const std::string &name, bool raycasting)
{
RigidBodyContainer* map = raycasting ? &mRaycastingObjectMap : &mCollisionObjectMap;
RigidBodyContainer::iterator it = map->find(name);
if (it != map->end() )
{
RigidBody* body = (*map)[name];
return body;
}
else
{
return NULL;
}
}
class ContactTestResultCallback : public btCollisionWorld::ContactResultCallback
{
public:
std::vector<std::string> mResult;
// added in bullet 2.81
// this is just a quick hack, as there does not seem to be a BULLET_VERSION macro?
#if defined(BT_COLLISION_OBJECT_WRAPPER_H)
virtual btScalar addSingleResult(btManifoldPoint& cp,
const btCollisionObjectWrapper* colObj0Wrap,int partId0,int index0,
const btCollisionObjectWrapper* colObj1Wrap,int partId1,int index1)
{
const RigidBody* body = dynamic_cast<const RigidBody*>(colObj0Wrap->m_collisionObject);
if (body && !(colObj0Wrap->m_collisionObject->getBroadphaseHandle()->m_collisionFilterGroup
& CollisionType_Raycasting))
mResult.push_back(body->mName);
return 0.f;
}
#else
virtual btScalar addSingleResult(btManifoldPoint& cp, const btCollisionObject* col0, int partId0, int index0,
const btCollisionObject* col1, int partId1, int index1)
{
const RigidBody* body = dynamic_cast<const RigidBody*>(col0);
if (body && !(col0->getBroadphaseHandle()->m_collisionFilterGroup
& CollisionType_Raycasting))
mResult.push_back(body->mName);
return 0.f;
}
#endif
};
class DeepestNotMeContactTestResultCallback : public btCollisionWorld::ContactResultCallback
{
const std::string &mFilter;
// Store the real origin, since the shape's origin is its center
btVector3 mOrigin;
public:
const RigidBody *mObject;
btVector3 mContactPoint;
btScalar mLeastDistSqr;
DeepestNotMeContactTestResultCallback(const std::string &filter, const btVector3 &origin)
: mFilter(filter), mOrigin(origin), mObject(0), mContactPoint(0,0,0),
mLeastDistSqr(std::numeric_limits<float>::max())
{ }
#if defined(BT_COLLISION_OBJECT_WRAPPER_H)
virtual btScalar addSingleResult(btManifoldPoint& cp,
const btCollisionObjectWrapper* col0Wrap,int partId0,int index0,
const btCollisionObjectWrapper* col1Wrap,int partId1,int index1)
{
const RigidBody* body = dynamic_cast<const RigidBody*>(col1Wrap->m_collisionObject);
if(body && body->mName != mFilter)
{
btScalar distsqr = mOrigin.distance2(cp.getPositionWorldOnA());
if(!mObject || distsqr < mLeastDistSqr)
{
mObject = body;
mLeastDistSqr = distsqr;
mContactPoint = cp.getPositionWorldOnA();
}
}
return 0.f;
}
#else
virtual btScalar addSingleResult(btManifoldPoint& cp,
const btCollisionObject* col0, int partId0, int index0,
const btCollisionObject* col1, int partId1, int index1)
{
const RigidBody* body = dynamic_cast<const RigidBody*>(col1);
if(body && body->mName != mFilter)
{
btScalar distsqr = mOrigin.distance2(cp.getPositionWorldOnA());
if(!mObject || distsqr < mLeastDistSqr)
{
mObject = body;
mLeastDistSqr = distsqr;
mContactPoint = cp.getPositionWorldOnA();
}
}
return 0.f;
}
#endif
};
std::vector<std::string> PhysicEngine::getCollisions(const std::string& name)
{
RigidBody* body = getRigidBody(name);
ContactTestResultCallback callback;
dynamicsWorld->contactTest(body, callback);
return callback.mResult;
}
std::pair<const RigidBody*,btVector3> PhysicEngine::getFilteredContact(const std::string &filter,
const btVector3 &origin,
btCollisionObject *object)
{
DeepestNotMeContactTestResultCallback callback(filter, origin);
dynamicsWorld->contactTest(object, callback);
return std::make_pair(callback.mObject, callback.mContactPoint);
}
void PhysicEngine::stepSimulation(double deltaT)
{
// This seems to be needed for character controller objects
dynamicsWorld->stepSimulation(deltaT,10, 1/60.0);
if(isDebugCreated)
{
mDebugDrawer->step();
}
}
void PhysicEngine::addCharacter(const std::string &name, const std::string &mesh,
const Ogre::Vector3 &position, float scale, const Ogre::Quaternion &rotation)
{
// Remove character with given name, so we don't make memory
// leak when character would be added twice
removeCharacter(name);
PhysicActor* newActor = new PhysicActor(name, mesh, this, position, rotation, scale);
//dynamicsWorld->addAction( newActor->mCharacter );
mActorMap[name] = newActor;
}
void PhysicEngine::removeCharacter(const std::string &name)
{
PhysicActorContainer::iterator it = mActorMap.find(name);
if (it != mActorMap.end() )
{
PhysicActor* act = it->second;
if(act != NULL)
{
delete act;
}
mActorMap.erase(it);
}
}
PhysicActor* PhysicEngine::getCharacter(const std::string &name)
{
PhysicActorContainer::iterator it = mActorMap.find(name);
if (it != mActorMap.end() )
{
PhysicActor* act = mActorMap[name];
return act;
}
else
{
return 0;
}
}
void PhysicEngine::emptyEventLists(void)
{
}
std::pair<std::string,float> PhysicEngine::rayTest(btVector3& from,btVector3& to,bool raycastingObjectOnly,bool ignoreHeightMap)
{
std::string name = "";
float d = -1;
btCollisionWorld::ClosestRayResultCallback resultCallback1(from, to);
if(raycastingObjectOnly)
resultCallback1.m_collisionFilterMask = CollisionType_Raycasting;
else
resultCallback1.m_collisionFilterMask = CollisionType_World;
if(!ignoreHeightMap)
resultCallback1.m_collisionFilterMask = resultCallback1.m_collisionFilterMask | CollisionType_HeightMap;
dynamicsWorld->rayTest(from, to, resultCallback1);
if (resultCallback1.hasHit())
{
name = static_cast<const RigidBody&>(*resultCallback1.m_collisionObject).mName;
d = resultCallback1.m_closestHitFraction;;
}
return std::pair<std::string,float>(name,d);
}
// callback that ignores player in results
struct OurClosestConvexResultCallback : public btCollisionWorld::ClosestConvexResultCallback
{
public:
OurClosestConvexResultCallback(const btVector3& convexFromWorld,const btVector3& convexToWorld)
: btCollisionWorld::ClosestConvexResultCallback(convexFromWorld, convexToWorld) {}
virtual btScalar addSingleResult(btCollisionWorld::LocalConvexResult& convexResult,bool normalInWorldSpace)
{
if (const RigidBody* body = dynamic_cast<const RigidBody*>(convexResult.m_hitCollisionObject))
if (body->mName == "player")
return 0;
return btCollisionWorld::ClosestConvexResultCallback::addSingleResult(convexResult, normalInWorldSpace);
}
};
std::pair<bool, float> PhysicEngine::sphereCast (float radius, btVector3& from, btVector3& to)
{
OurClosestConvexResultCallback callback(from, to);
callback.m_collisionFilterMask = OEngine::Physic::CollisionType_World|OEngine::Physic::CollisionType_HeightMap;
btSphereShape shape(radius);
const btQuaternion btrot(0.0f, 0.0f, 0.0f);
btTransform from_ (btrot, from);
btTransform to_ (btrot, to);
dynamicsWorld->convexSweepTest(&shape, from_, to_, callback);
if (callback.hasHit())
return std::make_pair(true, callback.m_closestHitFraction);
else
return std::make_pair(false, 1);
}
std::vector< std::pair<float, std::string> > PhysicEngine::rayTest2(btVector3& from, btVector3& to)
{
MyRayResultCallback resultCallback1;
resultCallback1.m_collisionFilterMask = CollisionType_Raycasting;
dynamicsWorld->rayTest(from, to, resultCallback1);
std::vector< std::pair<float, const btCollisionObject*> > results = resultCallback1.results;
std::vector< std::pair<float, std::string> > results2;
for (std::vector< std::pair<float, const btCollisionObject*> >::iterator it=results.begin();
it != results.end(); ++it)
{
results2.push_back( std::make_pair( (*it).first, static_cast<const RigidBody&>(*(*it).second).mName ) );
}
std::sort(results2.begin(), results2.end(), MyRayResultCallback::cmp);
return results2;
}
void PhysicEngine::getObjectAABB(const std::string &mesh, float scale, btVector3 &min, btVector3 &max)
{
std::string sid = (boost::format("%07.3f") % scale).str();
std::string outputstring = mesh + sid;
mShapeLoader->load(outputstring, "General");
BulletShapeManager::getSingletonPtr()->load(outputstring, "General");
BulletShapePtr shape =
BulletShapeManager::getSingleton().getByName(outputstring, "General");
btTransform trans;
trans.setIdentity();
if (shape->mRaycastingShape)
shape->mRaycastingShape->getAabb(trans, min, max);
else if (shape->mCollisionShape)
shape->mCollisionShape->getAabb(trans, min, max);
else
{
min = btVector3(0,0,0);
max = btVector3(0,0,0);
}
}
bool PhysicEngine::isAnyActorStandingOn (const std::string& objectName)
{
for (PhysicActorContainer::iterator it = mActorMap.begin(); it != mActorMap.end(); ++it)
{
if (!it->second->getOnGround())
continue;
Ogre::Vector3 pos = it->second->getPosition();
btVector3 from (pos.x, pos.y, pos.z);
btVector3 to = from - btVector3(0,0,5);
std::pair<std::string, float> result = rayTest(from, to);
if (result.first == objectName)
return true;
}
return false;
}
}
}

View file

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

View file

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

View file

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

View file

@ -0,0 +1,162 @@
#ifndef OENGINE_MYGUI_LAYOUT_H
#define OENGINE_MYGUI_LAYOUT_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::Widget* _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::Widget* 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::Widget* _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::Gui::getInstance().destroyWidget(mMainWidget);
mListWindowRoot.clear();
}
void setCoord(int x, int y, int w, int h)
{
mMainWidget->setCoord(x,y,w,h);
}
void adjustWindowCaption()
{
// adjust the size of the window caption so that all text is visible
// NOTE: this assumes that mMainWidget is of type Window.
MyGUI::TextBox* box = static_cast<MyGUI::Window*>(mMainWidget)->getCaptionWidget();
box->setSize(box->getTextSize().width + 24, box->getSize().height);
// in order to trigger alignment updates, we need to update the parent
// mygui doesn't provide a proper way of doing this, so we are just changing size
box->getParent()->setCoord(MyGUI::IntCoord(
box->getParent()->getCoord().left,
box->getParent()->getCoord().top,
box->getParent()->getCoord().width,
box->getParent()->getCoord().height+1
));
box->getParent()->setCoord(MyGUI::IntCoord(
box->getParent()->getCoord().left,
box->getParent()->getCoord().top,
box->getParent()->getCoord().width,
box->getParent()->getCoord().height-1
));
}
virtual void setVisible(bool b)
{
mMainWidget->setVisible(b);
}
void setText(const std::string& name, const std::string& caption)
{
MyGUI::Widget* pt;
getWidget(pt, name);
static_cast<MyGUI::TextBox*>(pt)->setCaption(caption);
}
void setTitle(const std::string& title)
{
// NOTE: this assume that mMainWidget is of type Window.
static_cast<MyGUI::Window*>(mMainWidget)->setCaptionWithReplacing(title);
adjustWindowCaption();
}
void setState(const std::string& widget, const std::string& state)
{
MyGUI::Widget* pt;
getWidget(pt, widget);
pt->_setWidgetState(state);
}
void setTextColor(const std::string& name, float r, float g, float b)
{
MyGUI::Widget* pt;
getWidget(pt, name);
MyGUI::TextBox *st = dynamic_cast<MyGUI::TextBox*>(pt);
if(st != NULL)
st->setTextColour(MyGUI::Colour(b,g,r));
}
void setImage(const std::string& name, const std::string& imgName)
{
MyGUI::ImageBox* pt;
getWidget(pt, name);
pt->setImageTexture(imgName);
}
void adjustButtonSize(MyGUI::Button* button)
{
// adjust size of button to fit its text
MyGUI::IntSize size = button->getTextSize();
button->setSize(size.width + 24, button->getSize().height);
}
protected:
MyGUI::Widget* mMainWidget;
std::string mPrefix;
std::string mLayoutName;
MyGUI::VectorWidgetPtr mListWindowRoot;
};
}}
#endif

View file

@ -0,0 +1,652 @@
#include "manager.hpp"
#include <MyGUI_Gui.h>
#include <MyGUI_OgrePlatform.h>
#include <MyGUI_Timer.h>
#include <cassert>
#include <extern/shiny/Main/Factory.hpp>
#include <extern/shiny/Platforms/Ogre/OgreMaterial.hpp>
using namespace OEngine::GUI;
namespace MyGUI
{
/*
* As of MyGUI 3.2.0, MyGUI::OgreDataManager::isDataExist is unnecessarily complex
* this override fixes the resulting performance issue.
*/
class FixedOgreDataManager : public MyGUI::OgreDataManager
{
public:
bool isDataExist(const std::string& _name)
{
return Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup (_name);
}
};
/*
* As of MyGUI 3.2.0, rendering with shaders is not supported.
* We definitely need this though to run in GL3 core / DX11 at all.
* To make matters worse, subclassing OgreRenderManager doesn't seem to be possible here. :/
*/
class ShaderBasedRenderManager : public RenderManager,
public IRenderTarget,
public Ogre::WindowEventListener,
public Ogre::RenderQueueListener,
public Ogre::RenderSystem::Listener
{
// флаг для обновления всех и вся
bool mUpdate;
IntSize mViewSize;
Ogre::SceneManager* mSceneManager;
VertexColourType mVertexFormat;
// окно, на которое мы подписываемся для изменения размеров
Ogre::RenderWindow* mWindow;
// вьюпорт, с которым работает система
unsigned short mActiveViewport;
Ogre::RenderSystem* mRenderSystem;
Ogre::TextureUnitState::UVWAddressingMode mTextureAddressMode;
Ogre::LayerBlendModeEx mColorBlendMode, mAlphaBlendMode;
RenderTargetInfo mInfo;
typedef std::map<std::string, ITexture*> MapTexture;
MapTexture mTextures;
bool mIsInitialise;
bool mManualRender;
size_t mCountBatch;
// ADDED
Ogre::GpuProgram* mVertexProgramNoTexture;
Ogre::GpuProgram* mVertexProgramOneTexture;
Ogre::GpuProgram* mFragmentProgramNoTexture;
Ogre::GpuProgram* mFragmentProgramOneTexture;
public:
ShaderBasedRenderManager& getInstance()
{
return *getInstancePtr();
}
ShaderBasedRenderManager* getInstancePtr()
{
return static_cast<ShaderBasedRenderManager*>(RenderManager::getInstancePtr());
}
ShaderBasedRenderManager() :
mUpdate(false),
mSceneManager(nullptr),
mWindow(nullptr),
mActiveViewport(0),
mRenderSystem(nullptr),
mIsInitialise(false),
mManualRender(false),
mCountBatch(0),
mVertexProgramNoTexture(NULL),
mFragmentProgramNoTexture(NULL),
mVertexProgramOneTexture(NULL),
mFragmentProgramOneTexture(NULL)
{
}
void initialise(Ogre::RenderWindow* _window, Ogre::SceneManager* _scene)
{
MYGUI_PLATFORM_ASSERT(!mIsInitialise, getClassTypeName() << " initialised twice");
MYGUI_PLATFORM_LOG(Info, "* Initialise: " << getClassTypeName());
mColorBlendMode.blendType = Ogre::LBT_COLOUR;
mColorBlendMode.source1 = Ogre::LBS_TEXTURE;
mColorBlendMode.source2 = Ogre::LBS_DIFFUSE;
mColorBlendMode.operation = Ogre::LBX_MODULATE;
mAlphaBlendMode.blendType = Ogre::LBT_ALPHA;
mAlphaBlendMode.source1 = Ogre::LBS_TEXTURE;
mAlphaBlendMode.source2 = Ogre::LBS_DIFFUSE;
mAlphaBlendMode.operation = Ogre::LBX_MODULATE;
mTextureAddressMode.u = Ogre::TextureUnitState::TAM_CLAMP;
mTextureAddressMode.v = Ogre::TextureUnitState::TAM_CLAMP;
mTextureAddressMode.w = Ogre::TextureUnitState::TAM_CLAMP;
mSceneManager = nullptr;
mWindow = nullptr;
mUpdate = false;
mRenderSystem = nullptr;
mActiveViewport = 0;
Ogre::Root* root = Ogre::Root::getSingletonPtr();
if (root != nullptr)
setRenderSystem(root->getRenderSystem());
setRenderWindow(_window);
setSceneManager(_scene);
// ADDED
sh::MaterialInstance* mat = sh::Factory::getInstance().getMaterialInstance("MyGUI/NoTexture");
sh::Factory::getInstance()._ensureMaterial("MyGUI/NoTexture", "Default");
mVertexProgramNoTexture = static_cast<sh::OgreMaterial*>(mat->getMaterial())->getOgreTechniqueForConfiguration("Default")->getPass(0)
->getVertexProgram()->_getBindingDelegate();
mat = sh::Factory::getInstance().getMaterialInstance("MyGUI/OneTexture");
sh::Factory::getInstance()._ensureMaterial("MyGUI/OneTexture", "Default");
mVertexProgramOneTexture = static_cast<sh::OgreMaterial*>(mat->getMaterial())->getOgreTechniqueForConfiguration("Default")->getPass(0)
->getVertexProgram()->_getBindingDelegate();
mat = sh::Factory::getInstance().getMaterialInstance("MyGUI/NoTexture");
sh::Factory::getInstance()._ensureMaterial("MyGUI/NoTexture", "Default");
mFragmentProgramNoTexture = static_cast<sh::OgreMaterial*>(mat->getMaterial())->getOgreTechniqueForConfiguration("Default")->getPass(0)
->getFragmentProgram()->_getBindingDelegate();
mat = sh::Factory::getInstance().getMaterialInstance("MyGUI/OneTexture");
sh::Factory::getInstance()._ensureMaterial("MyGUI/OneTexture", "Default");
mFragmentProgramOneTexture = static_cast<sh::OgreMaterial*>(mat->getMaterial())->getOgreTechniqueForConfiguration("Default")->getPass(0)
->getFragmentProgram()->_getBindingDelegate();
MYGUI_PLATFORM_LOG(Info, getClassTypeName() << " successfully initialized");
mIsInitialise = true;
}
void shutdown()
{
MYGUI_PLATFORM_ASSERT(mIsInitialise, getClassTypeName() << " is not initialised");
MYGUI_PLATFORM_LOG(Info, "* Shutdown: " << getClassTypeName());
destroyAllResources();
setSceneManager(nullptr);
setRenderWindow(nullptr);
setRenderSystem(nullptr);
MYGUI_PLATFORM_LOG(Info, getClassTypeName() << " successfully shutdown");
mIsInitialise = false;
}
void setRenderSystem(Ogre::RenderSystem* _render)
{
// отписываемся
if (mRenderSystem != nullptr)
{
mRenderSystem->removeListener(this);
mRenderSystem = nullptr;
}
mRenderSystem = _render;
// подписываемся на рендер евент
if (mRenderSystem != nullptr)
{
mRenderSystem->addListener(this);
// формат цвета в вершинах
Ogre::VertexElementType vertex_type = mRenderSystem->getColourVertexElementType();
if (vertex_type == Ogre::VET_COLOUR_ARGB)
mVertexFormat = VertexColourType::ColourARGB;
else if (vertex_type == Ogre::VET_COLOUR_ABGR)
mVertexFormat = VertexColourType::ColourABGR;
updateRenderInfo();
}
}
Ogre::RenderSystem* getRenderSystem()
{
return mRenderSystem;
}
void setRenderWindow(Ogre::RenderWindow* _window)
{
// отписываемся
if (mWindow != nullptr)
{
Ogre::WindowEventUtilities::removeWindowEventListener(mWindow, this);
mWindow = nullptr;
}
mWindow = _window;
if (mWindow != nullptr)
{
Ogre::WindowEventUtilities::addWindowEventListener(mWindow, this);
windowResized(mWindow);
}
}
void setSceneManager(Ogre::SceneManager* _scene)
{
if (nullptr != mSceneManager)
{
mSceneManager->removeRenderQueueListener(this);
mSceneManager = nullptr;
}
mSceneManager = _scene;
if (nullptr != mSceneManager)
{
mSceneManager->addRenderQueueListener(this);
}
}
void setActiveViewport(unsigned short _num)
{
mActiveViewport = _num;
if (mWindow != nullptr)
{
Ogre::WindowEventUtilities::removeWindowEventListener(mWindow, this);
Ogre::WindowEventUtilities::addWindowEventListener(mWindow, this);
// рассылка обновлений
windowResized(mWindow);
}
}
void renderQueueStarted(Ogre::uint8 queueGroupId, const Ogre::String& invocation, bool& skipThisInvocation)
{
Gui* gui = Gui::getInstancePtr();
if (gui == nullptr)
return;
if (Ogre::RENDER_QUEUE_OVERLAY != queueGroupId)
return;
Ogre::Viewport* viewport = mSceneManager->getCurrentViewport();
if (nullptr == viewport
|| !viewport->getOverlaysEnabled())
return;
if (mWindow->getNumViewports() <= mActiveViewport
|| viewport != mWindow->getViewport(mActiveViewport))
return;
mCountBatch = 0;
static Timer timer;
static unsigned long last_time = timer.getMilliseconds();
unsigned long now_time = timer.getMilliseconds();
unsigned long time = now_time - last_time;
onFrameEvent((float)((double)(time) / (double)1000));
last_time = now_time;
//begin();
setManualRender(true);
onRenderToTarget(this, mUpdate);
//end();
// сбрасываем флаг
mUpdate = false;
}
void renderQueueEnded(Ogre::uint8 queueGroupId, const Ogre::String& invocation, bool& repeatThisInvocation)
{
}
void eventOccurred(const Ogre::String& eventName, const Ogre::NameValuePairList* parameters)
{
if (eventName == "DeviceLost")
{
}
else if (eventName == "DeviceRestored")
{
// обновить всех
mUpdate = true;
}
}
IVertexBuffer* createVertexBuffer()
{
return new OgreVertexBuffer();
}
void destroyVertexBuffer(IVertexBuffer* _buffer)
{
delete _buffer;
}
// для оповещений об изменении окна рендера
void windowResized(Ogre::RenderWindow* _window)
{
if (_window->getNumViewports() > mActiveViewport)
{
Ogre::Viewport* port = _window->getViewport(mActiveViewport);
#if OGRE_VERSION >= MYGUI_DEFINE_VERSION(1, 7, 0) && OGRE_NO_VIEWPORT_ORIENTATIONMODE == 0
Ogre::OrientationMode orient = port->getOrientationMode();
if (orient == Ogre::OR_DEGREE_90 || orient == Ogre::OR_DEGREE_270)
mViewSize.set(port->getActualHeight(), port->getActualWidth());
else
mViewSize.set(port->getActualWidth(), port->getActualHeight());
#else
mViewSize.set(port->getActualWidth(), port->getActualHeight());
#endif
// обновить всех
mUpdate = true;
updateRenderInfo();
onResizeView(mViewSize);
}
}
void updateRenderInfo()
{
if (mRenderSystem != nullptr)
{
mInfo.maximumDepth = mRenderSystem->getMaximumDepthInputValue();
mInfo.hOffset = mRenderSystem->getHorizontalTexelOffset() / float(mViewSize.width);
mInfo.vOffset = mRenderSystem->getVerticalTexelOffset() / float(mViewSize.height);
mInfo.aspectCoef = float(mViewSize.height) / float(mViewSize.width);
mInfo.pixScaleX = 1.0f / float(mViewSize.width);
mInfo.pixScaleY = 1.0f / float(mViewSize.height);
}
}
void doRender(IVertexBuffer* _buffer, ITexture* _texture, size_t _count)
{
if (getManualRender())
{
begin();
setManualRender(false);
}
// ADDED
if (_texture)
{
Ogre::Root::getSingleton().getRenderSystem()->bindGpuProgram(mVertexProgramOneTexture);
Ogre::Root::getSingleton().getRenderSystem()->bindGpuProgram(mFragmentProgramOneTexture);
}
else
{
Ogre::Root::getSingleton().getRenderSystem()->bindGpuProgram(mVertexProgramNoTexture);
Ogre::Root::getSingleton().getRenderSystem()->bindGpuProgram(mFragmentProgramNoTexture);
}
if (_texture)
{
OgreTexture* texture = static_cast<OgreTexture*>(_texture);
Ogre::TexturePtr texture_ptr = texture->getOgreTexture();
if (!texture_ptr.isNull())
{
mRenderSystem->_setTexture(0, true, texture_ptr);
mRenderSystem->_setTextureUnitFiltering(0, Ogre::FO_LINEAR, Ogre::FO_LINEAR, Ogre::FO_NONE);
}
}
OgreVertexBuffer* buffer = static_cast<OgreVertexBuffer*>(_buffer);
Ogre::RenderOperation* operation = buffer->getRenderOperation();
operation->vertexData->vertexCount = _count;
mRenderSystem->_render(*operation);
++ mCountBatch;
}
void begin()
{
// set-up matrices
mRenderSystem->_setWorldMatrix(Ogre::Matrix4::IDENTITY);
mRenderSystem->_setViewMatrix(Ogre::Matrix4::IDENTITY);
#if OGRE_VERSION >= MYGUI_DEFINE_VERSION(1, 7, 0) && OGRE_NO_VIEWPORT_ORIENTATIONMODE == 0
Ogre::OrientationMode orient = mWindow->getViewport(mActiveViewport)->getOrientationMode();
mRenderSystem->_setProjectionMatrix(Ogre::Matrix4::IDENTITY * Ogre::Quaternion(Ogre::Degree(orient * 90.f), Ogre::Vector3::UNIT_Z));
#else
mRenderSystem->_setProjectionMatrix(Ogre::Matrix4::IDENTITY);
#endif
// initialise render settings
mRenderSystem->setLightingEnabled(false);
mRenderSystem->_setDepthBufferParams(false, false);
mRenderSystem->_setDepthBias(0, 0);
mRenderSystem->_setCullingMode(Ogre::CULL_NONE);
mRenderSystem->_setFog(Ogre::FOG_NONE);
mRenderSystem->_setColourBufferWriteEnabled(true, true, true, true);
mRenderSystem->unbindGpuProgram(Ogre::GPT_FRAGMENT_PROGRAM);
mRenderSystem->unbindGpuProgram(Ogre::GPT_VERTEX_PROGRAM);
mRenderSystem->setShadingType(Ogre::SO_GOURAUD);
// initialise texture settings
mRenderSystem->_setTextureCoordCalculation(0, Ogre::TEXCALC_NONE);
mRenderSystem->_setTextureCoordSet(0, 0);
mRenderSystem->_setTextureUnitFiltering(0, Ogre::FO_LINEAR, Ogre::FO_LINEAR, Ogre::FO_NONE);
mRenderSystem->_setTextureAddressingMode(0, mTextureAddressMode);
mRenderSystem->_setTextureMatrix(0, Ogre::Matrix4::IDENTITY);
#if OGRE_VERSION < MYGUI_DEFINE_VERSION(1, 6, 0)
mRenderSystem->_setAlphaRejectSettings(Ogre::CMPF_ALWAYS_PASS, 0);
#else
mRenderSystem->_setAlphaRejectSettings(Ogre::CMPF_ALWAYS_PASS, 0, false);
#endif
mRenderSystem->_setTextureBlendMode(0, mColorBlendMode);
mRenderSystem->_setTextureBlendMode(0, mAlphaBlendMode);
mRenderSystem->_disableTextureUnitsFrom(1);
// enable alpha blending
mRenderSystem->_setSceneBlending(Ogre::SBF_SOURCE_ALPHA, Ogre::SBF_ONE_MINUS_SOURCE_ALPHA);
// always use wireframe
// TODO: add option to enable wireframe mode in platform
mRenderSystem->_setPolygonMode(Ogre::PM_SOLID);
}
void end()
{
}
ITexture* createTexture(const std::string& _name)
{
MapTexture::const_iterator item = mTextures.find(_name);
MYGUI_PLATFORM_ASSERT(item == mTextures.end(), "Texture '" << _name << "' already exist");
OgreTexture* texture = new OgreTexture(_name, OgreDataManager::getInstance().getGroup());
mTextures[_name] = texture;
return texture;
}
void destroyTexture(ITexture* _texture)
{
if (_texture == nullptr) return;
MapTexture::iterator item = mTextures.find(_texture->getName());
MYGUI_PLATFORM_ASSERT(item != mTextures.end(), "Texture '" << _texture->getName() << "' not found");
mTextures.erase(item);
delete _texture;
}
ITexture* getTexture(const std::string& _name)
{
MapTexture::const_iterator item = mTextures.find(_name);
if (item == mTextures.end())
{
Ogre::TexturePtr texture = (Ogre::TexturePtr)Ogre::TextureManager::getSingleton().getByName(_name);
if (!texture.isNull())
{
ITexture* result = createTexture(_name);
static_cast<OgreTexture*>(result)->setOgreTexture(texture);
return result;
}
return nullptr;
}
return item->second;
}
bool isFormatSupported(PixelFormat _format, TextureUsage _usage)
{
return Ogre::TextureManager::getSingleton().isFormatSupported(
Ogre::TEX_TYPE_2D,
OgreTexture::convertFormat(_format),
OgreTexture::convertUsage(_usage));
}
void destroyAllResources()
{
for (MapTexture::const_iterator item = mTextures.begin(); item != mTextures.end(); ++item)
{
delete item->second;
}
mTextures.clear();
}
#if MYGUI_DEBUG_MODE == 1
bool checkTexture(ITexture* _texture)
{
for (MapTexture::const_iterator item = mTextures.begin(); item != mTextures.end(); ++item)
{
if (item->second == _texture)
return true;
}
return false;
}
#endif
const IntSize& getViewSize() const
{
return mViewSize;
}
VertexColourType getVertexFormat()
{
return mVertexFormat;
}
const RenderTargetInfo& getInfo()
{
return mInfo;
}
size_t getActiveViewport()
{
return mActiveViewport;
}
Ogre::RenderWindow* getRenderWindow()
{
return mWindow;
}
bool getManualRender()
{
return mManualRender;
}
void setManualRender(bool _value)
{
mManualRender = _value;
}
size_t getBatchCount() const
{
return mCountBatch;
}
};
}
void MyGUIManager::setup(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool logging, const std::string& logDir)
{
assert(wnd);
assert(mgr);
mSceneMgr = mgr;
mShaderRenderManager = NULL;
mRenderManager = NULL;
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.
std::string theLogFile = std::string(MYGUI_PLATFORM_LOG_FILENAME);
if(!logDir.empty())
theLogFile.insert(0, logDir);
// Set up OGRE platform (bypassing OgrePlatform). We might make this more generic later.
mLogManager = new LogManager();
if (!Ogre::Root::getSingleton().getRenderSystem()->getCapabilities()->hasCapability(Ogre::RSC_FIXED_FUNCTION))
mShaderRenderManager = new MyGUI::ShaderBasedRenderManager();
else
mRenderManager = new MyGUI::OgreRenderManager();
mDataManager = new MyGUI::FixedOgreDataManager();
LogManager::getInstance().setSTDOutputEnabled(logging);
if (!theLogFile.empty())
LogManager::getInstance().createDefaultSource(theLogFile);
if (mShaderRenderManager)
mShaderRenderManager->initialise(wnd, mgr);
else
mRenderManager->initialise(wnd, mgr);
mDataManager->initialise("General");
// Create GUI
mGui = new Gui();
mGui->initialise("");
}
void MyGUIManager::updateWindow (Ogre::RenderWindow *wnd)
{
if (mShaderRenderManager)
{
mShaderRenderManager->setRenderWindow (wnd);
mShaderRenderManager->setActiveViewport(0);
}
else
{
mRenderManager->setRenderWindow (wnd);
mRenderManager->setActiveViewport(0);
}
}
void MyGUIManager::windowResized()
{
mRenderManager->setActiveViewport(0);
}
void MyGUIManager::shutdown()
{
mGui->shutdown ();
delete mGui;
if(mRenderManager)
{
mRenderManager->shutdown();
delete mRenderManager;
mRenderManager = NULL;
}
if(mShaderRenderManager)
{
mShaderRenderManager->shutdown();
delete mShaderRenderManager;
mShaderRenderManager = NULL;
}
if(mDataManager)
{
mDataManager->shutdown();
delete mDataManager;
mDataManager = NULL;
}
if (mLogManager)
{
delete mLogManager;
mLogManager = NULL;
}
mGui = NULL;
}

View file

@ -0,0 +1,55 @@
#ifndef OENGINE_MYGUI_MANAGER_H
#define OENGINE_MYGUI_MANAGER_H
#include <string>
namespace MyGUI
{
class Gui;
class LogManager;
class OgreDataManager;
class OgreRenderManager;
class ShaderBasedRenderManager;
}
namespace Ogre
{
class RenderWindow;
class SceneManager;
}
namespace OEngine {
namespace GUI
{
class MyGUIManager
{
MyGUI::Gui *mGui;
MyGUI::LogManager* mLogManager;
MyGUI::OgreDataManager* mDataManager;
MyGUI::OgreRenderManager* mRenderManager;
MyGUI::ShaderBasedRenderManager* mShaderRenderManager;
Ogre::SceneManager* mSceneMgr;
public:
MyGUIManager(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool logging=false, const std::string& logDir = std::string(""))
{
setup(wnd,mgr,logging, logDir);
}
~MyGUIManager()
{
shutdown();
}
void updateWindow (Ogre::RenderWindow* wnd);
void windowResized();
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

View file

@ -0,0 +1,178 @@
#ifndef MISC_LIST_H
#define MISC_LIST_H
#include <cassert>
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

View file

@ -0,0 +1,135 @@
#include "fader.hpp"
#include <OgreMaterial.h>
#include <OgreTechnique.h>
#include <OgreMaterialManager.h>
#include <OgreResourceGroupManager.h>
#include <OgreRectangle2D.h>
#include <OgreSceneManager.h>
using namespace Ogre;
using namespace OEngine::Render;
Fader::Fader(Ogre::SceneManager* sceneMgr)
: mSceneMgr(sceneMgr)
, mMode(FadingMode_In)
, mRemainingTime(0.f)
, mTargetTime(0.f)
, mTargetAlpha(0.f)
, mCurrentAlpha(0.f)
, mStartAlpha(0.f)
, mFactor(1.f)
{
// Create the fading material
MaterialPtr material = MaterialManager::getSingleton().create("FadeInOutMaterial", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME );
Pass* pass = material->getTechnique(0)->getPass(0);
pass->setSceneBlending(SBT_TRANSPARENT_ALPHA);
pass->setDepthWriteEnabled (false);
mFadeTextureUnit = pass->createTextureUnitState("black.png");
mFadeTextureUnit->setColourOperationEx(LBX_SOURCE1, LBS_MANUAL, LBS_CURRENT, ColourValue(0.f, 0.f, 0.f)); // always black colour
mRectangle = new Ogre::Rectangle2D(true);
mRectangle->setCorners(-1.0, 1.0, 1.0, -1.0);
mRectangle->setMaterial("FadeInOutMaterial");
mRectangle->setRenderQueueGroup(Ogre::RENDER_QUEUE_OVERLAY-1);
// Use infinite AAB to always stay visible
Ogre::AxisAlignedBox aabInf;
aabInf.setInfinite();
mRectangle->setBoundingBox(aabInf);
// Attach background to the scene
Ogre::SceneNode* node = mSceneMgr->getRootSceneNode()->createChildSceneNode();
node->attachObject(mRectangle);
mRectangle->setVisible(false);
mRectangle->setVisibilityFlags (2048);
}
Fader::~Fader()
{
delete mRectangle;
}
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;
}
mRemainingTime -= dt;
}
if (1.f-((1.f-mCurrentAlpha) * mFactor) == 0.f)
mRectangle->setVisible(false);
else
applyAlpha();
}
void Fader::applyAlpha()
{
mRectangle->setVisible(true);
mFadeTextureUnit->setAlphaOperation(LBX_SOURCE1, LBS_MANUAL, LBS_CURRENT, 1.f-((1.f-mCurrentAlpha) * mFactor));
}
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;
}

View file

@ -0,0 +1,59 @@
#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 Rectangle2d
*/
namespace Ogre
{
class TextureUnitState;
class Rectangle2D;
class SceneManager;
}
namespace OEngine {
namespace Render
{
class Fader
{
public:
Fader(Ogre::SceneManager* sceneMgr);
~Fader();
void update(float dt);
void fadeIn(const float time);
void fadeOut(const float time);
void fadeTo(const int percent, const float time);
void setFactor (float factor) { mFactor = factor; }
private:
enum FadingMode
{
FadingMode_In,
FadingMode_Out
};
void applyAlpha();
Ogre::TextureUnitState* mFadeTextureUnit;
Ogre::Rectangle2D* mRectangle;
FadingMode mMode;
float mRemainingTime;
float mTargetTime;
float mTargetAlpha;
float mCurrentAlpha;
float mStartAlpha;
float mFactor;
Ogre::SceneManager* mSceneMgr;
};
}}
#endif

View file

@ -0,0 +1,88 @@
#include "imagerotate.hpp"
#include <OgreRoot.h>
#include <OgreSceneManager.h>
#include <OgreImage.h>
#include <OgreTexture.h>
#include <OgreRenderTarget.h>
#include <OgreCamera.h>
#include <OgreTextureUnitState.h>
#include <OgreHardwarePixelBuffer.h>
using namespace Ogre;
using namespace OEngine::Render;
void ImageRotate::rotate(const std::string& sourceImage, const std::string& destImage, const float angle)
{
Root* root = Ogre::Root::getSingletonPtr();
std::string destImageRot = std::string(destImage) + std::string("_rot");
SceneManager* sceneMgr = root->createSceneManager(ST_GENERIC);
Camera* camera = sceneMgr->createCamera("ImageRotateCamera");
MaterialPtr material = MaterialManager::getSingleton().create("ImageRotateMaterial", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
material->getTechnique(0)->getPass(0)->setLightingEnabled(false);
material->getTechnique(0)->getPass(0)->setDepthCheckEnabled(false);
TextureUnitState* tus = material->getTechnique(0)->getPass(0)->createTextureUnitState(sourceImage);
Degree deg(angle);
tus->setTextureRotate(Radian(deg.valueRadians()));
tus->setTextureAddressingMode(TextureUnitState::TAM_BORDER);
tus->setTextureBorderColour(ColourValue(0, 0, 0, 0));
Rectangle2D* rect = new Rectangle2D(true);
rect->setCorners(-1.0, 1.0, 1.0, -1.0);
rect->setMaterial("ImageRotateMaterial");
// Render the background before everything else
rect->setRenderQueueGroup(RENDER_QUEUE_BACKGROUND);
// Use infinite AAB to always stay visible
AxisAlignedBox aabInf;
aabInf.setInfinite();
rect->setBoundingBox(aabInf);
// Attach background to the scene
SceneNode* node = sceneMgr->getRootSceneNode()->createChildSceneNode();
node->attachObject(rect);
// retrieve image width and height
TexturePtr sourceTexture = TextureManager::getSingleton().getByName(sourceImage);
unsigned int width = sourceTexture->getWidth();
unsigned int height = sourceTexture->getHeight();
TexturePtr destTextureRot = TextureManager::getSingleton().createManual(
destImageRot,
ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
TEX_TYPE_2D,
width, height,
0,
PF_A8B8G8R8,
TU_RENDERTARGET);
RenderTarget* rtt = destTextureRot->getBuffer()->getRenderTarget();
rtt->setAutoUpdated(false);
Viewport* vp = rtt->addViewport(camera);
vp->setOverlaysEnabled(false);
vp->setShadowsEnabled(false);
vp->setBackgroundColour(ColourValue(0,0,0,0));
rtt->update();
//copy the rotated image to a static texture
TexturePtr destTexture = TextureManager::getSingleton().createManual(
destImage,
ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
TEX_TYPE_2D,
width, height,
0,
PF_A8B8G8R8,
Ogre::TU_STATIC);
destTexture->getBuffer()->blit(destTextureRot->getBuffer());
// remove all the junk we've created
TextureManager::getSingleton().remove(destImageRot);
MaterialManager::getSingleton().remove("ImageRotateMaterial");
root->destroySceneManager(sceneMgr);
delete rect;
}

View file

@ -0,0 +1,27 @@
#ifndef OENGINE_OGRE_IMAGEROTATE_HPP
#define OENGINE_OGRE_IMAGEROTATE_HPP
#include <string>
namespace OEngine
{
namespace Render
{
/// Rotate an image by certain degrees and save as file, uses the GPU
/// Make sure Ogre Root is initialised before calling
class ImageRotate
{
public:
/**
* @param source image (file name - has to exist in an resource group)
* @param name of the destination texture to save to (in memory)
* @param angle in degrees to turn
*/
static void rotate(const std::string& sourceImage, const std::string& destImage, const float angle);
};
}
}
#endif

View file

@ -0,0 +1,118 @@
#include "lights.hpp"
#include <OgreLight.h>
#include <OgreMath.h>
namespace OEngine {
namespace Render {
LightFunction::LightFunction(LightType type)
: ControllerFunction<Ogre::Real>(true)
, mType(type)
, mPhase(Ogre::Math::RangeRandom(-500.0f, +500.0f))
, mDirection(1.0f)
{
}
Ogre::Real LightFunction::pulseAmplitude(Ogre::Real time)
{
return std::sin(time);
}
Ogre::Real LightFunction::flickerAmplitude(Ogre::Real time)
{
static const float fb = 1.17024f;
static const float f[3] = { 1.5708f, 4.18774f, 5.19934f };
static const float o[3] = { 0.804248f, 2.11115f, 3.46832f };
static const float m[3] = { 1.0f, 0.785f, 0.876f };
static const float s = 0.394f;
float v = 0.0f;
for(int i = 0;i < 3;++i)
v += std::sin(fb*time*f[i] + o[1])*m[i];
return v * s;
}
Ogre::Real LightFunction::flickerFrequency(Ogre::Real phase)
{
static const float fa = 0.785398f;
static const float tdo = 0.94f;
static const float tdm = 2.48f;
return tdo + tdm*std::sin(fa * phase);
}
Ogre::Real LightFunction::calculate(Ogre::Real value)
{
Ogre::Real brightness = 1.0f;
float cycle_time;
float time_distortion;
if(mType == LT_Pulse || mType == LT_PulseSlow)
{
cycle_time = 2.0f * Ogre::Math::PI;
time_distortion = 20.0f;
}
else
{
static const float fa = 0.785398f;
static const float phase_wavelength = 120.0f * 3.14159265359f / fa;
cycle_time = 500.0f;
mPhase = std::fmod(mPhase + value, phase_wavelength);
time_distortion = flickerFrequency(mPhase);
}
mDeltaCount += mDirection*value*time_distortion;
if(mDirection > 0 && mDeltaCount > +cycle_time)
{
mDirection = -1.0f;
mDeltaCount = 2.0f*cycle_time - mDeltaCount;
}
if(mDirection < 0 && mDeltaCount < -cycle_time)
{
mDirection = +1.0f;
mDeltaCount = -2.0f*cycle_time - mDeltaCount;
}
static const float fast = 4.0f/1.0f;
static const float slow = 1.0f/1.0f;
// These formulas are just guesswork, but they work pretty well
if(mType == LT_Normal)
{
// Less than 1/255 light modifier for a constant light:
brightness = 1.0 + flickerAmplitude(mDeltaCount*slow)/255.0f;
}
else if(mType == LT_Flicker)
brightness = 0.75 + flickerAmplitude(mDeltaCount*fast)*0.25;
else if(mType == LT_FlickerSlow)
brightness = 0.75 + flickerAmplitude(mDeltaCount*slow)*0.25;
else if(mType == LT_Pulse)
brightness = 1.0 + pulseAmplitude(mDeltaCount*fast)*0.25;
else if(mType == LT_PulseSlow)
brightness = 1.0 + pulseAmplitude(mDeltaCount*slow)*0.25;
return brightness;
}
LightValue::LightValue(Ogre::Light *light, const Ogre::ColourValue &color)
: mTarget(light)
, mColor(color)
{
}
Ogre::Real LightValue::getValue() const
{
return 0.0f;
}
void LightValue::setValue(Ogre::Real value)
{
mTarget->setDiffuseColour(mColor * value);
}
}
}

View file

@ -0,0 +1,51 @@
#ifndef OENGINE_OGRE_LIGHTS_H
#define OENGINE_OGRE_LIGHTS_H
#include <OgreController.h>
#include <OgreColourValue.h>
/*
* Controller classes to handle pulsing and flicker lights
*/
namespace OEngine {
namespace Render {
enum LightType {
LT_Normal,
LT_Flicker,
LT_FlickerSlow,
LT_Pulse,
LT_PulseSlow
};
class LightFunction : public Ogre::ControllerFunction<Ogre::Real>
{
LightType mType;
Ogre::Real mPhase;
Ogre::Real mDirection;
static Ogre::Real pulseAmplitude(Ogre::Real time);
static Ogre::Real flickerAmplitude(Ogre::Real time);
static Ogre::Real flickerFrequency(Ogre::Real phase);
public:
LightFunction(LightType type);
virtual Ogre::Real calculate(Ogre::Real value);
};
class LightValue : public Ogre::ControllerValue<Ogre::Real>
{
Ogre::Light *mTarget;
Ogre::ColourValue mColor;
public:
LightValue(Ogre::Light *light, const Ogre::ColourValue &color);
virtual Ogre::Real getValue() const;
virtual void setValue(Ogre::Real value);
};
}
}
#endif

View file

@ -0,0 +1,693 @@
#include "particles.hpp"
#include <OgreStringConverter.h>
#include <OgreParticleSystem.h>
#include <OgreParticleEmitter.h>
#include <OgreParticleAffector.h>
#include <OgreParticle.h>
/* FIXME: "Nif" isn't really an appropriate emitter name. */
class NifEmitter : public Ogre::ParticleEmitter
{
public:
/** Command object for the emitter width (see Ogre::ParamCommand).*/
class CmdWidth : public Ogre::ParamCommand
{
public:
Ogre::String doGet(const void *target) const
{
return Ogre::StringConverter::toString(static_cast<const NifEmitter*>(target)->getWidth());
}
void doSet(void *target, const Ogre::String &val)
{
static_cast<NifEmitter*>(target)->setWidth(Ogre::StringConverter::parseReal(val));
}
};
/** Command object for the emitter height (see Ogre::ParamCommand).*/
class CmdHeight : public Ogre::ParamCommand
{
public:
Ogre::String doGet(const void *target) const
{
return Ogre::StringConverter::toString(static_cast<const NifEmitter*>(target)->getHeight());
}
void doSet(void *target, const Ogre::String &val)
{
static_cast<NifEmitter*>(target)->setHeight(Ogre::StringConverter::parseReal(val));
}
};
/** Command object for the emitter depth (see Ogre::ParamCommand).*/
class CmdDepth : public Ogre::ParamCommand
{
public:
Ogre::String doGet(const void *target) const
{
return Ogre::StringConverter::toString(static_cast<const NifEmitter*>(target)->getDepth());
}
void doSet(void *target, const Ogre::String &val)
{
static_cast<NifEmitter*>(target)->setDepth(Ogre::StringConverter::parseReal(val));
}
};
/** Command object for the emitter vertical_direction (see Ogre::ParamCommand).*/
class CmdVerticalDir : public Ogre::ParamCommand
{
public:
Ogre::String doGet(const void *target) const
{
const NifEmitter *self = static_cast<const NifEmitter*>(target);
return Ogre::StringConverter::toString(self->getVerticalDirection().valueDegrees());
}
void doSet(void *target, const Ogre::String &val)
{
NifEmitter *self = static_cast<NifEmitter*>(target);
self->setVerticalDirection(Ogre::Degree(Ogre::StringConverter::parseReal(val)));
}
};
/** Command object for the emitter vertical_angle (see Ogre::ParamCommand).*/
class CmdVerticalAngle : public Ogre::ParamCommand
{
public:
Ogre::String doGet(const void *target) const
{
const NifEmitter *self = static_cast<const NifEmitter*>(target);
return Ogre::StringConverter::toString(self->getVerticalAngle().valueDegrees());
}
void doSet(void *target, const Ogre::String &val)
{
NifEmitter *self = static_cast<NifEmitter*>(target);
self->setVerticalAngle(Ogre::Degree(Ogre::StringConverter::parseReal(val)));
}
};
/** Command object for the emitter horizontal_direction (see Ogre::ParamCommand).*/
class CmdHorizontalDir : public Ogre::ParamCommand
{
public:
Ogre::String doGet(const void *target) const
{
const NifEmitter *self = static_cast<const NifEmitter*>(target);
return Ogre::StringConverter::toString(self->getHorizontalDirection().valueDegrees());
}
void doSet(void *target, const Ogre::String &val)
{
NifEmitter *self = static_cast<NifEmitter*>(target);
self->setHorizontalDirection(Ogre::Degree(Ogre::StringConverter::parseReal(val)));
}
};
/** Command object for the emitter horizontal_angle (see Ogre::ParamCommand).*/
class CmdHorizontalAngle : public Ogre::ParamCommand
{
public:
Ogre::String doGet(const void *target) const
{
const NifEmitter *self = static_cast<const NifEmitter*>(target);
return Ogre::StringConverter::toString(self->getHorizontalAngle().valueDegrees());
}
void doSet(void *target, const Ogre::String &val)
{
NifEmitter *self = static_cast<NifEmitter*>(target);
self->setHorizontalAngle(Ogre::Degree(Ogre::StringConverter::parseReal(val)));
}
};
NifEmitter(Ogre::ParticleSystem *psys)
: Ogre::ParticleEmitter(psys)
{
initDefaults("Nif");
}
/** See Ogre::ParticleEmitter. */
unsigned short _getEmissionCount(Ogre::Real timeElapsed)
{
// Use basic constant emission
return genConstantEmissionCount(timeElapsed);
}
/** See Ogre::ParticleEmitter. */
void _initParticle(Ogre::Particle *particle)
{
Ogre::Vector3 xOff, yOff, zOff;
// Call superclass
ParticleEmitter::_initParticle(particle);
xOff = Ogre::Math::SymmetricRandom() * mXRange;
yOff = Ogre::Math::SymmetricRandom() * mYRange;
zOff = Ogre::Math::SymmetricRandom() * mZRange;
particle->position = mPosition + xOff + yOff + zOff;
// Generate complex data by reference
genEmissionColour(particle->colour);
// NOTE: We do not use mDirection/mAngle for the initial direction.
Ogre::Radian hdir = mHorizontalDir + mHorizontalAngle*Ogre::Math::SymmetricRandom();
Ogre::Radian vdir = mVerticalDir + mVerticalAngle*Ogre::Math::SymmetricRandom();
particle->direction = (Ogre::Quaternion(hdir, Ogre::Vector3::UNIT_Z) *
Ogre::Quaternion(vdir, Ogre::Vector3::UNIT_X)) *
Ogre::Vector3::UNIT_Z;
genEmissionVelocity(particle->direction);
// Generate simpler data
particle->timeToLive = particle->totalTimeToLive = genEmissionTTL();
}
/** Overloaded to update the trans. matrix */
void setDirection(const Ogre::Vector3 &dir)
{
ParticleEmitter::setDirection(dir);
genAreaAxes();
}
/** Sets the size of the area from which particles are emitted.
@param
size Vector describing the size of the area. The area extends
around the center point by half the x, y and z components of
this vector. The box is aligned such that it's local Z axis points
along it's direction (see setDirection)
*/
void setSize(const Ogre::Vector3 &size)
{
mSize = size;
genAreaAxes();
}
/** Sets the size of the area from which particles are emitted.
@param x,y,z
Individual axis lengths describing the size of the area. The area
extends around the center point by half the x, y and z components
of this vector. The box is aligned such that it's local Z axis
points along it's direction (see setDirection)
*/
void setSize(Ogre::Real x, Ogre::Real y, Ogre::Real z)
{
mSize.x = x;
mSize.y = y;
mSize.z = z;
genAreaAxes();
}
/** Sets the width (local x size) of the emitter. */
void setWidth(Ogre::Real width)
{
mSize.x = width;
genAreaAxes();
}
/** Gets the width (local x size) of the emitter. */
Ogre::Real getWidth(void) const
{ return mSize.x; }
/** Sets the height (local y size) of the emitter. */
void setHeight(Ogre::Real height)
{
mSize.y = height;
genAreaAxes();
}
/** Gets the height (local y size) of the emitter. */
Ogre::Real getHeight(void) const
{ return mSize.y; }
/** Sets the depth (local y size) of the emitter. */
void setDepth(Ogre::Real depth)
{
mSize.z = depth;
genAreaAxes();
}
/** Gets the depth (local y size) of the emitter. */
Ogre::Real getDepth(void) const
{ return mSize.z; }
void setVerticalDirection(Ogre::Radian vdir)
{ mVerticalDir = vdir; }
Ogre::Radian getVerticalDirection(void) const
{ return mVerticalDir; }
void setVerticalAngle(Ogre::Radian vangle)
{ mVerticalAngle = vangle; }
Ogre::Radian getVerticalAngle(void) const
{ return mVerticalAngle; }
void setHorizontalDirection(Ogre::Radian hdir)
{ mHorizontalDir = hdir; }
Ogre::Radian getHorizontalDirection(void) const
{ return mHorizontalDir; }
void setHorizontalAngle(Ogre::Radian hangle)
{ mHorizontalAngle = hangle; }
Ogre::Radian getHorizontalAngle(void) const
{ return mHorizontalAngle; }
protected:
/// Size of the area
Ogre::Vector3 mSize;
Ogre::Radian mVerticalDir;
Ogre::Radian mVerticalAngle;
Ogre::Radian mHorizontalDir;
Ogre::Radian mHorizontalAngle;
/// Local axes, not normalised, their magnitude reflects area size
Ogre::Vector3 mXRange, mYRange, mZRange;
/// Internal method for generating the area axes
void genAreaAxes(void)
{
Ogre::Vector3 mLeft = mUp.crossProduct(mDirection);
mXRange = mLeft * (mSize.x * 0.5f);
mYRange = mUp * (mSize.y * 0.5f);
mZRange = mDirection * (mSize.z * 0.5f);
}
/** Internal for initializing some defaults and parameters
@return True if custom parameters need initialising
*/
bool initDefaults(const Ogre::String &t)
{
// Defaults
mDirection = Ogre::Vector3::UNIT_Z;
mUp = Ogre::Vector3::UNIT_Y;
setSize(100.0f, 100.0f, 100.0f);
mType = t;
// Set up parameters
if(createParamDictionary(mType + "Emitter"))
{
addBaseParameters();
Ogre::ParamDictionary *dict = getParamDictionary();
// Custom params
dict->addParameter(Ogre::ParameterDef("width",
"Width of the shape in world coordinates.",
Ogre::PT_REAL),
&msWidthCmd);
dict->addParameter(Ogre::ParameterDef("height",
"Height of the shape in world coordinates.",
Ogre::PT_REAL),
&msHeightCmd);
dict->addParameter(Ogre::ParameterDef("depth",
"Depth of the shape in world coordinates.",
Ogre::PT_REAL),
&msDepthCmd);
dict->addParameter(Ogre::ParameterDef("vertical_direction",
"Vertical direction of emitted particles (in degrees).",
Ogre::PT_REAL),
&msVerticalDirCmd);
dict->addParameter(Ogre::ParameterDef("vertical_angle",
"Vertical direction variance of emitted particles (in degrees).",
Ogre::PT_REAL),
&msVerticalAngleCmd);
dict->addParameter(Ogre::ParameterDef("horizontal_direction",
"Horizontal direction of emitted particles (in degrees).",
Ogre::PT_REAL),
&msHorizontalDirCmd);
dict->addParameter(Ogre::ParameterDef("horizontal_angle",
"Horizontal direction variance of emitted particles (in degrees).",
Ogre::PT_REAL),
&msHorizontalAngleCmd);
return true;
}
return false;
}
/// Command objects
static CmdWidth msWidthCmd;
static CmdHeight msHeightCmd;
static CmdDepth msDepthCmd;
static CmdVerticalDir msVerticalDirCmd;
static CmdVerticalAngle msVerticalAngleCmd;
static CmdHorizontalDir msHorizontalDirCmd;
static CmdHorizontalAngle msHorizontalAngleCmd;
};
NifEmitter::CmdWidth NifEmitter::msWidthCmd;
NifEmitter::CmdHeight NifEmitter::msHeightCmd;
NifEmitter::CmdDepth NifEmitter::msDepthCmd;
NifEmitter::CmdVerticalDir NifEmitter::msVerticalDirCmd;
NifEmitter::CmdVerticalAngle NifEmitter::msVerticalAngleCmd;
NifEmitter::CmdHorizontalDir NifEmitter::msHorizontalDirCmd;
NifEmitter::CmdHorizontalAngle NifEmitter::msHorizontalAngleCmd;
Ogre::ParticleEmitter* NifEmitterFactory::createEmitter(Ogre::ParticleSystem *psys)
{
Ogre::ParticleEmitter *emit = OGRE_NEW NifEmitter(psys);
mEmitters.push_back(emit);
return emit;
}
class GrowFadeAffector : public Ogre::ParticleAffector
{
public:
/** Command object for grow_time (see Ogre::ParamCommand).*/
class CmdGrowTime : public Ogre::ParamCommand
{
public:
Ogre::String doGet(const void *target) const
{
const GrowFadeAffector *self = static_cast<const GrowFadeAffector*>(target);
return Ogre::StringConverter::toString(self->getGrowTime());
}
void doSet(void *target, const Ogre::String &val)
{
GrowFadeAffector *self = static_cast<GrowFadeAffector*>(target);
self->setGrowTime(Ogre::StringConverter::parseReal(val));
}
};
/** Command object for fade_time (see Ogre::ParamCommand).*/
class CmdFadeTime : public Ogre::ParamCommand
{
public:
Ogre::String doGet(const void *target) const
{
const GrowFadeAffector *self = static_cast<const GrowFadeAffector*>(target);
return Ogre::StringConverter::toString(self->getFadeTime());
}
void doSet(void *target, const Ogre::String &val)
{
GrowFadeAffector *self = static_cast<GrowFadeAffector*>(target);
self->setFadeTime(Ogre::StringConverter::parseReal(val));
}
};
/** Default constructor. */
GrowFadeAffector(Ogre::ParticleSystem *psys) : ParticleAffector(psys)
{
mGrowTime = 0.0f;
mFadeTime = 0.0f;
mType = "GrowFade";
// Init parameters
if(createParamDictionary("GrowFadeAffector"))
{
Ogre::ParamDictionary *dict = getParamDictionary();
Ogre::String grow_title("grow_time");
Ogre::String fade_title("fade_time");
Ogre::String grow_descr("Time from begin to reach full size.");
Ogre::String fade_descr("Time from end to shrink.");
dict->addParameter(Ogre::ParameterDef(grow_title, grow_descr, Ogre::PT_REAL), &msGrowCmd);
dict->addParameter(Ogre::ParameterDef(fade_title, fade_descr, Ogre::PT_REAL), &msFadeCmd);
}
}
/** See Ogre::ParticleAffector. */
void _initParticle(Ogre::Particle *particle)
{
const Ogre::Real life_time = particle->totalTimeToLive;
Ogre::Real particle_time = particle->timeToLive;
Ogre::Real width = mParent->getDefaultWidth();
Ogre::Real height = mParent->getDefaultHeight();
if(life_time-particle_time < mGrowTime)
{
Ogre::Real scale = (life_time-particle_time) / mGrowTime;
width *= scale;
height *= scale;
}
if(particle_time < mFadeTime)
{
Ogre::Real scale = particle_time / mFadeTime;
width *= scale;
height *= scale;
}
particle->setDimensions(width, height);
}
/** See Ogre::ParticleAffector. */
void _affectParticles(Ogre::ParticleSystem *psys, Ogre::Real timeElapsed)
{
Ogre::ParticleIterator pi = psys->_getIterator();
while (!pi.end())
{
Ogre::Particle *p = pi.getNext();
const Ogre::Real life_time = p->totalTimeToLive;
Ogre::Real particle_time = p->timeToLive;
Ogre::Real width = mParent->getDefaultWidth();
Ogre::Real height = mParent->getDefaultHeight();
if(life_time-particle_time < mGrowTime)
{
Ogre::Real scale = (life_time-particle_time) / mGrowTime;
width *= scale;
height *= scale;
}
if(particle_time < mFadeTime)
{
Ogre::Real scale = particle_time / mFadeTime;
width *= scale;
height *= scale;
}
p->setDimensions(width, height);
}
}
void setGrowTime(Ogre::Real time)
{
mGrowTime = time;
}
Ogre::Real getGrowTime() const
{ return mGrowTime; }
void setFadeTime(Ogre::Real time)
{
mFadeTime = time;
}
Ogre::Real getFadeTime() const
{ return mFadeTime; }
static CmdGrowTime msGrowCmd;
static CmdFadeTime msFadeCmd;
protected:
Ogre::Real mGrowTime;
Ogre::Real mFadeTime;
};
GrowFadeAffector::CmdGrowTime GrowFadeAffector::msGrowCmd;
GrowFadeAffector::CmdFadeTime GrowFadeAffector::msFadeCmd;
Ogre::ParticleAffector *GrowFadeAffectorFactory::createAffector(Ogre::ParticleSystem *psys)
{
Ogre::ParticleAffector *p = new GrowFadeAffector(psys);
mAffectors.push_back(p);
return p;
}
class GravityAffector : public Ogre::ParticleAffector
{
enum ForceType {
Type_Wind,
Type_Point
};
public:
/** Command object for force (see Ogre::ParamCommand).*/
class CmdForce : public Ogre::ParamCommand
{
public:
Ogre::String doGet(const void *target) const
{
const GravityAffector *self = static_cast<const GravityAffector*>(target);
return Ogre::StringConverter::toString(self->getForce());
}
void doSet(void *target, const Ogre::String &val)
{
GravityAffector *self = static_cast<GravityAffector*>(target);
self->setForce(Ogre::StringConverter::parseReal(val));
}
};
/** Command object for force_type (see Ogre::ParamCommand).*/
class CmdForceType : public Ogre::ParamCommand
{
static ForceType getTypeFromString(const Ogre::String &type)
{
if(type == "wind")
return Type_Wind;
if(type == "point")
return Type_Point;
OGRE_EXCEPT(Ogre::Exception::ERR_INVALIDPARAMS, "Invalid force type string: "+type,
"CmdForceType::getTypeFromString");
}
static Ogre::String getStringFromType(ForceType type)
{
switch(type)
{
case Type_Wind: return "wind";
case Type_Point: return "point";
}
OGRE_EXCEPT(Ogre::Exception::ERR_INVALIDPARAMS, "Invalid force type enum: "+Ogre::StringConverter::toString(type),
"CmdForceType::getStringFromType");
}
public:
Ogre::String doGet(const void *target) const
{
const GravityAffector *self = static_cast<const GravityAffector*>(target);
return getStringFromType(self->getForceType());
}
void doSet(void *target, const Ogre::String &val)
{
GravityAffector *self = static_cast<GravityAffector*>(target);
self->setForceType(getTypeFromString(val));
}
};
/** Command object for direction (see Ogre::ParamCommand).*/
class CmdDirection : public Ogre::ParamCommand
{
public:
Ogre::String doGet(const void *target) const
{
const GravityAffector *self = static_cast<const GravityAffector*>(target);
return Ogre::StringConverter::toString(self->getDirection());
}
void doSet(void *target, const Ogre::String &val)
{
GravityAffector *self = static_cast<GravityAffector*>(target);
self->setDirection(Ogre::StringConverter::parseVector3(val));
}
};
/** Command object for position (see Ogre::ParamCommand).*/
class CmdPosition : public Ogre::ParamCommand
{
public:
Ogre::String doGet(const void *target) const
{
const GravityAffector *self = static_cast<const GravityAffector*>(target);
return Ogre::StringConverter::toString(self->getPosition());
}
void doSet(void *target, const Ogre::String &val)
{
GravityAffector *self = static_cast<GravityAffector*>(target);
self->setPosition(Ogre::StringConverter::parseVector3(val));
}
};
/** Default constructor. */
GravityAffector(Ogre::ParticleSystem *psys)
: ParticleAffector(psys)
, mForce(0.0f)
, mForceType(Type_Wind)
, mPosition(0.0f)
, mDirection(0.0f)
{
mType = "Gravity";
// Init parameters
if(createParamDictionary("GravityAffector"))
{
Ogre::ParamDictionary *dict = getParamDictionary();
Ogre::String force_title("force");
Ogre::String force_descr("Amount of force applied to particles.");
Ogre::String force_type_title("force_type");
Ogre::String force_type_descr("Type of force applied to particles (point or wind).");
Ogre::String direction_title("direction");
Ogre::String direction_descr("Direction of wind forces.");
Ogre::String position_title("position");
Ogre::String position_descr("Position of point forces.");
dict->addParameter(Ogre::ParameterDef(force_title, force_descr, Ogre::PT_REAL), &msForceCmd);
dict->addParameter(Ogre::ParameterDef(force_type_title, force_type_descr, Ogre::PT_STRING), &msForceTypeCmd);
dict->addParameter(Ogre::ParameterDef(direction_title, direction_descr, Ogre::PT_VECTOR3), &msDirectionCmd);
dict->addParameter(Ogre::ParameterDef(position_title, position_descr, Ogre::PT_VECTOR3), &msPositionCmd);
}
}
/** See Ogre::ParticleAffector. */
void _affectParticles(Ogre::ParticleSystem *psys, Ogre::Real timeElapsed)
{
switch(mForceType)
{
case Type_Wind:
applyWindForce(psys, timeElapsed);
break;
case Type_Point:
applyPointForce(psys, timeElapsed);
break;
}
}
void setForce(Ogre::Real force)
{ mForce = force; }
Ogre::Real getForce() const
{ return mForce; }
void setForceType(ForceType type)
{ mForceType = type; }
ForceType getForceType() const
{ return mForceType; }
void setDirection(const Ogre::Vector3 &dir)
{ mDirection = dir; }
const Ogre::Vector3 &getDirection() const
{ return mDirection; }
void setPosition(const Ogre::Vector3 &pos)
{ mPosition = pos; }
const Ogre::Vector3 &getPosition() const
{ return mPosition; }
static CmdForce msForceCmd;
static CmdForceType msForceTypeCmd;
static CmdDirection msDirectionCmd;
static CmdPosition msPositionCmd;
protected:
void applyWindForce(Ogre::ParticleSystem *psys, Ogre::Real timeElapsed)
{
const Ogre::Vector3 vec = mDirection * mForce * timeElapsed;
Ogre::ParticleIterator pi = psys->_getIterator();
while (!pi.end())
{
Ogre::Particle *p = pi.getNext();
p->direction += vec;
}
}
void applyPointForce(Ogre::ParticleSystem *psys, Ogre::Real timeElapsed)
{
const Ogre::Real force = mForce * timeElapsed;
Ogre::ParticleIterator pi = psys->_getIterator();
while (!pi.end())
{
Ogre::Particle *p = pi.getNext();
const Ogre::Vector3 vec = (p->position - mPosition).normalisedCopy() * force;
p->direction += vec;
}
}
float mForce;
ForceType mForceType;
Ogre::Vector3 mPosition;
Ogre::Vector3 mDirection;
};
GravityAffector::CmdForce GravityAffector::msForceCmd;
GravityAffector::CmdForceType GravityAffector::msForceTypeCmd;
GravityAffector::CmdDirection GravityAffector::msDirectionCmd;
GravityAffector::CmdPosition GravityAffector::msPositionCmd;
Ogre::ParticleAffector *GravityAffectorFactory::createAffector(Ogre::ParticleSystem *psys)
{
Ogre::ParticleAffector *p = new GravityAffector(psys);
mAffectors.push_back(p);
return p;
}

View file

@ -0,0 +1,41 @@
#ifndef OENGINE_OGRE_PARTICLES_H
#define OENGINE_OGRE_PARTICLES_H
#include <OgreParticleEmitterFactory.h>
#include <OgreParticleAffectorFactory.h>
/** Factory class for NifEmitter. */
class NifEmitterFactory : public Ogre::ParticleEmitterFactory
{
public:
/** See ParticleEmitterFactory */
Ogre::String getName() const
{ return "Nif"; }
/** See ParticleEmitterFactory */
Ogre::ParticleEmitter* createEmitter(Ogre::ParticleSystem *psys);
};
/** Factory class for GrowFadeAffector. */
class GrowFadeAffectorFactory : public Ogre::ParticleAffectorFactory
{
/** See Ogre::ParticleAffectorFactory */
Ogre::String getName() const
{ return "GrowFade"; }
/** See Ogre::ParticleAffectorFactory */
Ogre::ParticleAffector *createAffector(Ogre::ParticleSystem *psys);
};
/** Factory class for GravityAffector. */
class GravityAffectorFactory : public Ogre::ParticleAffectorFactory
{
/** See Ogre::ParticleAffectorFactory */
Ogre::String getName() const
{ return "Gravity"; }
/** See Ogre::ParticleAffectorFactory */
Ogre::ParticleAffector *createAffector(Ogre::ParticleSystem *psys);
};
#endif /* OENGINE_OGRE_PARTICLES_H */

View file

@ -0,0 +1,319 @@
#include "renderer.hpp"
#include "fader.hpp"
#include "particles.hpp"
#include <SDL.h>
#include "OgreRoot.h"
#include "OgreRenderWindow.h"
#include "OgreLogManager.h"
#include "OgreLog.h"
#include "OgreTextureManager.h"
#include "OgreTexture.h"
#include "OgreHardwarePixelBuffer.h"
#include <OgreParticleSystemManager.h>
#include "OgreParticleAffectorFactory.h"
#include <boost/filesystem.hpp>
#include <components/files/ogreplugin.hpp>
#include <extern/sdl4ogre/sdlwindowhelper.hpp>
#include <cassert>
#include <cstdlib>
#include <stdexcept>
using namespace Ogre;
using namespace OEngine::Render;
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
CustomRoot::CustomRoot(const Ogre::String& pluginFileName,
const Ogre::String& configFileName,
const Ogre::String& logFileName)
: Ogre::Root(pluginFileName, configFileName, logFileName)
{}
bool CustomRoot::isQueuedEnd() const
{
return mQueuedEnd;
}
#endif
void OgreRenderer::cleanup()
{
delete mFader;
mFader = NULL;
delete mRoot;
mRoot = NULL;
// If we don't do this, the desktop resolution is not restored on exit
SDL_SetWindowFullscreen(mSDLWindow, 0);
SDL_DestroyWindow(mSDLWindow);
mSDLWindow = NULL;
unloadPlugins();
}
void OgreRenderer::start()
{
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
// we need this custom main loop because otherwise Ogre's Carbon message pump will
// steal input events even from our Cocoa window
// There's no way to disable Ogre's message pump other that comment pump code in Ogre's source
do {
if (!mRoot->renderOneFrame()) {
break;
}
} while (!mRoot->isQueuedEnd());
#else
mRoot->startRendering();
#endif
}
void OgreRenderer::loadPlugins()
{
#ifdef ENABLE_PLUGIN_GL
mGLPlugin = new Ogre::GLPlugin();
mRoot->installPlugin(mGLPlugin);
#endif
#ifdef ENABLE_PLUGIN_Direct3D9
mD3D9Plugin = new Ogre::D3D9Plugin();
mRoot->installPlugin(mD3D9Plugin);
#endif
#ifdef ENABLE_PLUGIN_CgProgramManager
mCgPlugin = new Ogre::CgPlugin();
mRoot->installPlugin(mCgPlugin);
#endif
#ifdef ENABLE_PLUGIN_OctreeSceneManager
mOctreePlugin = new Ogre::OctreePlugin();
mRoot->installPlugin(mOctreePlugin);
#endif
#ifdef ENABLE_PLUGIN_ParticleFX
mParticleFXPlugin = new Ogre::ParticleFXPlugin();
mRoot->installPlugin(mParticleFXPlugin);
#endif
}
void OgreRenderer::unloadPlugins()
{
std::vector<Ogre::ParticleEmitterFactory*>::iterator ei;
for(ei = mEmitterFactories.begin();ei != mEmitterFactories.end();++ei)
OGRE_DELETE (*ei);
mEmitterFactories.clear();
std::vector<Ogre::ParticleAffectorFactory*>::iterator ai;
for(ai = mAffectorFactories.begin();ai != mAffectorFactories.end();++ai)
OGRE_DELETE (*ai);
mAffectorFactories.clear();
#ifdef ENABLE_PLUGIN_GL
delete mGLPlugin;
mGLPlugin = NULL;
#endif
#ifdef ENABLE_PLUGIN_Direct3D9
delete mD3D9Plugin;
mD3D9Plugin = NULL;
#endif
#ifdef ENABLE_PLUGIN_CgProgramManager
delete mCgPlugin;
mCgPlugin = NULL;
#endif
#ifdef ENABLE_PLUGIN_OctreeSceneManager
delete mOctreePlugin;
mOctreePlugin = NULL;
#endif
#ifdef ENABLE_PLUGIN_ParticleFX
delete mParticleFXPlugin;
mParticleFXPlugin = NULL;
#endif
}
void OgreRenderer::update(float dt)
{
mFader->update(dt);
}
void OgreRenderer::screenshot(const std::string &file)
{
mWindow->writeContentsToFile(file);
}
float OgreRenderer::getFPS()
{
return mWindow->getLastFPS();
}
void OgreRenderer::configure(const std::string &logPath,
const std::string& renderSystem,
const std::string& rttMode,
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);
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
mRoot = new CustomRoot("", "", "");
#else
mRoot = new Root("", "", "");
#endif
#if defined(ENABLE_PLUGIN_GL) || defined(ENABLE_PLUGIN_Direct3D9) || defined(ENABLE_PLUGIN_CgProgramManager) || defined(ENABLE_PLUGIN_OctreeSceneManager) || defined(ENABLE_PLUGIN_ParticleFX)
loadPlugins();
#endif
std::string pluginDir;
const char* pluginEnv = getenv("OPENMW_OGRE_PLUGIN_DIR");
if (pluginEnv)
pluginDir = pluginEnv;
else
{
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
pluginDir = ".\\";
#endif
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
pluginDir = OGRE_PLUGIN_DIR;
#endif
#if OGRE_PLATFORM == OGRE_PLATFORM_LINUX
pluginDir = OGRE_PLUGIN_DIR_REL;
#endif
}
boost::filesystem::path absPluginPath = boost::filesystem::absolute(boost::filesystem::path(pluginDir));
pluginDir = absPluginPath.string();
Files::loadOgrePlugin(pluginDir, "RenderSystem_GL", *mRoot);
Files::loadOgrePlugin(pluginDir, "RenderSystem_GLES2", *mRoot);
Files::loadOgrePlugin(pluginDir, "RenderSystem_GL3Plus", *mRoot);
Files::loadOgrePlugin(pluginDir, "RenderSystem_Direct3D9", *mRoot);
Files::loadOgrePlugin(pluginDir, "Plugin_CgProgramManager", *mRoot);
Files::loadOgrePlugin(pluginDir, "Plugin_ParticleFX", *mRoot);
Ogre::ParticleEmitterFactory *emitter;
emitter = OGRE_NEW NifEmitterFactory();
Ogre::ParticleSystemManager::getSingleton().addEmitterFactory(emitter);
mEmitterFactories.push_back(emitter);
Ogre::ParticleAffectorFactory *affector;
affector = OGRE_NEW GrowFadeAffectorFactory();
Ogre::ParticleSystemManager::getSingleton().addAffectorFactory(affector);
mAffectorFactories.push_back(affector);
affector = OGRE_NEW GravityAffectorFactory();
Ogre::ParticleSystemManager::getSingleton().addAffectorFactory(affector);
mAffectorFactories.push_back(affector);
RenderSystem* rs = mRoot->getRenderSystemByName(renderSystem);
if (rs == 0)
throw std::runtime_error ("RenderSystem with name " + renderSystem + " not found, make sure the plugins are loaded");
mRoot->setRenderSystem(rs);
if (rs->getName().find("OpenGL") != std::string::npos)
rs->setConfigOption ("RTT Preferred Mode", rttMode);
}
void OgreRenderer::createWindow(const std::string &title, const WindowSettings& settings)
{
assert(mRoot);
mRoot->initialise(false);
NameValuePairList params;
params.insert(std::make_pair("title", title));
params.insert(std::make_pair("FSAA", settings.fsaa));
params.insert(std::make_pair("vsync", settings.vsync ? "true" : "false"));
int pos_x = SDL_WINDOWPOS_CENTERED_DISPLAY(settings.screen),
pos_y = SDL_WINDOWPOS_CENTERED_DISPLAY(settings.screen);
if(settings.fullscreen)
{
pos_x = SDL_WINDOWPOS_UNDEFINED_DISPLAY(settings.screen);
pos_y = SDL_WINDOWPOS_UNDEFINED_DISPLAY(settings.screen);
}
// Create an application window with the following settings:
mSDLWindow = SDL_CreateWindow(
"OpenMW", // window title
pos_x, // initial x position
pos_y, // initial y position
settings.window_x, // width, in pixels
settings.window_y, // height, in pixels
SDL_WINDOW_SHOWN
| (settings.fullscreen ? SDL_WINDOW_FULLSCREEN : 0) | SDL_WINDOW_RESIZABLE
);
SFO::SDLWindowHelper helper(mSDLWindow, settings.window_x, settings.window_y, title, settings.fullscreen, params);
if (settings.icon != "")
helper.setWindowIcon(settings.icon);
mWindow = helper.getWindow();
// create the semi-transparent black background texture used by the GUI.
// has to be created in code with TU_DYNAMIC_WRITE_ONLY param
// so that it can be modified at runtime.
Ogre::TextureManager::getSingleton().createManual(
"transparent.png",
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
Ogre::TEX_TYPE_2D,
1, 1,
0,
Ogre::PF_A8R8G8B8,
Ogre::TU_WRITE_ONLY);
mScene = mRoot->createSceneManager(ST_GENERIC);
mFader = new Fader(mScene);
mCamera = mScene->createCamera("cam");
// 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()));
}
void OgreRenderer::adjustCamera(float fov, float nearClip)
{
mCamera->setNearClipDistance(nearClip);
mCamera->setFOVy(Degree(fov));
}
void OgreRenderer::adjustViewport()
{
// Alter the camera aspect ratio to match the viewport
if(mCamera != NULL)
{
mView->setDimensions(0, 0, 1, 1);
mCamera->setAspectRatio(Real(mView->getActualWidth()) / Real(mView->getActualHeight()));
}
}
void OgreRenderer::setFov(float fov)
{
mCamera->setFOVy(Degree(fov));
}
void OgreRenderer::windowResized(int x, int y)
{
mWindowListener->windowResized(x,y);
}

View file

@ -0,0 +1,213 @@
#ifndef OENGINE_OGRE_RENDERER_H
#define OENGINE_OGRE_RENDERER_H
/*
Ogre renderer class
*/
#include <string>
// Static plugin headers
#ifdef ENABLE_PLUGIN_CgProgramManager
# include "OgreCgPlugin.h"
#endif
#ifdef ENABLE_PLUGIN_OctreeSceneManager
# include "OgreOctreePlugin.h"
#endif
#ifdef ENABLE_PLUGIN_ParticleFX
# include "OgreParticleFXPlugin.h"
#endif
#ifdef ENABLE_PLUGIN_GL
# include "OgreGLPlugin.h"
#endif
#ifdef ENABLE_PLUGIN_Direct3D9
# include "OgreD3D9Plugin.h"
#endif
#include "OgreTexture.h"
#include <OgreWindowEventUtilities.h>
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
#include <OgreRoot.h>
#endif
struct SDL_Window;
struct SDL_Surface;
namespace Ogre
{
#if OGRE_PLATFORM != OGRE_PLATFORM_APPLE
class Root;
#endif
class RenderWindow;
class SceneManager;
class Camera;
class Viewport;
class ParticleEmitterFactory;
class ParticleAffectorFactory;
}
namespace OEngine
{
namespace Render
{
struct WindowSettings
{
bool vsync;
bool fullscreen;
int window_x, window_y;
int screen;
std::string fsaa;
std::string icon;
};
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
class CustomRoot : public Ogre::Root {
public:
bool isQueuedEnd() const;
CustomRoot(const Ogre::String& pluginFileName = "plugins.cfg",
const Ogre::String& configFileName = "ogre.cfg",
const Ogre::String& logFileName = "Ogre.log");
};
#endif
class Fader;
class WindowSizeListener
{
public:
virtual void windowResized (int x, int y) = 0;
};
class OgreRenderer
{
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
CustomRoot *mRoot;
#else
Ogre::Root *mRoot;
#endif
Ogre::RenderWindow *mWindow;
SDL_Window *mSDLWindow;
Ogre::SceneManager *mScene;
Ogre::Camera *mCamera;
Ogre::Viewport *mView;
#ifdef ENABLE_PLUGIN_CgProgramManager
Ogre::CgPlugin* mCgPlugin;
#endif
#ifdef ENABLE_PLUGIN_OctreeSceneManager
Ogre::OctreePlugin* mOctreePlugin;
#endif
#ifdef ENABLE_PLUGIN_ParticleFX
Ogre::ParticleFXPlugin* mParticleFXPlugin;
#endif
#ifdef ENABLE_PLUGIN_GL
Ogre::GLPlugin* mGLPlugin;
#endif
#ifdef ENABLE_PLUGIN_Direct3D9
Ogre::D3D9Plugin* mD3D9Plugin;
#endif
Fader* mFader;
std::vector<Ogre::ParticleEmitterFactory*> mEmitterFactories;
std::vector<Ogre::ParticleAffectorFactory*> mAffectorFactories;
bool logging;
WindowSizeListener* mWindowListener;
public:
OgreRenderer()
: mRoot(NULL)
, mWindow(NULL)
, mSDLWindow(NULL)
, mScene(NULL)
, mCamera(NULL)
, mView(NULL)
, mWindowListener(NULL)
#ifdef ENABLE_PLUGIN_CgProgramManager
, mCgPlugin(NULL)
#endif
#ifdef ENABLE_PLUGIN_OctreeSceneManager
, mOctreePlugin(NULL)
#endif
#ifdef ENABLE_PLUGIN_ParticleFX
, mParticleFXPlugin(NULL)
#endif
#ifdef ENABLE_PLUGIN_GL
, mGLPlugin(NULL)
#endif
#ifdef ENABLE_PLUGIN_Direct3D9
, mD3D9Plugin(NULL)
#endif
, mFader(NULL)
, logging(false)
{
}
~OgreRenderer() { cleanup(); }
/** Configure the renderer. This will load configuration files and
set up the Root and logging classes. */
void configure(
const std::string &logPath, // Path to directory where to store log files
const std::string &renderSystem,
const std::string &rttMode,
bool _logging); // Enable or disable logging
/// Create a window with the given title
void createWindow(const std::string &title, const WindowSettings& settings);
/// Set up the scene manager, camera and viewport
void adjustCamera(
float fov=55, // Field of view angle
float nearClip=5 // Near clip distance
);
void setFov(float fov);
/// Kill the renderer.
void cleanup();
/// Start the main rendering loop
void start();
void loadPlugins();
void unloadPlugins();
void update(float dt);
/// Write a screenshot to file
void screenshot(const std::string &file);
float getFPS();
void windowResized(int x, int y);
/// Get the Root
Ogre::Root *getRoot() { return mRoot; }
/// Get the rendering window
Ogre::RenderWindow *getWindow() { return mWindow; }
/// Get the SDL Window
SDL_Window *getSDLWindow() { return mSDLWindow; }
/// 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; }
void setWindowListener(WindowSizeListener* listener) { mWindowListener = listener; }
void adjustViewport();
};
}
}
#endif

View file

@ -0,0 +1,134 @@
#include "selectionbuffer.hpp"
#include <OgreHardwarePixelBuffer.h>
#include <OgreRenderTexture.h>
#include <OgreSubEntity.h>
#include <OgreEntity.h>
#include <OgreTechnique.h>
#include <stdexcept>
#include <extern/shiny/Main/Factory.hpp>
namespace OEngine
{
namespace Render
{
SelectionBuffer::SelectionBuffer(Ogre::Camera *camera, int sizeX, int sizeY, int visibilityFlags)
{
mTexture = Ogre::TextureManager::getSingleton().createManual("SelectionBuffer",
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, Ogre::TEX_TYPE_2D, sizeX, sizeY, 0, Ogre::PF_R8G8B8, Ogre::TU_RENDERTARGET);
mRenderTarget = mTexture->getBuffer()->getRenderTarget();
Ogre::Viewport* vp = mRenderTarget->addViewport(camera);
vp->setOverlaysEnabled(false);
vp->setBackgroundColour(Ogre::ColourValue(0, 0, 0, 0));
vp->setShadowsEnabled(false);
vp->setMaterialScheme("selectionbuffer");
if (visibilityFlags != 0)
vp->setVisibilityMask (visibilityFlags);
mRenderTarget->setActive(true);
mRenderTarget->setAutoUpdated (false);
mCurrentColour = Ogre::ColourValue(0.3, 0.3, 0.3);
}
SelectionBuffer::~SelectionBuffer()
{
Ogre::TextureManager::getSingleton ().remove("SelectionBuffer");
}
void SelectionBuffer::update ()
{
Ogre::MaterialManager::getSingleton ().addListener (this);
mRenderTarget->update();
Ogre::MaterialManager::getSingleton ().removeListener (this);
mTexture->convertToImage(mBuffer);
}
int SelectionBuffer::getSelected(int xPos, int yPos)
{
Ogre::ColourValue clr = mBuffer.getColourAt (xPos, yPos, 0);
clr.a = 1;
if (mColourMap.find(clr) != mColourMap.end())
return mColourMap[clr];
else
return -1; // nothing selected
}
Ogre::Technique* SelectionBuffer::handleSchemeNotFound (
unsigned short schemeIndex, const Ogre::String &schemeName, Ogre::Material *originalMaterial,
unsigned short lodIndex, const Ogre::Renderable *rend)
{
if (schemeName == "selectionbuffer")
{
sh::Factory::getInstance ()._ensureMaterial ("SelectionColour", "Default");
Ogre::MaterialPtr m = Ogre::MaterialManager::getSingleton ().getByName("SelectionColour");
if(typeid(*rend) == typeid(Ogre::SubEntity))
{
const Ogre::SubEntity *subEntity = static_cast<const Ogre::SubEntity *>(rend);
int id = Ogre::any_cast<int>(subEntity->getParent ()->getUserObjectBindings().getUserAny());
bool found = false;
Ogre::ColourValue colour;
for (std::map<Ogre::ColourValue, int, cmp_ColourValue>::iterator it = mColourMap.begin(); it != mColourMap.end(); ++it)
{
if (it->second == id)
{
found = true;
colour = it->first;
}
}
if (!found)
{
getNextColour();
const_cast<Ogre::SubEntity *>(subEntity)->setCustomParameter(1, Ogre::Vector4(mCurrentColour.r, mCurrentColour.g, mCurrentColour.b, 1.0));
mColourMap[mCurrentColour] = id;
}
else
{
const_cast<Ogre::SubEntity *>(subEntity)->setCustomParameter(1, Ogre::Vector4(colour.r, colour.g, colour.b, 1.0));
}
assert(m->getTechnique(1));
return m->getTechnique(1);
}
else
{
m = Ogre::MaterialManager::getSingleton().getByName("NullMaterial");
if(m.isNull())
{
m = Ogre::MaterialManager::getSingleton().create("NullMaterial", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
m->getTechnique(0)->getPass(0)->setDepthCheckEnabled(true);
m->getTechnique(0)->getPass(0)->setDepthFunction(Ogre::CMPF_ALWAYS_FAIL);
}
return m->getTechnique(0);
}
}
return NULL;
}
void SelectionBuffer::getNextColour ()
{
Ogre::ARGB color = (float(rand()) / float(RAND_MAX)) * std::numeric_limits<Ogre::uint32>::max();
if (mCurrentColour.getAsARGB () == color)
{
getNextColour();
return;
}
mCurrentColour.setAsARGB(color);
mCurrentColour.a = 1;
}
}
}

View file

@ -0,0 +1,54 @@
#ifndef OENGINE_SELECTIONBUFFER_H
#define OENGINE_SELECTIONBUFFER_H
#include <OgreTexture.h>
#include <OgreRenderTarget.h>
#include <OgreMaterialManager.h>
namespace OEngine
{
namespace Render
{
struct cmp_ColourValue
{
bool operator()(const Ogre::ColourValue &a, const Ogre::ColourValue &b) const
{
return a.getAsBGRA() < b.getAsBGRA();
}
};
class SelectionBuffer : public Ogre::MaterialManager::Listener
{
public:
SelectionBuffer(Ogre::Camera* camera, int sizeX, int sizeY, int visibilityFlags);
virtual ~SelectionBuffer();
int getSelected(int xPos, int yPos);
///< @return ID of the selected object
void update();
virtual Ogre::Technique* handleSchemeNotFound (
unsigned short schemeIndex, const Ogre::String &schemeName, Ogre::Material *originalMaterial,
unsigned short lodIndex, const Ogre::Renderable *rend);
private:
Ogre::TexturePtr mTexture;
Ogre::RenderTexture* mRenderTarget;
Ogre::Image mBuffer;
std::map<Ogre::ColourValue, int, cmp_ColourValue> mColourMap;
Ogre::ColourValue mCurrentColour;
void getNextColour();
};
}
}
#endif

11
libs/openengine/testall.sh Executable file
View file

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

21
libs/platform/stdint.h Normal file
View file

@ -0,0 +1,21 @@
// Wrapper for MSVC
#ifndef _STDINT_WRAPPER_H
#define _STDINT_WRAPPER_H
#if (_MSC_VER >= 1600)
#include <cstdint>
#else
#include <boost/cstdint.hpp>
// Pull the boost names into the global namespace for convenience
using boost::int32_t;
using boost::uint32_t;
using boost::int64_t;
using boost::uint64_t;
#endif
#endif

34
libs/platform/string.h Normal file
View file

@ -0,0 +1,34 @@
// Wrapper for string.h on Mac and MinGW
#ifndef _STRING_WRAPPER_H
#define _STRING_WRAPPER_H
#ifdef __APPLE__
#include <Availability.h>
#endif
#include <string.h>
#if (defined(__APPLE__) && __MAC_OS_X_VERSION_MIN_REQUIRED < 1070) || defined(__MINGW32__)
// need our own implementation of strnlen
#ifdef __MINGW32__
static size_t strnlen(const char *s, size_t n)
{
const char *p = (const char *)memchr(s, 0, n);
return(p ? p-s : n);
}
#elif (defined(__APPLE__) && __MAC_OS_X_VERSION_MIN_REQUIRED < 1070)
static size_t mw_strnlen(const char *s, size_t n)
{
if (strnlen != NULL) {
return strnlen(s, n);
}
else {
const char *p = (const char *)memchr(s, 0, n);
return(p ? p-s : n);
}
}
#define strnlen mw_strnlen
#endif
#endif
#endif

18
libs/platform/strings.h Normal file
View file

@ -0,0 +1,18 @@
// Wrapper for MSVC/GCC
#ifndef _STRINGS_WRAPPER_H
#define _STRINGS_WRAPPER_H
// For GCC, just use strings.h (this applies to mingw too)
#if defined(__GNUC__)
# include <strings.h>
#elif defined(MSVC) || defined(_MSC_VER)
# pragma warning(disable: 4996)
# define strcasecmp stricmp
# define snprintf _snprintf
#else
# warning "Unable to determine your compiler, you should probably take a look here."
# include <strings.h> // Just take a guess
#endif
#endif /* _STRINGS_WRAPPER_H */