diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 8631834c9..775a4358f 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -57,7 +57,7 @@ add_openmw_dir (mwscript add_openmw_dir (mwsound soundmanagerimp openal_output ffmpeg_decoder sound sound_buffer sound_decoder sound_output - loudness movieaudiofactory alext efx efx-presets regionsoundselector + loudness movieaudiofactory alext efx efx-presets regionsoundselector watersoundupdater ) add_openmw_dir (mwworld diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 79cff8bf0..c3ee5ea64 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -30,6 +30,23 @@ namespace MWSound { + namespace + { + WaterSoundUpdaterSettings makeWaterSoundUpdaterSettings() + { + WaterSoundUpdaterSettings settings; + + settings.mNearWaterRadius = Fallback::Map::getInt("Water_NearWaterRadius"); + settings.mNearWaterPoints = Fallback::Map::getInt("Water_NearWaterPoints"); + settings.mNearWaterIndoorTolerance = Fallback::Map::getFloat("Water_NearWaterIndoorTolerance"); + settings.mNearWaterOutdoorTolerance = Fallback::Map::getFloat("Water_NearWaterOutdoorTolerance"); + settings.mNearWaterIndoorID = Misc::StringUtils::lowerCase(Fallback::Map::getString("Water_NearWaterIndoorID")); + settings.mNearWaterOutdoorID = Misc::StringUtils::lowerCase(Fallback::Map::getString("Water_NearWaterOutdoorID")); + + return settings; + } + } + // For combining PlayMode and Type flags inline int operator|(PlayMode a, Type b) { return static_cast(a) | static_cast(b); } @@ -41,6 +58,7 @@ namespace MWSound , mMusicVolume(1.0f) , mVoiceVolume(1.0f) , mFootstepsVolume(1.0f) + , mWaterSoundUpdater(makeWaterSoundUpdaterSettings()) , mSoundBuffers(new SoundBufferList::element_type()) , mBufferCacheSize(0) , mSounds(new std::deque()) @@ -65,13 +83,6 @@ namespace MWSound mFootstepsVolume = Settings::Manager::getFloat("footsteps volume", "Sound"); mFootstepsVolume = std::min(std::max(mFootstepsVolume, 0.0f), 1.0f); - mNearWaterRadius = Fallback::Map::getInt("Water_NearWaterRadius"); - mNearWaterPoints = Fallback::Map::getInt("Water_NearWaterPoints"); - mNearWaterIndoorTolerance = Fallback::Map::getFloat("Water_NearWaterIndoorTolerance"); - mNearWaterOutdoorTolerance = Fallback::Map::getFloat("Water_NearWaterOutdoorTolerance"); - mNearWaterIndoorID = Misc::StringUtils::lowerCase(Fallback::Map::getString("Water_NearWaterIndoorID")); - mNearWaterOutdoorID = Misc::StringUtils::lowerCase(Fallback::Map::getString("Water_NearWaterOutdoorID")); - mBufferCacheMin = std::max(Settings::Manager::getInt("buffer cache min", "Sound"), 1); mBufferCacheMax = std::max(Settings::Manager::getInt("buffer cache max", "Sound"), 1); mBufferCacheMax *= 1024*1024; @@ -912,57 +923,12 @@ namespace MWSound static const ESM::Cell *LastCell; MWBase::World* world = MWBase::Environment::get().getWorld(); const MWWorld::ConstPtr player = world->getPlayerPtr(); - osg::Vec3f pos = player.getRefData().getPosition().asVec3(); const ESM::Cell *curcell = player.getCell()->getCell(); - - float volume = 0.0f; - const std::string& soundId = player.getCell()->isExterior() ? mNearWaterOutdoorID : mNearWaterIndoorID; - - if (!mListenerUnderwater) - { - if (curcell->hasWater()) - { - float dist = std::abs(player.getCell()->getWaterLevel() - pos.z()); - - if (player.getCell()->isExterior() && dist < mNearWaterOutdoorTolerance) - { - volume = (mNearWaterOutdoorTolerance - dist) / mNearWaterOutdoorTolerance; - - if (mNearWaterPoints > 1) - { - int underwaterPoints = 0; - - float step = mNearWaterRadius * 2.0f / (mNearWaterPoints - 1); - - for (int x = 0; x < mNearWaterPoints; x++) - { - for (int y = 0; y < mNearWaterPoints; y++) - { - float height = world->getTerrainHeightAt( - osg::Vec3f(pos.x() - mNearWaterRadius + x*step, pos.y() - mNearWaterRadius + y*step, 0.0f)); - - if (height < 0) - underwaterPoints++; - } - } - - volume *= underwaterPoints * 2.0f / (mNearWaterPoints*mNearWaterPoints); - } - } - else if (!player.getCell()->isExterior() && dist < mNearWaterIndoorTolerance) - { - volume = (mNearWaterIndoorTolerance - dist) / mNearWaterIndoorTolerance; - } - } - } - else - volume = 1.0f; - - volume = std::min(volume, 1.0f); + const auto update = mWaterSoundUpdater.update(player, *world); if (mNearWaterSound) { - if (volume == 0.0f) + if (update.mVolume == 0.0f) { mOutput->finishSound(mNearWaterSound); mNearWaterSound = nullptr; @@ -971,7 +937,7 @@ namespace MWSound { bool soundIdChanged = false; - Sound_Buffer *sfx = lookupSound(soundId); + Sound_Buffer *sfx = lookupSound(update.mId); if(LastCell != curcell) { LastCell = curcell; @@ -991,16 +957,16 @@ namespace MWSound if(soundIdChanged) { mOutput->finishSound(mNearWaterSound); - mNearWaterSound = playSound(soundId, volume, 1.0f, Type::Sfx, PlayMode::Loop); + mNearWaterSound = playSound(update.mId, update.mVolume, 1.0f, Type::Sfx, PlayMode::Loop); } else if (sfx) - mNearWaterSound->setVolume(volume * sfx->mVolume); + mNearWaterSound->setVolume(update.mVolume * sfx->mVolume); } } - else if (volume > 0.0f) + else if (update.mVolume > 0.0f) { LastCell = curcell; - mNearWaterSound = playSound(soundId, volume, 1.0f, Type::Sfx, PlayMode::Loop); + mNearWaterSound = playSound(update.mId, update.mVolume, 1.0f, Type::Sfx, PlayMode::Loop); } } @@ -1165,7 +1131,7 @@ namespace MWSound mMusic->updateFade(duration); mOutput->updateStream(mMusic); - + if (mMusic->getRealVolume() <= 0.f) { streamMusicFull(mNextMusic); @@ -1242,6 +1208,8 @@ namespace MWSound mListenerUp = up; mListenerUnderwater = underwater; + + mWaterSoundUpdater.setUnderwater(underwater); } void SoundManager::updatePtr(const MWWorld::ConstPtr &old, const MWWorld::ConstPtr &updated) diff --git a/apps/openmw/mwsound/soundmanagerimp.hpp b/apps/openmw/mwsound/soundmanagerimp.hpp index be457ae38..81f7cd84a 100644 --- a/apps/openmw/mwsound/soundmanagerimp.hpp +++ b/apps/openmw/mwsound/soundmanagerimp.hpp @@ -15,6 +15,7 @@ #include "../mwbase/soundmanager.hpp" #include "regionsoundselector.hpp" +#include "watersoundupdater.hpp" namespace VFS { @@ -61,12 +62,8 @@ namespace MWSound float mVoiceVolume; float mFootstepsVolume; - int mNearWaterRadius; - int mNearWaterPoints; - float mNearWaterIndoorTolerance; - float mNearWaterOutdoorTolerance; - std::string mNearWaterIndoorID; - std::string mNearWaterOutdoorID; + WaterSoundUpdater mWaterSoundUpdater; + typedef std::unique_ptr > SoundBufferList; // List of sound buffers, grown as needed. New enties are added to the // back, allowing existing Sound_Buffer references/pointers to remain diff --git a/apps/openmw/mwsound/watersoundupdater.cpp b/apps/openmw/mwsound/watersoundupdater.cpp new file mode 100644 index 000000000..b1646c404 --- /dev/null +++ b/apps/openmw/mwsound/watersoundupdater.cpp @@ -0,0 +1,71 @@ +#include "watersoundupdater.hpp" + +#include "../mwbase/world.hpp" +#include "../mwworld/cellstore.hpp" +#include "../mwworld/ptr.hpp" + +#include + +#include + +namespace MWSound +{ + WaterSoundUpdater::WaterSoundUpdater(const WaterSoundUpdaterSettings& settings) + : mSettings(settings) + { + } + + WaterSoundUpdate WaterSoundUpdater::update(const MWWorld::ConstPtr& player, const MWBase::World& world) const + { + WaterSoundUpdate result; + + result.mId = player.getCell()->isExterior() ? mSettings.mNearWaterOutdoorID : mSettings.mNearWaterIndoorID; + result.mVolume = std::min(1.0f, getVolume(player, world)); + + return result; + } + + float WaterSoundUpdater::getVolume(const MWWorld::ConstPtr& player, const MWBase::World& world) const + { + if (mListenerUnderwater) + return 1.0f; + + const MWWorld::CellStore& cell = *player.getCell(); + + if (!cell.getCell()->hasWater()) + return 0.0f; + + const osg::Vec3f pos = player.getRefData().getPosition().asVec3(); + const float dist = std::abs(cell.getWaterLevel() - pos.z()); + + if (cell.isExterior() && dist < mSettings.mNearWaterOutdoorTolerance) + { + if (mSettings.mNearWaterPoints <= 1) + return (mSettings.mNearWaterOutdoorTolerance - dist) / mSettings.mNearWaterOutdoorTolerance; + + const float step = mSettings.mNearWaterRadius * 2.0f / (mSettings.mNearWaterPoints - 1); + + int underwaterPoints = 0; + + for (int x = 0; x < mSettings.mNearWaterPoints; x++) + { + for (int y = 0; y < mSettings.mNearWaterPoints; y++) + { + const float terrainX = pos.x() - mSettings.mNearWaterRadius + x * step; + const float terrainY = pos.y() - mSettings.mNearWaterRadius + y * step; + const float height = world.getTerrainHeightAt(osg::Vec3f(terrainX, terrainY, 0.0f)); + + if (height < 0) + underwaterPoints++; + } + } + + return underwaterPoints * 2.0f / (mSettings.mNearWaterPoints * mSettings.mNearWaterPoints); + } + + if (!cell.isExterior() && dist < mSettings.mNearWaterIndoorTolerance) + return (mSettings.mNearWaterIndoorTolerance - dist) / mSettings.mNearWaterIndoorTolerance; + + return 0.0f; + } +} diff --git a/apps/openmw/mwsound/watersoundupdater.hpp b/apps/openmw/mwsound/watersoundupdater.hpp new file mode 100644 index 000000000..b20b9db6c --- /dev/null +++ b/apps/openmw/mwsound/watersoundupdater.hpp @@ -0,0 +1,54 @@ +#ifndef GAME_SOUND_WATERSOUNDUPDATER_H +#define GAME_SOUND_WATERSOUNDUPDATER_H + +#include + +namespace MWBase +{ + class World; +} + +namespace MWWorld +{ + class ConstPtr; +} + +namespace MWSound +{ + struct WaterSoundUpdaterSettings + { + int mNearWaterRadius; + int mNearWaterPoints; + float mNearWaterIndoorTolerance; + float mNearWaterOutdoorTolerance; + std::string mNearWaterIndoorID; + std::string mNearWaterOutdoorID; + }; + + struct WaterSoundUpdate + { + std::string mId; + float mVolume; + }; + + class WaterSoundUpdater + { + public: + explicit WaterSoundUpdater(const WaterSoundUpdaterSettings& settings); + + WaterSoundUpdate update(const MWWorld::ConstPtr& player, const MWBase::World& world) const; + + void setUnderwater(bool value) + { + mListenerUnderwater = value; + } + + private: + const WaterSoundUpdaterSettings mSettings; + bool mListenerUnderwater = false; + + float getVolume(const MWWorld::ConstPtr& player, const MWBase::World& world) const; + }; +} + +#endif