Merge remote-tracking branch 'scrawl/sky'

sceneinput
Marc Zinnschlag 9 years ago
commit 8a37d3ad8c

@ -23,6 +23,7 @@ add_openmw_dir (mwrender
actors objects renderingmanager animation rotatecontroller sky npcanimation vismask
creatureanimation effectmanager util renderinginterface pathgrid rendermode weaponanimation
bulletdebugdraw globalmap characterpreview camera localmap water terrainstorage ripplesimulation
renderbin
)
add_openmw_dir (mwinput

@ -0,0 +1,20 @@
#ifndef OPENMW_MWRENDER_RENDERBIN_H
#define OPENMW_MWRENDER_RENDERBIN_H
namespace MWRender
{
/// Defines the render bin numbers used in the OpenMW scene graph. The bin with the lowest number is rendered first.
/// Beware of RenderBin nesting, in most cases you will want to use setNestRenderBins(false).
enum RenderBins
{
RenderBin_Sky = -1,
RenderBin_Default = 0,
RenderBin_Water = 9,
RenderBin_OcclusionQuery = 10,
RenderBin_SunGlare = 11
};
}
#endif

@ -11,6 +11,11 @@
#include <osg/TexEnvCombine>
#include <osg/TexMat>
#include <osg/Version>
#include <osg/OcclusionQueryNode>
#include <osg/ColorMask>
#include <osg/MatrixTransform>
#include <osg/BlendFunc>
#include <osg/AlphaFunc>
#include <osgParticle/ParticleSystem>
#include <osgParticle/ParticleSystemUpdater>
@ -38,6 +43,7 @@
#include "../mwworld/fallback.hpp"
#include "vismask.hpp"
#include "renderbin.hpp"
namespace
{
@ -359,19 +365,57 @@ class Sun : public CelestialBody
public:
Sun(osg::Group* parentNode, Resource::TextureManager& textureManager)
: CelestialBody(parentNode, 1.0f, 1)
, mUpdater(new Updater(textureManager))
, mUpdater(new Updater)
{
mGeode->addUpdateCallback(mUpdater);
mTransform->addUpdateCallback(mUpdater);
osg::ref_ptr<osg::Texture2D> sunTex = textureManager.getTexture2D("textures/tx_sun_05.dds",
osg::Texture::CLAMP,
osg::Texture::CLAMP);
mGeode->getOrCreateStateSet()->setTextureAttributeAndModes(0, sunTex, osg::StateAttribute::ON);
osg::ref_ptr<osg::Group> queryNode (new osg::Group);
// Need to render after the world geometry so we can correctly test for occlusions
queryNode->getOrCreateStateSet()->setRenderBinDetails(RenderBin_OcclusionQuery, "RenderBin");
queryNode->getOrCreateStateSet()->setNestRenderBins(false);
// Set up alpha testing on the occlusion testing subgraph, that way we can get the occlusion tested fragments to match the circular shape of the sun
osg::ref_ptr<osg::AlphaFunc> alphaFunc (new osg::AlphaFunc);
alphaFunc->setFunction(osg::AlphaFunc::GREATER, 0.8);
queryNode->getOrCreateStateSet()->setAttributeAndModes(alphaFunc, osg::StateAttribute::ON);
queryNode->getOrCreateStateSet()->setTextureAttributeAndModes(0, sunTex, osg::StateAttribute::ON);
queryNode->getOrCreateStateSet()->setAttributeAndModes(createUnlitMaterial(), osg::StateAttribute::ON);
mTransform->addChild(queryNode);
mOcclusionQueryVisiblePixels = createOcclusionQueryNode(queryNode, true);
mOcclusionQueryTotalPixels = createOcclusionQueryNode(queryNode, false);
createSunFlash(textureManager);
createSunGlare();
}
~Sun()
{
mGeode->removeUpdateCallback(mUpdater);
mTransform->removeUpdateCallback(mUpdater);
destroySunFlash();
destroySunGlare();
}
void setColor(const osg::Vec4f& color)
{
mUpdater->mColor.r() = color.r();
mUpdater->mColor.g() = color.g();
mUpdater->mColor.b() = color.b();
}
virtual void adjustTransparency(const float ratio)
{
mUpdater->mColor.a() = ratio;
if (mSunGlareCallback)
mSunGlareCallback->setGlareView(ratio);
if (mSunFlashCallback)
mSunFlashCallback->setGlareView(ratio);
}
void setDirection(const osg::Vec3f& direction)
@ -384,37 +428,390 @@ public:
mTransform->setAttitude(quat);
}
void setGlareTimeOfDayFade(float val)
{
if (mSunGlareCallback)
mSunGlareCallback->setTimeOfDayFade(val);
}
private:
struct Updater : public SceneUtil::StateSetUpdater
/// @param queryVisible If true, queries the amount of visible pixels. If false, queries the total amount of pixels.
osg::ref_ptr<osg::OcclusionQueryNode> createOcclusionQueryNode(osg::Group* parent, bool queryVisible)
{
Resource::TextureManager& mTextureManager;
osg::ref_ptr<osg::OcclusionQueryNode> oqn = new osg::OcclusionQueryNode;
oqn->setQueriesEnabled(true);
// Make it fast! A DYNAMIC query geometry means we can't break frame until the flare is rendered (which is rendered after all the other geometry,
// so that would be pretty bad). STATIC should be safe, since our node's local bounds are static, thus computeBounds() which modifies the queryGeometry
// is only called once.
// Note the debug geometry setDebugDisplay(true) is always DYNAMIC and that can't be changed, not a big deal.
oqn->getQueryGeometry()->setDataVariance(osg::Object::STATIC);
osg::ref_ptr<osg::Geode> queryGeode = osg::clone(mGeode.get(), osg::CopyOp::DEEP_COPY_ALL);
// Disable writing to the color buffer. We are using this geode for visibility tests only.
osg::ref_ptr<osg::ColorMask> colormask (new osg::ColorMask(0, 0, 0, 0));
queryGeode->getOrCreateStateSet()->setAttributeAndModes(colormask, osg::StateAttribute::ON);
oqn->addChild(queryGeode);
// Remove the default OFF|PROTECTED setting for texturing. We *want* to enable texturing for alpha testing purposes
oqn->getQueryStateSet()->removeTextureMode(0, GL_TEXTURE_2D);
// Need to add texture coordinates so that texturing works. A bit ugly, relies on the vertex ordering
// used within OcclusionQueryNode.
osg::ref_ptr<osg::Vec2Array> texCoordArray (new osg::Vec2Array);
for (int i=0; i<8; ++i)
{
texCoordArray->push_back(osg::Vec2(0,0));
texCoordArray->push_back(osg::Vec2(1,0));
texCoordArray->push_back(osg::Vec2(0,0));
texCoordArray->push_back(osg::Vec2(1,0));
texCoordArray->push_back(osg::Vec2(1,1));
texCoordArray->push_back(osg::Vec2(0,1));
texCoordArray->push_back(osg::Vec2(0,1));
texCoordArray->push_back(osg::Vec2(1,1));
}
oqn->getQueryGeometry()->setTexCoordArray(0, texCoordArray, osg::Array::BIND_PER_VERTEX);
if (queryVisible)
{
osg::ref_ptr<osg::Depth> depth (new osg::Depth);
depth->setFunction(osg::Depth::LESS);
// This is a trick to make fragments written by the query always use the maximum depth value,
// without having to retrieve the current far clipping distance.
// We want the sun glare to be "infinitely" far away.
depth->setZNear(1.0);
depth->setZFar(1.0);
oqn->getQueryStateSet()->setAttributeAndModes(depth, osg::StateAttribute::ON);
}
else
{
oqn->getQueryStateSet()->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
}
parent->addChild(oqn);
return oqn;
}
void createSunFlash(Resource::TextureManager& textureManager)
{
osg::ref_ptr<osg::Texture2D> tex = textureManager.getTexture2D("textures/tx_sun_flash_grey_05.dds",
osg::Texture::CLAMP,
osg::Texture::CLAMP);
osg::ref_ptr<osg::PositionAttitudeTransform> transform (new osg::PositionAttitudeTransform);
const float scale = 2.6f;
transform->setScale(osg::Vec3f(scale,scale,scale));
mTransform->addChild(transform);
osg::ref_ptr<osg::Geode> geode (new osg::Geode);
transform->addChild(geode);
geode->addDrawable(createTexturedQuad());
osg::StateSet* stateset = geode->getOrCreateStateSet();
stateset->setTextureAttributeAndModes(0, tex, osg::StateAttribute::ON);
stateset->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
stateset->setRenderBinDetails(RenderBin_SunGlare, "RenderBin");
stateset->setNestRenderBins(false);
mSunFlashNode = transform;
mSunFlashCallback = new SunFlashCallback(mOcclusionQueryVisiblePixels, mOcclusionQueryTotalPixels);
mSunFlashNode->addCullCallback(mSunFlashCallback);
}
void destroySunFlash()
{
if (mSunFlashNode)
{
mSunFlashNode->removeCullCallback(mSunFlashCallback);
mSunFlashCallback = NULL;
}
}
void createSunGlare()
{
osg::ref_ptr<osg::Camera> camera (new osg::Camera);
camera->setProjectionMatrix(osg::Matrix::identity());
camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF); // add to skyRoot instead?
camera->setViewMatrix(osg::Matrix::identity());
camera->setClearMask(0);
camera->setRenderOrder(osg::Camera::NESTED_RENDER);
camera->setAllowEventFocus(false);
osg::ref_ptr<osg::Geode> geode (new osg::Geode);
osg::ref_ptr<osg::Geometry> geom = osg::createTexturedQuadGeometry(osg::Vec3f(-1,-1,0), osg::Vec3f(2,0,0), osg::Vec3f(0,2,0));
geode->addDrawable(geom);
camera->addChild(geode);
osg::StateSet* stateset = geom->getOrCreateStateSet();
stateset->setRenderBinDetails(RenderBin_SunGlare, "RenderBin");
stateset->setNestRenderBins(false);
stateset->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
// set up additive blending
osg::ref_ptr<osg::BlendFunc> blendFunc (new osg::BlendFunc);
blendFunc->setSource(osg::BlendFunc::SRC_ALPHA);
blendFunc->setDestination(osg::BlendFunc::ONE);
stateset->setAttributeAndModes(blendFunc, osg::StateAttribute::ON);
mSunGlareCallback = new SunGlareCallback(mOcclusionQueryVisiblePixels, mOcclusionQueryTotalPixels, mTransform);
mSunGlareNode = camera;
mSunGlareNode->addCullCallback(mSunGlareCallback);
mTransform->addChild(camera);
}
void destroySunGlare()
{
if (mSunGlareNode)
{
mSunGlareNode->removeCullCallback(mSunGlareCallback);
mSunGlareCallback = NULL;
}
}
class Updater : public SceneUtil::StateSetUpdater
{
public:
osg::Vec4f mColor;
Updater(Resource::TextureManager& textureManager)
: mTextureManager(textureManager)
, mColor(0.0f, 0.0f, 0.0f, 1.0f)
Updater()
: mColor(1.f, 1.f, 1.f, 1.f)
{
}
virtual void setDefaults(osg::StateSet* stateset)
{
osg::ref_ptr<osg::Texture2D> tex = mTextureManager.getTexture2D("textures/tx_sun_05.dds",
osg::Texture::CLAMP,
osg::Texture::CLAMP);
stateset->setTextureAttributeAndModes(0, tex, osg::StateAttribute::ON);
stateset->setAttributeAndModes(createUnlitMaterial(),
osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
stateset->setAttributeAndModes(createUnlitMaterial(), osg::StateAttribute::ON);
}
virtual void apply(osg::StateSet* stateset, osg::NodeVisitor*)
{
osg::Material* mat = static_cast<osg::Material*>(stateset->getAttribute(osg::StateAttribute::MATERIAL));
mat->setDiffuse(osg::Material::FRONT_AND_BACK, mColor);
mat->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0,0,0,mColor.a()));
mat->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(mColor.r(), mColor.g(), mColor.b(), 1));
}
};
class OcclusionCallback : public osg::NodeCallback
{
public:
OcclusionCallback(osg::ref_ptr<osg::OcclusionQueryNode> oqnVisible, osg::ref_ptr<osg::OcclusionQueryNode> oqnTotal)
: mOcclusionQueryVisiblePixels(oqnVisible)
, mOcclusionQueryTotalPixels(oqnTotal)
{
}
protected:
float getVisibleRatio (osg::Camera* camera)
{
int visible = mOcclusionQueryVisiblePixels->getQueryGeometry()->getNumPixels(camera);
int total = mOcclusionQueryTotalPixels->getQueryGeometry()->getNumPixels(camera);
float visibleRatio = 0.f;
if (total > 0)
visibleRatio = static_cast<float>(visible) / static_cast<float>(total);
float dt = MWBase::Environment::get().getFrameDuration();
float lastRatio = mLastRatio[osg::observer_ptr<osg::Camera>(camera)];
float change = dt*10;
if (visibleRatio > lastRatio)
visibleRatio = std::min(visibleRatio, lastRatio + change);
else
visibleRatio = std::max(visibleRatio, lastRatio - change);
mLastRatio[osg::observer_ptr<osg::Camera>(camera)] = visibleRatio;
return visibleRatio;
}
private:
osg::ref_ptr<osg::OcclusionQueryNode> mOcclusionQueryVisiblePixels;
osg::ref_ptr<osg::OcclusionQueryNode> mOcclusionQueryTotalPixels;
std::map<osg::observer_ptr<osg::Camera>, float> mLastRatio;
};
/// SunFlashCallback handles fading/scaling of a node depending on occlusion query result. Must be attached as a cull callback.
class SunFlashCallback : public OcclusionCallback
{
public:
SunFlashCallback(osg::ref_ptr<osg::OcclusionQueryNode> oqnVisible, osg::ref_ptr<osg::OcclusionQueryNode> oqnTotal)
: OcclusionCallback(oqnVisible, oqnTotal)
, mGlareView(1.f)
{
}
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
{
osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>(nv);
float visibleRatio = getVisibleRatio(cv->getCurrentCamera());
osg::ref_ptr<osg::StateSet> stateset;
if (visibleRatio > 0.f)
{
const float fadeThreshold = 0.1;
if (visibleRatio < fadeThreshold)
{
float fade = 1.f - (fadeThreshold - visibleRatio) / fadeThreshold;
osg::ref_ptr<osg::Material> mat (createUnlitMaterial());
mat->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0,0,0,fade*mGlareView));
stateset = new osg::StateSet;
stateset->setAttributeAndModes(mat, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
}
const float threshold = 0.6;
visibleRatio = visibleRatio * (1.f - threshold) + threshold;
}
float scale = visibleRatio;
if (scale == 0.f)
{
// no traverse
return;
}
else
{
osg::Matrix modelView = *cv->getModelViewMatrix();
modelView.preMultScale(osg::Vec3f(visibleRatio, visibleRatio, visibleRatio));
if (stateset)
cv->pushStateSet(stateset);
cv->pushModelViewMatrix(new osg::RefMatrix(modelView), osg::Transform::RELATIVE_RF);
traverse(node, nv);
cv->popModelViewMatrix();
if (stateset)
cv->popStateSet();
}
}
void setGlareView(float value)
{
mGlareView = value;
}
private:
float mGlareView;
};
/// SunGlareCallback controls a full-screen glare effect depending on occlusion query result and the angle between sun and camera.
/// Must be attached as a cull callback to the node above the glare node.
class SunGlareCallback : public OcclusionCallback
{
public:
SunGlareCallback(osg::ref_ptr<osg::OcclusionQueryNode> oqnVisible, osg::ref_ptr<osg::OcclusionQueryNode> oqnTotal,
osg::ref_ptr<osg::PositionAttitudeTransform> sunTransform)
: OcclusionCallback(oqnVisible, oqnTotal)
, mSunTransform(sunTransform)
, mTimeOfDayFade(1.f)
, mGlareView(1.f)
{
}
virtual void operator ()(osg::Node* node, osg::NodeVisitor* nv)
{
osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>(nv);
float angleRadians = getAngleToSunInRadians(cv->getCurrentCamera());
float visibleRatio = getVisibleRatio(cv->getCurrentCamera());
const float angleMaxRadians = osg::DegreesToRadians(30.f); // Sun Glare Fader Angle Max
float value = 1.f - std::min(1.f, angleRadians / angleMaxRadians);
const float sunGlareFaderMax = 0.5f;
float fade = value * sunGlareFaderMax;
fade *= mTimeOfDayFade * mGlareView * visibleRatio;
if (fade == 0.f)
{
// no traverse
return;
}
else
{
osg::ref_ptr<osg::StateSet> stateset (new osg::StateSet);
osg::ref_ptr<osg::Material> mat (createUnlitMaterial());
osg::Vec4f sunGlareFaderColor (222/255.f, 95/255.f, 39/255.f, 1);
// Replicating a design flaw in MW. The color was being set on both ambient and emissive properties, which multiplies the result by two,
// then finally gets clamped by the fixed function pipeline. With the default INI settings, only the red component gets clamped,
// so the resulting color looks more orange than red.
sunGlareFaderColor *= 2;
for (int i=0; i<3; ++i)
sunGlareFaderColor[i] = std::min(1.f, sunGlareFaderColor[i]);
mat->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0,0,0,fade));
mat->setEmission(osg::Material::FRONT_AND_BACK, sunGlareFaderColor);
stateset->setAttributeAndModes(mat, osg::StateAttribute::ON);
cv->pushStateSet(stateset);
traverse(node, nv);
cv->popStateSet();
}
}
void setTimeOfDayFade(float val)
{
mTimeOfDayFade = val;
}
void setGlareView(float glareView)
{
mGlareView = glareView;
}
private:
float getAngleToSunInRadians(osg::Camera* camera) const
{
osg::Vec3d eye, center, up;
camera->getViewMatrixAsLookAt(eye, center, up);
osg::Vec3d forward = center - eye;
osg::Vec3d sun = mSunTransform->getPosition();
forward.normalize();
sun.normalize();
float angleRadians = std::acos(forward * sun);
return angleRadians;
}
osg::ref_ptr<osg::PositionAttitudeTransform> mSunTransform;
float mTimeOfDayFade;
float mGlareView;
};
osg::ref_ptr<Updater> mUpdater;
osg::ref_ptr<SunFlashCallback> mSunFlashCallback;
osg::ref_ptr<osg::Node> mSunFlashNode;
osg::ref_ptr<SunGlareCallback> mSunGlareCallback;
osg::ref_ptr<osg::Node> mSunGlareNode;
osg::ref_ptr<osg::OcclusionQueryNode> mOcclusionQueryVisiblePixels;
osg::ref_ptr<osg::OcclusionQueryNode> mOcclusionQueryTotalPixels;
};
class Moon : public CelestialBody
@ -608,8 +1005,6 @@ SkyManager::SkyManager(osg::Group* parentNode, Resource::SceneManager* sceneMana
, mCloudSpeed(0.0f)
, mStarsOpacity(0.0f)
, mRemainingTransitionTime(0.0f)
, mGlare(0.0f)
, mGlareFade(0.0f)
, mRainEnabled(false)
, mRainSpeed(0)
, mRainFrequency(1)
@ -624,7 +1019,7 @@ SkyManager::SkyManager(osg::Group* parentNode, Resource::SceneManager* sceneMana
mRootNode = skyroot;
// By default render before the world is rendered
mRootNode->getOrCreateStateSet()->setRenderBinDetails(-1, "RenderBin");
mRootNode->getOrCreateStateSet()->setRenderBinDetails(RenderBin_Sky, "RenderBin");
}
void SkyManager::create()
@ -926,32 +1321,6 @@ void SkyManager::update(float duration)
mCloudUpdater->setAnimationTimer(mCloudAnimationTimer);
mCloudUpdater2->setAnimationTimer(mCloudAnimationTimer);
if (mSunEnabled)
{
// take 1/10 sec for fading the glare effect from invisible to full
if (mGlareFade > mGlare)
{
mGlareFade -= duration*10;
if (mGlareFade < mGlare) mGlareFade = mGlare;
}
else if (mGlareFade < mGlare)
{
mGlareFade += duration*10;
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 cam = mCamera->getRealDirection();
const Degree angle = sun.angleBetween( cam );
float val = 1- (angle.valueDegrees() / 180.f);
val = (val*val*val*val)*6;
mSunGlare->setSize(val * mGlareFade);
*/
}
// rotate the stars by 360 degrees every 4 days
mAtmosphereNightRoll += MWBase::Environment::get().getWorld()->getTimeScaleFactor()*duration*osg::DegreesToRadians(360.f) / (3600*96.f);
if (mAtmosphereNightNode->getNodeMask() != 0)
@ -1102,7 +1471,9 @@ void SkyManager::setWeather(const WeatherResult& weather)
mMasser->adjustTransparency(weather.mGlareView);
mSecunda->adjustTransparency(weather.mGlareView);
mSun->adjustTransparency(weather.mGlareView);
mSun->setColor(weather.mSunDiscColor);
mSun->adjustTransparency(weather.mGlareView * weather.mSunDiscColor.a());
float nextStarsOpacity = weather.mNightFade * weather.mGlareView;
if(weather.mNight && mStarsOpacity != nextStarsOpacity)
@ -1114,31 +1485,12 @@ void SkyManager::setWeather(const WeatherResult& weather)
mAtmosphereNightNode->setNodeMask(weather.mNight ? ~0 : 0);
/*
float strength;
float timeofday_angle = std::abs(mSunGlare->getPosition().z/mSunGlare->getPosition().length());
if (timeofday_angle <= 0.44)
strength = timeofday_angle/0.44f;
else
strength = 1.f;
mSunGlare->setVisibility(weather.mGlareView * mGlareFade * strength);
mSun->setVisibility(weather.mGlareView * strength);
*/
if (mRainFader)
mRainFader->setAlpha(weather.mEffectFade * 0.6); // * Rain_Threshold?
if (mParticleFader)
mParticleFader->setAlpha(weather.mEffectFade);
}
void SkyManager::setGlare(const float glare)
{
mGlare = glare;
}
void SkyManager::sunEnable()
{
if (!mCreated) return;
@ -1163,8 +1515,6 @@ void SkyManager::setSunDirection(const osg::Vec3f& direction)
if (!mCreated) return;
mSun->setDirection(direction);
//mSunGlare->setPosition(direction);
}
void SkyManager::setMasserState(const MoonState& state)
@ -1187,11 +1537,9 @@ void SkyManager::setDate(int day, int month)
mMonth = month;
}
void SkyManager::setGlareEnabled (bool enabled)
void SkyManager::setGlareTimeOfDayFade(float val)
{
if (!mCreated || !mEnabled)
return;
//mSunGlare->setVisible (mSunEnabled && enabled);
mSun->setGlareTimeOfDayFade(val);
}
}

@ -48,8 +48,10 @@ namespace MWRender
osg::Vec4f mSkyColor;
// sun light color
osg::Vec4f mSunColor;
// alpha is the sun transparency
osg::Vec4f mSunDiscColor;
float mFogDepth;
@ -140,8 +142,7 @@ namespace MWRender
void setMasserState(const MoonState& state);
void setSecundaState(const MoonState& state);
void setGlare(const float glare);
void setGlareEnabled(bool enabled);
void setGlareTimeOfDayFade(float val);
private:
void create();
@ -210,9 +211,6 @@ namespace MWRender
float mRemainingTransitionTime;
float mGlare; // target
float mGlareFade; // actual
bool mRainEnabled;
std::string mRainEffect;
float mRainSpeed;

@ -21,6 +21,7 @@
#include "vismask.hpp"
#include "ripplesimulation.hpp"
#include "renderbin.hpp"
namespace
{
@ -86,7 +87,7 @@ namespace
depth->setWriteMask(false);
stateset->setAttributeAndModes(depth, osg::StateAttribute::ON);
stateset->setRenderBinDetails(9, "RenderBin");
stateset->setRenderBinDetails(MWRender::RenderBin_Water, "RenderBin");
std::vector<osg::ref_ptr<osg::Texture2D> > textures;
for (int i=0; i<32; ++i)

@ -432,6 +432,7 @@ WeatherManager::WeatherManager(MWRender::RenderingManager& rendering, const MWWo
, mSunsetTime(fallback.getFallbackFloat("Weather_Sunset_Time"))
, mSunriseDuration(fallback.getFallbackFloat("Weather_Sunrise_Duration"))
, mSunsetDuration(fallback.getFallbackFloat("Weather_Sunset_Duration"))
, mSunPreSunsetTime(fallback.getFallbackFloat("Weather_Sun_Pre-Sunset_Time"))
, mNightStart(mSunsetTime + mSunsetDuration)
, mNightEnd(mSunriseTime - 0.5f)
, mDayStart(mSunriseTime + mSunriseDuration)
@ -458,16 +459,16 @@ WeatherManager::WeatherManager(MWRender::RenderingManager& rendering, const MWWo
, mPlayingSoundID()
{
mWeatherSettings.reserve(10);
addWeather("Clear", fallback);
addWeather("Cloudy", fallback);
addWeather("Foggy", fallback);
addWeather("Overcast", fallback);
addWeather("Rain", fallback, "rain");
addWeather("Thunderstorm", fallback, "rain heavy");
addWeather("Ashstorm", fallback, "ashstorm", "meshes\\ashcloud.nif");
addWeather("Blight", fallback, "blight", "meshes\\blightcloud.nif");
addWeather("Snow", fallback, "", "meshes\\snow.nif");
addWeather("Blizzard", fallback, "BM Blizzard", "meshes\\blizzard.nif");
addWeather("Clear", fallback); // 0
addWeather("Cloudy", fallback); // 1
addWeather("Foggy", fallback); // 2
addWeather("Overcast", fallback); // 3
addWeather("Rain", fallback, "rain"); // 4
addWeather("Thunderstorm", fallback, "rain heavy"); // 5
addWeather("Ashstorm", fallback, "ashstorm", "meshes\\ashcloud.nif"); // 6
addWeather("Blight", fallback, "blight", "meshes\\blightcloud.nif"); // 7
addWeather("Snow", fallback, "", "meshes\\snow.nif"); // 8
addWeather("Blizzard", fallback, "BM Blizzard", "meshes\\blizzard.nif"); // 9
Store<ESM::Region>::iterator it = store.get<ESM::Region>().begin();
for(; it != store.get<ESM::Region>().end(); ++it)
@ -624,6 +625,14 @@ void WeatherManager::update(float duration, bool paused)
mRendering.setSunDirection( final * -1 );
}
float peakHour = mSunriseTime + (mSunsetTime - mSunriseTime) / 2;
if (time.getHour() < mSunriseTime || time.getHour() > mSunsetTime)
mRendering.getSkyManager()->setGlareTimeOfDayFade(0);
else if (time.getHour() < peakHour)
mRendering.getSkyManager()->setGlareTimeOfDayFade(1 - (peakHour - time.getHour()) / (peakHour - mSunriseTime));
else
mRendering.getSkyManager()->setGlareTimeOfDayFade(1 - (time.getHour() - peakHour) / (mSunsetTime - peakHour));
mRendering.getSkyManager()->setMasserState(mMasser.calculateState(time));
mRendering.getSkyManager()->setSecundaState(mSecunda.calculateState(time));
@ -958,7 +967,6 @@ inline void WeatherManager::calculateResult(const int weatherID, const float gam
mResult.mAmbientLoopSoundID = current.mAmbientLoopSoundID;
mResult.mAmbientSoundVolume = 1.f;
mResult.mEffectFade = 1.f;
mResult.mSunColor = current.mSunDiscSunsetColor;
mResult.mIsStorm = current.mIsStorm;
@ -972,6 +980,7 @@ inline void WeatherManager::calculateResult(const int weatherID, const float gam
mResult.mFogDepth = mResult.mNight ? current.mLandFogNightDepth : current.mLandFogDayDepth;
// TODO: use pre/post sunset/sunrise time values in [Weather] section
// night
if (gameHour <= mNightEnd || gameHour >= mNightStart + 1)
{
@ -1042,6 +1051,36 @@ inline void WeatherManager::calculateResult(const int weatherID, const float gam
mResult.mNightFade = factor;
}
}
if (gameHour >= mSunsetTime - mSunPreSunsetTime)
{
float factor = (gameHour - (mSunsetTime - mSunPreSunsetTime)) / mSunPreSunsetTime;
factor = std::min(1.f, factor);
mResult.mSunDiscColor = lerp(osg::Vec4f(1,1,1,1), current.mSunDiscSunsetColor, factor);
// The SunDiscSunsetColor in the INI isn't exactly the resulting color on screen, most likely because
// MW applied the color to the ambient term as well. After the ambient and emissive terms are added together, the fixed pipeline
// would then clamp the total lighting to (1,1,1). A noticable change in color tone can be observed when only one of the color components gets clamped.
// Unfortunately that means we can't use the INI color as is, have to replicate the above nonsense.
mResult.mSunDiscColor = mResult.mSunDiscColor + osg::componentMultiply(mResult.mSunDiscColor, mResult.mAmbientColor);
for (int i=0; i<3; ++i)
mResult.mSunDiscColor[i] = std::min(1.f, mResult.mSunDiscColor[i]);
}
else
mResult.mSunDiscColor = osg::Vec4f(1,1,1,1);
if (gameHour >= mSunsetTime)
{
float fade = std::min(1.f, (gameHour - mSunsetTime) / 2.f);
fade = fade*fade;
mResult.mSunDiscColor.a() = 1.f - fade;
}
else if (gameHour >= mSunriseTime && gameHour <= mSunriseTime + 1)
{
mResult.mSunDiscColor.a() = gameHour - mSunriseTime;
}
else
mResult.mSunDiscColor.a() = 1;
}
inline void WeatherManager::calculateTransitionResult(const float factor, const float gameHour)

@ -75,7 +75,7 @@ namespace MWWorld
float mLandFogDayDepth;
float mLandFogNightDepth;
// Color modulation for the sun itself during sunset (not completely sure)
// Color modulation for the sun itself during sunset
osg::Vec4f mSunDiscSunsetColor;
// Used by scripts to animate signs, etc based on the wind (GetWindSpeed)
@ -242,12 +242,7 @@ namespace MWWorld
float mSunsetTime;
float mSunriseDuration;
float mSunsetDuration;
// Some useful values
/* TODO: Use pre-sunrise_time, pre-sunset_time,
* post-sunrise_time, and post-sunset_time to better
* describe sunrise/sunset time.
* These values are fallbacks attached to weather.
*/
float mSunPreSunsetTime;
float mNightStart;
float mNightEnd;
float mDayStart;

@ -6,7 +6,7 @@
namespace SceneUtil
{
/// @brief Implements efficient pre-frame updating of StateSets.
/// @brief Implements efficient per-frame updating of StateSets.
/// @par With a naive update there would be race conditions when the OSG draw thread of the last frame
/// queues up a StateSet that we want to modify for the next frame. To solve this we could set the StateSet to
/// DYNAMIC data variance but that would undo all the benefits of the threading model - having the cull and draw

Loading…
Cancel
Save