From 8de3ce90a7e0d219137eaf61ec120c24f8419c2c Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 19 Sep 2015 18:04:24 +0200 Subject: [PATCH 01/15] Add comments for weather IDs --- apps/openmw/mwworld/weather.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index cdd0a8d79..b0e617f2b 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -458,16 +458,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::iterator it = store.get().begin(); for(; it != store.get().end(); ++it) From 6bafa564d4b96a7336490d6ea4be8b96438ee72b Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 19 Sep 2015 18:08:31 +0200 Subject: [PATCH 02/15] Move sun texture setting out of the Updater class so we can reuse the Updater for fading the flash texture --- apps/openmw/mwrender/sky.cpp | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index dba2e5b0e..67e842c8d 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -359,14 +359,20 @@ 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 sunTex = textureManager.getTexture2D("textures/tx_sun_05.dds", + osg::Texture::CLAMP, + osg::Texture::CLAMP); + + mGeode->getOrCreateStateSet()->setTextureAttributeAndModes(0, sunTex, osg::StateAttribute::ON); } ~Sun() { - mGeode->removeUpdateCallback(mUpdater); + mTransform->removeUpdateCallback(mUpdater); } virtual void adjustTransparency(const float ratio) @@ -387,22 +393,15 @@ public: private: struct Updater : public SceneUtil::StateSetUpdater { - Resource::TextureManager& mTextureManager; 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.0f) { } virtual void setDefaults(osg::StateSet* stateset) { - osg::ref_ptr 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); } From d191a528470b990be094bf243b697694ad159dda Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 19 Sep 2015 18:10:02 +0200 Subject: [PATCH 03/15] Create occlusion query nodes for the sun flash --- apps/openmw/mwrender/sky.cpp | 57 ++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 67e842c8d..3d2b35c37 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -11,6 +11,9 @@ #include #include #include +#include +#include +#include #include #include @@ -368,6 +371,18 @@ public: osg::Texture::CLAMP); mGeode->getOrCreateStateSet()->setTextureAttributeAndModes(0, sunTex, osg::StateAttribute::ON); + + // Slightly downscale the query geometry since the sun quad has a transparent texture that doesn't cover the whole area + osg::ref_ptr queryTransform (new osg::PositionAttitudeTransform); + queryTransform->setScale(osg::Vec3f(0.5f, 0.5f, 0.5f)); + // Need to render after the world geometry so we can correctly test for occlusions + queryTransform->getOrCreateStateSet()->setRenderBinDetails(10, "RenderBin"); + queryTransform->getOrCreateStateSet()->setNestRenderBins(false); + + mTransform->addChild(queryTransform); + + mOcclusionQueryVisiblePixels = createOcclusionQueryNode(queryTransform, true); + mOcclusionQueryTotalPixels = createOcclusionQueryNode(queryTransform, false); } ~Sun() @@ -391,6 +406,46 @@ public: } private: + /// @param queryVisible If true, queries the amount of visible pixels. If false, queries the total amount of pixels. + osg::ref_ptr createOcclusionQueryNode(osg::Group* parent, bool queryVisible) + { + osg::ref_ptr 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 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 colormask (new osg::ColorMask(0, 0, 0, 0)); + queryGeode->getOrCreateStateSet()->setAttributeAndModes(colormask, osg::StateAttribute::ON); + + oqn->addChild(queryGeode); + + if (queryVisible) + { + osg::ref_ptr 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; + } + struct Updater : public SceneUtil::StateSetUpdater { osg::Vec4f mColor; @@ -414,6 +469,8 @@ private: }; osg::ref_ptr mUpdater; + osg::ref_ptr mOcclusionQueryVisiblePixels; + osg::ref_ptr mOcclusionQueryTotalPixels; }; class Moon : public CelestialBody From 89d9323c2b6d870d7ea5d0b3d33a84a834916ef5 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 19 Sep 2015 18:19:36 +0200 Subject: [PATCH 04/15] Document RenderBin numbers in a common header to keep them organised --- apps/openmw/CMakeLists.txt | 1 + apps/openmw/mwrender/renderbin.hpp | 20 ++++++++++++++++++++ apps/openmw/mwrender/sky.cpp | 5 +++-- apps/openmw/mwrender/water.cpp | 3 ++- 4 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 apps/openmw/mwrender/renderbin.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 2dd16a4e6..5a9b52cb2 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -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 diff --git a/apps/openmw/mwrender/renderbin.hpp b/apps/openmw/mwrender/renderbin.hpp new file mode 100644 index 000000000..63ccdd259 --- /dev/null +++ b/apps/openmw/mwrender/renderbin.hpp @@ -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 diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 3d2b35c37..8588d2158 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -41,6 +41,7 @@ #include "../mwworld/fallback.hpp" #include "vismask.hpp" +#include "renderbin.hpp" namespace { @@ -376,7 +377,7 @@ public: osg::ref_ptr queryTransform (new osg::PositionAttitudeTransform); queryTransform->setScale(osg::Vec3f(0.5f, 0.5f, 0.5f)); // Need to render after the world geometry so we can correctly test for occlusions - queryTransform->getOrCreateStateSet()->setRenderBinDetails(10, "RenderBin"); + queryTransform->getOrCreateStateSet()->setRenderBinDetails(RenderBin_OcclusionQuery, "RenderBin"); queryTransform->getOrCreateStateSet()->setNestRenderBins(false); mTransform->addChild(queryTransform); @@ -680,7 +681,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() diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index b5f141963..03ab58e6b 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -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 > textures; for (int i=0; i<32; ++i) From ac5d0bf40502f355a2bd46cdf57c12b3723d59b6 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 19 Sep 2015 18:22:16 +0200 Subject: [PATCH 05/15] Render the sun flash (not adjusted based on occlusion yet) --- apps/openmw/mwrender/sky.cpp | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 8588d2158..17b4f2f6a 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -364,6 +364,7 @@ public: Sun(osg::Group* parentNode, Resource::TextureManager& textureManager) : CelestialBody(parentNode, 1.0f, 1) , mUpdater(new Updater) + , mInitialFlashScale(2.6f) { mTransform->addUpdateCallback(mUpdater); @@ -384,6 +385,8 @@ public: mOcclusionQueryVisiblePixels = createOcclusionQueryNode(queryTransform, true); mOcclusionQueryTotalPixels = createOcclusionQueryNode(queryTransform, false); + + createSunFlash(textureManager); } ~Sun() @@ -447,6 +450,34 @@ private: return oqn; } + void createSunFlash(Resource::TextureManager& textureManager) + { + osg::ref_ptr tex = textureManager.getTexture2D("textures/tx_sun_flash_grey_05.dds", + osg::Texture::CLAMP, + osg::Texture::CLAMP); + + osg::ref_ptr transform (new osg::PositionAttitudeTransform); + transform->setScale(osg::Vec3f(mInitialFlashScale, mInitialFlashScale, mInitialFlashScale)); + + mTransform->addChild(transform); + + osg::ref_ptr geode (new osg::Geode); + transform->addChild(geode); + + geode->addDrawable(createTexturedQuad()); + + osg::StateSet* stateset = geode->getOrCreateStateSet(); + + stateset->setTextureAttributeAndModes(0, tex, osg::StateAttribute::ON); + stateset->setAttributeAndModes(createUnlitMaterial(), + osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); + stateset->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF); + stateset->setRenderBinDetails(RenderBin_SunGlare, "RenderBin"); + stateset->setNestRenderBins(false); + + // TODO: change size depending on occlusion + } + struct Updater : public SceneUtil::StateSetUpdater { osg::Vec4f mColor; @@ -472,6 +503,7 @@ private: osg::ref_ptr mUpdater; osg::ref_ptr mOcclusionQueryVisiblePixels; osg::ref_ptr mOcclusionQueryTotalPixels; + float mInitialFlashScale; }; class Moon : public CelestialBody From a2a4532e71317332068e6617a29a2bfc5aa20544 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 21 Sep 2015 16:03:30 +0200 Subject: [PATCH 06/15] Add the full-screen sun glare effect --- apps/openmw/mwrender/sky.cpp | 260 ++++++++++++++++++++++++++++++-- apps/openmw/mwrender/sky.hpp | 3 +- apps/openmw/mwworld/weather.cpp | 8 + 3 files changed, 253 insertions(+), 18 deletions(-) diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 17b4f2f6a..69041a724 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -376,7 +377,7 @@ public: // Slightly downscale the query geometry since the sun quad has a transparent texture that doesn't cover the whole area osg::ref_ptr queryTransform (new osg::PositionAttitudeTransform); - queryTransform->setScale(osg::Vec3f(0.5f, 0.5f, 0.5f)); + queryTransform->setScale(osg::Vec3f(0.4f, 0.4f, 0.4f)); // Need to render after the world geometry so we can correctly test for occlusions queryTransform->getOrCreateStateSet()->setRenderBinDetails(RenderBin_OcclusionQuery, "RenderBin"); queryTransform->getOrCreateStateSet()->setNestRenderBins(false); @@ -386,17 +387,26 @@ public: mOcclusionQueryVisiblePixels = createOcclusionQueryNode(queryTransform, true); mOcclusionQueryTotalPixels = createOcclusionQueryNode(queryTransform, false); - createSunFlash(textureManager); + osg::PositionAttitudeTransform* sunFlashNode = createSunFlash(textureManager); + + mSunFlashCallback = new SunFlashCallback(mOcclusionQueryVisiblePixels, mOcclusionQueryTotalPixels, sunFlashNode, mTransform); + mTransform->addCullCallback(mSunFlashCallback); + + createSunGlare(); } ~Sun() { mTransform->removeUpdateCallback(mUpdater); + destroySunFlash(); + destroySunGlare(); } virtual void adjustTransparency(const float ratio) { mUpdater->mColor.a() = ratio; + if (mSunGlareCallback) + mSunGlareCallback->setGlareView(ratio); } void setDirection(const osg::Vec3f& direction) @@ -409,6 +419,12 @@ public: mTransform->setAttitude(quat); } + void setGlareTimeOfDayFade(float val) + { + if (mSunGlareCallback) + mSunGlareCallback->setTimeOfDayFade(val); + } + private: /// @param queryVisible If true, queries the amount of visible pixels. If false, queries the total amount of pixels. osg::ref_ptr createOcclusionQueryNode(osg::Group* parent, bool queryVisible) @@ -450,7 +466,7 @@ private: return oqn; } - void createSunFlash(Resource::TextureManager& textureManager) + osg::PositionAttitudeTransform* createSunFlash(Resource::TextureManager& textureManager) { osg::ref_ptr tex = textureManager.getTexture2D("textures/tx_sun_flash_grey_05.dds", osg::Texture::CLAMP, @@ -474,12 +490,58 @@ private: stateset->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF); stateset->setRenderBinDetails(RenderBin_SunGlare, "RenderBin"); stateset->setNestRenderBins(false); + return transform; + } + void destroySunFlash() + { + mTransform->removeCullCallback(mSunFlashCallback); + mSunFlashCallback = NULL; + } + + void createSunGlare() + { + osg::ref_ptr 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 geode (new osg::Geode); + osg::ref_ptr 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(); - // TODO: change size depending on occlusion + stateset->setRenderBinDetails(RenderBin_SunGlare, "RenderBin"); + stateset->setNestRenderBins(false); + stateset->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF); + + // set up additive blending + osg::ref_ptr 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() + { + mSunGlareNode->removeCullCallback(mSunGlareCallback); + mSunGlareCallback = NULL; } - struct Updater : public SceneUtil::StateSetUpdater + class Updater : public SceneUtil::StateSetUpdater { + public: osg::Vec4f mColor; Updater() @@ -489,8 +551,7 @@ private: virtual void setDefaults(osg::StateSet* stateset) { - stateset->setAttributeAndModes(createUnlitMaterial(), - osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); + stateset->setAttributeAndModes(createUnlitMaterial(), osg::StateAttribute::ON); } virtual void apply(osg::StateSet* stateset, osg::NodeVisitor*) @@ -500,7 +561,181 @@ private: } }; + class OcclusionCallback : public osg::NodeCallback + { + public: + OcclusionCallback(osg::ref_ptr oqnVisible, osg::ref_ptr 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(visible) / static_cast(total); + + float dt = MWBase::Environment::get().getFrameDuration(); + + float lastRatio = mLastRatio[osg::observer_ptr(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(camera)] = visibleRatio; + + return visibleRatio; + } + + private: + osg::ref_ptr mOcclusionQueryVisiblePixels; + osg::ref_ptr mOcclusionQueryTotalPixels; + + std::map, float> mLastRatio; + }; + + /// SunFlashCallback handles fading/scaling of the sun flash depending on occlusion query result. Must be attached as a cull callback. + class SunFlashCallback : public OcclusionCallback + { + public: + SunFlashCallback(osg::ref_ptr oqnVisible, osg::ref_ptr oqnTotal, + osg::ref_ptr flashNode, + osg::ref_ptr sunTransform) + : OcclusionCallback(oqnVisible, oqnTotal) + , mFlashNode(flashNode) + { + mInitialScale = mFlashNode->getScale().x(); + } + + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) + { + osgUtil::CullVisitor* cv = static_cast(nv); + + float visibleRatio = getVisibleRatio(cv->getCurrentCamera()); + + handleOcclusionResult (visibleRatio, cv); + + traverse(node, nv); + } + + void handleOcclusionResult(float visibleRatio, osgUtil::CullVisitor* cv) + { + // TODO + } + + private: + float mInitialScale; + osg::ref_ptr mFlashNode; + }; + + + /// 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 oqnVisible, osg::ref_ptr oqnTotal, + osg::ref_ptr 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(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 stateset (new osg::StateSet); + + osg::ref_ptr 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->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(0,0,0,1)); + mat->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0,0,0,fade)); + mat->setEmission(osg::Material::FRONT_AND_BACK, sunGlareFaderColor); + mat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0,0,0,0)); + + 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 mSunTransform; + float mTimeOfDayFade; + float mGlareView; + }; + osg::ref_ptr mUpdater; + osg::ref_ptr mSunFlashCallback; + osg::ref_ptr mSunGlareCallback; + osg::ref_ptr mSunGlareNode; osg::ref_ptr mOcclusionQueryVisiblePixels; osg::ref_ptr mOcclusionQueryTotalPixels; float mInitialFlashScale; @@ -1223,11 +1458,6 @@ void SkyManager::setWeather(const WeatherResult& weather) mParticleFader->setAlpha(weather.mEffectFade); } -void SkyManager::setGlare(const float glare) -{ - mGlare = glare; -} - void SkyManager::sunEnable() { if (!mCreated) return; @@ -1276,11 +1506,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); } } diff --git a/apps/openmw/mwrender/sky.hpp b/apps/openmw/mwrender/sky.hpp index de27cf447..b9b27b3d0 100644 --- a/apps/openmw/mwrender/sky.hpp +++ b/apps/openmw/mwrender/sky.hpp @@ -140,8 +140,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(); diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index b0e617f2b..050b6652c 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -624,6 +624,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)); From 96b31d3bba084e2a95ae63c3f245b427dc1f3977 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 21 Sep 2015 16:34:30 +0200 Subject: [PATCH 07/15] Scale the sun flash texture depending on occlusion query --- apps/openmw/mwrender/sky.cpp | 71 ++++++++++++++++++++++-------------- 1 file changed, 44 insertions(+), 27 deletions(-) diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 69041a724..78b1aba70 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -387,11 +387,7 @@ public: mOcclusionQueryVisiblePixels = createOcclusionQueryNode(queryTransform, true); mOcclusionQueryTotalPixels = createOcclusionQueryNode(queryTransform, false); - osg::PositionAttitudeTransform* sunFlashNode = createSunFlash(textureManager); - - mSunFlashCallback = new SunFlashCallback(mOcclusionQueryVisiblePixels, mOcclusionQueryTotalPixels, sunFlashNode, mTransform); - mTransform->addCullCallback(mSunFlashCallback); - + createSunFlash(textureManager); createSunGlare(); } @@ -466,7 +462,7 @@ private: return oqn; } - osg::PositionAttitudeTransform* createSunFlash(Resource::TextureManager& textureManager) + void createSunFlash(Resource::TextureManager& textureManager) { osg::ref_ptr tex = textureManager.getTexture2D("textures/tx_sun_flash_grey_05.dds", osg::Texture::CLAMP, @@ -490,12 +486,19 @@ private: stateset->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF); stateset->setRenderBinDetails(RenderBin_SunGlare, "RenderBin"); stateset->setNestRenderBins(false); - return transform; + + mSunFlashNode = transform; + + mSunFlashCallback = new SunFlashCallback(mOcclusionQueryVisiblePixels, mOcclusionQueryTotalPixels); + mSunFlashNode->addCullCallback(mSunFlashCallback); } void destroySunFlash() { - mTransform->removeCullCallback(mSunFlashCallback); - mSunFlashCallback = NULL; + if (mSunFlashNode) + { + mSunFlashNode->removeCullCallback(mSunFlashCallback); + mSunFlashCallback = NULL; + } } void createSunGlare() @@ -535,8 +538,11 @@ private: } void destroySunGlare() { - mSunGlareNode->removeCullCallback(mSunGlareCallback); - mSunGlareCallback = NULL; + if (mSunGlareNode) + { + mSunGlareNode->removeCullCallback(mSunGlareCallback); + mSunGlareCallback = NULL; + } } class Updater : public SceneUtil::StateSetUpdater @@ -603,17 +609,13 @@ private: std::map, float> mLastRatio; }; - /// SunFlashCallback handles fading/scaling of the sun flash depending on occlusion query result. Must be attached as a cull callback. + /// 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 oqnVisible, osg::ref_ptr oqnTotal, - osg::ref_ptr flashNode, - osg::ref_ptr sunTransform) + SunFlashCallback(osg::ref_ptr oqnVisible, osg::ref_ptr oqnTotal) : OcclusionCallback(oqnVisible, oqnTotal) - , mFlashNode(flashNode) { - mInitialScale = mFlashNode->getScale().x(); } virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) @@ -622,19 +624,33 @@ private: float visibleRatio = getVisibleRatio(cv->getCurrentCamera()); - handleOcclusionResult (visibleRatio, cv); + if (visibleRatio > 0.f) + { + // rescale into [0.35, 1.0] range + const float threshold = 0.35; + visibleRatio = visibleRatio * (1.f - threshold) + threshold; + } - traverse(node, nv); - } + float scale = visibleRatio; - void handleOcclusionResult(float visibleRatio, osgUtil::CullVisitor* cv) - { - // TODO - } + if (scale == 0.f) + { + // no traverse + return; + } + else + { + osg::Matrix modelView = *cv->getModelViewMatrix(); - private: - float mInitialScale; - osg::ref_ptr mFlashNode; + modelView.preMultScale(osg::Vec3f(visibleRatio, visibleRatio, visibleRatio)); + + cv->pushModelViewMatrix(new osg::RefMatrix(modelView), osg::Transform::RELATIVE_RF); + + traverse(node, nv); + + cv->popModelViewMatrix(); + } + } }; @@ -734,6 +750,7 @@ private: osg::ref_ptr mUpdater; osg::ref_ptr mSunFlashCallback; + osg::ref_ptr mSunFlashNode; osg::ref_ptr mSunGlareCallback; osg::ref_ptr mSunGlareNode; osg::ref_ptr mOcclusionQueryVisiblePixels; From 9bb6c3f288c001ba11a2c08bf260ad927410f253 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 21 Sep 2015 17:09:24 +0200 Subject: [PATCH 08/15] Improve accuracy of sun occlusion query (use circular shape) --- apps/openmw/mwrender/sky.cpp | 43 +++++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 78b1aba70..63a041579 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -375,17 +376,20 @@ public: mGeode->getOrCreateStateSet()->setTextureAttributeAndModes(0, sunTex, osg::StateAttribute::ON); - // Slightly downscale the query geometry since the sun quad has a transparent texture that doesn't cover the whole area - osg::ref_ptr queryTransform (new osg::PositionAttitudeTransform); - queryTransform->setScale(osg::Vec3f(0.4f, 0.4f, 0.4f)); + osg::ref_ptr queryNode (new osg::Group); // Need to render after the world geometry so we can correctly test for occlusions - queryTransform->getOrCreateStateSet()->setRenderBinDetails(RenderBin_OcclusionQuery, "RenderBin"); - queryTransform->getOrCreateStateSet()->setNestRenderBins(false); + 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 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); - mTransform->addChild(queryTransform); + mTransform->addChild(queryNode); - mOcclusionQueryVisiblePixels = createOcclusionQueryNode(queryTransform, true); - mOcclusionQueryTotalPixels = createOcclusionQueryNode(queryTransform, false); + mOcclusionQueryVisiblePixels = createOcclusionQueryNode(queryNode, true); + mOcclusionQueryTotalPixels = createOcclusionQueryNode(queryNode, false); createSunFlash(textureManager); createSunGlare(); @@ -441,6 +445,26 @@ private: 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 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 depth (new osg::Depth); @@ -626,8 +650,7 @@ private: if (visibleRatio > 0.f) { - // rescale into [0.35, 1.0] range - const float threshold = 0.35; + const float threshold = 0.6; visibleRatio = visibleRatio * (1.f - threshold) + threshold; } From 854fd9fe051c17395ec49a4e8321497b1c9e721e Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 21 Sep 2015 17:18:27 +0200 Subject: [PATCH 09/15] Remove dead code --- apps/openmw/mwrender/sky.cpp | 53 ++++++------------------------------ apps/openmw/mwrender/sky.hpp | 3 -- 2 files changed, 9 insertions(+), 47 deletions(-) diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 63a041579..86a9a1765 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -972,8 +972,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) @@ -1290,32 +1288,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) @@ -1466,6 +1438,15 @@ void SkyManager::setWeather(const WeatherResult& weather) mMasser->adjustTransparency(weather.mGlareView); mSecunda->adjustTransparency(weather.mGlareView); + + /* + float timeofday_angle = std::abs(mSun->getPosition().z/mSunGlare->getPosition().length()); + float strength; + if (timeofday_angle <= 0.44) + strength = timeofday_angle/0.44f; + else + strength = 1.f; + */ mSun->adjustTransparency(weather.mGlareView); float nextStarsOpacity = weather.mNightFade * weather.mGlareView; @@ -1478,20 +1459,6 @@ 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) @@ -1522,8 +1489,6 @@ void SkyManager::setSunDirection(const osg::Vec3f& direction) if (!mCreated) return; mSun->setDirection(direction); - - //mSunGlare->setPosition(direction); } void SkyManager::setMasserState(const MoonState& state) diff --git a/apps/openmw/mwrender/sky.hpp b/apps/openmw/mwrender/sky.hpp index b9b27b3d0..0cef46346 100644 --- a/apps/openmw/mwrender/sky.hpp +++ b/apps/openmw/mwrender/sky.hpp @@ -209,9 +209,6 @@ namespace MWRender float mRemainingTransitionTime; - float mGlare; // target - float mGlareFade; // actual - bool mRainEnabled; std::string mRainEffect; float mRainSpeed; From d812434feeb8b32aa1edada2e240994e5715cd43 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 21 Sep 2015 17:32:57 +0200 Subject: [PATCH 10/15] Add a subtle fading effect to the sun flash texture --- apps/openmw/mwrender/sky.cpp | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 86a9a1765..7f5666b17 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -385,6 +385,7 @@ public: 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); @@ -407,6 +408,8 @@ public: mUpdater->mColor.a() = ratio; if (mSunGlareCallback) mSunGlareCallback->setGlareView(ratio); + if (mSunFlashCallback) + mSunFlashCallback->setGlareView(ratio); } void setDirection(const osg::Vec3f& direction) @@ -505,8 +508,6 @@ private: osg::StateSet* stateset = geode->getOrCreateStateSet(); stateset->setTextureAttributeAndModes(0, tex, osg::StateAttribute::ON); - stateset->setAttributeAndModes(createUnlitMaterial(), - osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); stateset->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF); stateset->setRenderBinDetails(RenderBin_SunGlare, "RenderBin"); stateset->setNestRenderBins(false); @@ -639,6 +640,7 @@ private: public: SunFlashCallback(osg::ref_ptr oqnVisible, osg::ref_ptr oqnTotal) : OcclusionCallback(oqnVisible, oqnTotal) + , mGlareView(1.f) { } @@ -648,8 +650,20 @@ private: float visibleRatio = getVisibleRatio(cv->getCurrentCamera()); + osg::ref_ptr stateset; + if (visibleRatio > 0.f) { + const float fadeThreshold = 0.1; + if (visibleRatio < fadeThreshold) + { + float fade = 1.f - (fadeThreshold - visibleRatio) / fadeThreshold; + osg::ref_ptr 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; } @@ -667,13 +681,27 @@ private: 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; }; @@ -728,10 +756,8 @@ private: for (int i=0; i<3; ++i) sunGlareFaderColor[i] = std::min(1.f, sunGlareFaderColor[i]); - mat->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(0,0,0,1)); mat->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0,0,0,fade)); mat->setEmission(osg::Material::FRONT_AND_BACK, sunGlareFaderColor); - mat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0,0,0,0)); stateset->setAttributeAndModes(mat, osg::StateAttribute::ON); From 1a1f1fae87097885d11773f0137aed5ba0f03b51 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 21 Sep 2015 17:50:36 +0200 Subject: [PATCH 11/15] Minor cleanup --- apps/openmw/mwrender/sky.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 7f5666b17..754e00909 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -366,7 +366,6 @@ public: Sun(osg::Group* parentNode, Resource::TextureManager& textureManager) : CelestialBody(parentNode, 1.0f, 1) , mUpdater(new Updater) - , mInitialFlashScale(2.6f) { mTransform->addUpdateCallback(mUpdater); @@ -496,7 +495,8 @@ private: osg::Texture::CLAMP); osg::ref_ptr transform (new osg::PositionAttitudeTransform); - transform->setScale(osg::Vec3f(mInitialFlashScale, mInitialFlashScale, mInitialFlashScale)); + const float scale = 2.6f; + transform->setScale(osg::Vec3f(scale,scale,scale)); mTransform->addChild(transform); @@ -804,7 +804,6 @@ private: osg::ref_ptr mSunGlareNode; osg::ref_ptr mOcclusionQueryVisiblePixels; osg::ref_ptr mOcclusionQueryTotalPixels; - float mInitialFlashScale; }; class Moon : public CelestialBody From f7e5a40143d0db37e349ee5e3541c3e2e2ace1b7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 21 Sep 2015 17:58:57 +0200 Subject: [PATCH 12/15] Fix typo --- components/sceneutil/statesetupdater.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/sceneutil/statesetupdater.hpp b/components/sceneutil/statesetupdater.hpp index a4fcd7866..37d08e025 100644 --- a/components/sceneutil/statesetupdater.hpp +++ b/components/sceneutil/statesetupdater.hpp @@ -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 From 385f4f729c967c02fbcc6fac062af9c0d27a82b6 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 21 Sep 2015 19:43:48 +0200 Subject: [PATCH 13/15] Implement SunDiscSunsetColor, fade the sun during sunrise & sunset --- apps/openmw/mwrender/sky.cpp | 23 ++++++++++++----------- apps/openmw/mwrender/sky.hpp | 2 ++ apps/openmw/mwworld/weather.cpp | 33 ++++++++++++++++++++++++++++++++- apps/openmw/mwworld/weather.hpp | 9 ++------- 4 files changed, 48 insertions(+), 19 deletions(-) diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 754e00909..317f2229d 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -402,6 +402,13 @@ public: 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; @@ -576,7 +583,7 @@ private: osg::Vec4f mColor; Updater() - : mColor(1.f, 1.f, 1.f, 1.0f) + : mColor(1.f, 1.f, 1.f, 1.f) { } @@ -588,7 +595,8 @@ private: virtual void apply(osg::StateSet* stateset, osg::NodeVisitor*) { osg::Material* mat = static_cast(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)); } }; @@ -1464,15 +1472,8 @@ void SkyManager::setWeather(const WeatherResult& weather) mMasser->adjustTransparency(weather.mGlareView); mSecunda->adjustTransparency(weather.mGlareView); - /* - float timeofday_angle = std::abs(mSun->getPosition().z/mSunGlare->getPosition().length()); - float strength; - if (timeofday_angle <= 0.44) - strength = timeofday_angle/0.44f; - else - strength = 1.f; - */ - 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) diff --git a/apps/openmw/mwrender/sky.hpp b/apps/openmw/mwrender/sky.hpp index 0cef46346..072083d27 100644 --- a/apps/openmw/mwrender/sky.hpp +++ b/apps/openmw/mwrender/sky.hpp @@ -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; diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index 050b6652c..78ae6fa0b 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -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) @@ -966,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; @@ -980,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) { @@ -1050,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) diff --git a/apps/openmw/mwworld/weather.hpp b/apps/openmw/mwworld/weather.hpp index a0c93a460..7ce7c1bf8 100644 --- a/apps/openmw/mwworld/weather.hpp +++ b/apps/openmw/mwworld/weather.hpp @@ -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; From 7bef97bf3331dbdf9544c2caad17ecf68b3be96b Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 22 Sep 2015 15:36:00 +0200 Subject: [PATCH 14/15] fixed local variable caching issue in automatic error checking (Fixes #2927) --- apps/opencs/model/world/scriptcontext.cpp | 17 +++++++++++-- apps/opencs/model/world/scriptcontext.hpp | 3 +++ apps/opencs/view/world/scripterrortable.cpp | 5 ++++ apps/opencs/view/world/scripterrortable.hpp | 5 ++++ apps/opencs/view/world/scriptsubview.cpp | 27 ++++++++++++++++++++- apps/opencs/view/world/scriptsubview.hpp | 1 + 6 files changed, 55 insertions(+), 3 deletions(-) diff --git a/apps/opencs/model/world/scriptcontext.cpp b/apps/opencs/model/world/scriptcontext.cpp index bcbca4b28..f644ad37a 100644 --- a/apps/opencs/model/world/scriptcontext.cpp +++ b/apps/opencs/model/world/scriptcontext.cpp @@ -39,8 +39,6 @@ char CSMWorld::ScriptContext::getGlobalType (const std::string& name) const std::pair CSMWorld::ScriptContext::getMemberType (const std::string& name, const std::string& id) const { - /// \todo invalidate locals cache on change to scripts - std::string id2 = Misc::StringUtils::lowerCase (id); int index = mData.getScripts().searchId (id2); @@ -120,3 +118,18 @@ void CSMWorld::ScriptContext::clear() mIdsUpdated = false; mLocals.clear(); } + +bool CSMWorld::ScriptContext::clearLocals (const std::string& script) +{ + std::map::iterator iter = + mLocals.find (Misc::StringUtils::lowerCase (script)); + + if (iter!=mLocals.end()) + { + mLocals.erase (iter); + mIdsUpdated = false; + return true; + } + + return false; +} diff --git a/apps/opencs/model/world/scriptcontext.hpp b/apps/opencs/model/world/scriptcontext.hpp index 29ee42645..2cd59f070 100644 --- a/apps/opencs/model/world/scriptcontext.hpp +++ b/apps/opencs/model/world/scriptcontext.hpp @@ -46,6 +46,9 @@ namespace CSMWorld void clear(); ///< Remove all cached data. + + /// \return Were there any locals that needed clearing? + bool clearLocals (const std::string& script); }; } diff --git a/apps/opencs/view/world/scripterrortable.cpp b/apps/opencs/view/world/scripterrortable.cpp index b44e1c0bd..a9e315c73 100644 --- a/apps/opencs/view/world/scripterrortable.cpp +++ b/apps/opencs/view/world/scripterrortable.cpp @@ -131,6 +131,11 @@ void CSVWorld::ScriptErrorTable::clear() setRowCount (0); } +bool CSVWorld::ScriptErrorTable::clearLocals (const std::string& script) +{ + return mContext.clearLocals (script); +} + void CSVWorld::ScriptErrorTable::cellClicked (int row, int column) { if (item (row, 1)) diff --git a/apps/opencs/view/world/scripterrortable.hpp b/apps/opencs/view/world/scripterrortable.hpp index 98db425cf..33af7c864 100644 --- a/apps/opencs/view/world/scripterrortable.hpp +++ b/apps/opencs/view/world/scripterrortable.hpp @@ -44,6 +44,11 @@ namespace CSVWorld void clear(); + /// Clear local variable cache for \a script. + /// + /// \return Were there any locals that needed clearing? + bool clearLocals (const std::string& script); + private slots: void cellClicked (int row, int column); diff --git a/apps/opencs/view/world/scriptsubview.cpp b/apps/opencs/view/world/scriptsubview.cpp index d405d1765..d99f789b3 100644 --- a/apps/opencs/view/world/scriptsubview.cpp +++ b/apps/opencs/view/world/scriptsubview.cpp @@ -89,6 +89,7 @@ CSVWorld::ScriptSubView::ScriptSubView (const CSMWorld::UniversalId& id, CSMDoc: *document.getData().getTableModel (CSMWorld::UniversalId::Type_Scripts)); mColumn = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_ScriptText); + mIdColumn = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id); mStateColumn = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Modification); QString source = mModel->data (mModel->getModelIndex (id.getId(), mColumn)).toString(); @@ -241,6 +242,15 @@ void CSVWorld::ScriptSubView::dataChanged (const QModelIndex& topLeft, const QMo ScriptEdit::ChangeLock lock (*mEditor); + bool updateRequired = false; + + for (int i=topLeft.row(); i<=bottomRight.row(); ++i) + { + std::string id = mModel->data (mModel->index (i, mIdColumn)).toString().toUtf8().constData(); + if (mErrors->clearLocals (id)) + updateRequired = true; + } + QModelIndex index = mModel->getModelIndex (getUniversalId().getId(), mColumn); if (index.row()>=topLeft.row() && index.row()<=bottomRight.row()) @@ -256,13 +266,28 @@ void CSVWorld::ScriptSubView::dataChanged (const QModelIndex& topLeft, const QMo mEditor->setPlainText (source); mEditor->setTextCursor (cursor); - recompile(); + updateRequired = true; } } + + if (updateRequired) + recompile(); } void CSVWorld::ScriptSubView::rowsAboutToBeRemoved (const QModelIndex& parent, int start, int end) { + bool updateRequired = false; + + for (int i=start; i<=end; ++i) + { + std::string id = mModel->data (mModel->index (i, mIdColumn)).toString().toUtf8().constData(); + if (mErrors->clearLocals (id)) + updateRequired = true; + } + + if (updateRequired) + recompile(); + QModelIndex index = mModel->getModelIndex (getUniversalId().getId(), mColumn); if (!parent.isValid() && index.row()>=start && index.row()<=end) diff --git a/apps/opencs/view/world/scriptsubview.hpp b/apps/opencs/view/world/scriptsubview.hpp index 6125dd259..907dc7958 100644 --- a/apps/opencs/view/world/scriptsubview.hpp +++ b/apps/opencs/view/world/scriptsubview.hpp @@ -38,6 +38,7 @@ namespace CSVWorld CSMDoc::Document& mDocument; CSMWorld::IdTable *mModel; int mColumn; + int mIdColumn; int mStateColumn; TableBottomBox *mBottom; RecordButtonBar *mButtons; From 12b8fcf0bfb4cd5950a1f1dd7e7be460d5f3a75a Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 23 Sep 2015 23:37:09 +0200 Subject: [PATCH 15/15] OpenCS: Fix camera position retrieval in WorldspaceWidget --- apps/opencs/view/render/pagedworldspacewidget.cpp | 5 ++++- apps/opencs/view/render/unpagedworldspacewidget.cpp | 4 +++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/apps/opencs/view/render/pagedworldspacewidget.cpp b/apps/opencs/view/render/pagedworldspacewidget.cpp index 274c77620..b0c1e74a9 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.cpp +++ b/apps/opencs/view/render/pagedworldspacewidget.cpp @@ -170,7 +170,10 @@ void CSVRender::PagedWorldspaceWidget::referenceAdded (const QModelIndex& parent std::string CSVRender::PagedWorldspaceWidget::getStartupInstruction() { - osg::Vec3d position = mView->getCamera()->getViewMatrix().getTrans(); + osg::Vec3d eye, center, up; + mView->getCamera()->getViewMatrixAsLookAt(eye, center, up); + osg::Vec3d position = eye; + std::ostringstream stream; stream diff --git a/apps/opencs/view/render/unpagedworldspacewidget.cpp b/apps/opencs/view/render/unpagedworldspacewidget.cpp index a5733ad10..3e1733c81 100644 --- a/apps/opencs/view/render/unpagedworldspacewidget.cpp +++ b/apps/opencs/view/render/unpagedworldspacewidget.cpp @@ -166,7 +166,9 @@ void CSVRender::UnpagedWorldspaceWidget::addVisibilitySelectorButtons ( std::string CSVRender::UnpagedWorldspaceWidget::getStartupInstruction() { - osg::Vec3d position = mView->getCamera()->getViewMatrix().getTrans(); + osg::Vec3d eye, center, up; + mView->getCamera()->getViewMatrixAsLookAt(eye, center, up); + osg::Vec3d position = eye; std::ostringstream stream;