object pickup should work everywhere

This commit is contained in:
scrawl 2012-03-25 20:52:56 +02:00
parent 17a4adfe88
commit 53d4be5cf6
8 changed files with 198 additions and 46 deletions

View file

@ -47,7 +47,7 @@ OcclusionQuery::OcclusionQuery(OEngine::Render::OgreRenderer* renderer, SceneNod
matQueryArea->setDepthCheckEnabled(false); // Not occluded by objects matQueryArea->setDepthCheckEnabled(false); // Not occluded by objects
MaterialPtr matQueryVisible = matBase->clone("QueryVisiblePixels"); MaterialPtr matQueryVisible = matBase->clone("QueryVisiblePixels");
matQueryVisible->setDepthWriteEnabled(false); matQueryVisible->setDepthWriteEnabled(false);
matQueryVisible->setColourWriteEnabled(false); matQueryVisible->setColourWriteEnabled(false); // Uncomment this to visualize the occlusion query
matQueryVisible->setDepthCheckEnabled(true); // Occluded by objects matQueryVisible->setDepthCheckEnabled(true); // Occluded by objects
mBBNode = mSunNode->getParentSceneNode()->createChildSceneNode(); mBBNode = mSunNode->getParentSceneNode()->createChildSceneNode();
@ -69,13 +69,14 @@ OcclusionQuery::OcclusionQuery(OEngine::Render::OgreRenderer* renderer, SceneNod
mBBNode->attachObject(mBBQueryVisible); mBBNode->attachObject(mBBQueryVisible);
mBBQuerySingleObject = mRendering->getScene()->createBillboardSet(1); mBBQuerySingleObject = mRendering->getScene()->createBillboardSet(1);
mBBQuerySingleObject->setDefaultDimensions(10, 10); mBBQuerySingleObject->setDefaultDimensions(0.01, 0.01);
mBBQuerySingleObject->createBillboard(Vector3::ZERO); mBBQuerySingleObject->createBillboard(Vector3::ZERO);
mBBQuerySingleObject->setMaterialName("QueryVisiblePixels"); mBBQuerySingleObject->setMaterialName("QueryVisiblePixels");
mBBQuerySingleObject->setRenderQueueGroup(queue); mBBQuerySingleObject->setRenderQueueGroup(queue);
mObjectNode->attachObject(mBBQuerySingleObject); mObjectNode->attachObject(mBBQuerySingleObject);
mRendering->getScene()->addRenderObjectListener(this); mRendering->getScene()->addRenderObjectListener(this);
mRendering->getScene()->addRenderQueueListener(this);
mDoQuery = true; mDoQuery = true;
} }
@ -95,8 +96,6 @@ bool OcclusionQuery::supported()
void OcclusionQuery::notifyRenderSingleObject(Renderable* rend, const Pass* pass, const AutoParamDataSource* source, void OcclusionQuery::notifyRenderSingleObject(Renderable* rend, const Pass* pass, const AutoParamDataSource* source,
const LightList* pLightList, bool suppressRenderStateChanges) const LightList* pLightList, bool suppressRenderStateChanges)
{ {
if (!mSupported) return;
// The following code activates and deactivates the occlusion queries // The following code activates and deactivates the occlusion queries
// so that the queries only include the rendering of their intended targets // so that the queries only include the rendering of their intended targets
@ -112,10 +111,16 @@ void OcclusionQuery::notifyRenderSingleObject(Renderable* rend, const Pass* pass
if (mDoQuery == true) if (mDoQuery == true)
{ {
if (rend == mBBQueryTotal) if (rend == mBBQueryTotal)
{
mActiveQuery = mSunTotalAreaQuery; mActiveQuery = mSunTotalAreaQuery;
mWasVisible = true;
}
else if (rend == mBBQueryVisible) else if (rend == mBBQueryVisible)
{
mActiveQuery = mSunVisibleAreaQuery; mActiveQuery = mSunVisibleAreaQuery;
else if (rend == mBBQuerySingleObject && mQuerySingleObjectRequested) }
}
if (rend == mBBQuerySingleObject && mQuerySingleObjectRequested)
{ {
mQuerySingleObjectStarted = true; mQuerySingleObjectStarted = true;
mQuerySingleObjectRequested = false; mQuerySingleObjectRequested = false;
@ -125,12 +130,27 @@ void OcclusionQuery::notifyRenderSingleObject(Renderable* rend, const Pass* pass
if (mActiveQuery != NULL) if (mActiveQuery != NULL)
mActiveQuery->beginOcclusionQuery(); mActiveQuery->beginOcclusionQuery();
} }
void OcclusionQuery::renderQueueEnded(uint8 queueGroupId, const String& invocation, bool& repeatThisInvocation)
{
if (queueGroupId == RENDER_QUEUE_SKIES_LATE && mWasVisible == false)
{
// for some reason our single object query returns wrong results when the sun query was never executed
// (which can happen when we are in interiors, or when the sun is outside of the view frustum and gets culled)
// so we force it here once everything has been rendered
mSunTotalAreaQuery->beginOcclusionQuery();
mSunTotalAreaQuery->endOcclusionQuery();
mSunVisibleAreaQuery->beginOcclusionQuery();
mSunVisibleAreaQuery->endOcclusionQuery();
}
} }
void OcclusionQuery::update() void OcclusionQuery::update()
{ {
if (!mSupported) return; if (!mSupported) return;
mWasVisible = false;
// Adjust the position of the sun billboards according to camera viewing distance // 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 // we need to do this to make sure that _everything_ can occlude the sun
float dist = mRendering->getCamera()->getFarClipDistance(); float dist = mRendering->getCamera()->getFarClipDistance();
@ -145,8 +165,7 @@ void OcclusionQuery::update()
mDoQuery = false; mDoQuery = false;
if (!mSunTotalAreaQuery->isStillOutstanding() if (!mSunTotalAreaQuery->isStillOutstanding()
&& !mSunVisibleAreaQuery->isStillOutstanding() && !mSunVisibleAreaQuery->isStillOutstanding())
&& !mSingleObjectQuery->isStillOutstanding())
{ {
unsigned int totalPixels; unsigned int totalPixels;
unsigned int visiblePixels; unsigned int visiblePixels;
@ -165,24 +184,33 @@ void OcclusionQuery::update()
if (mSunVisibility > 1) mSunVisibility = 1; if (mSunVisibility > 1) mSunVisibility = 1;
} }
if (mQuerySingleObjectStarted) mDoQuery = true;
}
if (!mSingleObjectQuery->isStillOutstanding() && mQuerySingleObjectStarted)
{ {
unsigned int visiblePixels; unsigned int result;
mSingleObjectQuery->pullOcclusionQuery(&visiblePixels); mSingleObjectQuery->pullOcclusionQuery(&result);
//std::cout << "Single object query result: " << result << " pixels " << std::endl;
mTestResult = (result != 0);
mBBQuerySingleObject->setVisible(false); mBBQuerySingleObject->setVisible(false);
mObject->setRenderQueueGroup(mObjectOldRenderQueue);
// restore old render queues
for (std::vector<ObjectInfo>::iterator it=mObjectsInfo.begin();
it!=mObjectsInfo.end(); ++it)
{
if (!mRendering->getScene()->hasMovableObject((*it).name, (*it).typeName)) return;
mRendering->getScene()->getMovableObject((*it).name, (*it).typeName)->setRenderQueueGroup( (*it).oldRenderqueue );
}
mQuerySingleObjectStarted = false; mQuerySingleObjectStarted = false;
mQuerySingleObjectRequested = false; mQuerySingleObjectRequested = false;
} }
mDoQuery = true;
}
} }
void OcclusionQuery::occlusionTest(const Ogre::Vector3& position, Ogre::Entity* entity) void OcclusionQuery::occlusionTest(const Ogre::Vector3& position, Ogre::SceneNode* object)
{ {
assert( !occlusionTestPending() assert( !occlusionTestPending()
&& "Occlusion test still pending"); && "Occlusion test still pending");
@ -190,11 +218,24 @@ void OcclusionQuery::occlusionTest(const Ogre::Vector3& position, Ogre::Entity*
mBBQuerySingleObject->setVisible(true); mBBQuerySingleObject->setVisible(true);
// we don't want the object to occlude itself // we don't want the object to occlude itself
mObjectOldRenderQueue = entity->getRenderQueueGroup(); // put it in a render queue _after_ the occlusion query
if (mObjectOldRenderQueue < RENDER_QUEUE_MAIN+2) mObjectsInfo.clear();
entity->setRenderQueueGroup(RENDER_QUEUE_MAIN+2); for (int i=0; i<object->numAttachedObjects(); ++i)
{
ObjectInfo info;
MovableObject* obj = object->getAttachedObject(i);
info.name = obj->getName();
info.typeName = obj->getMovableType();
info.oldRenderqueue = obj->getRenderQueueGroup();
mObjectsInfo.push_back(info);
object->getAttachedObject(i)->setRenderQueueGroup(RENDER_QUEUE_MAIN+5);
}
mObjectNode->setPosition(position); 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; mQuerySingleObjectRequested = true;
} }

View file

@ -2,6 +2,7 @@
#define _GAME_OCCLUSION_QUERY_H #define _GAME_OCCLUSION_QUERY_H
#include <OgreRenderObjectListener.h> #include <OgreRenderObjectListener.h>
#include <OgreRenderQueueListener.h>
namespace Ogre namespace Ogre
{ {
@ -17,7 +18,14 @@ namespace MWRender
/// ///
/// \brief Implements hardware occlusion queries on the GPU /// \brief Implements hardware occlusion queries on the GPU
/// ///
class OcclusionQuery : public Ogre::RenderObjectListener struct ObjectInfo
{
int oldRenderqueue;
std::string name;
std::string typeName;
};
class OcclusionQuery : public Ogre::RenderObjectListener, public Ogre::RenderQueueListener
{ {
public: public:
OcclusionQuery(OEngine::Render::OgreRenderer*, Ogre::SceneNode* sunNode); OcclusionQuery(OEngine::Render::OgreRenderer*, Ogre::SceneNode* sunNode);
@ -36,9 +44,9 @@ namespace MWRender
/** /**
* request occlusion test for a billboard at the given position, omitting an entity * request occlusion test for a billboard at the given position, omitting an entity
* @param position of the billboard in ogre coordinates * @param position of the billboard in ogre coordinates
* @param entity to exclude from the occluders * @param object to exclude from the occluders
*/ */
void occlusionTest(const Ogre::Vector3& position, Ogre::Entity* entity); void occlusionTest(const Ogre::Vector3& position, Ogre::SceneNode* object);
/** /**
* @return true if a request is still outstanding * @return true if a request is still outstanding
@ -66,10 +74,10 @@ namespace MWRender
Ogre::SceneNode* mBBNode; Ogre::SceneNode* mBBNode;
float mSunVisibility; float mSunVisibility;
Ogre::Entity* mObject;
Ogre::SceneNode* mObjectNode; Ogre::SceneNode* mObjectNode;
int mObjectOldRenderQueue; std::vector<ObjectInfo> mObjectsInfo;
bool mWasVisible;
bool mTestResult; bool mTestResult;
@ -84,6 +92,8 @@ namespace MWRender
protected: protected:
virtual void notifyRenderSingleObject(Ogre::Renderable* rend, const Ogre::Pass* pass, const Ogre::AutoParamDataSource* source, virtual void notifyRenderSingleObject(Ogre::Renderable* rend, const Ogre::Pass* pass, const Ogre::AutoParamDataSource* source,
const Ogre::LightList* pLightList, bool suppressRenderStateChanges); const Ogre::LightList* pLightList, bool suppressRenderStateChanges);
virtual void renderQueueEnded(Ogre::uint8 queueGroupId, const Ogre::String& invocation, bool& repeatThisInvocation);
}; };
} }

View file

@ -100,6 +100,7 @@ class RenderingManager: private RenderingInterface {
void sunDisable(); void sunDisable();
bool occlusionQuerySupported() { return mOcclusionQuery->supported(); }; bool occlusionQuerySupported() { return mOcclusionQuery->supported(); };
OcclusionQuery* getOcclusionQuery() { return mOcclusionQuery; };
void setGlare(bool glare); void setGlare(bool glare);
void skyEnable (); void skyEnable ();

View file

@ -51,6 +51,33 @@ namespace MWWorld
return mEngine->rayTest(from,to); return mEngine->rayTest(from,to);
} }
std::vector < std::pair <float, std::string> > PhysicsSystem::getFacedObjects ()
{
//get a ray pointing to the center of the viewport
Ray centerRay = mRender.getCamera()->getCameraToViewportRay(
mRender.getViewport()->getWidth()/2,
mRender.getViewport()->getHeight()/2);
//let's avoid the capsule shape of the player.
centerRay.setOrigin(centerRay.getOrigin() + 20*centerRay.getDirection());
btVector3 from(centerRay.getOrigin().x,-centerRay.getOrigin().z,centerRay.getOrigin().y);
btVector3 to(centerRay.getPoint(500).x,-centerRay.getPoint(500).z,centerRay.getPoint(500).y);
return mEngine->rayTest2(from,to);
}
btVector3 PhysicsSystem::getRayPoint(float extent)
{
//get a ray pointing to the center of the viewport
Ray centerRay = mRender.getCamera()->getCameraToViewportRay(
mRender.getViewport()->getWidth()/2,
mRender.getViewport()->getHeight()/2);
//let's avoid the capsule shape of the player.
centerRay.setOrigin(centerRay.getOrigin() + 20*centerRay.getDirection());
btVector3 from(centerRay.getOrigin().x,-centerRay.getOrigin().z,centerRay.getOrigin().y);
btVector3 to(centerRay.getPoint(500).x,-centerRay.getPoint(500).z,centerRay.getPoint(500).y);
return from * (1-extent) + to * extent;
}
bool PhysicsSystem::castRay(const Vector3& from, const Vector3& to) bool PhysicsSystem::castRay(const Vector3& from, const Vector3& to)
{ {
btVector3 _from, _to; btVector3 _from, _to;

View file

@ -36,6 +36,10 @@ namespace MWWorld
std::pair<std::string, float> getFacedHandle (MWWorld::World& world); std::pair<std::string, float> getFacedHandle (MWWorld::World& world);
btVector3 getRayPoint(float extent);
std::vector < std::pair <float, std::string> > getFacedObjects ();
// cast ray, return true if it hit something // cast ray, return true if it hit something
bool castRay(const Ogre::Vector3& from, const Ogre::Vector3& to); bool castRay(const Ogre::Vector3& from, const Ogre::Vector3& to);

View file

@ -497,6 +497,8 @@ namespace MWWorld
} }
std::string World::getFacedHandle() std::string World::getFacedHandle()
{
if (!mRendering->occlusionQuerySupported())
{ {
std::pair<std::string, float> result = mPhysics->getFacedHandle (*this); std::pair<std::string, float> result = mPhysics->getFacedHandle (*this);
@ -506,6 +508,12 @@ namespace MWWorld
return result.first; return result.first;
} }
else
{
// updated every few frames in update()
return mFacedHandle;
}
}
void World::deleteObject (Ptr ptr) void World::deleteObject (Ptr ptr)
{ {
@ -715,6 +723,61 @@ namespace MWWorld
sun = Vector3(sun.x, -sun.z, sun.y); sun = Vector3(sun.x, -sun.z, sun.y);
mRendering->getSkyManager()->setGlare(!mPhysics->castRay(Ogre::Vector3(p[0], p[1], p[2]), sun)); 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())
{
// get result of last query
if (mNumFacing == 0) mFacedHandle = "";
else if (mNumFacing == 1)
{
bool result = query->getTestResult();
mFacedHandle = result ? mFaced1Name : "";
}
else if (mNumFacing == 2)
{
bool result = query->getTestResult();
mFacedHandle = result ? mFaced2Name : mFaced1Name;
}
// send new query
// figure out which object we want to test against
std::vector < std::pair < float, std::string > > results = mPhysics->getFacedObjects();
if (results.size() == 0)
{
mNumFacing = 0;
}
else if (results.size() == 1)
{
mFaced1 = getPtrViaHandle(results.front().second);
mFaced1Name = results.front().second;
mNumFacing = 1;
btVector3 p = mPhysics->getRayPoint(results.front().first);
Ogre::Vector3 pos(p.x(), p.z(), -p.y());
Ogre::SceneNode* node = mFaced1.getRefData().getBaseNode();
query->occlusionTest(pos, node);
}
else
{
mFaced1Name = results.front().second;
mFaced2Name = results[1].second;
mFaced1 = getPtrViaHandle(results.front().second);
mFaced2 = getPtrViaHandle(results[1].second);
mNumFacing = 2;
btVector3 p = mPhysics->getRayPoint(results[1].first);
Ogre::Vector3 pos(p.x(), p.z(), -p.y());
Ogre::SceneNode* node = mFaced2.getRefData().getBaseNode();
query->occlusionTest(pos, node);
}
}
}
} }
bool World::isCellExterior() const bool World::isCellExterior() const

View file

@ -93,6 +93,12 @@ namespace MWWorld
Ptr getPtrViaHandle (const std::string& handle, Ptr::CellStore& cellStore); Ptr getPtrViaHandle (const std::string& handle, Ptr::CellStore& cellStore);
std::string mFacedHandle;
Ptr mFaced1;
Ptr mFaced2;
std::string mFaced1Name;
std::string mFaced2Name;
int mNumFacing;
int getDaysPerMonth (int month) const; int getDaysPerMonth (int month) const;

View file

@ -241,8 +241,8 @@ namespace Physic
static bool cmp( const std::pair<float, btCollisionObject*>& i, const std::pair<float, btCollisionObject*>& j ) static bool cmp( const std::pair<float, btCollisionObject*>& i, const std::pair<float, btCollisionObject*>& j )
{ {
if( i.first < j.first ) return false; if( i.first > j.first ) return false;
if( j.first < i.first ) return true; if( j.first > i.first ) return true;
return false; return false;
} }