diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 000a1d4090..2bbc95c899 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -236,6 +236,10 @@ SkyManager::SkyManager(Ogre::SceneNode *root, Ogre::Camera *pCamera) , mCloudAnimationTimer(0.f) , mMoonRed(false) , mParticleNode(NULL) + , mRainEnabled(false) + , mRainTimer(0) + , mRainSpeed(0) + , mRainFrequency(1) { mSceneMgr = root->getCreator(); mRootNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(); @@ -351,6 +355,7 @@ void SkyManager::create() SkyManager::~SkyManager() { + clearRain(); delete mSun; delete mSunGlare; delete mMasser; @@ -369,6 +374,66 @@ int SkyManager::getSecundaPhase() const return mSecunda->getPhaseInt(); } +void SkyManager::clearRain() +{ + for (std::map::iterator it = mRainModels.begin(); it != mRainModels.end();) + { + it->second.setNull(); + Ogre::SceneNode* parent = it->first->getParentSceneNode(); + mSceneMgr->destroySceneNode(it->first); + mSceneMgr->destroySceneNode(parent); + mRainModels.erase(it++); + } +} + +void SkyManager::updateRain(float dt) +{ + // Move existing rain + // Note: if rain gets disabled, we let the existing rain drops finish falling down. + float minHeight = 200; + for (std::map::iterator it = mRainModels.begin(); it != mRainModels.end();) + { + Ogre::Vector3 pos = it->first->getPosition(); + pos.z -= mRainSpeed * dt; + it->first->setPosition(pos); + if (pos.z < -minHeight) + { + it->second.setNull(); + Ogre::SceneNode* parent = it->first->getParentSceneNode(); + mSceneMgr->destroySceneNode(it->first); + mSceneMgr->destroySceneNode(parent); + mRainModels.erase(it++); + } + else + ++it; + } + + // Spawn new rain + float rainFrequency = mRainFrequency; + float startHeight = 700; + if (mRainEnabled) + { + mRainTimer += dt; + if (mRainTimer >= 1.f/rainFrequency) + { + mRainTimer = 0; + + const float rangeRandom = 100; + float xOffs = (std::rand()/(RAND_MAX+1.0)) * rangeRandom - (rangeRandom/2); + float yOffs = (std::rand()/(RAND_MAX+1.0)) * rangeRandom - (rangeRandom/2); + Ogre::SceneNode* sceneNode = mCamera->getParentSceneNode()->createChildSceneNode(); + sceneNode->setInheritOrientation(false); + + // Create a separate node to control the offset, since a node with setInheritOrientation(false) will still + // consider the orientation of the parent node for its position, just not for its orientation + Ogre::SceneNode* offsetNode = sceneNode->createChildSceneNode(Ogre::Vector3(xOffs,yOffs,startHeight)); + + NifOgre::ObjectScenePtr objects = NifOgre::Loader::createObjects(offsetNode, mRainEffect); + mRainModels[offsetNode] = objects; + } + } +} + void SkyManager::update(float duration) { if (!mEnabled) return; @@ -380,6 +445,8 @@ void SkyManager::update(float duration) mParticle->mControllers[i].update(); } + updateRain(duration); + // UV Scroll the clouds mCloudAnimationTimer += duration * mCloudSpeed; sh::Factory::getInstance().setSharedParameter ("cloudAnimationTimer", @@ -430,13 +497,22 @@ void SkyManager::enable() if (!mCreated) create(); + if (mParticleNode) + mParticleNode->setVisible(true); + mRootNode->setVisible(true); mEnabled = true; } void SkyManager::disable() { + if (mParticleNode) + mParticleNode->setVisible(false); + + clearRain(); + mRootNode->setVisible(false); + mEnabled = false; } @@ -449,6 +525,11 @@ void SkyManager::setWeather(const MWWorld::WeatherResult& weather) { if (!mCreated) return; + mRainEffect = weather.mRainEffect; + mRainEnabled = !mRainEffect.empty(); + mRainFrequency = weather.mRainFrequency; + mRainSpeed = weather.mRainSpeed; + if (mCurrentParticleEffect != weather.mParticleEffect) { mCurrentParticleEffect = weather.mParticleEffect; @@ -461,7 +542,7 @@ void SkyManager::setWeather(const MWWorld::WeatherResult& weather) { if (!mParticleNode) { - mParticleNode = mCamera->getParentSceneNode()->createChildSceneNode(Ogre::Vector3(0,0,100)); + mParticleNode = mCamera->getParentSceneNode()->createChildSceneNode(); mParticleNode->setInheritOrientation(false); } diff --git a/apps/openmw/mwrender/sky.hpp b/apps/openmw/mwrender/sky.hpp index 07edc461ee..f81a42782c 100644 --- a/apps/openmw/mwrender/sky.hpp +++ b/apps/openmw/mwrender/sky.hpp @@ -148,6 +148,8 @@ namespace MWRender void sunDisable(); + void setRainSpeed(float speed); + void setSunDirection(const Ogre::Vector3& direction); void setMasserDirection(const Ogre::Vector3& direction); @@ -176,6 +178,9 @@ namespace MWRender void create(); ///< no need to call this, automatically done on first enable() + void updateRain(float dt); + void clearRain(); + bool mCreated; bool mMoonRed; @@ -203,6 +208,9 @@ namespace MWRender Ogre::SceneNode* mParticleNode; NifOgre::ObjectScenePtr mParticle; + std::map mRainModels; + float mRainTimer; + // remember some settings so we don't have to apply them again if they didnt change Ogre::String mClouds; Ogre::String mNextClouds; @@ -223,6 +231,11 @@ namespace MWRender float mGlare; // target float mGlareFade; // actual + bool mRainEnabled; + std::string mRainEffect; + float mRainSpeed; + float mRainFrequency; + bool mEnabled; bool mSunEnabled; bool mMasserEnabled; diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index df78711ad8..6050e0c298 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -59,6 +59,21 @@ void WeatherManager::setFallbackWeather(Weather& weather,const std::string& name weather.mCloudSpeed = mFallback->getFallbackFloat("Weather_"+upper+"_Cloud_Speed"); weather.mGlareView = mFallback->getFallbackFloat("Weather_"+upper+"_Glare_View"); weather.mCloudTexture = mFallback->getFallbackString("Weather_"+upper+"_Cloud_Texture"); + + bool usesPrecip = mFallback->getFallbackBool("Weather_"+upper+"_Using_Precip"); + if (usesPrecip) + weather.mRainEffect = "meshes\\raindrop.nif"; + weather.mRainSpeed = mRainSpeed; + weather.mRainFrequency = mFallback->getFallbackFloat("Weather_"+upper+"_Rain_Entrance_Speed"); + /* +Unhandled: +Rain Diameter=600 ? +Rain Height Min=200 ? +Rain Height Max=700 ? +Rain Threshold=0.6 ? +Max Raindrops=650 ? +*/ + size_t offset = weather.mCloudTexture.find(".tga"); if (offset != std::string::npos) weather.mCloudTexture.replace(offset, weather.mCloudTexture.length() - offset, ".dds"); @@ -100,7 +115,7 @@ WeatherManager::WeatherManager(MWRender::RenderingManager* rendering,MWWorld::Fa mHour(14), mCurrentWeather("clear"), mNextWeather(""), mFirstUpdate(true), mWeatherUpdateTime(0), mThunderFlash(0), mThunderChance(0), mThunderChanceNeeded(50), mThunderSoundDelay(0), mRemainingTransitionTime(0), - mTimePassed(0), mFallback(fallback), mWindSpeed(0.f), mRendering(rendering) + mTimePassed(0), mFallback(fallback), mWindSpeed(0.f), mRendering(rendering), mIsStorm(false) { //Globals mThunderSoundID0 = mFallback->getFallbackString("Weather_Thunderstorm_Thunder_Sound_ID_0"); @@ -117,6 +132,8 @@ WeatherManager::WeatherManager(MWRender::RenderingManager* rendering,MWWorld::Fa mThunderThreshold = mFallback->getFallbackFloat("Weather_Thunderstorm_Thunder_Threshold"); mThunderSoundDelay = 0.25; + mRainSpeed = mFallback->getFallbackFloat("Weather_Precip_Gravity"); + //Some useful values /* TODO: Use pre-sunrise_time, pre-sunset_time, * post-sunrise_time, and post-sunset_time to better @@ -140,10 +157,12 @@ WeatherManager::WeatherManager(MWRender::RenderingManager* rendering,MWWorld::Fa Weather thunderstorm; thunderstorm.mRainLoopSoundID = "rain heavy"; + thunderstorm.mRainEffect = "meshes\\raindrop.nif"; setFallbackWeather(thunderstorm,"thunderstorm"); Weather rain; rain.mRainLoopSoundID = "rain"; + rain.mRainEffect = "meshes\\raindrop.nif"; setFallbackWeather(rain,"rain"); Weather overcast; @@ -217,7 +236,11 @@ void WeatherManager::setResult(const String& weatherType) mResult.mIsStorm = current.mIsStorm; + mResult.mRainSpeed = current.mRainSpeed; + mResult.mRainFrequency = current.mRainFrequency; + mResult.mParticleEffect = current.mParticleEffect; + mResult.mRainEffect = current.mRainEffect; mResult.mNight = (mHour < mSunriseTime || mHour > mNightStart - 1); @@ -324,6 +347,9 @@ void WeatherManager::transition(float factor) mResult.mIsStorm = current.mIsStorm; mResult.mParticleEffect = current.mParticleEffect; + mResult.mRainEffect = current.mRainEffect; + mResult.mRainSpeed = current.mRainSpeed; + mResult.mRainFrequency = current.mRainFrequency; } void WeatherManager::update(float duration) @@ -361,6 +387,7 @@ void WeatherManager::update(float duration) setResult(mCurrentWeather); mWindSpeed = mResult.mWindSpeed; + mIsStorm = mResult.mIsStorm; mRendering->configureFog(mResult.mFogDepth, mResult.mFogColor); @@ -499,7 +526,6 @@ void WeatherManager::update(float duration) mRendering->getSkyManager()->setWeather(mResult); - // Play sounds if (mNextWeather == "") { @@ -779,7 +805,7 @@ void WeatherManager::switchToNextWeather(bool instantly) bool WeatherManager::isInStorm() const { - return mResult.mIsStorm; + return mIsStorm; } Ogre::Vector3 WeatherManager::getStormDirection() const diff --git a/apps/openmw/mwworld/weather.hpp b/apps/openmw/mwworld/weather.hpp index 8aa3d26353..9693014a91 100644 --- a/apps/openmw/mwworld/weather.hpp +++ b/apps/openmw/mwworld/weather.hpp @@ -62,6 +62,10 @@ namespace MWWorld std::string mAmbientLoopSoundID; std::string mParticleEffect; + + std::string mRainEffect; + float mRainSpeed; + float mRainFrequency; }; @@ -129,8 +133,17 @@ namespace MWWorld // Possible effect on movement speed? bool mIsStorm; + // How fast does rain travel down? + // In Morrowind.ini this is set globally, but we may want to change it per weather later. + float mRainSpeed; + + // How often does a new rain mesh spawn? + float mRainFrequency; + std::string mParticleEffect; + std::string mRainEffect; + // Note: For Weather Blight, there is a "Disease Chance" (=0.1) setting. But according to MWSFD this feature // is broken in the vanilla game and was disabled. }; @@ -188,6 +201,7 @@ namespace MWWorld private: float mHour; float mWindSpeed; + bool mIsStorm; MWWorld::Fallback* mFallback; void setFallbackWeather(Weather& weather,const std::string& name); MWRender::RenderingManager* mRendering; @@ -226,6 +240,7 @@ namespace MWWorld typedef std::map > RegionModMap; RegionModMap mRegionMods; + float mRainSpeed; float mSunriseTime; float mSunsetTime; float mSunriseDuration;