diff --git a/apps/openmw/mwrender/occlusionquery.cpp b/apps/openmw/mwrender/occlusionquery.cpp index 8dd30394c..66086a90b 100644 --- a/apps/openmw/mwrender/occlusionquery.cpp +++ b/apps/openmw/mwrender/occlusionquery.cpp @@ -47,7 +47,7 @@ OcclusionQuery::OcclusionQuery(OEngine::Render::OgreRenderer* renderer, SceneNod matQueryArea->setDepthCheckEnabled(false); // Not occluded by objects MaterialPtr matQueryVisible = matBase->clone("QueryVisiblePixels"); matQueryVisible->setDepthWriteEnabled(false); - matQueryVisible->setColourWriteEnabled(false); + matQueryVisible->setColourWriteEnabled(false); // Uncomment this to visualize the occlusion query matQueryVisible->setDepthCheckEnabled(true); // Occluded by objects mBBNode = mSunNode->getParentSceneNode()->createChildSceneNode(); @@ -69,13 +69,14 @@ OcclusionQuery::OcclusionQuery(OEngine::Render::OgreRenderer* renderer, SceneNod mBBNode->attachObject(mBBQueryVisible); mBBQuerySingleObject = mRendering->getScene()->createBillboardSet(1); - mBBQuerySingleObject->setDefaultDimensions(10, 10); + mBBQuerySingleObject->setDefaultDimensions(0.01, 0.01); mBBQuerySingleObject->createBillboard(Vector3::ZERO); mBBQuerySingleObject->setMaterialName("QueryVisiblePixels"); mBBQuerySingleObject->setRenderQueueGroup(queue); mObjectNode->attachObject(mBBQuerySingleObject); mRendering->getScene()->addRenderObjectListener(this); + mRendering->getScene()->addRenderQueueListener(this); mDoQuery = true; } @@ -95,8 +96,6 @@ bool OcclusionQuery::supported() void OcclusionQuery::notifyRenderSingleObject(Renderable* rend, const Pass* pass, const AutoParamDataSource* source, const LightList* pLightList, bool suppressRenderStateChanges) { - if (!mSupported) return; - // The following code activates and deactivates the occlusion queries // so that the queries only include the rendering of their intended targets @@ -111,26 +110,47 @@ void OcclusionQuery::notifyRenderSingleObject(Renderable* rend, const Pass* pass // Open a new occlusion query if (mDoQuery == true) { - if (rend == mBBQueryTotal) - mActiveQuery = mSunTotalAreaQuery; - else if (rend == mBBQueryVisible) - mActiveQuery = mSunVisibleAreaQuery; - else if (rend == mBBQuerySingleObject && mQuerySingleObjectRequested) + if (rend == mBBQueryTotal) { - mQuerySingleObjectStarted = true; - mQuerySingleObjectRequested = false; - mActiveQuery = mSingleObjectQuery; + mActiveQuery = mSunTotalAreaQuery; + mWasVisible = true; + } + else if (rend == mBBQueryVisible) + { + mActiveQuery = mSunVisibleAreaQuery; } - - if (mActiveQuery != NULL) - mActiveQuery->beginOcclusionQuery(); } + if (rend == mBBQuerySingleObject && mQuerySingleObjectRequested) + { + mQuerySingleObjectStarted = true; + mQuerySingleObjectRequested = false; + mActiveQuery = mSingleObjectQuery; + } + + if (mActiveQuery != NULL) + 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() { if (!mSupported) return; + mWasVisible = 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 float dist = mRendering->getCamera()->getFarClipDistance(); @@ -145,8 +165,7 @@ void OcclusionQuery::update() mDoQuery = false; if (!mSunTotalAreaQuery->isStillOutstanding() - && !mSunVisibleAreaQuery->isStillOutstanding() - && !mSingleObjectQuery->isStillOutstanding()) + && !mSunVisibleAreaQuery->isStillOutstanding()) { unsigned int totalPixels; unsigned int visiblePixels; @@ -165,24 +184,33 @@ void OcclusionQuery::update() if (mSunVisibility > 1) mSunVisibility = 1; } - if (mQuerySingleObjectStarted) + mDoQuery = true; + } + if (!mSingleObjectQuery->isStillOutstanding() && mQuerySingleObjectStarted) + { + unsigned int result; + + mSingleObjectQuery->pullOcclusionQuery(&result); + + //std::cout << "Single object query result: " << result << " pixels " << std::endl; + mTestResult = (result != 0); + + mBBQuerySingleObject->setVisible(false); + + // restore old render queues + for (std::vector::iterator it=mObjectsInfo.begin(); + it!=mObjectsInfo.end(); ++it) { - unsigned int visiblePixels; - - mSingleObjectQuery->pullOcclusionQuery(&visiblePixels); - - mBBQuerySingleObject->setVisible(false); - mObject->setRenderQueueGroup(mObjectOldRenderQueue); - - mQuerySingleObjectStarted = false; - mQuerySingleObjectRequested = false; + if (!mRendering->getScene()->hasMovableObject((*it).name, (*it).typeName)) return; + mRendering->getScene()->getMovableObject((*it).name, (*it).typeName)->setRenderQueueGroup( (*it).oldRenderqueue ); } - mDoQuery = true; + mQuerySingleObjectStarted = false; + mQuerySingleObjectRequested = false; } } -void OcclusionQuery::occlusionTest(const Ogre::Vector3& position, Ogre::Entity* entity) +void OcclusionQuery::occlusionTest(const Ogre::Vector3& position, Ogre::SceneNode* object) { assert( !occlusionTestPending() && "Occlusion test still pending"); @@ -190,11 +218,24 @@ void OcclusionQuery::occlusionTest(const Ogre::Vector3& position, Ogre::Entity* mBBQuerySingleObject->setVisible(true); // we don't want the object to occlude itself - mObjectOldRenderQueue = entity->getRenderQueueGroup(); - if (mObjectOldRenderQueue < RENDER_QUEUE_MAIN+2) - entity->setRenderQueueGroup(RENDER_QUEUE_MAIN+2); + // put it in a render queue _after_ the occlusion query + mObjectsInfo.clear(); + for (int i=0; inumAttachedObjects(); ++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); + // 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; } diff --git a/apps/openmw/mwrender/occlusionquery.hpp b/apps/openmw/mwrender/occlusionquery.hpp index bcc45d96c..987087e27 100644 --- a/apps/openmw/mwrender/occlusionquery.hpp +++ b/apps/openmw/mwrender/occlusionquery.hpp @@ -2,6 +2,7 @@ #define _GAME_OCCLUSION_QUERY_H #include +#include namespace Ogre { @@ -17,7 +18,14 @@ namespace MWRender /// /// \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: 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 * @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 @@ -66,10 +74,10 @@ namespace MWRender Ogre::SceneNode* mBBNode; float mSunVisibility; - Ogre::Entity* mObject; - Ogre::SceneNode* mObjectNode; - int mObjectOldRenderQueue; + std::vector mObjectsInfo; + + bool mWasVisible; bool mTestResult; @@ -84,6 +92,8 @@ namespace MWRender protected: virtual void notifyRenderSingleObject(Ogre::Renderable* rend, const Ogre::Pass* pass, const Ogre::AutoParamDataSource* source, const Ogre::LightList* pLightList, bool suppressRenderStateChanges); + + virtual void renderQueueEnded(Ogre::uint8 queueGroupId, const Ogre::String& invocation, bool& repeatThisInvocation); }; } diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 450e46173..569447bca 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -100,6 +100,7 @@ class RenderingManager: private RenderingInterface { void sunDisable(); bool occlusionQuerySupported() { return mOcclusionQuery->supported(); }; + OcclusionQuery* getOcclusionQuery() { return mOcclusionQuery; }; void setGlare(bool glare); void skyEnable (); diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index bb2f9f8a9..1d8f14cc4 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -50,6 +50,33 @@ namespace MWWorld return mEngine->rayTest(from,to); } + + std::vector < std::pair > 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) { diff --git a/apps/openmw/mwworld/physicssystem.hpp b/apps/openmw/mwworld/physicssystem.hpp index 78cbde083..7b2d77325 100644 --- a/apps/openmw/mwworld/physicssystem.hpp +++ b/apps/openmw/mwworld/physicssystem.hpp @@ -35,7 +35,11 @@ namespace MWWorld bool toggleCollisionMode(); std::pair getFacedHandle (MWWorld::World& world); - + + btVector3 getRayPoint(float extent); + + std::vector < std::pair > getFacedObjects (); + // cast ray, return true if it hit something bool castRay(const Ogre::Vector3& from, const Ogre::Vector3& to); diff --git a/apps/openmw/mwworld/world.cpp b/apps/openmw/mwworld/world.cpp index 261f66ff4..f185e411f 100644 --- a/apps/openmw/mwworld/world.cpp +++ b/apps/openmw/mwworld/world.cpp @@ -498,13 +498,21 @@ namespace MWWorld std::string World::getFacedHandle() { - std::pair result = mPhysics->getFacedHandle (*this); + if (!mRendering->occlusionQuerySupported()) + { + std::pair result = mPhysics->getFacedHandle (*this); - if (result.first.empty() || - result.second>getStore().gameSettings.find ("iMaxActivateDist")->i) - return ""; + if (result.first.empty() || + result.second>getStore().gameSettings.find ("iMaxActivateDist")->i) + return ""; - return result.first; + return result.first; + } + else + { + // updated every few frames in update() + return mFacedHandle; + } } void World::deleteObject (Ptr ptr) @@ -715,6 +723,61 @@ namespace MWWorld sun = Vector3(sun.x, -sun.z, sun.y); 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 diff --git a/apps/openmw/mwworld/world.hpp b/apps/openmw/mwworld/world.hpp index 71cca3545..7d8a1b9a0 100644 --- a/apps/openmw/mwworld/world.hpp +++ b/apps/openmw/mwworld/world.hpp @@ -93,6 +93,12 @@ namespace MWWorld 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; diff --git a/libs/openengine/bullet/physic.hpp b/libs/openengine/bullet/physic.hpp index 97d2c004d..3ecae7c0c 100644 --- a/libs/openengine/bullet/physic.hpp +++ b/libs/openengine/bullet/physic.hpp @@ -241,8 +241,8 @@ namespace Physic static bool cmp( const std::pair& i, const std::pair& j ) { - if( i.first < j.first ) return false; - if( j.first < i.first ) return true; + if( i.first > j.first ) return false; + if( j.first > i.first ) return true; return false; }