1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-01-30 15:15:31 +00:00

Merge remote branch 'scrawl/occlusionquery'

This commit is contained in:
Marc Zinnschlag 2012-03-31 22:09:47 +02:00
commit 40310f3663
13 changed files with 599 additions and 68 deletions

View file

@ -15,7 +15,7 @@ source_group(game FILES ${GAME} ${GAME_HEADER})
add_openmw_dir (mwrender add_openmw_dir (mwrender
renderingmanager debugging sky player animation npcanimation creatureanimation actors objects renderingmanager debugging sky player animation npcanimation creatureanimation actors objects
renderinginterface localmap water terrain terrainmaterial renderinginterface localmap occlusionquery terrain terrainmaterial water
) )
add_openmw_dir (mwinput add_openmw_dir (mwinput

View file

@ -0,0 +1,254 @@
#include "occlusionquery.hpp"
#include <OgreRenderSystem.h>
#include <OgreRoot.h>
#include <OgreBillboardSet.h>
#include <OgreHardwareOcclusionQuery.h>
#include <OgreEntity.h>
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), mDoQuery2(false)
{
mRendering = renderer;
mSunNode = sunNode;
try {
RenderSystem* renderSystem = Root::getSingleton().getRenderSystem();
mSunTotalAreaQuery = renderSystem->createHardwareOcclusionQuery();
mSunVisibleAreaQuery = renderSystem->createHardwareOcclusionQuery();
mSingleObjectQuery = renderSystem->createHardwareOcclusionQuery();
mSupported = (mSunTotalAreaQuery != 0) && (mSunVisibleAreaQuery != 0) && (mSingleObjectQuery != 0);
}
catch (Ogre::Exception e)
{
mSupported = false;
}
if (!mSupported)
{
std::cout << "Hardware occlusion queries not supported." << std::endl;
return;
}
// This means that everything up to RENDER_QUEUE_MAIN can occlude the objects that are tested
const int queue = RENDER_QUEUE_MAIN+1;
MaterialPtr matBase = MaterialManager::getSingleton().getByName("BaseWhiteNoLighting");
MaterialPtr matQueryArea = matBase->clone("QueryTotalPixels");
matQueryArea->setDepthWriteEnabled(false);
matQueryArea->setColourWriteEnabled(false);
matQueryArea->setDepthCheckEnabled(false); // Not occluded by objects
MaterialPtr matQueryVisible = matBase->clone("QueryVisiblePixels");
matQueryVisible->setDepthWriteEnabled(false);
matQueryVisible->setColourWriteEnabled(false); // Uncomment this to visualize the occlusion query
matQueryVisible->setDepthCheckEnabled(true); // Occluded by objects
matQueryVisible->setCullingMode(CULL_NONE);
matQueryVisible->setManualCullingMode(MANUAL_CULL_NONE);
mBBNode = mSunNode->getParentSceneNode()->createChildSceneNode();
mObjectNode = mRendering->getScene()->getRootSceneNode()->createChildSceneNode();
mBBNodeReal = mRendering->getScene()->getRootSceneNode()->createChildSceneNode();
mBBQueryTotal = mRendering->getScene()->createBillboardSet(1);
mBBQueryTotal->setDefaultDimensions(150, 150);
mBBQueryTotal->createBillboard(Vector3::ZERO);
mBBQueryTotal->setMaterialName("QueryTotalPixels");
mBBQueryTotal->setRenderQueueGroup(queue+1);
mBBNodeReal->attachObject(mBBQueryTotal);
mBBQueryVisible = mRendering->getScene()->createBillboardSet(1);
mBBQueryVisible->setDefaultDimensions(150, 150);
mBBQueryVisible->createBillboard(Vector3::ZERO);
mBBQueryVisible->setMaterialName("QueryVisiblePixels");
mBBQueryVisible->setRenderQueueGroup(queue+1);
mBBNodeReal->attachObject(mBBQueryVisible);
mBBQuerySingleObject = mRendering->getScene()->createBillboardSet(1);
/// \todo ideally this should occupy exactly 1 pixel on the screen
mBBQuerySingleObject->setDefaultDimensions(0.003, 0.003);
mBBQuerySingleObject->createBillboard(Vector3::ZERO);
mBBQuerySingleObject->setMaterialName("QueryVisiblePixels");
mBBQuerySingleObject->setRenderQueueGroup(queue);
mObjectNode->attachObject(mBBQuerySingleObject);
mRendering->getScene()->addRenderObjectListener(this);
mRendering->getScene()->addRenderQueueListener(this);
mDoQuery = true;
mDoQuery2 = true;
}
OcclusionQuery::~OcclusionQuery()
{
RenderSystem* renderSystem = Root::getSingleton().getRenderSystem();
if (mSunTotalAreaQuery) renderSystem->destroyHardwareOcclusionQuery(mSunTotalAreaQuery);
if (mSunVisibleAreaQuery) renderSystem->destroyHardwareOcclusionQuery(mSunVisibleAreaQuery);
if (mSingleObjectQuery) renderSystem->destroyHardwareOcclusionQuery(mSingleObjectQuery);
}
bool OcclusionQuery::supported()
{
return mSupported;
}
void OcclusionQuery::notifyRenderSingleObject(Renderable* rend, const Pass* pass, const AutoParamDataSource* source,
const LightList* pLightList, bool suppressRenderStateChanges)
{
// The following code activates and deactivates the occlusion queries
// so that the queries only include the rendering of their intended targets
// Close the last occlusion query
// Each occlusion query should only last a single rendering
if (mActiveQuery != NULL)
{
mActiveQuery->endOcclusionQuery();
mActiveQuery = NULL;
}
// Open a new occlusion query
if (mDoQuery == true)
{
if (rend == mBBQueryTotal)
{
mActiveQuery = mSunTotalAreaQuery;
mWasVisible = true;
}
else if (rend == mBBQueryVisible)
{
mActiveQuery = mSunVisibleAreaQuery;
}
}
if (mDoQuery == true && rend == mBBQuerySingleObject)
{
mQuerySingleObjectStarted = true;
mQuerySingleObjectRequested = false;
mActiveQuery = mSingleObjectQuery;
mObjectWasVisible = true;
}
if (mActiveQuery != NULL)
mActiveQuery->beginOcclusionQuery();
}
void OcclusionQuery::renderQueueEnded(uint8 queueGroupId, const String& invocation, bool& repeatThisInvocation)
{
if (mActiveQuery != NULL)
{
mActiveQuery->endOcclusionQuery();
mActiveQuery = NULL;
}
/**
* for every beginOcclusionQuery(), we want a respective pullOcclusionQuery() and vice versa
* this also means that results can be wrong at other places if we pull, but beginOcclusionQuery() was never called
* this can happen for example if the object that is tested is outside of the view frustum
* to prevent this, check if the queries have been performed after everything has been rendered and if not, start them manually
*/
if (queueGroupId == RENDER_QUEUE_SKIES_LATE)
{
if (mWasVisible == false && mDoQuery)
{
mSunTotalAreaQuery->beginOcclusionQuery();
mSunTotalAreaQuery->endOcclusionQuery();
mSunVisibleAreaQuery->beginOcclusionQuery();
mSunVisibleAreaQuery->endOcclusionQuery();
}
if (mObjectWasVisible == false && mDoQuery)
{
mSingleObjectQuery->beginOcclusionQuery();
mSingleObjectQuery->endOcclusionQuery();
mQuerySingleObjectStarted = true;
mQuerySingleObjectRequested = false;
}
}
}
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
float dist = mRendering->getCamera()->getFarClipDistance();
if (dist==0) dist = 10000000;
dist -= 1000; // bias
dist /= 1000.f;
mBBNode->setPosition(mSunNode->getPosition() * dist);
mBBNode->setScale(dist, dist, dist);
mBBNodeReal->setPosition(mBBNode->_getDerivedPosition());
mBBNodeReal->setScale(mBBNode->getScale());
// Stop occlusion queries until we get their information
// (may not happen on the same frame they are requested in)
mDoQuery = false;
mDoQuery2 = false;
if (!mSunTotalAreaQuery->isStillOutstanding()
&& !mSunVisibleAreaQuery->isStillOutstanding()
&& !mSingleObjectQuery->isStillOutstanding())
{
unsigned int totalPixels;
unsigned int visiblePixels;
mSunTotalAreaQuery->pullOcclusionQuery(&totalPixels);
mSunVisibleAreaQuery->pullOcclusionQuery(&visiblePixels);
if (totalPixels == 0)
{
// probably outside of the view frustum
mSunVisibility = 0;
}
else
{
mSunVisibility = float(visiblePixels) / float(totalPixels);
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;
}
bool OcclusionQuery::occlusionTestPending()
{
return (mQuerySingleObjectRequested || mQuerySingleObjectStarted);
}
bool OcclusionQuery::getTestResult()
{
assert( !occlusionTestPending()
&& "Occlusion test still pending");
return mTestResult;
}

View file

@ -0,0 +1,95 @@
#ifndef _GAME_OCCLUSION_QUERY_H
#define _GAME_OCCLUSION_QUERY_H
#include <OgreRenderObjectListener.h>
#include <OgreRenderQueueListener.h>
namespace Ogre
{
class HardwareOcclusionQuery;
class Entity;
class SceneNode;
}
#include <openengine/ogre/renderer.hpp>
namespace MWRender
{
///
/// \brief Implements hardware occlusion queries on the GPU
///
class OcclusionQuery : public Ogre::RenderObjectListener, public Ogre::RenderQueueListener
{
public:
OcclusionQuery(OEngine::Render::OgreRenderer*, Ogre::SceneNode* sunNode);
~OcclusionQuery();
/**
* @return true if occlusion queries are supported on the user's hardware
*/
bool supported();
/**
* per-frame update
*/
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();
/**
* @return true if the object tested in the last request was occluded
*/
bool getTestResult();
float getSunVisibility() const {return mSunVisibility;};
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 mSupported;
bool mDoQuery;
bool mDoQuery2;
bool mQuerySingleObjectRequested;
bool mQuerySingleObjectStarted;
OEngine::Render::OgreRenderer* mRendering;
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);
};
}
#endif

View file

@ -46,9 +46,6 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const
mMwRoot->pitch(Degree(-90)); mMwRoot->pitch(Degree(-90));
mObjects.setMwRoot(mMwRoot); mObjects.setMwRoot(mMwRoot);
mActors.setMwRoot(mMwRoot); mActors.setMwRoot(mMwRoot);
//used to obtain ingame information of ogre objects (which are faced or selected)
mRaySceneQuery = mRendering.getScene()->createRayQuery(Ray());
Ogre::SceneNode *playerNode = mMwRoot->createChildSceneNode ("player"); Ogre::SceneNode *playerNode = mMwRoot->createChildSceneNode ("player");
playerNode->pitch(Degree(90)); playerNode->pitch(Degree(90));
@ -59,10 +56,10 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const
//mSkyManager = 0; //mSkyManager = 0;
mSkyManager = new SkyManager(mMwRoot, mRendering.getCamera(), &environment); mSkyManager = new SkyManager(mMwRoot, mRendering.getCamera(), &environment);
mOcclusionQuery = new OcclusionQuery(&mRendering, mSkyManager->getSunNode());
mWater = 0; mWater = 0;
mPlayer = new MWRender::Player (mRendering.getCamera(), playerNode); mPlayer = new MWRender::Player (mRendering.getCamera(), playerNode);
mSun = 0; mSun = 0;
@ -76,6 +73,7 @@ RenderingManager::~RenderingManager ()
delete mSkyManager; delete mSkyManager;
delete mTerrainManager; delete mTerrainManager;
delete mLocalMap; delete mLocalMap;
delete mOcclusionQuery;
} }
MWRender::SkyManager* RenderingManager::getSkyManager() MWRender::SkyManager* RenderingManager::getSkyManager()
@ -166,9 +164,13 @@ void RenderingManager::moveObjectToCell (const MWWorld::Ptr& ptr, const Ogre::Ve
void RenderingManager::update (float duration){ void RenderingManager::update (float duration){
mActors.update (duration); mActors.update (duration);
mOcclusionQuery->update(duration);
mSkyManager->update(duration); mSkyManager->update(duration);
mSkyManager->setGlare(mOcclusionQuery->getSunVisibility());
mRendering.update(duration); mRendering.update(duration);
mLocalMap->updatePlayer( mRendering.getCamera()->getRealPosition(), mRendering.getCamera()->getRealDirection() ); mLocalMap->updatePlayer( mRendering.getCamera()->getRealPosition(), mRendering.getCamera()->getRealDirection() );

View file

@ -27,6 +27,7 @@
#include "player.hpp" #include "player.hpp"
#include "water.hpp" #include "water.hpp"
#include "localmap.hpp" #include "localmap.hpp"
#include "occlusionquery.hpp"
namespace Ogre namespace Ogre
{ {
@ -108,6 +109,9 @@ class RenderingManager: private RenderingInterface {
void sunEnable(); void sunEnable();
void sunDisable(); void sunDisable();
bool occlusionQuerySupported() { return mOcclusionQuery->supported(); };
OcclusionQuery* getOcclusionQuery() { return mOcclusionQuery; };
void setGlare(bool glare); void setGlare(bool glare);
void skyEnable (); void skyEnable ();
void skyDisable (); void skyDisable ();
@ -145,10 +149,12 @@ class RenderingManager: private RenderingInterface {
SkyManager* mSkyManager; SkyManager* mSkyManager;
MWRender::Water *mWater; OcclusionQuery* mOcclusionQuery;
TerrainManager* mTerrainManager; TerrainManager* mTerrainManager;
MWRender::Water *mWater;
OEngine::Render::OgreRenderer &mRendering; OEngine::Render::OgreRenderer &mRendering;
MWRender::Objects mObjects; MWRender::Objects mObjects;
@ -164,7 +170,6 @@ class RenderingManager: private RenderingInterface {
/// that the OGRE coordinate system matches that used internally in /// that the OGRE coordinate system matches that used internally in
/// Morrowind. /// Morrowind.
Ogre::SceneNode *mMwRoot; Ogre::SceneNode *mMwRoot;
Ogre::RaySceneQuery *mRaySceneQuery;
OEngine::Physic::PhysicEngine* mPhysicsEngine; OEngine::Physic::PhysicEngine* mPhysicsEngine;

View file

@ -12,6 +12,7 @@
#include "../mwworld/environment.hpp" #include "../mwworld/environment.hpp"
#include "../mwworld/world.hpp" #include "../mwworld/world.hpp"
#include "occlusionquery.hpp"
using namespace MWRender; using namespace MWRender;
using namespace Ogre; using namespace Ogre;
@ -30,7 +31,7 @@ BillboardObject::BillboardObject()
void BillboardObject::setVisible(const bool visible) void BillboardObject::setVisible(const bool visible)
{ {
mNode->setVisible(visible); mBBSet->setVisible(visible);
} }
void BillboardObject::setSize(const float size) void BillboardObject::setSize(const float size)
@ -88,7 +89,7 @@ void BillboardObject::init(const String& textureName,
/// \todo These billboards are not 100% correct, might want to revisit them later /// \todo These billboards are not 100% correct, might want to revisit them later
mBBSet = sceneMgr->createBillboardSet("SkyBillboardSet"+StringConverter::toString(bodyCount), 1); mBBSet = sceneMgr->createBillboardSet("SkyBillboardSet"+StringConverter::toString(bodyCount), 1);
mBBSet->setDefaultDimensions(550.f*initialSize, 550.f*initialSize); mBBSet->setDefaultDimensions(550.f*initialSize, 550.f*initialSize);
mBBSet->setRenderQueueGroup(RENDER_QUEUE_SKIES_EARLY+2); mBBSet->setRenderQueueGroup(RENDER_QUEUE_MAIN+2);
mBBSet->setBillboardType(BBT_PERPENDICULAR_COMMON); mBBSet->setBillboardType(BBT_PERPENDICULAR_COMMON);
mBBSet->setCommonDirection( -position.normalisedCopy() ); mBBSet->setCommonDirection( -position.normalisedCopy() );
mNode = rootNode->createChildSceneNode(); mNode = rootNode->createChildSceneNode();
@ -319,8 +320,8 @@ SkyManager::SkyManager (SceneNode* pMwRoot, Camera* pCamera, MWWorld::Environmen
, mThunderTextureUnit(NULL) , mThunderTextureUnit(NULL)
, mRemainingTransitionTime(0.0f) , mRemainingTransitionTime(0.0f)
, mGlareFade(0.0f) , mGlareFade(0.0f)
, mGlare(0.0f)
, mEnabled(true) , mEnabled(true)
, mGlareEnabled(true)
, mSunEnabled(true) , mSunEnabled(true)
, mMasserEnabled(true) , mMasserEnabled(true)
, mSecundaEnabled(true) , mSecundaEnabled(true)
@ -592,10 +593,23 @@ void SkyManager::update(float duration)
mMasser->setPhase( static_cast<Moon::Phase>( (int) ((mDay % 32)/4.f)) ); mMasser->setPhase( static_cast<Moon::Phase>( (int) ((mDay % 32)/4.f)) );
mSecunda->setPhase ( static_cast<Moon::Phase>( (int) ((mDay % 32)/4.f)) ); mSecunda->setPhase ( static_cast<Moon::Phase>( (int) ((mDay % 32)/4.f)) );
// increase the strength of the sun glare effect depending
// on how directly the player is looking at the sun
if (mSunEnabled) if (mSunEnabled)
{ {
// take 1/5 sec for fading the glare effect from invisible to full
if (mGlareFade > mGlare)
{
mGlareFade -= duration*5;
if (mGlareFade < mGlare) mGlareFade = mGlare;
}
else if (mGlareFade < mGlare)
{
mGlareFade += duration*5;
if (mGlareFade > mGlare) mGlareFade = mGlare;
}
// increase the strength of the sun glare effect depending
// on how directly the player is looking at the sun
Vector3 sun = mSunGlare->getPosition(); Vector3 sun = mSunGlare->getPosition();
sun = Vector3(sun.x, sun.z, -sun.y); sun = Vector3(sun.x, sun.z, -sun.y);
Vector3 cam = mViewport->getCamera()->getRealDirection(); Vector3 cam = mViewport->getCamera()->getRealDirection();
@ -603,21 +617,10 @@ void SkyManager::update(float duration)
float val = 1- (angle.valueDegrees() / 180.f); float val = 1- (angle.valueDegrees() / 180.f);
val = (val*val*val*val)*2; val = (val*val*val*val)*2;
if (mGlareEnabled) mSunGlare->setSize(val * mGlareFade);
{
mGlareFade += duration*3;
if (mGlareFade > 1) mGlareFade = 1;
}
else
{
mGlareFade -= duration*3;
if (mGlareFade < 0.3) mGlareFade = 0;
}
mSunGlare->setSize(val * (mGlareFade));
} }
mSunGlare->setVisible(mGlareFade>0 && mSunEnabled); mSunGlare->setVisible(mSunEnabled);
mSun->setVisible(mSunEnabled); mSun->setVisible(mSunEnabled);
mMasser->setVisible(mMasserEnabled); mMasser->setVisible(mMasserEnabled);
mSecunda->setVisible(mSecundaEnabled); mSecunda->setVisible(mSecundaEnabled);
@ -719,15 +722,15 @@ void SkyManager::setWeather(const MWWorld::WeatherResult& weather)
else else
strength = 1.f; strength = 1.f;
mSunGlare->setVisibility(weather.mGlareView * strength); mSunGlare->setVisibility(weather.mGlareView * mGlareFade * strength);
mSun->setVisibility(strength); mSun->setVisibility(mGlareFade >= 0.5 ? weather.mGlareView * mGlareFade * strength : 0);
mAtmosphereNight->setVisible(weather.mNight && mEnabled); mAtmosphereNight->setVisible(weather.mNight && mEnabled);
} }
void SkyManager::setGlare(bool glare) void SkyManager::setGlare(const float glare)
{ {
mGlareEnabled = glare; mGlare = glare;
} }
Vector3 SkyManager::getRealSunPos() Vector3 SkyManager::getRealSunPos()
@ -812,3 +815,8 @@ void SkyManager::setDate(int day, int month)
mDay = day; mDay = day;
mMonth = month; mMonth = month;
} }
Ogre::SceneNode* SkyManager::getSunNode()
{
return mSun->getNode();
}

View file

@ -109,58 +109,60 @@ namespace MWRender
public: public:
SkyManager(Ogre::SceneNode* pMwRoot, Ogre::Camera* pCamera, MWWorld::Environment* env); SkyManager(Ogre::SceneNode* pMwRoot, Ogre::Camera* pCamera, MWWorld::Environment* env);
~SkyManager(); ~SkyManager();
void update(float duration); void update(float duration);
void enable(); void enable();
void disable(); void disable();
void setHour (double hour); void setHour (double hour);
///< will be called even when sky is disabled. ///< will be called even when sky is disabled.
void setDate (int day, int month); void setDate (int day, int month);
///< will be called even when sky is disabled. ///< will be called even when sky is disabled.
int getMasserPhase() const; int getMasserPhase() const;
///< 0 new moon, 1 waxing or waning cresecent, 2 waxing or waning half, ///< 0 new moon, 1 waxing or waning cresecent, 2 waxing or waning half,
/// 3 waxing or waning gibbous, 4 full moon /// 3 waxing or waning gibbous, 4 full moon
int getSecundaPhase() const; int getSecundaPhase() const;
///< 0 new moon, 1 waxing or waning cresecent, 2 waxing or waning half, ///< 0 new moon, 1 waxing or waning cresecent, 2 waxing or waning half,
/// 3 waxing or waning gibbous, 4 full moon /// 3 waxing or waning gibbous, 4 full moon
void setMoonColour (bool red); void setMoonColour (bool red);
///< change Secunda colour to red ///< change Secunda colour to red
void setCloudsOpacity(float opacity); void setCloudsOpacity(float opacity);
///< change opacity of the clouds ///< change opacity of the clouds
void setWeather(const MWWorld::WeatherResult& weather); void setWeather(const MWWorld::WeatherResult& weather);
Ogre::SceneNode* getSunNode();
void sunEnable(); void sunEnable();
void sunDisable(); void sunDisable();
void setSunDirection(const Ogre::Vector3& direction); void setSunDirection(const Ogre::Vector3& direction);
void setMasserDirection(const Ogre::Vector3& direction); void setMasserDirection(const Ogre::Vector3& direction);
void setSecundaDirection(const Ogre::Vector3& direction); void setSecundaDirection(const Ogre::Vector3& direction);
void setMasserFade(const float fade); void setMasserFade(const float fade);
void setSecundaFade(const float fade); void setSecundaFade(const float fade);
void masserEnable(); void masserEnable();
void masserDisable(); void masserDisable();
void secundaEnable(); void secundaEnable();
void secundaDisable(); void secundaDisable();
void setThunder(const float factor); void setThunder(const float factor);
void setGlare(bool glare); void setGlare(const float glare);
Ogre::Vector3 getRealSunPos(); Ogre::Vector3 getRealSunPos();
private: private:
@ -203,12 +205,12 @@ namespace MWRender
float mRemainingTransitionTime; float mRemainingTransitionTime;
float mGlareFade; float mGlare; // target
float mGlareFade; // actual
void ModVertexAlpha(Ogre::Entity* ent, unsigned int meshType); void ModVertexAlpha(Ogre::Entity* ent, unsigned int meshType);
bool mEnabled; bool mEnabled;
bool mGlareEnabled;
bool mSunEnabled; bool mSunEnabled;
bool mMasserEnabled; bool mMasserEnabled;
bool mSecundaEnabled; bool mSecundaEnabled;

View file

@ -50,6 +50,28 @@ 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);
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);
btVector3 result(centerRay.getPoint(500*extent).x,-centerRay.getPoint(500*extent).z,centerRay.getPoint(500*extent).y);
return result;
}
bool PhysicsSystem::castRay(const Vector3& from, const Vector3& to) bool PhysicsSystem::castRay(const Vector3& from, const Vector3& to)
{ {

View file

@ -35,7 +35,11 @@ namespace MWWorld
bool toggleCollisionMode(); bool toggleCollisionMode();
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

@ -157,7 +157,8 @@ namespace MWWorld
const std::string& master, const boost::filesystem::path& resDir, const std::string& master, const boost::filesystem::path& resDir,
bool newGame, Environment& environment, const std::string& encoding) bool newGame, Environment& environment, const std::string& encoding)
: mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0), : mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0),
mSky (true), mEnvironment (environment), mNextDynamicRecord (0), mCells (mStore, mEsm, *this) mSky (true), mEnvironment (environment), mNextDynamicRecord (0), mCells (mStore, mEsm, *this),
mNumFacing(0)
{ {
mPhysics = new PhysicsSystem(renderer); mPhysics = new PhysicsSystem(renderer);
mPhysEngine = mPhysics->getEngine(); mPhysEngine = mPhysics->getEngine();
@ -498,13 +499,21 @@ namespace MWWorld
std::string World::getFacedHandle() std::string World::getFacedHandle()
{ {
std::pair<std::string, float> result = mPhysics->getFacedHandle (*this); if (!mRendering->occlusionQuerySupported())
{
std::pair<std::string, float> result = mPhysics->getFacedHandle (*this);
if (result.first.empty() || if (result.first.empty() ||
result.second>getStore().gameSettings.find ("iMaxActivateDist")->i) result.second>getStore().gameSettings.find ("iMaxActivateDist")->i)
return ""; return "";
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)
@ -706,13 +715,82 @@ namespace MWWorld
mWeatherManager->update (duration); mWeatherManager->update (duration);
// cast a ray from player to sun to detect if the sun is visible if (!mRendering->occlusionQuerySupported())
// this is temporary until we find a better place to put this code {
// currently its here because we need to access the physics system // cast a ray from player to sun to detect if the sun is visible
float* p = mPlayer->getPlayer().getRefData().getPosition().pos; // this is temporary until we find a better place to put this code
Vector3 sun = mRendering->getSkyManager()->getRealSunPos(); // currently its here because we need to access the physics system
sun = Vector3(sun.x, -sun.z, sun.y); float* p = mPlayer->getPlayer().getRefData().getPosition().pos;
mRendering->getSkyManager()->setGlare(!mPhysics->castRay(Ogre::Vector3(p[0], p[1], p[2]), sun)); Vector3 sun = mRendering->getSkyManager()->getRealSunPos();
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();
// ignore the player
for (std::vector < std::pair < float, std::string > >::iterator it = results.begin();
it != results.end(); ++it)
{
if ( (*it).second == mPlayer->getPlayer().getRefData().getHandle() )
{
results.erase(it);
break;
}
}
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

@ -426,4 +426,35 @@ namespace Physic
return std::pair<std::string,float>(name,d); 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 = COL_WORLD;
dynamicsWorld->rayTest(from, to, resultCallback1);
std::vector< std::pair<float, btCollisionObject*> > results = resultCallback1.results;
MyRayResultCallback resultCallback2;
resultCallback2.m_collisionFilterMask = COL_ACTOR_INTERNAL|COL_ACTOR_EXTERNAL;
dynamicsWorld->rayTest(from, to, resultCallback2);
std::vector< std::pair<float, btCollisionObject*> > actorResults = resultCallback2.results;
std::vector< std::pair<float, std::string> > results2;
for (std::vector< std::pair<float, btCollisionObject*> >::iterator it=results.begin();
it != results.end(); ++it)
{
results2.push_back( std::make_pair( (*it).first, static_cast<RigidBody&>(*(*it).second).mName ) );
}
for (std::vector< std::pair<float, btCollisionObject*> >::iterator it=actorResults.begin();
it != actorResults.end(); ++it)
{
results2.push_back( std::make_pair( (*it).first, static_cast<PairCachingGhostObject&>(*(*it).second).mName ) );
}
std::sort(results2.begin(), results2.end(), MyRayResultCallback::cmp);
return results2;
}
}}; }};

View file

@ -206,6 +206,11 @@ namespace Physic
*/ */
std::pair<std::string,float> rayTest(btVector3& from,btVector3& to); std::pair<std::string,float> rayTest(btVector3& from,btVector3& to);
/**
* Return all objects hit by a ray.
*/
std::vector< std::pair<float, std::string> > rayTest2(btVector3& from, btVector3& to);
//event list of non player object //event list of non player object
std::list<PhysicEvent> NPEventList; std::list<PhysicEvent> NPEventList;
@ -235,6 +240,25 @@ namespace Physic
bool mDebugActive; 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, btCollisionObject*> > results;
};
}} }}
#endif #endif