diff --git a/CHANGELOG.md b/CHANGELOG.md index 40cb23a01..bd8bf9f48 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ Bug #4077: Enchanted items are not recharged if they are not in the player's inventory Bug #4202: Open .omwaddon files without needing toopen openmw-cs first Bug #4240: Ash storm origin coordinates and hand shielding animation behavior are incorrect + Bug #4262: Rain settings are hardcoded Bug #4270: Closing doors while they are obstructed desyncs closing sfx Bug #4276: Resizing character window differs from vanilla Bug #4329: Removed birthsign abilities are restored after reloading the save diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 27d586ea0..32d32a40c 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -18,10 +18,9 @@ #include #include +#include #include #include -#include -#include #include #include @@ -1117,7 +1116,11 @@ SkyManager::SkyManager(osg::Group* parentNode, Resource::SceneManager* sceneMana , mRemainingTransitionTime(0.0f) , mRainEnabled(false) , mRainSpeed(0) - , mRainFrequency(1) + , mRainDiameter(0) + , mRainMinHeight(0) + , mRainMaxHeight(0) + , mRainEntranceSpeed(1) + , mRainMaxRaindrops(0) , mWindSpeed(0.f) , mEnabled(true) , mSunEnabled(true) @@ -1458,7 +1461,7 @@ void SkyManager::createRain() mRainNode = new osg::Group; mRainParticleSystem = new osgParticle::ParticleSystem; - osg::Vec3 rainRange = osg::Vec3(600,600,600); + osg::Vec3 rainRange = osg::Vec3(mRainDiameter, mRainDiameter, (mRainMinHeight+mRainMaxHeight)/2.f); mRainParticleSystem->setParticleAlignment(osgParticle::ParticleSystem::FIXED); mRainParticleSystem->setAlignVectorX(osg::Vec3f(0.1,0,0)); @@ -1485,14 +1488,19 @@ void SkyManager::createRain() emitter->setParticleSystem(mRainParticleSystem); osg::ref_ptr placer (new osgParticle::BoxPlacer); - placer->setXRange(-rainRange.x() / 2, rainRange.x() / 2); // Rain_Diameter + placer->setXRange(-rainRange.x() / 2, rainRange.x() / 2); placer->setYRange(-rainRange.y() / 2, rainRange.y() / 2); - placer->setZRange(300, 300); + placer->setZRange(-rainRange.z() / 2, rainRange.z() / 2); emitter->setPlacer(placer); + mPlacer = placer; + // FIXME: vanilla engine does not use a particle system to handle rain, it uses a NIF-file with 20 raindrops in it. + // It spawns the (maxRaindrops-getParticleSystem()->numParticles())*dt/rainEntranceSpeed batches every frame (near 1-2). + // Since the rain is a regular geometry, it produces water ripples, also in theory it can be removed if collides with something. osg::ref_ptr counter (new RainCounter); - counter->setNumberOfParticlesPerSecondToCreate(600.0); + counter->setNumberOfParticlesPerSecondToCreate(mRainMaxRaindrops/mRainEntranceSpeed*20); emitter->setCounter(counter); + mCounter = counter; osg::ref_ptr shooter (new RainShooter); mRainShooter = shooter; @@ -1525,6 +1533,8 @@ void SkyManager::destroyRain() mRootNode->removeChild(mRainNode); mRainNode = nullptr; + mPlacer = nullptr; + mCounter = nullptr; mRainParticleSystem = nullptr; mRainShooter = nullptr; mRainFader = nullptr; @@ -1625,11 +1635,17 @@ void SkyManager::updateRainParameters() { if (mRainShooter) { - // Note: an arbitrary value. An original engine seems to use a different approach to rotate raindrops. - float windFactor = mWindSpeed/32.f; - float angle = windFactor * osg::PI/4; - mRainShooter->setVelocity(osg::Vec3f(0, mRainSpeed * windFactor, -mRainSpeed)); + float angle = -std::atan2(1, 50.f/mWindSpeed); + mRainShooter->setVelocity(osg::Vec3f(0, mRainSpeed*std::sin(angle), -mRainSpeed/std::cos(angle))); mRainShooter->setAngle(angle); + + osg::Vec3 rainRange = osg::Vec3(mRainDiameter, mRainDiameter, (mRainMinHeight+mRainMaxHeight)/2.f); + + mPlacer->setXRange(-rainRange.x() / 2, rainRange.x() / 2); + mPlacer->setYRange(-rainRange.y() / 2, rainRange.y() / 2); + mPlacer->setZRange(-rainRange.z() / 2, rainRange.z() / 2); + + mCounter->setNumberOfParticlesPerSecondToCreate(mRainMaxRaindrops/mRainEntranceSpeed*20); } } @@ -1646,6 +1662,14 @@ void SkyManager::setWeather(const WeatherResult& weather) { if (!mCreated) return; + mRainEntranceSpeed = weather.mRainEntranceSpeed; + mRainMaxRaindrops = weather.mRainMaxRaindrops; + mRainDiameter = weather.mRainDiameter; + mRainMinHeight = weather.mRainMinHeight; + mRainMaxHeight = weather.mRainMaxHeight; + mRainSpeed = weather.mRainSpeed; + mWindSpeed = weather.mWindSpeed; + if (mRainEffect != weather.mRainEffect) { mRainEffect = weather.mRainEffect; @@ -1659,9 +1683,6 @@ void SkyManager::setWeather(const WeatherResult& weather) } } - mRainFrequency = weather.mRainFrequency; - mRainSpeed = weather.mRainSpeed; - mWindSpeed = weather.mWindSpeed; updateRainParameters(); mIsStorm = weather.mIsStorm; diff --git a/apps/openmw/mwrender/sky.hpp b/apps/openmw/mwrender/sky.hpp index 9bae5ca50..990cd9b4f 100644 --- a/apps/openmw/mwrender/sky.hpp +++ b/apps/openmw/mwrender/sky.hpp @@ -9,6 +9,8 @@ #include #include +#include + namespace osg { class Camera; @@ -39,6 +41,7 @@ namespace MWRender class CloudUpdater; class Sun; class Moon; + class RainCounter; class RainShooter; class RainFader; class AlphaFader; @@ -87,8 +90,12 @@ namespace MWRender std::string mRainEffect; float mEffectFade; + float mRainDiameter; + float mRainMinHeight; + float mRainMaxHeight; float mRainSpeed; - float mRainFrequency; + float mRainEntranceSpeed; + int mRainMaxRaindrops; }; struct MoonState @@ -218,6 +225,8 @@ namespace MWRender osg::ref_ptr mRainNode; osg::ref_ptr mRainParticleSystem; + osg::ref_ptr mPlacer; + osg::ref_ptr mCounter; osg::ref_ptr mRainShooter; osg::ref_ptr mRainFader; @@ -251,7 +260,11 @@ namespace MWRender bool mRainEnabled; std::string mRainEffect; float mRainSpeed; - float mRainFrequency; + float mRainDiameter; + float mRainMinHeight; + float mRainMaxHeight; + float mRainEntranceSpeed; + int mRainMaxRaindrops; float mWindSpeed; bool mEnabled; diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index a7fd5b9d3..79a305fee 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -147,7 +147,12 @@ Weather::Weather(const std::string& name, , mGlareView(Fallback::Map::getFloat("Weather_" + name + "_Glare_View")) , mIsStorm(mWindSpeed > stormWindSpeed) , mRainSpeed(rainSpeed) - , mRainFrequency(Fallback::Map::getFloat("Weather_" + name + "_Rain_Entrance_Speed")) + , mRainEntranceSpeed(Fallback::Map::getFloat("Weather_" + name + "_Rain_Entrance_Speed")) + , mRainMaxRaindrops(Fallback::Map::getFloat("Weather_" + name + "_Max_Raindrops")) + , mRainDiameter(Fallback::Map::getFloat("Weather_" + name + "_Rain_Diameter")) + , mRainThreshold(Fallback::Map::getFloat("Weather_" + name + "_Rain_Threshold")) + , mRainMinHeight(Fallback::Map::getFloat("Weather_" + name + "_Rain_Height_Min")) + , mRainMaxHeight(Fallback::Map::getFloat("Weather_" + name + "_Rain_Height_Max")) , mParticleEffect(particleEffect) , mRainEffect(Fallback::Map::getBool("Weather_" + name + "_Using_Precip") ? "meshes\\raindrop.nif" : "") , mTransitionDelta(Fallback::Map::getFloat("Weather_" + name + "_Transition_Delta")) @@ -178,15 +183,6 @@ Weather::Weather(const std::string& name, if (Misc::StringUtils::ciEqual(mAmbientLoopSoundID, "None")) mAmbientLoopSoundID.clear(); - - /* - Unhandled: - Rain Diameter=600 ? - Rain Height Min=200 ? - Rain Height Max=700 ? - Rain Threshold=0.6 ? - Max Raindrops=650 ? - */ } float Weather::transitionDelta() const @@ -1149,7 +1145,11 @@ inline void WeatherManager::calculateResult(const int weatherID, const float gam mResult.mIsStorm = current.mIsStorm; mResult.mRainSpeed = current.mRainSpeed; - mResult.mRainFrequency = current.mRainFrequency; + mResult.mRainEntranceSpeed = current.mRainEntranceSpeed; + mResult.mRainDiameter = current.mRainDiameter; + mResult.mRainMinHeight = current.mRainMinHeight; + mResult.mRainMaxHeight = current.mRainMaxHeight; + mResult.mRainMaxRaindrops = current.mRainMaxRaindrops; mResult.mParticleEffect = current.mParticleEffect; mResult.mRainEffect = current.mRainEffect; @@ -1234,16 +1234,24 @@ inline void WeatherManager::calculateTransitionResult(const float factor, const mResult.mNight = current.mNight; - if(factor < 0.5) + float threshold = mWeatherSettings[mNextWeather].mRainThreshold; + if (threshold <= 0) + threshold = 0.5f; + + if(factor < threshold) { mResult.mIsStorm = current.mIsStorm; mResult.mParticleEffect = current.mParticleEffect; mResult.mRainEffect = current.mRainEffect; mResult.mRainSpeed = current.mRainSpeed; - mResult.mRainFrequency = current.mRainFrequency; - mResult.mAmbientSoundVolume = 1-(factor*2); + mResult.mRainEntranceSpeed = current.mRainEntranceSpeed; + mResult.mAmbientSoundVolume = 1 - factor / threshold; mResult.mEffectFade = mResult.mAmbientSoundVolume; mResult.mAmbientLoopSoundID = current.mAmbientLoopSoundID; + mResult.mRainDiameter = current.mRainDiameter; + mResult.mRainMinHeight = current.mRainMinHeight; + mResult.mRainMaxHeight = current.mRainMaxHeight; + mResult.mRainMaxRaindrops = current.mRainMaxRaindrops; } else { @@ -1251,10 +1259,15 @@ inline void WeatherManager::calculateTransitionResult(const float factor, const mResult.mParticleEffect = other.mParticleEffect; mResult.mRainEffect = other.mRainEffect; mResult.mRainSpeed = other.mRainSpeed; - mResult.mRainFrequency = other.mRainFrequency; - mResult.mAmbientSoundVolume = 2*(factor-0.5f); + mResult.mRainEntranceSpeed = other.mRainEntranceSpeed; + mResult.mAmbientSoundVolume = (factor - threshold) / (1 - threshold); mResult.mEffectFade = mResult.mAmbientSoundVolume; mResult.mAmbientLoopSoundID = other.mAmbientLoopSoundID; + + mResult.mRainDiameter = other.mRainDiameter; + mResult.mRainMinHeight = other.mRainMinHeight; + mResult.mRainMaxHeight = other.mRainMaxHeight; + mResult.mRainMaxRaindrops = other.mRainMaxRaindrops; } } diff --git a/apps/openmw/mwworld/weather.hpp b/apps/openmw/mwworld/weather.hpp index bdd6a014a..c4f787739 100644 --- a/apps/openmw/mwworld/weather.hpp +++ b/apps/openmw/mwworld/weather.hpp @@ -170,7 +170,20 @@ namespace MWWorld float mRainSpeed; // How often does a new rain mesh spawn? - float mRainFrequency; + float mRainEntranceSpeed; + + // Maximum count of rain particles + int mRainMaxRaindrops; + + // Radius of rain effect + float mRainDiameter; + + // Transition threshold to spawn rain + float mRainThreshold; + + // Height of rain particles spawn + float mRainMinHeight; + float mRainMaxHeight; std::string mParticleEffect;