From 9e4a339ad353590ad73cf72f2e110e12cc776af9 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sun, 27 Jan 2019 16:55:36 +0400 Subject: [PATCH] Daytime node switch support (feature #4836) --- CHANGELOG.md | 1 + apps/opencs/view/render/lighting.cpp | 33 +++++++++++++ apps/opencs/view/render/lighting.hpp | 4 +- apps/opencs/view/render/lightingbright.cpp | 4 +- apps/opencs/view/render/lightingbright.hpp | 2 +- apps/opencs/view/render/lightingday.cpp | 4 +- apps/opencs/view/render/lightingday.hpp | 2 +- apps/opencs/view/render/lightingnight.cpp | 4 +- apps/opencs/view/render/lightingnight.hpp | 2 +- apps/opencs/view/render/previewwidget.cpp | 2 + apps/opencs/view/render/scenewidget.cpp | 8 ++- apps/opencs/view/render/scenewidget.hpp | 3 ++ .../view/render/unpagedworldspacewidget.cpp | 5 ++ apps/openmw/mwbase/world.hpp | 2 + apps/openmw/mwrender/animation.cpp | 49 +++++++++++++++++++ apps/openmw/mwworld/weather.cpp | 14 ++++++ apps/openmw/mwworld/weather.hpp | 9 ++++ apps/openmw/mwworld/worldimp.cpp | 5 ++ apps/openmw/mwworld/worldimp.hpp | 2 + components/misc/constants.hpp | 5 ++ components/nifosg/nifloader.cpp | 6 +++ components/sceneutil/util.cpp | 20 ++++++++ components/sceneutil/util.hpp | 1 + 23 files changed, 179 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c30663a87..a2a07ae60 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ Feature #4673: Weapon sheathing Feature #4730: Native animated containers support Feature #4812: Support NiSwitchNode + Feature #4836: Daytime node switch Task #4686: Upgrade media decoder to a more current FFmpeg API 0.45.0 diff --git a/apps/opencs/view/render/lighting.cpp b/apps/opencs/view/render/lighting.cpp index 8e068168f..f62e86148 100644 --- a/apps/opencs/view/render/lighting.cpp +++ b/apps/opencs/view/render/lighting.cpp @@ -1,5 +1,38 @@ #include "lighting.hpp" #include +#include +#include + +#include + +class DayNightSwitchVisitor : public osg::NodeVisitor +{ +public: + DayNightSwitchVisitor(int index) + : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) + , mIndex(index) + { } + + virtual void apply(osg::Switch &switchNode) + { + if (switchNode.getName() == Constants::NightDayLabel) + switchNode.setSingleChildOn(mIndex); + + traverse(switchNode); + } + +private: + int mIndex; +}; CSVRender::Lighting::~Lighting() {} + +void CSVRender::Lighting::updateDayNightMode(int index) +{ + if (mRootNode == nullptr) + return; + + DayNightSwitchVisitor visitor(index); + mRootNode->accept(visitor); +} diff --git a/apps/opencs/view/render/lighting.hpp b/apps/opencs/view/render/lighting.hpp index a4315d02f..66b0eec00 100644 --- a/apps/opencs/view/render/lighting.hpp +++ b/apps/opencs/view/render/lighting.hpp @@ -19,7 +19,7 @@ namespace CSVRender Lighting() : mRootNode(0) {} virtual ~Lighting(); - virtual void activate (osg::Group* rootNode) = 0; + virtual void activate (osg::Group* rootNode, bool isExterior) = 0; virtual void deactivate() = 0; @@ -27,6 +27,8 @@ namespace CSVRender protected: + void updateDayNightMode(int index); + osg::ref_ptr mLightSource; osg::Group* mRootNode; }; diff --git a/apps/opencs/view/render/lightingbright.cpp b/apps/opencs/view/render/lightingbright.cpp index 07900d6b0..d76823fb3 100644 --- a/apps/opencs/view/render/lightingbright.cpp +++ b/apps/opencs/view/render/lightingbright.cpp @@ -4,7 +4,7 @@ CSVRender::LightingBright::LightingBright() {} -void CSVRender::LightingBright::activate (osg::Group* rootNode) +void CSVRender::LightingBright::activate (osg::Group* rootNode, bool /*isExterior*/) { mRootNode = rootNode; @@ -20,6 +20,8 @@ void CSVRender::LightingBright::activate (osg::Group* rootNode) mLightSource->setLight(light); mRootNode->addChild(mLightSource); + + updateDayNightMode(0); } void CSVRender::LightingBright::deactivate() diff --git a/apps/opencs/view/render/lightingbright.hpp b/apps/opencs/view/render/lightingbright.hpp index bc6422814..7bdebfd3d 100644 --- a/apps/opencs/view/render/lightingbright.hpp +++ b/apps/opencs/view/render/lightingbright.hpp @@ -17,7 +17,7 @@ namespace CSVRender LightingBright(); - virtual void activate (osg::Group* rootNode); + virtual void activate (osg::Group* rootNode, bool /*isExterior*/); virtual void deactivate(); diff --git a/apps/opencs/view/render/lightingday.cpp b/apps/opencs/view/render/lightingday.cpp index a841edc63..dc4592b21 100644 --- a/apps/opencs/view/render/lightingday.cpp +++ b/apps/opencs/view/render/lightingday.cpp @@ -4,7 +4,7 @@ CSVRender::LightingDay::LightingDay(){} -void CSVRender::LightingDay::activate (osg::Group* rootNode) +void CSVRender::LightingDay::activate (osg::Group* rootNode, bool /*isExterior*/) { mRootNode = rootNode; @@ -19,6 +19,8 @@ void CSVRender::LightingDay::activate (osg::Group* rootNode) mLightSource->setLight(light); mRootNode->addChild(mLightSource); + + updateDayNightMode(0); } void CSVRender::LightingDay::deactivate() diff --git a/apps/opencs/view/render/lightingday.hpp b/apps/opencs/view/render/lightingday.hpp index 2dc3c02b6..516dd2bbf 100644 --- a/apps/opencs/view/render/lightingday.hpp +++ b/apps/opencs/view/render/lightingday.hpp @@ -11,7 +11,7 @@ namespace CSVRender LightingDay(); - virtual void activate (osg::Group* rootNode); + virtual void activate (osg::Group* rootNode, bool /*isExterior*/); virtual void deactivate(); diff --git a/apps/opencs/view/render/lightingnight.cpp b/apps/opencs/view/render/lightingnight.cpp index 6f87d020b..fbebb46a1 100644 --- a/apps/opencs/view/render/lightingnight.cpp +++ b/apps/opencs/view/render/lightingnight.cpp @@ -4,7 +4,7 @@ CSVRender::LightingNight::LightingNight() {} -void CSVRender::LightingNight::activate (osg::Group* rootNode) +void CSVRender::LightingNight::activate (osg::Group* rootNode, bool isExterior) { mRootNode = rootNode; @@ -20,6 +20,8 @@ void CSVRender::LightingNight::activate (osg::Group* rootNode) mLightSource->setLight(light); mRootNode->addChild(mLightSource); + + updateDayNightMode(isExterior ? 1 : 0); } void CSVRender::LightingNight::deactivate() diff --git a/apps/opencs/view/render/lightingnight.hpp b/apps/opencs/view/render/lightingnight.hpp index dae2a8fa3..3f03150cd 100644 --- a/apps/opencs/view/render/lightingnight.hpp +++ b/apps/opencs/view/render/lightingnight.hpp @@ -11,7 +11,7 @@ namespace CSVRender LightingNight(); - virtual void activate (osg::Group* rootNode); + virtual void activate (osg::Group* rootNode, bool isExterior); virtual void deactivate(); virtual osg::Vec4f getAmbientColour(osg::Vec4f *defaultAmbient); diff --git a/apps/opencs/view/render/previewwidget.cpp b/apps/opencs/view/render/previewwidget.cpp index 972fb556d..522534adb 100644 --- a/apps/opencs/view/render/previewwidget.cpp +++ b/apps/opencs/view/render/previewwidget.cpp @@ -20,6 +20,8 @@ CSVRender::PreviewWidget::PreviewWidget (CSMWorld::Data& data, connect (&mData, SIGNAL (assetTablesChanged ()), this, SLOT (assetTablesChanged ())); + setExterior(false); + if (!referenceable) { QAbstractItemModel *references = diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index 7e1666dc1..8ae9d8a62 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -195,6 +195,7 @@ SceneWidget::SceneWidget(std::shared_ptr resourceSyste , mResourceSystem(resourceSystem) , mLighting(nullptr) , mHasDefaultAmbient(false) + , mIsExterior(true) , mPrevMouseX(0) , mPrevMouseY(0) , mCamPositionSet(false) @@ -250,7 +251,7 @@ void SceneWidget::setLighting(Lighting *lighting) mLighting->deactivate(); mLighting = lighting; - mLighting->activate (mRootNode); + mLighting->activate (mRootNode, mIsExterior); osg::Vec4f ambient = mLighting->getAmbientColour(mHasDefaultAmbient ? &mDefaultAmbient : 0); setAmbient(ambient); @@ -315,6 +316,11 @@ void SceneWidget::setDefaultAmbient (const osg::Vec4f& colour) setAmbient(mLighting->getAmbientColour(&mDefaultAmbient)); } +void SceneWidget::setExterior (bool isExterior) +{ + mIsExterior = isExterior; +} + void SceneWidget::mouseMoveEvent (QMouseEvent *event) { mCurrentCamControl->handleMouseMoveEvent(event->x() - mPrevMouseX, event->y() - mPrevMouseY); diff --git a/apps/opencs/view/render/scenewidget.hpp b/apps/opencs/view/render/scenewidget.hpp index 13a109a9b..c2c354274 100644 --- a/apps/opencs/view/render/scenewidget.hpp +++ b/apps/opencs/view/render/scenewidget.hpp @@ -89,6 +89,8 @@ namespace CSVRender void setDefaultAmbient (const osg::Vec4f& colour); ///< \note The actual ambient colour may differ based on lighting settings. + void setExterior (bool isExterior); + protected: void setLighting (Lighting *lighting); ///< \attention The ownership of \a lighting is not transferred to *this. @@ -104,6 +106,7 @@ namespace CSVRender osg::Vec4f mDefaultAmbient; bool mHasDefaultAmbient; + bool mIsExterior; LightingDay mLightingDay; LightingNight mLightingNight; LightingBright mLightingBright; diff --git a/apps/opencs/view/render/unpagedworldspacewidget.cpp b/apps/opencs/view/render/unpagedworldspacewidget.cpp index 3201f7303..a3f0636be 100644 --- a/apps/opencs/view/render/unpagedworldspacewidget.cpp +++ b/apps/opencs/view/render/unpagedworldspacewidget.cpp @@ -28,6 +28,11 @@ void CSVRender::UnpagedWorldspaceWidget::update() setDefaultAmbient (colour); + bool isInterior = (record.get().mData.mFlags & ESM::Cell::Interior) != 0; + bool behaveLikeExterior = (record.get().mData.mFlags & ESM::Cell::QuasiEx) != 0; + + setExterior(behaveLikeExterior || !isInterior); + /// \todo deal with mSunlight and mFog/mForDensity flagAsModified(); diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 429a12880..6e8d58642 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -226,6 +226,8 @@ namespace MWBase virtual int getCurrentWeather() const = 0; + virtual unsigned int getNightDayMode() const = 0; + virtual int getMasserPhase() const = 0; virtual int getSecundaPhase() const = 0; diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 7730b9104..7aa516a27 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -33,6 +34,7 @@ #include #include #include +#include #include @@ -91,6 +93,47 @@ namespace std::vector > mToRemove; }; + class DayNightCallback : public osg::NodeCallback + { + public: + DayNightCallback() : mCurrentState(0) + { + } + + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) + { + unsigned int state = MWBase::Environment::get().getWorld()->getNightDayMode(); + const unsigned int newState = node->asGroup()->getNumChildren() > state ? state : 0; + + if (newState != mCurrentState) + { + mCurrentState = newState; + node->asSwitch()->setSingleChildOn(mCurrentState); + } + + traverse(node, nv); + } + + private: + unsigned int mCurrentState; + }; + + class AddSwitchCallbacksVisitor : public osg::NodeVisitor + { + public: + AddSwitchCallbacksVisitor() + : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) + { } + + virtual void apply(osg::Switch &switchNode) + { + if (switchNode.getName() == Constants::NightDayLabel) + switchNode.addUpdateCallback(new DayNightCallback()); + + traverse(switchNode); + } + }; + NifOsg::TextKeyMap::const_iterator findGroupStart(const NifOsg::TextKeyMap &keys, const std::string &groupname) { NifOsg::TextKeyMap::const_iterator iter(keys.begin()); @@ -1934,6 +1977,12 @@ namespace MWRender mObjectRoot->accept(visitor); visitor.remove(); } + + if (SceneUtil::hasUserDescription(mObjectRoot, Constants::NightDayLabel)) + { + AddSwitchCallbacksVisitor visitor; + mObjectRoot->accept(visitor); + } } Animation::AnimState::~AnimState() diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index 7d3f41894..14d88a752 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -547,6 +547,7 @@ WeatherManager::WeatherManager(MWRender::RenderingManager& rendering, const Fall , mFastForward(false) , mWeatherUpdateTime(mHoursBetweenWeatherChanges) , mTransitionFactor(0) + , mNightDayMode(Default) , mCurrentWeather(0) , mNextWeather(0) , mQueuedWeather(0) @@ -683,6 +684,14 @@ void WeatherManager::update(float duration, bool paused, const TimeStamp& time, updateWeatherTransitions(duration); } + bool isDay = time.getHour() >= mSunriseTime && time.getHour() <= mTimeSettings.mNightStart; + if (isExterior && !isDay) + mNightDayMode = ExteriorNight; + else if (!isExterior && isDay && mWeatherSettings[mCurrentWeather].mGlareView >= 0.5f) + mNightDayMode = InteriorDay; + else + mNightDayMode = Default; + if(!isExterior) { mRendering.setSkyEnabled(false); @@ -823,6 +832,11 @@ unsigned int WeatherManager::getWeatherID() const return mCurrentWeather; } +NightDayMode WeatherManager::getNightDayMode() const +{ + return mNightDayMode; +} + bool WeatherManager::useTorches(float hour) const { bool isDark = hour < mSunriseTime || hour > mTimeSettings.mNightStart; diff --git a/apps/openmw/mwworld/weather.hpp b/apps/openmw/mwworld/weather.hpp index cf6868356..f71b34d3e 100644 --- a/apps/openmw/mwworld/weather.hpp +++ b/apps/openmw/mwworld/weather.hpp @@ -40,6 +40,13 @@ namespace MWWorld { class TimeStamp; + enum NightDayMode + { + Default = 0, + ExteriorNight = 1, + InteriorDay = 2 + }; + struct WeatherSetting { float mPreSunriseTime; @@ -277,6 +284,7 @@ namespace MWWorld void stopSounds(); float getWindSpeed() const; + NightDayMode getNightDayMode() const; /// Are we in an ash or blight storm? bool isInStorm() const; @@ -329,6 +337,7 @@ namespace MWWorld bool mFastForward; float mWeatherUpdateTime; float mTransitionFactor; + NightDayMode mNightDayMode; int mCurrentWeather; int mNextWeather; int mQueuedWeather; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 57556e1cb..35d653f7d 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2024,6 +2024,11 @@ namespace MWWorld return mWeatherManager->getWeatherID(); } + unsigned int World::getNightDayMode() const + { + return mWeatherManager->getNightDayMode(); + } + void World::changeWeather(const std::string& region, const unsigned int id) { mWeatherManager->changeWeather(region, id); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index bfd3e918c..07679d139 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -337,6 +337,8 @@ namespace MWWorld int getCurrentWeather() const override; + unsigned int getNightDayMode() const override; + int getMasserPhase() const override; int getSecundaPhase() const override; diff --git a/components/misc/constants.hpp b/components/misc/constants.hpp index 7174ae888..01aeb2fc1 100644 --- a/components/misc/constants.hpp +++ b/components/misc/constants.hpp @@ -1,6 +1,8 @@ #ifndef OPENMW_CONSTANTS_H #define OPENMW_CONSTANTS_H +#include + namespace Constants { @@ -22,6 +24,9 @@ const float GravityConst = 8.96f; // Size of one exterior cell in game units const int CellSizeInUnits = 8192; +// A label to mark night/day visual switches +const std::string NightDayLabel = "NightDaySwitch"; + } #endif diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 1b064c85c..542f2e440 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -11,9 +11,11 @@ // resource #include +#include #include #include #include +#include // particle #include @@ -608,6 +610,10 @@ namespace NifOsg const Nif::NiSwitchNode* niSwitchNode = static_cast(nifNode); osg::ref_ptr switchNode = handleSwitchNode(niSwitchNode); node->addChild(switchNode); + + if (niSwitchNode->name == Constants::NightDayLabel && !SceneUtil::hasUserDescription(rootNode, Constants::NightDayLabel)) + rootNode->getOrCreateUserDataContainer()->addDescription(Constants::NightDayLabel); + const Nif::NodeList &children = niSwitchNode->children; for(size_t i = 0;i < children.length();++i) { diff --git a/components/sceneutil/util.cpp b/components/sceneutil/util.cpp index bbeda683a..a9857bed3 100644 --- a/components/sceneutil/util.cpp +++ b/components/sceneutil/util.cpp @@ -1,5 +1,7 @@ #include "util.hpp" +#include + namespace SceneUtil { @@ -53,4 +55,22 @@ float makeOsgColorComponent(unsigned int value, unsigned int shift) return float((value >> shift) & 0xFFu) / 255.0f; } +bool hasUserDescription(const osg::Node* node, const std::string pattern) +{ + if (node == nullptr) + return false; + + const osg::UserDataContainer* udc = node->getUserDataContainer(); + if (udc && udc->getNumDescriptions() > 0) + { + for (auto& descr : udc->getDescriptions()) + { + if (descr == pattern) + return true; + } + } + + return false; +} + } diff --git a/components/sceneutil/util.hpp b/components/sceneutil/util.hpp index 156d173d2..f293b9975 100644 --- a/components/sceneutil/util.hpp +++ b/components/sceneutil/util.hpp @@ -19,6 +19,7 @@ namespace SceneUtil float makeOsgColorComponent (unsigned int value, unsigned int shift); + bool hasUserDescription(const osg::Node* node, const std::string pattern); } #endif