Create a separate rigid body / shape with full details for raycasting, remove the occlusion query hack

pull/37/head
scrawl 12 years ago
parent 285b4bf726
commit d7c4a622cf

@ -32,7 +32,7 @@ namespace MWGui
, mBalanceChangePause(0.0)
{
// items the NPC is wearing should not be for trade
mDisplayEquippedItems = true;
mDisplayEquippedItems = false;
MyGUI::ScrollView* itemView;
MyGUI::Widget* containerWidget;

@ -13,9 +13,9 @@ using namespace MWRender;
using namespace Ogre;
OcclusionQuery::OcclusionQuery(OEngine::Render::OgreRenderer* renderer, SceneNode* sunNode) :
mSunTotalAreaQuery(0), mSunVisibleAreaQuery(0), mSingleObjectQuery(0), mActiveQuery(0),
mDoQuery(0), mSunVisibility(0), mQuerySingleObjectStarted(false), mTestResult(false),
mQuerySingleObjectRequested(false), mWasVisible(false), mObjectWasVisible(false),
mSunTotalAreaQuery(0), mSunVisibleAreaQuery(0), mActiveQuery(0),
mDoQuery(0), mSunVisibility(0),
mWasVisible(false),
mBBNode(0), mActive(false)
{
mRendering = renderer;
@ -26,9 +26,8 @@ OcclusionQuery::OcclusionQuery(OEngine::Render::OgreRenderer* renderer, SceneNod
mSunTotalAreaQuery = renderSystem->createHardwareOcclusionQuery();
mSunVisibleAreaQuery = renderSystem->createHardwareOcclusionQuery();
mSingleObjectQuery = renderSystem->createHardwareOcclusionQuery();
mSupported = (mSunTotalAreaQuery != 0) && (mSunVisibleAreaQuery != 0) && (mSingleObjectQuery != 0);
mSupported = (mSunTotalAreaQuery != 0) && (mSunVisibleAreaQuery != 0);
}
catch (Ogre::Exception e)
{
@ -56,7 +55,6 @@ OcclusionQuery::OcclusionQuery(OEngine::Render::OgreRenderer* renderer, SceneNod
if (sunNode)
mBBNode = mSunNode->getParentSceneNode()->createChildSceneNode();
mObjectNode = mRendering->getScene()->getRootSceneNode()->createChildSceneNode();
mBBNodeReal = mRendering->getScene()->getRootSceneNode()->createChildSceneNode();
mBBQueryTotal = mRendering->getScene()->createBillboardSet(1);
@ -77,16 +75,6 @@ OcclusionQuery::OcclusionQuery(OEngine::Render::OgreRenderer* renderer, SceneNod
mBBQueryVisible->setVisibilityFlags(RV_OcclusionQuery);
mBBNodeReal->attachObject(mBBQueryVisible);
mBBQuerySingleObject = mRendering->getScene()->createBillboardSet(1);
/// \todo ideally this should occupy exactly 1 pixel on the screen
mBBQuerySingleObject->setCastShadows(false);
mBBQuerySingleObject->setDefaultDimensions(0.003, 0.003);
mBBQuerySingleObject->createBillboard(Vector3::ZERO);
mBBQuerySingleObject->setMaterialName("QueryVisiblePixels");
mBBQuerySingleObject->setRenderQueueGroup(RQG_OcclusionQuery);
mBBQuerySingleObject->setVisibilityFlags(RV_OcclusionQuery);
mObjectNode->attachObject(mBBQuerySingleObject);
mRendering->getScene()->addRenderObjectListener(this);
mRendering->getScene()->addRenderQueueListener(this);
mDoQuery = true;
@ -98,9 +86,10 @@ OcclusionQuery::~OcclusionQuery()
mRendering->getScene()->removeRenderQueueListener(this);
RenderSystem* renderSystem = Root::getSingleton().getRenderSystem();
if (mSunTotalAreaQuery) renderSystem->destroyHardwareOcclusionQuery(mSunTotalAreaQuery);
if (mSunVisibleAreaQuery) renderSystem->destroyHardwareOcclusionQuery(mSunVisibleAreaQuery);
if (mSingleObjectQuery) renderSystem->destroyHardwareOcclusionQuery(mSingleObjectQuery);
if (mSunTotalAreaQuery)
renderSystem->destroyHardwareOcclusionQuery(mSunTotalAreaQuery);
if (mSunVisibleAreaQuery)
renderSystem->destroyHardwareOcclusionQuery(mSunVisibleAreaQuery);
}
bool OcclusionQuery::supported()
@ -137,13 +126,6 @@ void OcclusionQuery::notifyRenderSingleObject(Renderable* rend, const Pass* pass
mActiveQuery = mSunVisibleAreaQuery;
}
}
if (mDoQuery == true && rend == mBBQuerySingleObject)
{
mQuerySingleObjectStarted = true;
mQuerySingleObjectRequested = false;
mActiveQuery = mSingleObjectQuery;
mObjectWasVisible = true;
}
if (mActiveQuery != NULL)
mActiveQuery->beginOcclusionQuery();
@ -173,13 +155,6 @@ void OcclusionQuery::renderQueueEnded(uint8 queueGroupId, const String& invocati
mSunVisibleAreaQuery->beginOcclusionQuery();
mSunVisibleAreaQuery->endOcclusionQuery();
}
if (mObjectWasVisible == false && mDoQuery)
{
mSingleObjectQuery->beginOcclusionQuery();
mSingleObjectQuery->endOcclusionQuery();
mQuerySingleObjectStarted = true;
mQuerySingleObjectRequested = false;
}
}
}
@ -188,7 +163,6 @@ void OcclusionQuery::update(float duration)
if (!mSupported) return;
mWasVisible = false;
mObjectWasVisible = false;
// Adjust the position of the sun billboards according to camera viewing distance
// we need to do this to make sure that _everything_ can occlude the sun
@ -209,8 +183,7 @@ void OcclusionQuery::update(float duration)
mDoQuery = false;
if (!mSunTotalAreaQuery->isStillOutstanding()
&& !mSunVisibleAreaQuery->isStillOutstanding()
&& !mSingleObjectQuery->isStillOutstanding())
&& !mSunVisibleAreaQuery->isStillOutstanding())
{
unsigned int totalPixels;
unsigned int visiblePixels;
@ -229,88 +202,13 @@ void OcclusionQuery::update(float duration)
if (mSunVisibility > 1) mSunVisibility = 1;
}
unsigned int result;
mSingleObjectQuery->pullOcclusionQuery(&result);
mTestResult = (result != 0);
mQuerySingleObjectStarted = false;
mQuerySingleObjectRequested = false;
mDoQuery = true;
}
}
void OcclusionQuery::occlusionTest(const Ogre::Vector3& position, Ogre::SceneNode* object)
{
assert( !occlusionTestPending()
&& "Occlusion test still pending");
mBBQuerySingleObject->setVisible(true);
mObjectNode->setPosition(position);
// scale proportional to camera distance, in order to always give the billboard the same size in screen-space
mObjectNode->setScale( Vector3(1,1,1)*(position - mRendering->getCamera()->getRealPosition()).length() );
mQuerySingleObjectRequested = true;
mDoQuery = true;
}
bool OcclusionQuery::occlusionTestPending()
{
return (mQuerySingleObjectRequested || mQuerySingleObjectStarted);
}
void OcclusionQuery::setSunNode(Ogre::SceneNode* node)
{
mSunNode = node;
if (!mBBNode)
mBBNode = node->getParentSceneNode()->createChildSceneNode();
}
bool OcclusionQuery::getTestResult()
{
assert( !occlusionTestPending()
&& "Occlusion test still pending");
return mTestResult;
}
bool OcclusionQuery::isPotentialOccluder(Ogre::SceneNode* node)
{
bool result = false;
for (unsigned int i=0; i < node->numAttachedObjects(); ++i)
{
MovableObject* ob = node->getAttachedObject(i);
std::string type = ob->getMovableType();
if (type == "Entity")
{
Entity* ent = static_cast<Entity*>(ob);
for (unsigned int j=0; j < ent->getNumSubEntities(); ++j)
{
// if any sub entity has a material with depth write off,
// consider the object as not an occluder
MaterialPtr mat = ent->getSubEntity(j)->getMaterial();
Material::TechniqueIterator techIt = mat->getTechniqueIterator();
while (techIt.hasMoreElements())
{
Technique* tech = techIt.getNext();
Technique::PassIterator passIt = tech->getPassIterator();
while (passIt.hasMoreElements())
{
Pass* pass = passIt.getNext();
if (pass->getDepthWriteEnabled() == false)
return false;
else
result = true;
}
}
}
}
}
return result;
}

@ -40,31 +40,6 @@ namespace MWRender
*/
void update(float duration);
/**
* request occlusion test for a billboard at the given position, omitting an entity
* @param position of the billboard in ogre coordinates
* @param object to exclude from the occluders
*/
void occlusionTest(const Ogre::Vector3& position, Ogre::SceneNode* object);
/**
* @return true if a request is still outstanding
*/
bool occlusionTestPending();
/**
* Checks if the objects held by this scenenode
* can be considered as potential occluders
* (which might not be the case when transparency is involved)
* @param Scene node
*/
bool isPotentialOccluder(Ogre::SceneNode* node);
/**
* @return true if the object tested in the last request was occluded
*/
bool getTestResult();
float getSunVisibility() const {return mSunVisibility;};
void setSunNode(Ogre::SceneNode* node);
@ -72,33 +47,23 @@ namespace MWRender
private:
Ogre::HardwareOcclusionQuery* mSunTotalAreaQuery;
Ogre::HardwareOcclusionQuery* mSunVisibleAreaQuery;
Ogre::HardwareOcclusionQuery* mSingleObjectQuery;
Ogre::HardwareOcclusionQuery* mActiveQuery;
Ogre::BillboardSet* mBBQueryVisible;
Ogre::BillboardSet* mBBQueryTotal;
Ogre::BillboardSet* mBBQuerySingleObject;
Ogre::SceneNode* mSunNode;
Ogre::SceneNode* mBBNode;
Ogre::SceneNode* mBBNodeReal;
float mSunVisibility;
Ogre::SceneNode* mObjectNode;
bool mWasVisible;
bool mObjectWasVisible;
bool mTestResult;
bool mActive;
bool mSupported;
bool mDoQuery;
bool mQuerySingleObjectRequested;
bool mQuerySingleObjectStarted;
OEngine::Render::OgreRenderer* mRendering;
protected:

@ -370,7 +370,9 @@ namespace MWWorld
Ogre::SceneNode* node = ptr.getRefData().getBaseNode();
handleToMesh[node->getName()] = mesh;
OEngine::Physic::RigidBody* body = mEngine->createAndAdjustRigidBody(mesh, node->getName(), node->getScale().x, node->getPosition(), node->getOrientation());
mEngine->addRigidBody(body);
OEngine::Physic::RigidBody* raycastingBody = mEngine->createAndAdjustRigidBody
(mesh, node->getName(), node->getScale().x, node->getPosition(), node->getOrientation(), 0, 0, true);
mEngine->addRigidBody(body, true, raycastingBody);
}
void PhysicsSystem::addActor (const Ptr& ptr)
@ -395,9 +397,14 @@ namespace MWWorld
Ogre::SceneNode *node = ptr.getRefData().getBaseNode();
const std::string &handle = node->getName();
const Ogre::Vector3 &position = node->getPosition();
if(OEngine::Physic::RigidBody *body = mEngine->getRigidBody(handle))
body->getWorldTransform().setOrigin(btVector3(position.x,position.y,position.z));
else if(OEngine::Physic::PhysicActor *physact = mEngine->getCharacter(handle))
if(OEngine::Physic::RigidBody *body = mEngine->getRigidBody(handle, true))
body->getWorldTransform().setOrigin(btVector3(position.x,position.y,position.z));
if(OEngine::Physic::PhysicActor *physact = mEngine->getCharacter(handle))
physact->setPosition(position);
}
@ -418,6 +425,13 @@ namespace MWWorld
else
mEngine->boxAdjustExternal(handleToMesh[handle], body, node->getScale().x, node->getPosition(), rotation);
}
if (OEngine::Physic::RigidBody* body = mEngine->getRigidBody(handle, true))
{
if(dynamic_cast<btBoxShape*>(body->getCollisionShape()) == NULL)
body->getWorldTransform().setRotation(btQuaternion(rotation.x, rotation.y, rotation.z, rotation.w));
else
mEngine->boxAdjustExternal(handleToMesh[handle], body, node->getScale().x, node->getPosition(), rotation);
}
}
void PhysicsSystem::scaleObject (const Ptr& ptr)

@ -1018,42 +1018,10 @@ namespace MWWorld
mRendering->getSkyManager()->setGlare(!mPhysics->castRay(Ogre::Vector3(p[0], p[1], p[2]), sun));
}
// update faced handle (object the player is looking at)
// this uses a mixture of raycasts and occlusion queries.
else // if (mRendering->occlusionQuerySupported())
{
MWRender::OcclusionQuery* query = mRendering->getOcclusionQuery();
if (!query->occlusionTestPending())
{
processFacedQueryResults (query);
beginFacedQueryProcess (query);
}
}
updateFacedHandle ();
}
void World::processFacedQueryResults (MWRender::OcclusionQuery* query)
{
// get result of last query
if (mNumFacing == 0)
{
mFacedHandle = "";
mFacedDistance = FLT_MAX;
}
else if (mNumFacing == 1)
{
bool result = query->getTestResult();
mFacedHandle = result ? mFaced1Name : "";
mFacedDistance = result ? mFaced1Distance : FLT_MAX;
}
else if (mNumFacing == 2)
{
bool result = query->getTestResult();
mFacedHandle = result ? mFaced2Name : mFaced1Name;
mFacedDistance = result ? mFaced1Distance : mFaced1Distance;
}
}
void World::beginFacedQueryProcess (MWRender::OcclusionQuery* query)
void World::updateFacedHandle ()
{
// send new query
// figure out which object we want to test against
@ -1084,93 +1052,14 @@ namespace MWWorld
if (results.size() == 0)
{
mNumFacing = 0;
}
else if (results.size() == 1)
{
beginSingleFacedQueryProcess (query, results);
}
else
{
beginDoubleFacedQueryProcess (query, results);
}
}
void World::beginSingleFacedQueryProcess (MWRender::OcclusionQuery* query, std::vector < std::pair < float, std::string > > const & results)
{
mFaced1 = getPtrViaHandle(results.front().second);
mFaced1Name = results.front().second;
mFaced1Distance = results.front().first;
mNumFacing = 1;
btVector3 p;
if (MWBase::Environment::get().getWindowManager()->isGuiMode())
{
float x, y;
MWBase::Environment::get().getWindowManager()->getMousePosition(x, y);
p = mPhysics->getRayPoint(results.front().first, x, y);
}
else
p = mPhysics->getRayPoint(results.front().first);
Ogre::Vector3 pos(p.x(), p.y(), p.z());
Ogre::SceneNode* node = mFaced1.getRefData().getBaseNode();
//std::cout << "Num facing 1 : " << mFaced1Name << std::endl;
//std::cout << "Type 1 " << mFaced1.getTypeName() << std::endl;
query->occlusionTest(pos, node);
}
void World::beginDoubleFacedQueryProcess (MWRender::OcclusionQuery* query, std::vector < std::pair < float, std::string > > const & results)
{
mFaced1Name = results.at (0).second;
mFaced2Name = results.at (1).second;
mFaced1Distance = results.at (0).first;
mFaced2Distance = results.at (1).first;
mFaced1 = getPtrViaHandle(results.at (0).second);
mFaced2 = getPtrViaHandle(results.at (1).second);
mNumFacing = 2;
btVector3 p;
if (MWBase::Environment::get().getWindowManager()->isGuiMode())
{
float x, y;
MWBase::Environment::get().getWindowManager()->getMousePosition(x, y);
p = mPhysics->getRayPoint(results.at (1).first, x, y);
mFacedHandle = "";
mFacedDistance = FLT_MAX;
}
else
p = mPhysics->getRayPoint(results.at (1).first);
Ogre::Vector3 pos(p.x(), p.y(), p.z());
Ogre::SceneNode* node1 = mFaced1.getRefData().getBaseNode();
Ogre::SceneNode* node2 = mFaced2.getRefData().getBaseNode();
// no need to test if the first node is not occluder
if (!query->isPotentialOccluder(node1) && (mFaced1.getTypeName().find("Static") == std::string::npos))
{
mFacedHandle = mFaced1Name;
mFacedDistance = mFaced1Distance;
//std::cout << "node1 Not an occluder" << std::endl;
return;
mFacedHandle = results.front().second;
mFacedDistance = results.front().first;
}
// no need to test if the second object is static (thus cannot be activated)
if (mFaced2.getTypeName().find("Static") != std::string::npos)
{
mFacedHandle = mFaced1Name;
mFacedDistance = mFaced1Distance;
return;
}
// work around door problems
if (mFaced1.getTypeName().find("Static") != std::string::npos
&& mFaced2.getTypeName().find("Door") != std::string::npos)
{
mFacedHandle = mFaced2Name;
mFacedDistance = mFaced2Distance;
return;
}
query->occlusionTest(pos, node2);
}
bool World::isCellExterior() const

@ -97,10 +97,7 @@ namespace MWWorld
void updateWindowManager ();
void performUpdateSceneQueries ();
void processFacedQueryResults (MWRender::OcclusionQuery* query);
void beginFacedQueryProcess (MWRender::OcclusionQuery* query);
void beginSingleFacedQueryProcess (MWRender::OcclusionQuery* query, std::vector < std::pair < float, std::string > > const & results);
void beginDoubleFacedQueryProcess (MWRender::OcclusionQuery* query, std::vector < std::pair < float, std::string > > const & results);
void updateFacedHandle ();
float getMaxActivationDistance ();
float getNpcActivationDistance ();

@ -22,9 +22,11 @@ http://www.gnu.org/licenses/ .
*/
#include "bulletnifloader.hpp"
#include <Ogre.h>
#include <cstdio>
#include <boost/algorithm/string.hpp>
#include "../nif/niffile.hpp"
#include "../nif/node.hpp"
#include "../nif/data.hpp"
@ -46,6 +48,20 @@ typedef unsigned char ubyte;
namespace NifBullet
{
struct TriangleMeshShape : public btBvhTriangleMeshShape
{
TriangleMeshShape(btStridingMeshInterface* meshInterface, bool useQuantizedAabbCompression)
: btBvhTriangleMeshShape(meshInterface, useQuantizedAabbCompression)
{
}
virtual ~TriangleMeshShape()
{
delete getTriangleInfoMap();
delete m_meshInterface;
}
};
ManualBulletShapeLoader::~ManualBulletShapeLoader()
{
}
@ -62,10 +78,11 @@ void ManualBulletShapeLoader::loadResource(Ogre::Resource *resource)
resourceName = cShape->getName();
cShape->mCollide = false;
mBoundingBox = NULL;
cShape->boxTranslation = Ogre::Vector3(0,0,0);
cShape->boxRotation = Ogre::Quaternion::IDENTITY;
cShape->mBoxTranslation = Ogre::Vector3(0,0,0);
cShape->mBoxRotation = Ogre::Quaternion::IDENTITY;
mHasShape = false;
mTriMesh = new btTriangleMesh();
btTriangleMesh* mesh1 = new btTriangleMesh();
// Load the NIF. TODO: Wrap this in a try-catch block once we're out
// of the early stages of development. Right now we WANT to catch
@ -94,35 +111,43 @@ void ManualBulletShapeLoader::loadResource(Ogre::Resource *resource)
bool hasCollisionNode = hasRootCollisionNode(node);
//do a first pass
handleNode(node,0,hasCollisionNode,false,false);
handleNode(mesh1, node,0,hasCollisionNode,false,false);
//if collide = false, then it does a second pass which create a shape for raycasting.
if(cShape->mCollide == false)
handleNode(node,0,hasCollisionNode,false,true);
if(mBoundingBox != NULL)
{
cShape->mCollisionShape = mBoundingBox;
delete mesh1;
}
else if (mHasShape && !cShape->mIgnore && cShape->mCollide)
{
cShape->mCollisionShape = new TriangleMeshShape(mesh1,true);
}
else
delete mesh1;
//cShape->collide = hasCollisionNode&&cShape->collide;
//second pass which create a shape for raycasting.
resourceName = cShape->getName();
cShape->mCollide = false;
mBoundingBox = NULL;
cShape->mBoxTranslation = Ogre::Vector3(0,0,0);
cShape->mBoxRotation = Ogre::Quaternion::IDENTITY;
mHasShape = false;
struct TriangleMeshShape : public btBvhTriangleMeshShape
{
TriangleMeshShape(btStridingMeshInterface* meshInterface, bool useQuantizedAabbCompression)
: btBvhTriangleMeshShape(meshInterface, useQuantizedAabbCompression)
{
}
btTriangleMesh* mesh2 = new btTriangleMesh();
virtual ~TriangleMeshShape()
{
delete getTriangleInfoMap();
delete m_meshInterface;
}
};
handleNode(mesh2, node,0,hasCollisionNode,true,true);
if(mBoundingBox != NULL)
cShape->Shape = mBoundingBox;
else
{
currentShape = new TriangleMeshShape(mTriMesh,true);
cShape->Shape = currentShape;
cShape->mRaycastingShape = mBoundingBox;
delete mesh2;
}
else if (mHasShape && !cShape->mIgnore)
{
cShape->mRaycastingShape = new TriangleMeshShape(mesh2,true);
}
else
delete mesh2;
}
bool ManualBulletShapeLoader::hasRootCollisionNode(Nif::Node const * node)
@ -147,22 +172,26 @@ bool ManualBulletShapeLoader::hasRootCollisionNode(Nif::Node const * node)
return false;
}
void ManualBulletShapeLoader::handleNode(const Nif::Node *node, int flags,
void ManualBulletShapeLoader::handleNode(btTriangleMesh* mesh, const Nif::Node *node, int flags,
bool hasCollisionNode, bool isCollisionNode,
bool raycastingOnly)
bool raycasting)
{
// Accumulate the flags from all the child nodes. This works for all
// the flags we currently use, at least.
flags |= node->flags;
isCollisionNode = isCollisionNode || (node->recType == Nif::RC_RootCollisionNode);
if (!raycasting)
isCollisionNode = isCollisionNode || (node->recType == Nif::RC_RootCollisionNode);
else
isCollisionNode = isCollisionNode && (node->recType != Nif::RC_RootCollisionNode);
// Marker objects: no collision
/// \todo don't do this in the editor
if (node->name.find("marker") != std::string::npos)
std::string nodename = node->name;
boost::algorithm::to_lower(nodename);
if (nodename.find("marker") != std::string::npos)
{
flags |= 0x800;
cShape->mIgnore = true;
return;
}
// Check for extra data
@ -185,7 +214,7 @@ void ManualBulletShapeLoader::handleNode(const Nif::Node *node, int flags,
// No collision. Use an internal flag setting to mark this.
flags |= 0x800;
}
else if (sd->string == "MRK" && !raycastingOnly)
else if (sd->string == "MRK")
// Marker objects. These are only visible in the
// editor. Until and unless we add an editor component to
// the engine, just skip this entire node.
@ -193,20 +222,16 @@ void ManualBulletShapeLoader::handleNode(const Nif::Node *node, int flags,
}
}
if(!hasCollisionNode || isCollisionNode)
if(node->hasBounds)
{
if(node->hasBounds)
{
cShape->boxTranslation = node->boundPos;
cShape->boxRotation = node->boundRot;
mBoundingBox = new btBoxShape(getbtVector(node->boundXYZ));
}
if(node->recType == Nif::RC_NiTriShape)
{
cShape->mCollide = !(flags&0x800);
handleNiTriShape(static_cast<const Nif::NiTriShape*>(node), flags, node->getWorldTransform(), raycastingOnly);
}
cShape->mBoxTranslation = node->boundPos;
cShape->mBoxRotation = node->boundRot;
mBoundingBox = new btBoxShape(getbtVector(node->boundXYZ));
}
else if( (isCollisionNode || (!hasCollisionNode && !raycasting)) && node->recType == Nif::RC_NiTriShape)
{
cShape->mCollide = !(flags&0x800);
handleNiTriShape(mesh, static_cast<const Nif::NiTriShape*>(node), flags, node->getWorldTransform(), raycasting);
}
// For NiNodes, loop through children
@ -217,13 +242,13 @@ void ManualBulletShapeLoader::handleNode(const Nif::Node *node, int flags,
for(size_t i = 0;i < list.length();i++)
{
if(!list[i].empty())
handleNode(list[i].getPtr(), flags, hasCollisionNode, isCollisionNode, raycastingOnly);
handleNode(mesh, list[i].getPtr(), flags, hasCollisionNode, isCollisionNode, raycasting);
}
}
}
void ManualBulletShapeLoader::handleNiTriShape(const Nif::NiTriShape *shape, int flags, const Ogre::Matrix4 &transform,
bool raycastingOnly)
void ManualBulletShapeLoader::handleNiTriShape(btTriangleMesh* mesh, const Nif::NiTriShape *shape, int flags, const Ogre::Matrix4 &transform,
bool raycasting)
{
assert(shape != NULL);
@ -234,18 +259,19 @@ void ManualBulletShapeLoader::handleNiTriShape(const Nif::NiTriShape *shape, int
// If the object was marked "NCO" earlier, it shouldn't collide with
// anything. So don't do anything.
if (flags & 0x800 && !raycastingOnly)
if ((flags & 0x800) && !raycasting)
{
collide = false;
bbcollide = false;
return;
}
if (!collide && !bbcollide && hidden && !raycastingOnly)
if (!collide && !bbcollide && hidden && !raycasting)
// This mesh apparently isn't being used for anything, so don't
// bother setting it up.
return;
mHasShape = true;
const Nif::NiTriShapeData *data = shape->data.getPtr();
const std::vector<Ogre::Vector3> &vertices = data->vertices;
@ -255,7 +281,7 @@ void ManualBulletShapeLoader::handleNiTriShape(const Nif::NiTriShape *shape, int
Ogre::Vector3 b1 = transform*vertices[triangles[i+0]];
Ogre::Vector3 b2 = transform*vertices[triangles[i+1]];
Ogre::Vector3 b3 = transform*vertices[triangles[i+2]];
mTriMesh->addTriangle(btVector3(b1.x,b1.y,b1.z),btVector3(b2.x,b2.y,b2.z),btVector3(b3.x,b3.y,b3.z));
mesh->addTriangle(btVector3(b1.x,b1.y,b1.z),btVector3(b2.x,b2.y,b2.z),btVector3(b3.x,b3.y,b3.z));
}
}

@ -24,13 +24,12 @@
#ifndef OPENMW_COMPONENTS_NIFBULLET_BULLETNIFLOADER_HPP
#define OPENMW_COMPONENTS_NIFBULLET_BULLETNIFLOADER_HPP
#include <OgreMesh.h>
#include <cassert>
#include <string>
#include <BulletCollision/CollisionShapes/btBvhTriangleMeshShape.h>
#include <BulletCollision/CollisionShapes/btConvexTriangleMeshShape.h>
#include <btBulletDynamicsCommon.h>
#include "openengine/bullet/BulletShapeLoader.h"
#include <openengine/bullet/BulletShapeLoader.h>
// For warning messages
#include <iostream>
@ -83,7 +82,7 @@ private:
/**
*Parse a node.
*/
void handleNode(Nif::Node const *node, int flags, bool hasCollisionNode, bool isCollisionNode, bool raycastingOnly);
void handleNode(btTriangleMesh* mesh, Nif::Node const *node, int flags, bool hasCollisionNode, bool isCollisionNode, bool raycasting);
/**
*Helper function
@ -93,15 +92,15 @@ private:
/**
*convert a NiTriShape to a bullet trishape.
*/
void handleNiTriShape(const Nif::NiTriShape *shape, int flags, const Ogre::Matrix4 &transform, bool raycastingOnly);
void handleNiTriShape(btTriangleMesh* mesh, const Nif::NiTriShape *shape, int flags, const Ogre::Matrix4 &transform, bool raycasting);
std::string resourceName;
std::string resourceGroup;
OEngine::Physic::BulletShape* cShape;//current shape
btTriangleMesh *mTriMesh;
btBoxShape *mBoundingBox;
btBvhTriangleMeshShape* currentShape;//the shape curently under construction
bool mHasShape;
};
}

@ -17,7 +17,8 @@ Ogre::Resource(creator, name, handle, group, isManual, loader)
list all the options that need to be set before loading, of which
we have none as such. Full details can be set through scripts.
*/
Shape = NULL;
mCollisionShape = NULL;
mRaycastingShape = NULL;
mCollide = true;
mIgnore = false;
createParamDictionary("BulletShape");
@ -25,7 +26,8 @@ Ogre::Resource(creator, name, handle, group, isManual, loader)
BulletShape::~BulletShape()
{
deleteShape(Shape);
deleteShape(mCollisionShape);
deleteShape(mRaycastingShape);
}
// farm out to BulletShapeLoader
@ -34,27 +36,28 @@ void BulletShape::loadImpl()
mLoader->loadResource(this);
}
void BulletShape::deleteShape(btCollisionShape* mShape)
void BulletShape::deleteShape(btCollisionShape* shape)
{
if(mShape!=NULL)
if(shape!=NULL)
{
if(mShape->isCompound())
if(shape->isCompound())
{
btCompoundShape* ms = static_cast<btCompoundShape*>(Shape);
btCompoundShape* ms = static_cast<btCompoundShape*>(mCollisionShape);
int a = ms->getNumChildShapes();
for(int i=0; i <a;i++)
{
deleteShape(ms->getChildShape(i));
}
}
delete mShape;
delete shape;
}
mShape = NULL;
shape = NULL;
}
void BulletShape::unloadImpl()
{
deleteShape(Shape);
deleteShape(mCollisionShape);
deleteShape(mRaycastingShape);
}
//TODO:change this?

@ -22,7 +22,7 @@ protected:
void unloadImpl();
size_t calculateSize() const;
void deleteShape(btCollisionShape* mShape);
void deleteShape(btCollisionShape* shape);
public:
@ -32,9 +32,11 @@ public:
virtual ~BulletShape();
btCollisionShape* Shape;
Ogre::Vector3 boxTranslation;
Ogre::Quaternion boxRotation;
btCollisionShape* mCollisionShape;
btCollisionShape* mRaycastingShape;
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;

@ -25,11 +25,10 @@ namespace Physic
if(name == "player")
collisionMode = false;
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); //Add rigid body to dynamics world, but do not add to object map
//mBody->setCollisionFlags(COL_NOTHING);
//mBody->setMas
mEngine->addRigidBody(mBody, false, mRaycastingBody); //Add rigid body to dynamics world, but do not add to object map
}
PhysicActor::~PhysicActor()
@ -39,6 +38,11 @@ namespace Physic
mEngine->dynamicsWorld->removeRigidBody(mBody);
delete mBody;
}
if(mRaycastingBody)
{
mEngine->dynamicsWorld->removeRigidBody(mRaycastingBody);
delete mRaycastingBody;
}
}
void PhysicActor::enableCollisions(bool collision)
@ -52,13 +56,18 @@ namespace Physic
void PhysicActor::setPosition(const Ogre::Vector3 &pos)
{
if(pos != getPosition())
{
mEngine->adjustRigidBody(mBody, pos, getRotation(), mBoxScaledTranslation, mBoxRotation);
mEngine->adjustRigidBody(mRaycastingBody, pos, getRotation(), mBoxScaledTranslation, mBoxRotation);
}
}
void PhysicActor::setRotation(const Ogre::Quaternion &quat)
{
if(!quat.equals(getRotation(), Ogre::Radian(0))){
mEngine->adjustRigidBody(mBody, getPosition(), quat, mBoxScaledTranslation, mBoxRotation);
mEngine->adjustRigidBody(mRaycastingBody, getPosition(), quat, mBoxScaledTranslation, mBoxRotation);
}
}
@ -135,7 +144,6 @@ namespace Physic
RigidBody::RigidBody(btRigidBody::btRigidBodyConstructionInfo& CI,std::string name)
: btRigidBody(CI)
, mName(name)
, mIgnore(false)
{
}
@ -226,8 +234,19 @@ namespace Physic
delete hf_it->second.mBody;
}
RigidBodyContainer::iterator rb_it = ObjectMap.begin();
for (; rb_it != ObjectMap.end(); ++rb_it)
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)
{
@ -243,8 +262,6 @@ namespace Physic
{
if (pa_it->second != NULL)
{
delete pa_it->second;
pa_it->second = NULL;
}
@ -296,7 +313,6 @@ namespace Physic
btRigidBody::btRigidBodyConstructionInfo CI = btRigidBody::btRigidBodyConstructionInfo(0,newMotionState,hfShape);
RigidBody* body = new RigidBody(CI,name);
body->mCollide = true;
body->getWorldTransform().setOrigin(btVector3( (x+0.5)*triSize*(sqrtVerts-1), (y+0.5)*triSize*(sqrtVerts-1), (maxh+minh)/2.f));
HeightField hf;
@ -347,12 +363,12 @@ namespace Physic
BulletShapeManager::getSingletonPtr()->load(outputstring,"General");
BulletShapePtr shape = BulletShapeManager::getSingleton().getByName(outputstring,"General");
adjustRigidBody(body, position, rotation, shape->boxTranslation * scale, shape->boxRotation);
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)
Ogre::Vector3* scaledBoxTranslation, Ogre::Quaternion* boxRotation, bool raycasting)
{
std::string sid = (boost::format("%07.3f") % scale).str();
std::string outputstring = mesh + sid;
@ -361,72 +377,77 @@ namespace Physic
mShapeLoader->load(outputstring,"General");
BulletShapeManager::getSingletonPtr()->load(outputstring,"General");
BulletShapePtr shape = BulletShapeManager::getSingleton().getByName(outputstring,"General");
shape->Shape->setLocalScaling( btVector3(scale,scale,scale));
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,shape->Shape);
btRigidBody::btRigidBodyConstructionInfo CI = btRigidBody::btRigidBodyConstructionInfo
(0,newMotionState, raycasting ? shape->mRaycastingShape : shape->mCollisionShape);
RigidBody* body = new RigidBody(CI,name);
body->mCollide = shape->mCollide;
body->mIgnore = shape->mIgnore;
if(scaledBoxTranslation != 0)
*scaledBoxTranslation = shape->boxTranslation * scale;
*scaledBoxTranslation = shape->mBoxTranslation * scale;
if(boxRotation != 0)
*boxRotation = shape->boxRotation;
*boxRotation = shape->mBoxRotation;
adjustRigidBody(body, position, rotation, shape->boxTranslation * scale, shape->boxRotation);
adjustRigidBody(body, position, rotation, shape->mBoxTranslation * scale, shape->mBoxRotation);
return body;
}
void PhysicEngine::addRigidBody(RigidBody* body, bool addToMap)
void PhysicEngine::addRigidBody(RigidBody* body, bool addToMap, RigidBody* raycastingBody)
{
if(body)
{
if (body->mIgnore)
return;
if(body->mCollide)
{
dynamicsWorld->addRigidBody(body,CollisionType_World,CollisionType_World|CollisionType_ActorInternal|CollisionType_ActorExternal);
}
else
{
dynamicsWorld->addRigidBody(body,CollisionType_Raycasting,CollisionType_Raycasting|CollisionType_World);
}
body->setActivationState(DISABLE_DEACTIVATION);
if(addToMap){
RigidBody* oldBody = ObjectMap[body->mName];
if (oldBody != NULL)
{
dynamicsWorld->removeRigidBody(oldBody);
delete oldBody;
}
ObjectMap[body->mName] = body;
}
if(!body && !raycastingBody)
return; // nothing to do
const std::string& name = (body ? body->mName : raycastingBody->mName);
if (body)
dynamicsWorld->addRigidBody(body,CollisionType_World,CollisionType_World|CollisionType_ActorInternal|CollisionType_ActorExternal);
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 = ObjectMap.find(name);
if (it != ObjectMap.end() )
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)
{
// broadphase->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(body->getBroadphaseProxy(),dispatcher);
/*PhysicActorContainer::iterator it2 = PhysicActorMap.begin();
for(;it2!=PhysicActorMap.end();it++)
{
it2->second->internalGhostObject->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(body->getBroadphaseProxy(),dispatcher);
it2->second->externalGhostObject->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(body->getBroadphaseProxy(),dispatcher);
}*/
dynamicsWorld->removeRigidBody(body);
}
}
@ -434,35 +455,42 @@ namespace Physic
void PhysicEngine::deleteRigidBody(const std::string &name)
{
RigidBodyContainer::iterator it = ObjectMap.find(name);
if (it != ObjectMap.end() )
RigidBodyContainer::iterator it = mCollisionObjectMap.find(name);
if (it != mCollisionObjectMap.end() )
{
RigidBody* body = it->second;
//btScaledBvhTriangleMeshShape* scaled = dynamic_cast<btScaledBvhTriangleMeshShape*> (body->getCollisionShape());
if(body != NULL)
{
delete body;
}
/*if(scaled != NULL)
mCollisionObjectMap.erase(it);
}
it = mRaycastingObjectMap.find(name);
if (it != mRaycastingObjectMap.end() )
{
RigidBody* body = it->second;
if(body != NULL)
{
delete scaled;
}*/
ObjectMap.erase(it);
delete body;
}
mRaycastingObjectMap.erase(it);
}
}
RigidBody* PhysicEngine::getRigidBody(const std::string &name)
RigidBody* PhysicEngine::getRigidBody(const std::string &name, bool raycasting)
{
RigidBodyContainer::iterator it = ObjectMap.find(name);
if (it != ObjectMap.end() )
RigidBodyContainer* map = raycasting ? &mRaycastingObjectMap : &mCollisionObjectMap;
RigidBodyContainer::iterator it = map->find(name);
if (it != map->end() )
{
RigidBody* body = ObjectMap[name];
RigidBody* body = (*map)[name];
return body;
}
else
{
return 0;
return NULL;
}
}
@ -530,7 +558,7 @@ namespace Physic
float d1 = 10000.;
btCollisionWorld::ClosestRayResultCallback resultCallback1(from, to);
resultCallback1.m_collisionFilterMask = CollisionType_World|CollisionType_Raycasting;
resultCallback1.m_collisionFilterMask = CollisionType_Raycasting;
dynamicsWorld->rayTest(from, to, resultCallback1);
if (resultCallback1.hasHit())
{
@ -539,35 +567,16 @@ namespace Physic
d = d1;
}
btCollisionWorld::ClosestRayResultCallback resultCallback2(from, to);
resultCallback2.m_collisionFilterMask = CollisionType_ActorInternal|CollisionType_ActorExternal;
dynamicsWorld->rayTest(from, to, resultCallback2);
float d2 = 10000.;
if (resultCallback2.hasHit())
{
d2 = resultCallback1.m_closestHitFraction;
if(d2<=d1)
{
name = static_cast<const PairCachingGhostObject&>(*resultCallback2.m_collisionObject).mName;
d = d2;
}
}
return std::pair<std::string,float>(name,d);
}
std::vector< std::pair<float, std::string> > PhysicEngine::rayTest2(btVector3& from, btVector3& to)
{
MyRayResultCallback resultCallback1;
resultCallback1.m_collisionFilterMask = CollisionType_World|CollisionType_Raycasting;
resultCallback1.m_collisionFilterMask = CollisionType_Raycasting;
dynamicsWorld->rayTest(from, to, resultCallback1);
std::vector< std::pair<float, const btCollisionObject*> > results = resultCallback1.results;
MyRayResultCallback resultCallback2;
resultCallback2.m_collisionFilterMask = CollisionType_ActorInternal|CollisionType_ActorExternal;
dynamicsWorld->rayTest(from, to, resultCallback2);
std::vector< std::pair<float, const btCollisionObject*> > actorResults = resultCallback2.results;
std::vector< std::pair<float, std::string> > results2;
for (std::vector< std::pair<float, const btCollisionObject*> >::iterator it=results.begin();
@ -576,12 +585,6 @@ namespace Physic
results2.push_back( std::make_pair( (*it).first, static_cast<const RigidBody&>(*(*it).second).mName ) );
}
for (std::vector< std::pair<float, const btCollisionObject*> >::iterator it=actorResults.begin();
it != actorResults.end(); ++it)
{
results2.push_back( std::make_pair( (*it).first, static_cast<const PairCachingGhostObject&>(*(*it).second).mName ) );
}
std::sort(results2.begin(), results2.end(), MyRayResultCallback::cmp);
return results2;
@ -600,6 +603,6 @@ namespace Physic
btTransform trans;
trans.setIdentity();
shape->Shape->getAabb(trans, min, max);
shape->mCollisionShape->getAabb(trans, min, max);
}
}}

@ -141,6 +141,7 @@ namespace Physic
private:
OEngine::Physic::RigidBody* mBody;
OEngine::Physic::RigidBody* mRaycastingBody;
Ogre::Vector3 mBoxScaledTranslation;
btQuaternion mBoxRotationInverse;
Ogre::Quaternion mBoxRotation;
@ -163,10 +164,6 @@ namespace Physic
RigidBody(btRigidBody::btRigidBodyConstructionInfo& CI,std::string name);
virtual ~RigidBody();
std::string mName;
//is this body used for raycasting only?
bool mCollide;
bool mIgnore;
};
struct HeightField
@ -200,7 +197,7 @@ namespace Physic
*/
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);
Ogre::Vector3* scaledBoxTranslation = 0, Ogre::Quaternion* boxRotation = 0, bool raycasting=false);
/**
* Adjusts a rigid body to the right position and rotation
@ -228,7 +225,7 @@ namespace Physic
/**
* Add a RigidBody to the simulation
*/
void addRigidBody(RigidBody* body, bool addToMap = true);
void addRigidBody(RigidBody* body, bool addToMap = true, RigidBody* raycastingBody = NULL);
/**
* Remove a RigidBody from the simulation. It does not delete it, and does not remove it from the RigidBodyMap.
@ -242,9 +239,8 @@ namespace Physic
/**
* Return a pointer to a given rigid body.
* TODO:check if exist
*/
RigidBody* getRigidBody(const std::string &name);
RigidBody* getRigidBody(const std::string &name, bool raycasting=false);
/**
* Create and add a character to the scene, and add it to the ActorMap.
@ -322,7 +318,9 @@ namespace Physic
HeightFieldContainer mHeightFieldMap;
typedef std::map<std::string,RigidBody*> RigidBodyContainer;
RigidBodyContainer ObjectMap;
RigidBodyContainer mCollisionObjectMap;
RigidBodyContainer mRaycastingObjectMap;
typedef std::map<std::string, PhysicActor*> PhysicActorContainer;
PhysicActorContainer PhysicActorMap;

@ -27,7 +27,7 @@ void newtrace(traceResults *results, const Ogre::Vector3& start, const Ogre::Vec
const btTransform to(btrot, btend);
btCollisionWorld::ClosestConvexResultCallback newTraceCallback(btstart, btend);
newTraceCallback.m_collisionFilterMask = OEngine::Physic::CollisionType_World|OEngine::Physic::CollisionType_Raycasting;
newTraceCallback.m_collisionFilterMask = OEngine::Physic::CollisionType_World;
enginePass->dynamicsWorld->convexSweepTest(&newshape, from, to, newTraceCallback);

Loading…
Cancel
Save