diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 61e389f195..d77db40590 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -7,19 +7,24 @@ Debian: - linux image: debian:bullseye cache: - key: apt-cache + key: cache.002 paths: - apt-cache/ + - ccache/ before_script: - export APT_CACHE_DIR=`pwd`/apt-cache && mkdir -pv $APT_CACHE_DIR - apt-get update -yq - - apt-get -o dir::cache::archives="$APT_CACHE_DIR" install -y cmake build-essential libboost-filesystem-dev libboost-program-options-dev libboost-system-dev libboost-iostreams-dev libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev libsdl2-dev libqt5opengl5-dev libopenal-dev libopenscenegraph-dev libunshield-dev libtinyxml-dev libmygui-dev libbullet-dev + - apt-get -o dir::cache::archives="$APT_CACHE_DIR" install -y cmake build-essential libboost-filesystem-dev libboost-program-options-dev libboost-system-dev libboost-iostreams-dev libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev libsdl2-dev libqt5opengl5-dev libopenal-dev libopenscenegraph-dev libunshield-dev libtinyxml-dev libmygui-dev libbullet-dev ccache stage: build - script: + script: + - export CCACHE_BASEDIR="`pwd`" + - export CCACHE_DIR="`pwd`/ccache" && mkdir -pv "$CCACHE_DIR" + - ccache -z -M 250M - cores_to_use=$((`nproc`-2)); if (( $cores_to_use < 1 )); then cores_to_use=1; fi - - mkdir build; cd build; cmake -DCMAKE_BUILD_TYPE=MinSizeRel ../ + - mkdir build; cd build; cmake -DCMAKE_BUILD_TYPE=MinSizeRel ../ -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache - make -j$cores_to_use - DESTDIR=artifacts make install + - ccache -s artifacts: paths: - build/artifacts/ diff --git a/.travis.yml b/.travis.yml index d877e8accb..cd0e6e713c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -37,9 +37,9 @@ addons: build_command: "make VERBOSE=1 -j3" matrix: include: - - name: OpenMW (all) on macOS Xcode 10.2 + - name: OpenMW (all) on MacOS 10.15 with Xcode 12 os: osx - osx_image: xcode10.2 + osx_image: xcode12 if: branch != coverity_scan - name: OpenMW (all) on Ubuntu Focal with GCC os: linux @@ -71,8 +71,8 @@ before_script: script: - cd ./build - if [ "${COVERITY_SCAN_BRANCH}" != 1 ]; then ${ANALYZE} make -j3; fi - - if [ "${COVERITY_SCAN_BRANCH}" != 1 ] && [ "${TRAVIS_OS_NAME}" = "osx" ]; then make package; fi - - if [ "${COVERITY_SCAN_BRANCH}" != 1 ] && [ "${TRAVIS_OS_NAME}" = "osx" ]; then ../CI/check_package.osx.sh; fi +# - if [ "${COVERITY_SCAN_BRANCH}" != 1 ] && [ "${TRAVIS_OS_NAME}" = "osx" ]; then make package; fi +# - if [ "${COVERITY_SCAN_BRANCH}" != 1 ] && [ "${TRAVIS_OS_NAME}" = "osx" ]; then ../CI/check_package.osx.sh; fi - if [ "${COVERITY_SCAN_BRANCH}" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ]; then ./openmw_test_suite; fi - if [ "${COVERITY_SCAN_BRANCH}" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ]; then cd .. && ./CI/check_tabs.sh; fi - cd "${TRAVIS_BUILD_DIR}" diff --git a/CHANGELOG.md b/CHANGELOG.md index ce2966cbfa..7be9cc2c71 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ Bug #5367: Selecting a spell on an enchanted item per hotkey always plays the equip sound Bug #5369: Spawnpoint in the Grazelands doesn't produce oversized creatures Bug #5370: Opening an unlocked but trapped door uses the key + Bug #5384: openmw-cs: deleting an instance requires reload of scene window to show in editor Bug #5397: NPC greeting does not reset if you leave + reenter area Bug #5400: Editor: Verifier checks race of non-skin bodyparts Bug #5403: Enchantment effect doesn't show on an enemy during death animation @@ -29,17 +30,21 @@ Bug #5441: Enemies can't push a player character when in critical strike stance Bug #5451: Magic projectiles don't disappear with the caster Bug #5452: Autowalk is being included in savegames + Bug #5472: Mistify mod causes CTD in 0.46 on Mac Bug #5479: NPCs who should be walking around town are standing around without walking Bug #5484: Zero value items shouldn't be able to be bought or sold for 1 gold Bug #5485: Intimidate doesn't increase disposition on marginal wins Bug #5490: Hits to carried left slot aren't redistributed if there's no shield equipped Bug #5499: Faction advance is available when requirements not met + Bug #5502: Dead zone for analogue stick movement is too small + Bug #5507: Sound volume is not clamped on ingame settings update Feature #390: 3rd person look "over the shoulder" Feature #2386: Distant Statics in the form of Object Paging Feature #5297: Add a search function to the "Datafiles" tab of the OpenMW launcher Feature #5362: Show the soul gems' trapped soul in count dialog Feature #5445: Handle NiLines Feature #5457: Realistic diagonal movement + Feature #5486: Fixes trainers to choose their training skills based on their base skill points Task #5480: Drop Qt4 support 0.46.0 diff --git a/CI/before_install.osx.sh b/CI/before_install.osx.sh index e279d1933b..5dae1f1021 100755 --- a/CI/before_install.osx.sh +++ b/CI/before_install.osx.sh @@ -1,8 +1,5 @@ #!/bin/sh -e -brew update -brew outdated pkgconfig || brew upgrade pkgconfig -brew install qt brew install ccache curl -fSL -R -J https://downloads.openmw.org/osx/dependencies/openmw-deps-ef2462c.zip -o ~/openmw-deps.zip diff --git a/CI/before_script.msvc.sh b/CI/before_script.msvc.sh index a9f6bd81fe..dc6eb7e44e 100644 --- a/CI/before_script.msvc.sh +++ b/CI/before_script.msvc.sh @@ -504,11 +504,11 @@ if [ -z $SKIP_DOWNLOAD ]; then # Google test and mock if [ ! -z $TEST_FRAMEWORK ]; then - echo "Google test 1.8.1..." + echo "Google test 1.10.0..." if [ -d googletest ]; then printf " Google test exists, skipping." else - git clone -b release-1.8.1 https://github.com/google/googletest.git + git clone -b release-1.10.0 https://github.com/google/googletest.git fi fi fi @@ -606,7 +606,7 @@ printf "Bullet 2.89 (${BULLET_DBL_DISPLAY})... " eval 7z x -y "${DEPS}/Bullet-2.89-msvc${MSVC_YEAR}-win${BITS}${BULLET_DBL}.7z" $STRIP mv "Bullet-2.89-msvc${MSVC_YEAR}-win${BITS}${BULLET_DBL}" Bullet fi - export BULLET_ROOT="$(real_pwd)/Bullet" + add_cmake_opts -DBULLET_ROOT="$(real_pwd)/Bullet" echo Done. } cd $DEPS @@ -820,7 +820,7 @@ cd $DEPS echo # Google Test and Google Mock if [ ! -z $TEST_FRAMEWORK ]; then - printf "Google test 1.8.1 ..." + printf "Google test 1.10.0 ..." cd googletest if [ ! -d build ]; then diff --git a/CI/before_script.osx.sh b/CI/before_script.osx.sh index 01d9d2b80a..15d6862db6 100755 --- a/CI/before_script.osx.sh +++ b/CI/before_script.osx.sh @@ -13,10 +13,17 @@ cmake \ -D CMAKE_PREFIX_PATH="$DEPENDENCIES_ROOT;$QT_PATH" \ -D CMAKE_C_COMPILER_LAUNCHER="$CCACHE_EXECUTABLE" \ -D CMAKE_CXX_COMPILER_LAUNCHER="$CCACHE_EXECUTABLE" \ +-D CMAKE_CXX_FLAGS="-std=c++11 -stdlib=libc++" \ +-D CMAKE_C_FLAGS_RELEASE="-g -O0" \ +-D CMAKE_CXX_FLAGS_RELEASE="-g -O0" \ -D CMAKE_OSX_DEPLOYMENT_TARGET="10.9" \ --D CMAKE_OSX_SYSROOT="macosx10.14" \ --D CMAKE_BUILD_TYPE=Release \ +-D CMAKE_BUILD_TYPE=RELEASE \ -D OPENMW_OSX_DEPLOYMENT=TRUE \ --D BUILD_ESMTOOL=FALSE \ +-D BUILD_OPENMW=TRUE \ +-D BUILD_OPENCS=FALSE \ +-D BUILD_ESMTOOL=TRUE \ +-D BUILD_BSATOOL=TRUE \ +-D BUILD_ESSIMPORTER=TRUE \ +-D BUILD_NIFTEST=TRUE \ -G"Unix Makefiles" \ .. diff --git a/CMakeLists.txt b/CMakeLists.txt index 7c5bf22fde..dbe28fd84f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -311,7 +311,7 @@ include_directories("." ${Boost_INCLUDE_DIR} ${MyGUI_INCLUDE_DIRS} ${OPENAL_INCLUDE_DIR} - ${Bullet_INCLUDE_DIRS} + ${BULLET_INCLUDE_DIRS} ) link_directories(${SDL2_LIBRARY_DIRS} ${Boost_LIBRARY_DIRS}) diff --git a/apps/launcher/advancedpage.cpp b/apps/launcher/advancedpage.cpp index 866cec7286..e9db74cae1 100644 --- a/apps/launcher/advancedpage.cpp +++ b/apps/launcher/advancedpage.cpp @@ -90,6 +90,7 @@ bool Launcher::AdvancedPage::loadSettings() loadSettingBool(shieldSheathingCheckBox, "shield sheathing", "Game"); } loadSettingBool(uncappedDamageFatigueCheckBox, "uncapped damage fatigue", "Game"); + loadSettingBool(trainersTrainingSkillsBasedOnBaseSkillCheckBox, "trainers training skills based on base skill", "Game"); // Input Settings loadSettingBool(grabCursorCheckBox, "grab cursor", "Input"); @@ -155,6 +156,7 @@ void Launcher::AdvancedPage::saveSettings() saveSettingBool(weaponSheathingCheckBox, "weapon sheathing", "Game"); saveSettingBool(shieldSheathingCheckBox, "shield sheathing", "Game"); saveSettingBool(uncappedDamageFatigueCheckBox, "uncapped damage fatigue", "Game"); + saveSettingBool(trainersTrainingSkillsBasedOnBaseSkillCheckBox, "trainers training skills based on base skill", "Game"); // Input Settings saveSettingBool(grabCursorCheckBox, "grab cursor", "Input"); diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 401e7896de..dc9b985969 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -216,7 +216,6 @@ endif(APPLE) target_link_libraries(openmw-cs ${OSG_LIBRARIES} - ${OPENTHREADS_LIBRARIES} ${OSGTEXT_LIBRARIES} ${OSGUTIL_LIBRARIES} ${OSGVIEWER_LIBRARIES} diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index 560f4215f9..bc5a8e69d5 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -94,7 +94,7 @@ bool CSMWorld::IdTable::setData (const QModelIndex &index, const QVariant &value // views that the whole row has changed. emit dataChanged(this->index(index.row(), 0), - this->index(index.row(), columnCount(index.parent()))); + this->index(index.row(), columnCount(index.parent()) - 1)); } else { diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index 9747c3de05..f3186e76a9 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -1,5 +1,8 @@ #include "scenewidget.hpp" +#include +#include + #include #include #include @@ -184,7 +187,7 @@ void CompositeViewer::update() double minFrameTime = _runMaxFrameRate > 0.0 ? 1.0 / _runMaxFrameRate : 0.0; if (dt < minFrameTime) { - OpenThreads::Thread::microSleep(1000*1000*(minFrameTime-dt)); + std::this_thread::sleep_for(std::chrono::duration(minFrameTime - dt)); } } diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index a9f3d24e29..fddfa276f9 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -41,7 +41,7 @@ add_openmw_dir (mwgui itemmodel containeritemmodel inventoryitemmodel sortfilteritemmodel itemview tradeitemmodel companionitemmodel pickpocketitemmodel controllers savegamedialog recharge mode videowidget backgroundimage itemwidget screenfader debugwindow spellmodel spellview - draganddrop timeadvancer jailscreen itemchargeview keyboardnavigation textcolours + draganddrop timeadvancer jailscreen itemchargeview keyboardnavigation textcolours statswatcher ) add_openmw_dir (mwdialogue @@ -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 + loudness movieaudiofactory alext efx efx-presets regionsoundselector watersoundupdater volumesettings ) add_openmw_dir (mwworld @@ -123,7 +123,6 @@ include_directories( target_link_libraries(openmw ${OSG_LIBRARIES} - ${OPENTHREADS_LIBRARIES} ${OSGPARTICLE_LIBRARIES} ${OSGUTIL_LIBRARIES} ${OSGDB_LIBRARIES} diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 0792f4eaf4..ec12d6a1f7 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -2,6 +2,8 @@ #include #include +#include +#include #include @@ -67,6 +69,140 @@ namespace if (ret != 0) Log(Debug::Error) << "SDL error: " << SDL_GetError(); } + + struct UserStats + { + const std::string mLabel; + const std::string mBegin; + const std::string mEnd; + const std::string mTaken; + + UserStats(const std::string& label, const std::string& prefix) + : mLabel(label), + mBegin(prefix + "_time_begin"), + mEnd(prefix + "_time_end"), + mTaken(prefix + "_time_taken") + {} + }; + + enum class UserStatsType : std::size_t + { + Input, + Sound, + State, + Script, + Mechanics, + Physics, + World, + Gui, + + Number, + }; + + template + struct UserStatsValue + { + static const UserStats sValue; + }; + + template <> + const UserStats UserStatsValue::sValue {"Input", "input"}; + + template <> + const UserStats UserStatsValue::sValue {"Sound", "sound"}; + + template <> + const UserStats UserStatsValue::sValue {"State", "state"}; + + template <> + const UserStats UserStatsValue::sValue {"Script", "script"}; + + template <> + const UserStats UserStatsValue::sValue {"Mech", "mechanics"}; + + template <> + const UserStats UserStatsValue::sValue {"Phys", "physics"}; + + template <> + const UserStats UserStatsValue::sValue {"World", "world"}; + + template <> + const UserStats UserStatsValue::sValue {"Gui", "gui"}; + + template + struct ForEachUserStatsValue + { + template + static void apply(F&& f) + { + f(UserStatsValue::sValue); + using Next = ForEachUserStatsValue(static_cast(type) + 1)>; + Next::apply(std::forward(f)); + } + }; + + template <> + struct ForEachUserStatsValue + { + template + static void apply(F&&) {} + }; + + template + void forEachUserStatsValue(F&& f) + { + ForEachUserStatsValue(0)>::apply(std::forward(f)); + } + + template + class ScopedProfile + { + public: + ScopedProfile(osg::Timer_t frameStart, unsigned int frameNumber, const osg::Timer& timer, osg::Stats& stats) + : mScopeStart(timer.tick()), + mFrameStart(frameStart), + mFrameNumber(frameNumber), + mTimer(timer), + mStats(stats) + { + } + + ScopedProfile(const ScopedProfile&) = delete; + ScopedProfile& operator=(const ScopedProfile&) = delete; + + ~ScopedProfile() + { + const osg::Timer_t end = mTimer.tick(); + const UserStats& stats = UserStatsValue::sValue; + + mStats.setAttribute(mFrameNumber, stats.mBegin, mTimer.delta_s(mFrameStart, mScopeStart)); + mStats.setAttribute(mFrameNumber, stats.mTaken, mTimer.delta_s(mScopeStart, end)); + mStats.setAttribute(mFrameNumber, stats.mEnd, mTimer.delta_s(mFrameStart, end)); + } + + private: + const osg::Timer_t mScopeStart; + const osg::Timer_t mFrameStart; + const unsigned int mFrameNumber; + const osg::Timer& mTimer; + osg::Stats& mStats; + }; + + void initStatsHandler(Resource::Profiler& profiler) + { + const osg::Vec4f textColor(1.f, 1.f, 1.f, 1.f); + const osg::Vec4f barColor(1.f, 1.f, 1.f, 1.f); + const float multiplier = 1000; + const bool average = true; + const bool averageInInverseSpace = false; + const float maxValue = 10000; + + forEachUserStatsValue([&] (const UserStats& v) + { + profiler.addUserStatsLine(v.mLabel, textColor, barColor, v.mTaken, multiplier, + average, averageInInverseSpace, v.mBegin, v.mEnd, maxValue); + }); + } } void OMW::Engine::executeLocalScripts() @@ -87,119 +223,119 @@ bool OMW::Engine::frame(float frametime) { try { - mStartTick = mViewer->getStartTick(); + const osg::Timer_t frameStart = mViewer->getStartTick(); + const unsigned int frameNumber = mViewer->getFrameStamp()->getFrameNumber(); + const osg::Timer* const timer = osg::Timer::instance(); + osg::Stats* const stats = mViewer->getViewerStats(); mEnvironment.setFrameDuration(frametime); // update input - mEnvironment.getInputManager()->update(frametime, false); + { + ScopedProfile profile(frameStart, frameNumber, *timer, *stats); + mEnvironment.getInputManager()->update(frametime, false); + } // When the window is minimized, pause the game. Currently this *has* to be here to work around a MyGUI bug. // If we are not currently rendering, then RenderItems will not be reused resulting in a memory leak upon changing widget textures (fixed in MyGUI 3.3.2), // and destroyed widgets will not be deleted (not fixed yet, https://github.com/MyGUI/mygui/issues/21) - if (!mEnvironment.getWindowManager()->isWindowVisible()) { - mEnvironment.getSoundManager()->pausePlayback(); - return false; - } - else - mEnvironment.getSoundManager()->resumePlayback(); + ScopedProfile profile(frameStart, frameNumber, *timer, *stats); - // sound - if (mUseSound) - mEnvironment.getSoundManager()->update(frametime); + if (!mEnvironment.getWindowManager()->isWindowVisible()) + { + mEnvironment.getSoundManager()->pausePlayback(); + return false; + } + else + mEnvironment.getSoundManager()->resumePlayback(); + + // sound + if (mUseSound) + mEnvironment.getSoundManager()->update(frametime); + } // Main menu opened? Then scripts are also paused. bool paused = mEnvironment.getWindowManager()->containsMode(MWGui::GM_MainMenu); // update game state - mEnvironment.getStateManager()->update (frametime); + { + ScopedProfile profile(frameStart, frameNumber, *timer, *stats); + mEnvironment.getStateManager()->update (frametime); + } bool guiActive = mEnvironment.getWindowManager()->isGuiMode(); - osg::Timer_t beforeScriptTick = osg::Timer::instance()->tick(); - if (mEnvironment.getStateManager()->getState()!= - MWBase::StateManager::State_NoGame) { - if (!paused) - { - if (mEnvironment.getWorld()->getScriptsEnabled()) - { - // local scripts - executeLocalScripts(); + ScopedProfile profile(frameStart, frameNumber, *timer, *stats); - // global scripts - mEnvironment.getScriptManager()->getGlobalScripts().run(); + if (mEnvironment.getStateManager()->getState() != MWBase::StateManager::State_NoGame) + { + if (!paused) + { + if (mEnvironment.getWorld()->getScriptsEnabled()) + { + // local scripts + executeLocalScripts(); + + // global scripts + mEnvironment.getScriptManager()->getGlobalScripts().run(); + } + + mEnvironment.getWorld()->markCellAsUnchanged(); } - mEnvironment.getWorld()->markCellAsUnchanged(); + if (!guiActive) + { + double hours = (frametime * mEnvironment.getWorld()->getTimeScaleFactor()) / 3600.0; + mEnvironment.getWorld()->advanceTime(hours, true); + mEnvironment.getWorld()->rechargeItems(frametime, true); + } } + } - if (!guiActive) + // update mechanics + { + ScopedProfile profile(frameStart, frameNumber, *timer, *stats); + + if (mEnvironment.getStateManager()->getState() != MWBase::StateManager::State_NoGame) { - double hours = (frametime * mEnvironment.getWorld()->getTimeScaleFactor()) / 3600.0; - mEnvironment.getWorld()->advanceTime(hours, true); - mEnvironment.getWorld()->rechargeItems(frametime, true); + mEnvironment.getMechanicsManager()->update(frametime, guiActive); } - } - osg::Timer_t afterScriptTick = osg::Timer::instance()->tick(); - // update actors - osg::Timer_t beforeMechanicsTick = osg::Timer::instance()->tick(); - if (mEnvironment.getStateManager()->getState()!= - MWBase::StateManager::State_NoGame) - { - mEnvironment.getMechanicsManager()->update(frametime, - guiActive); - } - osg::Timer_t afterMechanicsTick = osg::Timer::instance()->tick(); - - if (mEnvironment.getStateManager()->getState()== - MWBase::StateManager::State_Running) - { - MWWorld::Ptr player = mEnvironment.getWorld()->getPlayerPtr(); - if(!guiActive && player.getClass().getCreatureStats(player).isDead()) - mEnvironment.getStateManager()->endGame(); + if (mEnvironment.getStateManager()->getState() == MWBase::StateManager::State_Running) + { + MWWorld::Ptr player = mEnvironment.getWorld()->getPlayerPtr(); + if(!guiActive && player.getClass().getCreatureStats(player).isDead()) + mEnvironment.getStateManager()->endGame(); + } } // update physics - osg::Timer_t beforePhysicsTick = osg::Timer::instance()->tick(); - if (mEnvironment.getStateManager()->getState()!= - MWBase::StateManager::State_NoGame) { - mEnvironment.getWorld()->updatePhysics(frametime, guiActive); + ScopedProfile profile(frameStart, frameNumber, *timer, *stats); + + if (mEnvironment.getStateManager()->getState() != MWBase::StateManager::State_NoGame) + { + mEnvironment.getWorld()->updatePhysics(frametime, guiActive); + } } - osg::Timer_t afterPhysicsTick = osg::Timer::instance()->tick(); // update world - osg::Timer_t beforeWorldTick = osg::Timer::instance()->tick(); - if (mEnvironment.getStateManager()->getState()!= - MWBase::StateManager::State_NoGame) { - mEnvironment.getWorld()->update(frametime, guiActive); + ScopedProfile profile(frameStart, frameNumber, *timer, *stats); + + if (mEnvironment.getStateManager()->getState() != MWBase::StateManager::State_NoGame) + { + mEnvironment.getWorld()->update(frametime, guiActive); + } } - osg::Timer_t afterWorldTick = osg::Timer::instance()->tick(); // update GUI - mEnvironment.getWindowManager()->update(frametime); - - unsigned int frameNumber = mViewer->getFrameStamp()->getFrameNumber(); - osg::Stats* stats = mViewer->getViewerStats(); - stats->setAttribute(frameNumber, "script_time_begin", osg::Timer::instance()->delta_s(mStartTick, beforeScriptTick)); - stats->setAttribute(frameNumber, "script_time_taken", osg::Timer::instance()->delta_s(beforeScriptTick, afterScriptTick)); - stats->setAttribute(frameNumber, "script_time_end", osg::Timer::instance()->delta_s(mStartTick, afterScriptTick)); - - stats->setAttribute(frameNumber, "mechanics_time_begin", osg::Timer::instance()->delta_s(mStartTick, beforeMechanicsTick)); - stats->setAttribute(frameNumber, "mechanics_time_taken", osg::Timer::instance()->delta_s(beforeMechanicsTick, afterMechanicsTick)); - stats->setAttribute(frameNumber, "mechanics_time_end", osg::Timer::instance()->delta_s(mStartTick, afterMechanicsTick)); - - stats->setAttribute(frameNumber, "physics_time_begin", osg::Timer::instance()->delta_s(mStartTick, beforePhysicsTick)); - stats->setAttribute(frameNumber, "physics_time_taken", osg::Timer::instance()->delta_s(beforePhysicsTick, afterPhysicsTick)); - stats->setAttribute(frameNumber, "physics_time_end", osg::Timer::instance()->delta_s(mStartTick, afterPhysicsTick)); - - stats->setAttribute(frameNumber, "world_time_begin", osg::Timer::instance()->delta_s(mStartTick, beforeWorldTick)); - stats->setAttribute(frameNumber, "world_time_taken", osg::Timer::instance()->delta_s(beforeWorldTick, afterWorldTick)); - stats->setAttribute(frameNumber, "world_time_end", osg::Timer::instance()->delta_s(mStartTick, afterWorldTick)); + { + ScopedProfile profile(frameStart, frameNumber, *timer, *stats); + mEnvironment.getWindowManager()->update(frametime); + } if (stats->collectStats("resource")) { @@ -212,7 +348,6 @@ bool OMW::Engine::frame(float frametime) mEnvironment.reportStats(frameNumber, *stats); } - } catch (const std::exception& e) { @@ -255,8 +390,6 @@ OMW::Engine::Engine(Files::ConfigurationManager& configurationManager) throw std::runtime_error("Could not initialize SDL! " + std::string(SDL_GetError())); } } - - mStartTick = osg::Timer::instance()->tick(); } OMW::Engine::~Engine() @@ -703,14 +836,7 @@ void OMW::Engine::go() // Setup profiler osg::ref_ptr statshandler = new Resource::Profiler; - statshandler->addUserStatsLine("Script", osg::Vec4f(1.f, 1.f, 1.f, 1.f), osg::Vec4f(1.f, 1.f, 1.f, 1.f), - "script_time_taken", 1000.0, true, false, "script_time_begin", "script_time_end", 10000); - statshandler->addUserStatsLine("Mech", osg::Vec4f(1.f, 1.f, 1.f, 1.f), osg::Vec4f(1.f, 1.f, 1.f, 1.f), - "mechanics_time_taken", 1000.0, true, false, "mechanics_time_begin", "mechanics_time_end", 10000); - statshandler->addUserStatsLine("Phys", osg::Vec4f(1.f, 1.f, 1.f, 1.f), osg::Vec4f(1.f, 1.f, 1.f, 1.f), - "physics_time_taken", 1000.0, true, false, "physics_time_begin", "physics_time_end", 10000); - statshandler->addUserStatsLine("World", osg::Vec4f(1.f, 1.f, 1.f, 1.f), osg::Vec4f(1.f, 1.f, 1.f, 1.f), - "world_time_taken", 1000.0, true, false, "world_time_begin", "world_time_end", 10000); + initStatsHandler(*statshandler); mViewer->addEventHandler(statshandler); @@ -762,7 +888,7 @@ void OMW::Engine::go() if (!frame(dt)) { - OpenThreads::Thread::microSleep(5000); + std::this_thread::sleep_for(std::chrono::milliseconds(5)); continue; } else diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp index a00cf474a9..3dd1a69b27 100644 --- a/apps/openmw/engine.hpp +++ b/apps/openmw/engine.hpp @@ -111,8 +111,6 @@ namespace OMW bool mScriptBlacklistUse; bool mNewGame; - osg::Timer_t mStartTick; - // not implemented Engine (const Engine&); Engine& operator= (const Engine&); diff --git a/apps/openmw/mwbase/environment.cpp b/apps/openmw/mwbase/environment.cpp index c70debda1d..764a07ec96 100644 --- a/apps/openmw/mwbase/environment.cpp +++ b/apps/openmw/mwbase/environment.cpp @@ -1,8 +1,8 @@ #include "environment.hpp" #include - -#include +#include +#include #include "world.hpp" #include "scriptmanager.hpp" @@ -99,7 +99,7 @@ void MWBase::Environment::limitFrameRate(double dt) const double minFrameTime = 1.0 / static_cast(mFrameRateLimit); if (thisFrameTime < minFrameTime) { - OpenThreads::Thread::microSleep(1000*1000*(minFrameTime-thisFrameTime)); + std::this_thread::sleep_for(std::chrono::duration(minFrameTime - thisFrameTime)); } } } diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index c9ce43e013..b6ce4d0619 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -67,10 +67,6 @@ namespace MWBase virtual void drop (const MWWorld::CellStore *cellStore) = 0; ///< Deregister all objects in the given cell. - virtual void watchActor (const MWWorld::Ptr& ptr) = 0; - ///< On each update look for changes in a previously registered actor and update the - /// GUI accordingly. - virtual void update (float duration, bool paused) = 0; ///< Update objects /// diff --git a/apps/openmw/mwbase/soundmanager.hpp b/apps/openmw/mwbase/soundmanager.hpp index cc933d4bbb..2bac561fdd 100644 --- a/apps/openmw/mwbase/soundmanager.hpp +++ b/apps/openmw/mwbase/soundmanager.hpp @@ -6,6 +6,7 @@ #include #include "../mwworld/ptr.hpp" +#include "../mwsound/type.hpp" namespace MWWorld { @@ -44,14 +45,7 @@ namespace MWSound LoopNoEnv = Loop | NoEnv, LoopRemoveAtDistance = Loop | RemoveAtDistance }; - enum class Type { - Sfx = 1<<4, /* Normal SFX sound */ - Voice = 1<<5, /* Voice sound */ - Foot = 1<<6, /* Footstep sound */ - Music = 1<<7, /* Music track */ - Movie = 1<<8, /* Movie audio track */ - Mask = Sfx | Voice | Foot | Music | Movie - }; + // Used for creating a type mask for SoundManager::pauseSounds and resumeSounds inline int operator~(Type a) { return ~static_cast(a); } inline int operator&(Type a, Type b) { return static_cast(a) & static_cast(b); } @@ -163,9 +157,6 @@ namespace MWBase virtual void stopSound(const MWWorld::CellStore *cell) = 0; ///< Stop all sounds for the given cell. - virtual void stopSound(const std::string& soundId) = 0; - ///< Stop a non-3d looping sound - virtual void fadeOutSound3D(const MWWorld::ConstPtr &reference, const std::string& soundId, float duration) = 0; ///< Fade out given sound (that is already playing) of given object ///< @param reference Reference to object, whose sound is faded out diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index d6afe92435..f35df78817 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -154,27 +154,11 @@ namespace MWBase virtual void setConsoleSelectedObject(const MWWorld::Ptr& object) = 0; - /// Set value for the given ID. - virtual void setValue (const std::string& id, const MWMechanics::AttributeValue& value) = 0; - virtual void setValue (int parSkill, const MWMechanics::SkillValue& value) = 0; - virtual void setValue (const std::string& id, const MWMechanics::DynamicStat& value) = 0; - virtual void setValue (const std::string& id, const std::string& value) = 0; - virtual void setValue (const std::string& id, int value) = 0; - /// Set time left for the player to start drowning (update the drowning bar) /// @param time time left to start drowning /// @param maxTime how long we can be underwater (in total) until drowning starts virtual void setDrowningTimeLeft (float time, float maxTime) = 0; - virtual void setPlayerClass (const ESM::Class &class_) = 0; - ///< set current class of player - - virtual void configureSkills (const SkillList& major, const SkillList& minor) = 0; - ///< configure skill groups, each set contains the skill ID for that group. - - virtual void updateSkillArea() = 0; - ///< update display of skills, factions, birth sign, reputation and bounty - virtual void changeCell(const MWWorld::CellStore* cell) = 0; ///< change the active cell @@ -359,6 +343,9 @@ namespace MWBase virtual void windowResized(int x, int y) = 0; virtual void windowClosed() = 0; virtual bool isWindowVisible() = 0; + + virtual void watchActor(const MWWorld::Ptr& ptr) = 0; + virtual MWWorld::Ptr getWatchedActor() const = 0; }; } diff --git a/apps/openmw/mwgui/charactercreation.cpp b/apps/openmw/mwgui/charactercreation.cpp index 54e1a69a49..31fe3afb04 100644 --- a/apps/openmw/mwgui/charactercreation.cpp +++ b/apps/openmw/mwgui/charactercreation.cpp @@ -368,7 +368,6 @@ namespace MWGui if (klass) { mPlayerClass = *klass; - MWBase::Environment::get().getWindowManager()->setPlayerClass(mPlayerClass); } MWBase::Environment::get().getWindowManager()->removeDialog(mPickClassDialog); mPickClassDialog = 0; @@ -422,7 +421,6 @@ namespace MWGui if (mNameDialog) { mPlayerName = mNameDialog->getTextInput(); - MWBase::Environment::get().getWindowManager()->setValue("name", mPlayerName); MWBase::Environment::get().getMechanicsManager()->setPlayerName(mPlayerName); MWBase::Environment::get().getWindowManager()->removeDialog(mNameDialog); mNameDialog = 0; @@ -525,7 +523,6 @@ namespace MWGui MWBase::Environment::get().getMechanicsManager()->setPlayerClass(klass); mPlayerClass = klass; - MWBase::Environment::get().getWindowManager()->setPlayerClass(klass); // Do not delete dialog, so that choices are remembered in case we want to go back and adjust them later mCreateClassDialog->setVisible(false); @@ -722,7 +719,6 @@ namespace MWGui MWBase::Environment::get().getWorld()->getStore().get().find(mGenerateClass); mPlayerClass = *klass; - MWBase::Environment::get().getWindowManager()->setPlayerClass(mPlayerClass); updatePlayerHealth(); } diff --git a/apps/openmw/mwgui/charactercreation.hpp b/apps/openmw/mwgui/charactercreation.hpp index 4f62967858..beb8715fcd 100644 --- a/apps/openmw/mwgui/charactercreation.hpp +++ b/apps/openmw/mwgui/charactercreation.hpp @@ -1,13 +1,12 @@ #ifndef CHARACTER_CREATION_HPP #define CHARACTER_CREATION_HPP -#include #include #include #include -#include "../mwmechanics/stat.hpp" +#include "statswatcher.hpp" namespace osg { @@ -35,21 +34,21 @@ namespace MWGui class ReviewDialog; class MessageBoxManager; - class CharacterCreation + class CharacterCreation : public StatsListener { public: typedef std::vector SkillList; CharacterCreation(osg::Group* parent, Resource::ResourceSystem* resourceSystem); - ~CharacterCreation(); + virtual ~CharacterCreation(); //Show a dialog void spawnDialog(const char id); - void setValue (const std::string& id, const MWMechanics::AttributeValue& value); - void setValue (const std::string& id, const MWMechanics::DynamicStat& value); - void setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::SkillValue& value); - void configureSkills (const SkillList& major, const SkillList& minor); + void setValue (const std::string& id, const MWMechanics::AttributeValue& value) override; + void setValue (const std::string& id, const MWMechanics::DynamicStat& value) override; + void setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::SkillValue& value) override; + void configureSkills(const SkillList& major, const SkillList& minor) override; void onFrame(float duration); diff --git a/apps/openmw/mwgui/hud.hpp b/apps/openmw/mwgui/hud.hpp index c33c4efad8..15235fe917 100644 --- a/apps/openmw/mwgui/hud.hpp +++ b/apps/openmw/mwgui/hud.hpp @@ -2,8 +2,7 @@ #define OPENMW_GAME_MWGUI_HUD_H #include "mapwindow.hpp" - -#include "../mwmechanics/stat.hpp" +#include "statswatcher.hpp" namespace MWWorld { @@ -17,12 +16,12 @@ namespace MWGui class ItemWidget; class SpellWidget; - class HUD : public WindowBase, public LocalMapBase + class HUD : public WindowBase, public LocalMapBase, public StatsListener { public: HUD(CustomMarkerCollection& customMarkers, DragAndDrop* dragAndDrop, MWRender::LocalMap* localMapRender); virtual ~HUD(); - void setValue (const std::string& id, const MWMechanics::DynamicStat& value); + void setValue (const std::string& id, const MWMechanics::DynamicStat& value) override; /// Set time left for the player to start drowning /// @param time time left to start drowning @@ -48,7 +47,7 @@ namespace MWGui void setCrosshairVisible(bool visible); void setCrosshairOwned(bool owned); - void onFrame(float dt); + void onFrame(float dt) override; void setCellName(const std::string& cellName); @@ -59,7 +58,7 @@ namespace MWGui void setEnemy(const MWWorld::Ptr& enemy); void resetEnemy(); - void clear(); + void clear() override; private: MyGUI::ProgressBar *mHealth, *mMagicka, *mStamina, *mEnemyHealth, *mDrowning; @@ -113,8 +112,8 @@ namespace MWGui void onMapClicked(MyGUI::Widget* _sender); // LocalMapBase - virtual void customMarkerCreated(MyGUI::Widget* marker); - virtual void doorMarkerCreated(MyGUI::Widget* marker); + virtual void customMarkerCreated(MyGUI::Widget* marker) override; + virtual void doorMarkerCreated(MyGUI::Widget* marker) override; void updateEnemyHealthBar(); diff --git a/apps/openmw/mwgui/statswatcher.cpp b/apps/openmw/mwgui/statswatcher.cpp new file mode 100644 index 0000000000..7baec2f2c9 --- /dev/null +++ b/apps/openmw/mwgui/statswatcher.cpp @@ -0,0 +1,209 @@ +#include "statswatcher.hpp" + +#include "../mwbase/environment.hpp" +#include "../mwbase/windowmanager.hpp" +#include "../mwbase/world.hpp" + +#include "../mwmechanics/npcstats.hpp" +#include "../mwmechanics/spellutil.hpp" + +#include "../mwworld/class.hpp" +#include "../mwworld/esmstore.hpp" +#include "../mwworld/inventorystore.hpp" + +namespace MWGui +{ + // mWatchedTimeToStartDrowning = -1 for correct drowning state check, + // if stats.getTimeToStartDrowning() == 0 already on game start + StatsWatcher::StatsWatcher() + : mWatchedLevel(-1), mWatchedTimeToStartDrowning(-1), mWatchedStatsEmpty(true) + { + } + + void StatsWatcher::watchActor(const MWWorld::Ptr& ptr) + { + mWatched = ptr; + } + + void StatsWatcher::update() + { + if (mWatched.isEmpty()) + return; + + MWBase::WindowManager *winMgr = MWBase::Environment::get().getWindowManager(); + const MWMechanics::NpcStats &stats = mWatched.getClass().getNpcStats(mWatched); + for (int i = 0;i < ESM::Attribute::Length;++i) + { + if (stats.getAttribute(i) != mWatchedAttributes[i] || mWatchedStatsEmpty) + { + std::stringstream attrname; + attrname << "AttribVal"<<(i+1); + + mWatchedAttributes[i] = stats.getAttribute(i); + setValue(attrname.str(), stats.getAttribute(i)); + } + } + + if (stats.getHealth() != mWatchedHealth || mWatchedStatsEmpty) + { + static const std::string hbar("HBar"); + mWatchedHealth = stats.getHealth(); + setValue(hbar, stats.getHealth()); + } + if (stats.getMagicka() != mWatchedMagicka || mWatchedStatsEmpty) + { + static const std::string mbar("MBar"); + mWatchedMagicka = stats.getMagicka(); + setValue(mbar, stats.getMagicka()); + } + if (stats.getFatigue() != mWatchedFatigue || mWatchedStatsEmpty) + { + static const std::string fbar("FBar"); + mWatchedFatigue = stats.getFatigue(); + setValue(fbar, stats.getFatigue()); + } + + float timeToDrown = stats.getTimeToStartDrowning(); + + if (timeToDrown != mWatchedTimeToStartDrowning) + { + static const float fHoldBreathTime = MWBase::Environment::get().getWorld()->getStore().get() + .find("fHoldBreathTime")->mValue.getFloat(); + + mWatchedTimeToStartDrowning = timeToDrown; + + if(timeToDrown >= fHoldBreathTime || timeToDrown == -1.0) // -1.0 is a special value during initialization + winMgr->setDrowningBarVisibility(false); + else + { + winMgr->setDrowningBarVisibility(true); + winMgr->setDrowningTimeLeft(stats.getTimeToStartDrowning(), fHoldBreathTime); + } + } + + //Loop over ESM::Skill::SkillEnum + for (int i = 0; i < ESM::Skill::Length; ++i) + { + if(stats.getSkill(i) != mWatchedSkills[i] || mWatchedStatsEmpty) + { + mWatchedSkills[i] = stats.getSkill(i); + setValue((ESM::Skill::SkillEnum)i, stats.getSkill(i)); + } + } + + if (stats.getLevel() != mWatchedLevel || mWatchedStatsEmpty) + { + mWatchedLevel = stats.getLevel(); + setValue("level", mWatchedLevel); + } + + if (mWatched.getClass().isNpc()) + { + const ESM::NPC *watchedRecord = mWatched.get()->mBase; + + if (watchedRecord->mName != mWatchedName || mWatchedStatsEmpty) + { + mWatchedName = watchedRecord->mName; + setValue("name", watchedRecord->mName); + } + + if (watchedRecord->mRace != mWatchedRace || mWatchedStatsEmpty) + { + mWatchedRace = watchedRecord->mRace; + const ESM::Race *race = MWBase::Environment::get().getWorld()->getStore() + .get().find(watchedRecord->mRace); + setValue("race", race->mName); + } + + if (watchedRecord->mClass != mWatchedClass || mWatchedStatsEmpty) + { + mWatchedClass = watchedRecord->mClass; + const ESM::Class *cls = MWBase::Environment::get().getWorld()->getStore() + .get().find(watchedRecord->mClass); + setValue("class", cls->mName); + + MWBase::WindowManager::SkillList majorSkills (5); + MWBase::WindowManager::SkillList minorSkills (5); + + for (int i=0; i<5; ++i) + { + minorSkills[i] = cls->mData.mSkills[i][0]; + majorSkills[i] = cls->mData.mSkills[i][1]; + } + + configureSkills(majorSkills, minorSkills); + } + } + + mWatchedStatsEmpty = false; + + // Update the equipped weapon icon + MWWorld::InventoryStore& inv = mWatched.getClass().getInventoryStore(mWatched); + MWWorld::ContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); + if (weapon == inv.end()) + winMgr->unsetSelectedWeapon(); + else + winMgr->setSelectedWeapon(*weapon); + + // Update the selected spell icon + MWWorld::ContainerStoreIterator enchantItem = inv.getSelectedEnchantItem(); + if (enchantItem != inv.end()) + winMgr->setSelectedEnchantItem(*enchantItem); + else + { + const std::string& spell = winMgr->getSelectedSpell(); + if (!spell.empty()) + winMgr->setSelectedSpell(spell, int(MWMechanics::getSpellSuccessChance(spell, mWatched))); + else + winMgr->unsetSelectedSpell(); + } + } + + void StatsWatcher::addListener(StatsListener* listener) + { + mListeners.insert(listener); + } + + void StatsWatcher::removeListener(StatsListener* listener) + { + mListeners.erase(listener); + } + + void StatsWatcher::setValue(const std::string& id, const MWMechanics::AttributeValue& value) + { + for (StatsListener* listener : mListeners) + listener->setValue(id, value); + } + + void StatsWatcher::setValue(ESM::Skill::SkillEnum parSkill, const MWMechanics::SkillValue& value) + { + /// \todo Don't use the skill enum as a parameter type (we will have to drop it anyway, once we + /// allow custom skills. + for (StatsListener* listener : mListeners) + listener->setValue(parSkill, value); + } + + void StatsWatcher::setValue(const std::string& id, const MWMechanics::DynamicStat& value) + { + for (StatsListener* listener : mListeners) + listener->setValue(id, value); + } + + void StatsWatcher::setValue(const std::string& id, const std::string& value) + { + for (StatsListener* listener : mListeners) + listener->setValue(id, value); + } + + void StatsWatcher::setValue(const std::string& id, int value) + { + for (StatsListener* listener : mListeners) + listener->setValue(id, value); + } + + void StatsWatcher::configureSkills(const std::vector& major, const std::vector& minor) + { + for (StatsListener* listener : mListeners) + listener->configureSkills(major, minor); + } +} diff --git a/apps/openmw/mwgui/statswatcher.hpp b/apps/openmw/mwgui/statswatcher.hpp new file mode 100644 index 0000000000..41ab4fd252 --- /dev/null +++ b/apps/openmw/mwgui/statswatcher.hpp @@ -0,0 +1,69 @@ +#ifndef MWGUI_STATSWATCHER_H +#define MWGUI_STATSWATCHER_H + +#include + +#include +#include + +#include "../mwmechanics/stat.hpp" + +#include "../mwworld/ptr.hpp" + +namespace MWGui +{ + class StatsListener + { + public: + /// Set value for the given ID. + virtual void setValue(const std::string& id, const MWMechanics::AttributeValue& value) {} + virtual void setValue(const std::string& id, const MWMechanics::DynamicStat& value) {} + virtual void setValue(const std::string& id, const std::string& value) {} + virtual void setValue(const std::string& id, int value) {} + virtual void setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::SkillValue& value) {} + virtual void configureSkills(const std::vector& major, const std::vector& minor) {} + }; + + class StatsWatcher + { + MWWorld::Ptr mWatched; + + MWMechanics::AttributeValue mWatchedAttributes[ESM::Attribute::Length]; + MWMechanics::SkillValue mWatchedSkills[ESM::Skill::Length]; + + MWMechanics::DynamicStat mWatchedHealth; + MWMechanics::DynamicStat mWatchedMagicka; + MWMechanics::DynamicStat mWatchedFatigue; + + std::string mWatchedName; + std::string mWatchedRace; + std::string mWatchedClass; + + int mWatchedLevel; + + float mWatchedTimeToStartDrowning; + + bool mWatchedStatsEmpty; + + std::set mListeners; + + void setValue(const std::string& id, const MWMechanics::AttributeValue& value); + void setValue(const std::string& id, const MWMechanics::DynamicStat& value); + void setValue(const std::string& id, const std::string& value); + void setValue(const std::string& id, int value); + void setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::SkillValue& value); + void configureSkills(const std::vector& major, const std::vector& minor); + + public: + StatsWatcher(); + + void update(); + void addListener(StatsListener* listener); + void removeListener(StatsListener* listener); + + void watchActor(const MWWorld::Ptr& ptr); + MWWorld::Ptr getWatchedActor() const { return mWatched; } + }; +} + +#endif diff --git a/apps/openmw/mwgui/statswindow.hpp b/apps/openmw/mwgui/statswindow.hpp index 7d1be6a155..1b6c607932 100644 --- a/apps/openmw/mwgui/statswindow.hpp +++ b/apps/openmw/mwgui/statswindow.hpp @@ -1,16 +1,14 @@ #ifndef MWGUI_STATS_WINDOW_H #define MWGUI_STATS_WINDOW_H -#include "../mwmechanics/stat.hpp" +#include "statswatcher.hpp" #include "windowpinnablebase.hpp" -#include - namespace MWGui { class WindowManager; - class StatsWindow : public WindowPinnableBase, public NoDrop + class StatsWindow : public WindowPinnableBase, public NoDrop, public StatsListener { public: typedef std::map FactionList; @@ -20,24 +18,24 @@ namespace MWGui StatsWindow(DragAndDrop* drag); /// automatically updates all the data in the stats window, but only if it has changed. - void onFrame(float dt); + void onFrame(float dt) override; void setBar(const std::string& name, const std::string& tname, int val, int max); void setPlayerName(const std::string& playerName); /// Set value for the given ID. - void setValue (const std::string& id, const MWMechanics::AttributeValue& value); - void setValue (const std::string& id, const MWMechanics::DynamicStat& value); - void setValue (const std::string& id, const std::string& value); - void setValue (const std::string& id, int value); - void setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::SkillValue& value); + void setValue (const std::string& id, const MWMechanics::AttributeValue& value) override; + void setValue (const std::string& id, const MWMechanics::DynamicStat& value) override; + void setValue (const std::string& id, const std::string& value) override; + void setValue (const std::string& id, int value) override; + void setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::SkillValue& value) override; + void configureSkills(const SkillList& major, const SkillList& minor) override; - void configureSkills (const SkillList& major, const SkillList& minor); void setReputation (int reputation) { if (reputation != mReputation) mChanged = true; this->mReputation = reputation; } void setBounty (int bounty) { if (bounty != mBounty) mChanged = true; this->mBounty = bounty; } void updateSkillArea(); - virtual void onOpen() { onWindowResize(mMainWidget->castType()); } + virtual void onOpen() override { onWindowResize(mMainWidget->castType()); } private: void addSkills(const SkillList &skills, const std::string &titleId, const std::string &titleDefault, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2); @@ -72,8 +70,8 @@ namespace MWGui const int mMinFullWidth; protected: - virtual void onPinToggled(); - virtual void onTitleDoubleClicked(); + virtual void onPinToggled() override; + virtual void onTitleDoubleClicked() override; }; } #endif diff --git a/apps/openmw/mwgui/trainingwindow.cpp b/apps/openmw/mwgui/trainingwindow.cpp index e4e4bae5a5..d0176ece4b 100644 --- a/apps/openmw/mwgui/trainingwindow.cpp +++ b/apps/openmw/mwgui/trainingwindow.cpp @@ -14,6 +14,8 @@ #include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/actorutil.hpp" +#include + #include "tooltips.hpp" namespace @@ -40,6 +42,7 @@ namespace MWGui TrainingWindow::TrainingWindow() : WindowBase("openmw_trainingwindow.layout") , mTimeAdvancer(0.05f) + , mTrainingSkillBasedOnBaseSkill(Settings::Manager::getBool("trainers training skills based on base skill", "Game")) { getWidget(mTrainingOptions, "TrainingOptions"); getWidget(mCancelButton, "CancelButton"); @@ -76,9 +79,10 @@ namespace MWGui // NPC can train you in his best 3 skills std::vector< std::pair > skills; + MWMechanics::NpcStats const& actorStats(actor.getClass().getNpcStats(actor)); for (int i=0; i player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId)) return; - if (mPtr.getClass().getSkill(mPtr, skillId) <= pcStats.getSkill (skillId).getBase ()) + if (getSkillForTraining(mPtr.getClass().getNpcStats(mPtr), skillId) <= pcStats.getSkill(skillId).getBase()) { MWBase::Environment::get().getWindowManager()->messageBox ("#{sServiceTrainingWords}"); return; @@ -195,6 +199,13 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode(); } + float TrainingWindow::getSkillForTraining(const MWMechanics::NpcStats& stats, int skillId) const + { + if (mTrainingSkillBasedOnBaseSkill) + return stats.getSkill(skillId).getBase(); + return stats.getSkill(skillId).getModified(); + } + void TrainingWindow::onFrame(float dt) { checkReferenceAvailable(); diff --git a/apps/openmw/mwgui/trainingwindow.hpp b/apps/openmw/mwgui/trainingwindow.hpp index 2edad1f27c..955615516f 100644 --- a/apps/openmw/mwgui/trainingwindow.hpp +++ b/apps/openmw/mwgui/trainingwindow.hpp @@ -6,6 +6,11 @@ #include "timeadvancer.hpp" #include "waitdialog.hpp" +namespace MWMechanics +{ + class NpcStats; +} + namespace MWGui { @@ -35,12 +40,17 @@ namespace MWGui void onTrainingProgressChanged(int cur, int total); void onTrainingFinished(); + // Retrieve the base skill value if the setting 'training skills based on base skill' is set; + // otherwise returns the modified skill + float getSkillForTraining(const MWMechanics::NpcStats& stats, int skillId) const; + MyGUI::Widget* mTrainingOptions; MyGUI::Button* mCancelButton; MyGUI::TextBox* mPlayerGold; WaitDialogProgressBar mProgressBar; TimeAdvancer mTimeAdvancer; + bool mTrainingSkillBasedOnBaseSkill; //corresponds to the setting 'training skills based on base skill' }; } diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index e1f7a4fbf3..02b42c9cde 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -1,6 +1,8 @@ #include "windowmanagerimp.hpp" #include +#include +#include #include @@ -272,6 +274,8 @@ namespace MWGui mVideoWrapper = new SDLUtil::VideoWrapper(window, viewer); mVideoWrapper->setGammaContrast(Settings::Manager::getFloat("gamma", "Video"), Settings::Manager::getFloat("contrast", "Video")); + + mStatsWatcher.reset(new StatsWatcher()); } void WindowManager::loadUserFonts() @@ -466,6 +470,10 @@ namespace MWGui // Set up visibility updateVisible(); + + mStatsWatcher->addListener(mHud); + mStatsWatcher->addListener(mStatsWindow); + mStatsWatcher->addListener(mCharGen); } int WindowManager::getFontHeight() const @@ -478,8 +486,11 @@ namespace MWGui if (newgame) { disallowAll(); + + mStatsWatcher->removeListener(mCharGen); delete mCharGen; mCharGen = new CharacterCreation(mViewer->getSceneData()->asGroup(), mResourceSystem); + mStatsWatcher->addListener(mCharGen); } else allow(GW_ALL); @@ -489,6 +500,8 @@ namespace MWGui { try { + mStatsWatcher.reset(); + mKeyboardNavigation.reset(); MyGUI::LanguageManager::getInstance().eventRequestTag.clear(); @@ -649,58 +662,11 @@ namespace MWGui } } - void WindowManager::setValue (const std::string& id, const MWMechanics::AttributeValue& value) - { - mStatsWindow->setValue (id, value); - mCharGen->setValue(id, value); - } - - void WindowManager::setValue (int parSkill, const MWMechanics::SkillValue& value) - { - /// \todo Don't use the skill enum as a parameter type (we will have to drop it anyway, once we - /// allow custom skills. - mStatsWindow->setValue(static_cast (parSkill), value); - mCharGen->setValue(static_cast (parSkill), value); - } - - void WindowManager::setValue (const std::string& id, const MWMechanics::DynamicStat& value) - { - mStatsWindow->setValue (id, value); - mHud->setValue (id, value); - mCharGen->setValue(id, value); - } - - void WindowManager::setValue (const std::string& id, const std::string& value) - { - mStatsWindow->setValue (id, value); - } - - void WindowManager::setValue (const std::string& id, int value) - { - mStatsWindow->setValue (id, value); - } - void WindowManager::setDrowningTimeLeft (float time, float maxTime) { mHud->setDrowningTimeLeft(time, maxTime); } - void WindowManager::setPlayerClass (const ESM::Class &class_) - { - mStatsWindow->setValue("class", class_.mName); - } - - void WindowManager::configureSkills (const SkillList& major, const SkillList& minor) - { - mStatsWindow->configureSkills (major, minor); - mCharGen->configureSkills(major, minor); - } - - void WindowManager::updateSkillArea() - { - mStatsWindow->updateSkillArea(); - } - void WindowManager::removeDialog(Layout*dialog) { if (!dialog) @@ -751,7 +717,7 @@ namespace MWGui MWBase::Environment::get().getInputManager()->update(dt, true, false); if (!mWindowVisible) - OpenThreads::Thread::microSleep(5000); + std::this_thread::sleep_for(std::chrono::milliseconds(5)); else { mViewer->eventTraversal(); @@ -904,7 +870,9 @@ namespace MWGui if (mCharGen) mCharGen->onFrame(frameDuration); - updateActivatedQuickKey (); + updateActivatedQuickKey(); + + mStatsWatcher->update(); cleanupGarbage(); } @@ -1788,7 +1756,7 @@ namespace MWGui if (!mWindowVisible) { mVideoWidget->pause(); - OpenThreads::Thread::microSleep(5000); + std::this_thread::sleep_for(std::chrono::milliseconds(5)); } else { @@ -2210,4 +2178,14 @@ namespace MWGui for (unsigned int i=0; isetVisible(visible); } + + void WindowManager::watchActor(const MWWorld::Ptr& ptr) + { + mStatsWatcher->watchActor(ptr); + } + + MWWorld::Ptr WindowManager::getWatchedActor() const + { + return mStatsWatcher->getWatchedActor(); + } } diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 42750705d1..a8cb0b10ab 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -13,13 +13,12 @@ #include "../mwbase/windowmanager.hpp" -#include "../mwworld/ptr.hpp" - #include #include #include #include "mapwindow.hpp" +#include "statswatcher.hpp" #include "textcolours.hpp" #include @@ -195,22 +194,11 @@ namespace MWGui virtual void setConsoleSelectedObject(const MWWorld::Ptr& object); - ///< Set value for the given ID. - virtual void setValue (const std::string& id, const MWMechanics::AttributeValue& value); - virtual void setValue (int parSkill, const MWMechanics::SkillValue& value); - virtual void setValue (const std::string& id, const MWMechanics::DynamicStat& value); - virtual void setValue (const std::string& id, const std::string& value); - virtual void setValue (const std::string& id, int value); - /// Set time left for the player to start drowning (update the drowning bar) /// @param time time left to start drowning /// @param maxTime how long we can be underwater (in total) until drowning starts virtual void setDrowningTimeLeft (float time, float maxTime); - virtual void setPlayerClass (const ESM::Class &class_); ///< set current class of player - virtual void configureSkills (const SkillList& major, const SkillList& minor); ///< configure skill groups, each set contains the skill ID for that group. - virtual void updateSkillArea(); ///< update display of skills, factions, birth sign, reputation and bounty - virtual void changeCell(const MWWorld::CellStore* cell); ///< change the active cell virtual void setFocusObject(const MWWorld::Ptr& focus); @@ -298,6 +286,9 @@ namespace MWGui virtual void windowClosed(); virtual bool isWindowVisible(); + virtual void watchActor(const MWWorld::Ptr& ptr); + virtual MWWorld::Ptr getWatchedActor() const; + virtual void executeInConsole (const std::string& path); virtual void enableRest() { mRestAllowed = true; } @@ -400,6 +391,7 @@ namespace MWGui osgViewer::Viewer* mViewer; std::unique_ptr mFontLoader; + std::unique_ptr mStatsWatcher; bool mConsoleOnlyScripts; diff --git a/apps/openmw/mwinput/bindingsmanager.cpp b/apps/openmw/mwinput/bindingsmanager.cpp index 11f714205d..9d77ec99cf 100644 --- a/apps/openmw/mwinput/bindingsmanager.cpp +++ b/apps/openmw/mwinput/bindingsmanager.cpp @@ -226,6 +226,11 @@ namespace MWInput } } + void BindingsManager::setJoystickDeadZone(float deadZone) + { + mInputBinder->setJoystickDeadZone(deadZone); + } + float BindingsManager::getActionValue (int id) const { return mInputBinder->getChannel(id)->getValue(); diff --git a/apps/openmw/mwinput/bindingsmanager.hpp b/apps/openmw/mwinput/bindingsmanager.hpp index 35b26c877c..7a44a1a335 100644 --- a/apps/openmw/mwinput/bindingsmanager.hpp +++ b/apps/openmw/mwinput/bindingsmanager.hpp @@ -36,6 +36,8 @@ namespace MWInput void setPlayerControlsEnabled(bool enabled); + void setJoystickDeadZone(float deadZone); + bool isLeftOrRightButton(int action, bool joystick) const; bool actionIsActive(int id) const; diff --git a/apps/openmw/mwinput/controllermanager.cpp b/apps/openmw/mwinput/controllermanager.cpp index 3b1f587ce7..b0e769cc6f 100644 --- a/apps/openmw/mwinput/controllermanager.cpp +++ b/apps/openmw/mwinput/controllermanager.cpp @@ -72,6 +72,10 @@ namespace MWInput float uiScale = Settings::Manager::getFloat("scaling factor", "GUI"); if (uiScale != 0.f) mInvUiScalingFactor = 1.f / uiScale; + + float deadZoneRadius = Settings::Manager::getFloat("joystick dead zone", "Input"); + deadZoneRadius = std::min(std::max(deadZoneRadius, 0.0f), 0.5f); + mBindingsManager->setJoystickDeadZone(deadZoneRadius); } void ControllerManager::processChangedSettings(const Settings::CategorySettingVector& changed) @@ -100,10 +104,9 @@ namespace MWInput // game mode does not move the position of the GUI cursor float xMove = xAxis * dt * 1500.0f * mInvUiScalingFactor * mGamepadCursorSpeed; float yMove = yAxis * dt * 1500.0f * mInvUiScalingFactor * mGamepadCursorSpeed; - if (xMove != 0 || yMove != 0 || zAxis != 0) + float mouseWheelMove = -zAxis * dt * 1500.0f; + if (xMove != 0 || yMove != 0 || mouseWheelMove != 0) { - int mouseWheelMove = static_cast(-zAxis * dt * 1500.0f); - mMouseManager->injectMouseMove(xMove, yMove, mouseWheelMove); mMouseManager->warpMouse(); MWBase::Environment::get().getWindowManager()->setCursorActive(true); diff --git a/apps/openmw/mwinput/mousemanager.cpp b/apps/openmw/mwinput/mousemanager.cpp index df38868e1b..3fef623454 100644 --- a/apps/openmw/mwinput/mousemanager.cpp +++ b/apps/openmw/mwinput/mousemanager.cpp @@ -229,10 +229,10 @@ namespace MWInput bool MouseManager::injectMouseButtonRelease(Uint8 button) { - return MyGUI::InputManager::getInstance().injectMousePress(static_cast(mGuiCursorX), static_cast(mGuiCursorY), sdlButtonToMyGUI(button)); + return MyGUI::InputManager::getInstance().injectMouseRelease(static_cast(mGuiCursorX), static_cast(mGuiCursorY), sdlButtonToMyGUI(button)); } - void MouseManager::injectMouseMove(int xMove, int yMove, int mouseWheelMove) + void MouseManager::injectMouseMove(float xMove, float yMove, float mouseWheelMove) { mGuiCursorX += xMove; mGuiCursorY += yMove; @@ -242,7 +242,7 @@ namespace MWInput mGuiCursorX = std::max(0.f, std::min(mGuiCursorX, float(viewSize.width - 1))); mGuiCursorY = std::max(0.f, std::min(mGuiCursorY, float(viewSize.height - 1))); - MyGUI::InputManager::getInstance().injectMouseMove(static_cast(mGuiCursorX), static_cast(mGuiCursorY), mMouseWheel); + MyGUI::InputManager::getInstance().injectMouseMove(static_cast(mGuiCursorX), static_cast(mGuiCursorY), static_cast(mMouseWheel)); } void MouseManager::warpMouse() diff --git a/apps/openmw/mwinput/mousemanager.hpp b/apps/openmw/mwinput/mousemanager.hpp index 0f523591a4..f3d16cd809 100644 --- a/apps/openmw/mwinput/mousemanager.hpp +++ b/apps/openmw/mwinput/mousemanager.hpp @@ -32,7 +32,7 @@ namespace MWInput bool injectMouseButtonPress(Uint8 button); bool injectMouseButtonRelease(Uint8 button); - void injectMouseMove(int xMove, int yMove, int mouseWheelMove); + void injectMouseMove(float xMove, float yMove, float mouseWheelMove); void warpMouse(); void setMouseLookEnabled(bool enabled) { mMouseLookEnabled = enabled; } diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index b82d089642..93b0e0a89d 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1966,13 +1966,17 @@ namespace MWMechanics } else if (killResult == CharacterController::Result_DeathAnimJustFinished) { + bool isPlayer = iter->first == getPlayer(); notifyDied(iter->first); // Reset magic effects and recalculate derived effects // One case where we need this is to make sure bound items are removed upon death stats.modifyMagicEffects(MWMechanics::MagicEffects()); stats.getActiveSpells().clear(); - stats.getSpells().clear(); + + if (!isPlayer) + stats.getSpells().clear(); + // Make sure spell effects are removed purgeSpellEffects(stats.getActorId()); @@ -1981,7 +1985,7 @@ namespace MWMechanics if (iter->first.getClass().isNpc()) calculateNpcStatModifiers(iter->first, 0); - if( iter->first == getPlayer()) + if (isPlayer) { //player's death animation is over MWBase::Environment::get().getStateManager()->askLoadRecent(); diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index da862b7eab..aed6388955 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1914,6 +1914,7 @@ void CharacterController::update(float duration, bool animationOnly) mTimeUntilWake -= duration; bool isPlayer = mPtr == MWMechanics::getPlayer(); + bool isFirstPersonPlayer = isPlayer && MWBase::Environment::get().getWorld()->isFirstPerson(); bool godmode = isPlayer && MWBase::Environment::get().getWorld()->getGodModeState(); float scale = mPtr.getCellRef().getScale(); @@ -1977,7 +1978,7 @@ void CharacterController::update(float duration, bool animationOnly) float effectiveRotation = rot.z(); static const bool turnToMovementDirection = Settings::Manager::getBool("turn to movement direction", "Game"); - if (turnToMovementDirection && !(isPlayer && MWBase::Environment::get().getWorld()->isFirstPerson())) + if (turnToMovementDirection && !isFirstPersonPlayer) { float targetMovementAngle = vec.y() >= 0 ? std::atan2(-vec.x(), vec.y()) : std::atan2(vec.x(), -vec.y()); movementSettings.mIsStrafing = (stats.getDrawState() != MWMechanics::DrawState_Nothing || inwater) @@ -2206,8 +2207,7 @@ void CharacterController::update(float duration, bool animationOnly) // It seems only bipedal actors use turning animations. // Also do not use turning animations in the first-person view and when sneaking. - bool isFirstPlayer = isPlayer && MWBase::Environment::get().getWorld()->isFirstPerson(); - if (!sneak && jumpstate == JumpState_None && !isFirstPlayer && mPtr.getClass().isBipedal(mPtr)) + if (!sneak && jumpstate == JumpState_None && !isFirstPersonPlayer && mPtr.getClass().isBipedal(mPtr)) { if(effectiveRotation > rotationThreshold) movestate = inwater ? CharState_SwimTurnRight : CharState_TurnRight; @@ -2231,6 +2231,26 @@ void CharacterController::update(float duration, bool animationOnly) sndMgr->playSound3D(mPtr, sound, 1.f, 1.f, MWSound::Type::Foot, MWSound::PlayMode::NoPlayerLocal); } + if (turnToMovementDirection) + { + float targetSwimmingPitch; + if (inwater && vec.y() != 0 && !isFirstPersonPlayer && !movementSettings.mIsStrafing) + targetSwimmingPitch = -mPtr.getRefData().getPosition().rot[0]; + else + targetSwimmingPitch = 0; + float maxSwimPitchDelta = 3.0f * duration; + float swimmingPitch = mAnimation->getBodyPitchRadians(); + swimmingPitch += osg::clampBetween(targetSwimmingPitch - swimmingPitch, -maxSwimPitchDelta, maxSwimPitchDelta); + mAnimation->setBodyPitchRadians(swimmingPitch); + } + if (inwater && isPlayer && !isFirstPersonPlayer) + { + static const float swimUpwardCoef = Settings::Manager::getFloat("swim upward coef", "Game"); + static const float swimForwardCoef = sqrtf(1.0f - swimUpwardCoef * swimUpwardCoef); + vec.z() = std::abs(vec.y()) * swimUpwardCoef; + vec.y() *= swimForwardCoef; + } + // Player can not use smooth turning as NPCs, so we play turning animation a bit to avoid jittering if (isPlayer) { @@ -2368,8 +2388,21 @@ void CharacterController::update(float duration, bool animationOnly) moved.y() *= scale; // Ensure we're moving in generally the right direction... - if(speed > 0.f && (movement - moved).length2() * 4 > moved.length2()) - moved = movement; + if(speed > 0.f) + { + float l = moved.length(); + if (std::abs(movement.x() - moved.x()) > std::abs(moved.x()) / 2 || + std::abs(movement.y() - moved.y()) > std::abs(moved.y()) / 2 || + std::abs(movement.z() - moved.z()) > std::abs(moved.z()) / 2) + { + moved = movement; + // For some creatures getSpeed doesn't work, so we adjust speed to the animation. + // TODO: Fix Creature::getSpeed. + float newLength = moved.length(); + if (newLength > 0 && !cls.isNpc()) + moved *= (l / newLength); + } + } if (mFloatToSurface && cls.isActor() && cls.getCreatureStats(mPtr).isDead() && cls.canSwim(mPtr)) moved.z() = 1.0; diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index d7da4ebb72..fcae60c584 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -236,10 +236,8 @@ namespace MWMechanics invStore.autoEquip(ptr); } - // mWatchedTimeToStartDrowning = -1 for correct drowning state check, - // if stats.getTimeToStartDrowning() == 0 already on game start MechanicsManager::MechanicsManager() - : mWatchedLevel(-1), mWatchedTimeToStartDrowning(-1), mWatchedStatsEmpty (true), mUpdatePlayer (true), mClassSelected (false), + : mUpdatePlayer (true), mClassSelected (false), mRaceSelected (false), mAI(true) { //buildPlayer no longer here, needs to be done explicitly after all subsystems are up and running @@ -261,16 +259,16 @@ namespace MWMechanics void MechanicsManager::remove(const MWWorld::Ptr& ptr) { - if(ptr == mWatched) - mWatched = MWWorld::Ptr(); + if(ptr == MWBase::Environment::get().getWindowManager()->getWatchedActor()) + MWBase::Environment::get().getWindowManager()->watchActor(MWWorld::Ptr()); mActors.removeActor(ptr); mObjects.removeObject(ptr); } void MechanicsManager::updateCell(const MWWorld::Ptr &old, const MWWorld::Ptr &ptr) { - if(old == mWatched) - mWatched = ptr; + if(old == MWBase::Environment::get().getWindowManager()->getWatchedActor()) + MWBase::Environment::get().getWindowManager()->watchActor(ptr); if(ptr.getClass().isActor()) mActors.updateActor(old, ptr); @@ -278,19 +276,12 @@ namespace MWMechanics mObjects.updateObject(old, ptr); } - void MechanicsManager::drop(const MWWorld::CellStore *cellStore) { - mActors.dropActors(cellStore, mWatched); + mActors.dropActors(cellStore, getPlayer()); mObjects.dropObjects(cellStore); } - - void MechanicsManager::watchActor(const MWWorld::Ptr& ptr) - { - mWatched = ptr; - } - void MechanicsManager::restoreStatsAfterCorprus(const MWWorld::Ptr& actor, const std::string& sourceId) { auto& stats = actor.getClass().getCreatureStats (actor); @@ -311,133 +302,10 @@ namespace MWMechanics void MechanicsManager::update(float duration, bool paused) { - if(!mWatched.isEmpty()) - { - MWBase::WindowManager *winMgr = MWBase::Environment::get().getWindowManager(); - const MWMechanics::NpcStats &stats = mWatched.getClass().getNpcStats(mWatched); - for(int i = 0;i < ESM::Attribute::Length;++i) - { - if(stats.getAttribute(i) != mWatchedAttributes[i] || mWatchedStatsEmpty) - { - std::stringstream attrname; - attrname << "AttribVal"<<(i+1); - - mWatchedAttributes[i] = stats.getAttribute(i); - winMgr->setValue(attrname.str(), stats.getAttribute(i)); - } - } - - if(stats.getHealth() != mWatchedHealth || mWatchedStatsEmpty) - { - static const std::string hbar("HBar"); - mWatchedHealth = stats.getHealth(); - winMgr->setValue(hbar, stats.getHealth()); - } - if(stats.getMagicka() != mWatchedMagicka || mWatchedStatsEmpty) - { - static const std::string mbar("MBar"); - mWatchedMagicka = stats.getMagicka(); - winMgr->setValue(mbar, stats.getMagicka()); - } - if(stats.getFatigue() != mWatchedFatigue || mWatchedStatsEmpty) - { - static const std::string fbar("FBar"); - mWatchedFatigue = stats.getFatigue(); - winMgr->setValue(fbar, stats.getFatigue()); - } - - float timeToDrown = stats.getTimeToStartDrowning(); - - if(timeToDrown != mWatchedTimeToStartDrowning) - { - static const float fHoldBreathTime = MWBase::Environment::get().getWorld()->getStore().get() - .find("fHoldBreathTime")->mValue.getFloat(); - - mWatchedTimeToStartDrowning = timeToDrown; - - if(timeToDrown >= fHoldBreathTime || timeToDrown == -1.0) // -1.0 is a special value during initialization - winMgr->setDrowningBarVisibility(false); - else - { - winMgr->setDrowningBarVisibility(true); - winMgr->setDrowningTimeLeft(stats.getTimeToStartDrowning(), fHoldBreathTime); - } - } - - //Loop over ESM::Skill::SkillEnum - for(int i = 0; i < ESM::Skill::Length; ++i) - { - if(stats.getSkill(i) != mWatchedSkills[i] || mWatchedStatsEmpty) - { - mWatchedSkills[i] = stats.getSkill(i); - winMgr->setValue((ESM::Skill::SkillEnum)i, stats.getSkill(i)); - } - } - - if(stats.getLevel() != mWatchedLevel) - { - mWatchedLevel = stats.getLevel(); - winMgr->setValue("level", mWatchedLevel); - } - - mWatchedStatsEmpty = false; - - // Update the equipped weapon icon - MWWorld::InventoryStore& inv = mWatched.getClass().getInventoryStore(mWatched); - MWWorld::ContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); - if (weapon == inv.end()) - winMgr->unsetSelectedWeapon(); - else - winMgr->setSelectedWeapon(*weapon); - - // Update the selected spell icon - MWWorld::ContainerStoreIterator enchantItem = inv.getSelectedEnchantItem(); - if (enchantItem != inv.end()) - winMgr->setSelectedEnchantItem(*enchantItem); - else - { - const std::string& spell = winMgr->getSelectedSpell(); - if (!spell.empty()) - winMgr->setSelectedSpell(spell, int(getSpellSuccessChance(spell, mWatched))); - else - winMgr->unsetSelectedSpell(); - } - - } - if (mUpdatePlayer) { - MWBase::World *world = MWBase::Environment::get().getWorld(); - - // basic player profile; should not change anymore after the creation phase is finished. - MWBase::WindowManager *winMgr = - MWBase::Environment::get().getWindowManager(); - - const ESM::NPC *player = - world->getPlayerPtr().get()->mBase; - - const ESM::Race *race = - world->getStore().get().find(player->mRace); - const ESM::Class *cls = - world->getStore().get().find(player->mClass); - - winMgr->setValue ("name", player->mName); - winMgr->setValue ("race", race->mName); - winMgr->setValue ("class", cls->mName); - mUpdatePlayer = false; - MWBase::WindowManager::SkillList majorSkills (5); - MWBase::WindowManager::SkillList minorSkills (5); - - for (int i=0; i<5; ++i) - { - minorSkills[i] = cls->mData.mSkills[i][0]; - majorSkills[i] = cls->mData.mSkills[i][1]; - } - - winMgr->configureSkills (majorSkills, minorSkills); - // HACK? The player has been changed, so a new Animation object may // have been made for them. Make sure they're properly updated. MWWorld::Ptr ptr = getPlayer(); @@ -517,7 +385,7 @@ namespace MWMechanics int MechanicsManager::getHoursToRest() const { - return mActors.getHoursToRest(mWatched); + return mActors.getHoursToRest(getPlayer()); } void MechanicsManager::setPlayerName (const std::string& name) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 86bc4c720b..c546497bb8 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -21,20 +21,6 @@ namespace MWMechanics { class MechanicsManager : public MWBase::MechanicsManager { - MWWorld::Ptr mWatched; - - AttributeValue mWatchedAttributes[8]; - SkillValue mWatchedSkills[27]; - - DynamicStat mWatchedHealth; - DynamicStat mWatchedMagicka; - DynamicStat mWatchedFatigue; - - int mWatchedLevel; - - float mWatchedTimeToStartDrowning; - - bool mWatchedStatsEmpty; bool mUpdatePlayer; bool mClassSelected; bool mRaceSelected; @@ -68,10 +54,6 @@ namespace MWMechanics virtual void drop(const MWWorld::CellStore *cellStore) override; ///< Deregister all objects in the given cell. - virtual void watchActor(const MWWorld::Ptr& ptr) override; - ///< On each update look for changes in a previously registered actor and update the - /// GUI accordingly. - virtual void update (float duration, bool paused) override; ///< Update objects /// diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 69136bac31..3835e26de8 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -623,6 +623,7 @@ namespace MWRender , mHeadPitchRadians(0.f) , mUpperBodyYawRadians(0.f) , mLegsYawRadians(0.f) + , mBodyPitchRadians(0.f) , mHasMagicEffects(false) , mAlpha(1.f) { @@ -1340,11 +1341,11 @@ namespace MWRender float yawOffset = 0; if (mRootController) { - bool enable = std::abs(mLegsYawRadians) > epsilon; + bool enable = std::abs(mLegsYawRadians) > epsilon || std::abs(mBodyPitchRadians) > epsilon; mRootController->setEnabled(enable); if (enable) { - mRootController->setRotate(osg::Quat(mLegsYawRadians, osg::Vec3f(0,0,1))); + mRootController->setRotate(osg::Quat(mLegsYawRadians, osg::Vec3f(0,0,1)) * osg::Quat(mBodyPitchRadians, osg::Vec3f(1,0,0))); yawOffset = mLegsYawRadians; } } @@ -1485,6 +1486,8 @@ namespace MWRender { if (mLightListCallback) mObjectRoot->removeCullCallback(mLightListCallback); + if (mTransparencyUpdater) + mObjectRoot->removeCullCallback(mTransparencyUpdater); previousStateset = mObjectRoot->getStateSet(); mObjectRoot->getParent(0)->removeChild(mObjectRoot); } @@ -1576,6 +1579,8 @@ namespace MWRender if (!mLightListCallback) mLightListCallback = new SceneUtil::LightListCallback; mObjectRoot->addCullCallback(mLightListCallback); + if (mTransparencyUpdater) + mObjectRoot->addCullCallback(mTransparencyUpdater); } osg::Group* Animation::getObjectRoot() diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 564952a905..a04a3f999b 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -273,6 +273,7 @@ protected: float mHeadPitchRadians; float mUpperBodyYawRadians; float mLegsYawRadians; + float mBodyPitchRadians; RotateController* addRotateController(std::string bone); @@ -489,6 +490,8 @@ public: virtual void setLegsYawRadians(float v) { mLegsYawRadians = v; } virtual float getUpperBodyYawRadians() const { return mUpperBodyYawRadians; } virtual float getLegsYawRadians() const { return mLegsYawRadians; } + virtual void setBodyPitchRadians(float v) { mBodyPitchRadians = v; } + virtual float getBodyPitchRadians() const { return mBodyPitchRadians; } virtual void setAccurateAiming(bool enabled) {} virtual bool canBeHarvested() const { return false; } diff --git a/apps/openmw/mwrender/camera.cpp b/apps/openmw/mwrender/camera.cpp index 148aa33ed0..09ad404287 100644 --- a/apps/openmw/mwrender/camera.cpp +++ b/apps/openmw/mwrender/camera.cpp @@ -3,6 +3,7 @@ #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" @@ -56,7 +57,7 @@ namespace MWRender mFurthest(800.f), mIsNearest(false), mHeight(124.f), - mBaseCameraDistance(192.f), + mBaseCameraDistance(Settings::Manager::getFloat("third person camera distance", "Camera")), mVanityToggleQueued(false), mVanityToggleQueuedValue(false), mViewModeToggleQueued(false), @@ -380,7 +381,7 @@ namespace MWRender return mCameraDistance; } - void Camera::setBaseCameraDistance(float dist, bool adjust) + void Camera::updateBaseCameraDistance(float dist, bool adjust) { if(mFirstPersonView && !mPreviewMode && !mVanity.enabled) return; @@ -407,7 +408,10 @@ namespace MWRender if (mVanity.enabled || mPreviewMode) mPreviewCam.offset = dist; else if (!mFirstPersonView) + { mBaseCameraDistance = dist; + Settings::Manager::setFloat("third person camera distance", "Camera", dist); + } setCameraDistance(); } diff --git a/apps/openmw/mwrender/camera.hpp b/apps/openmw/mwrender/camera.hpp index c04bf31c24..6398f48252 100644 --- a/apps/openmw/mwrender/camera.hpp +++ b/apps/openmw/mwrender/camera.hpp @@ -119,7 +119,7 @@ namespace MWRender /// Set base camera distance for current mode. Don't work on 1st person view. /// \param adjust Indicates should distance be adjusted or set. - void setBaseCameraDistance(float dist, bool adjust = false); + void updateBaseCameraDistance(float dist, bool adjust = false); /// Set camera distance for current mode. Don't work on 1st person view. /// \param adjust Indicates should distance be adjusted or set. diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index 8e7f306873..489a7a9871 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -273,7 +273,7 @@ osg::Vec3f CreatureWeaponAnimation::runAnimation(float duration) { osg::Vec3f ret = Animation::runAnimation(duration); - WeaponAnimation::configureControllers(mPtr.getRefData().getPosition().rot[0]); + WeaponAnimation::configureControllers(mPtr.getRefData().getPosition().rot[0] + getBodyPitchRadians()); return ret; } diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 6e7669976e..468938d22d 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -349,6 +349,8 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, osg::ref_ptr par mPartPriorities[i] = 0; } + std::fill(mSounds.begin(), mSounds.end(), nullptr); + updateNpcBase(); } @@ -745,7 +747,7 @@ osg::Vec3f NpcAnimation::runAnimation(float timepassed) mFirstPersonNeckController->setOffset(mFirstPersonOffset); } - WeaponAnimation::configureControllers(mPtr.getRefData().getPosition().rot[0]); + WeaponAnimation::configureControllers(mPtr.getRefData().getPosition().rot[0] + getBodyPitchRadians()); return ret; } @@ -756,10 +758,10 @@ void NpcAnimation::removeIndividualPart(ESM::PartReferenceType type) mPartslots[type] = -1; mObjectParts[type].reset(); - if (!mSoundIds[type].empty() && !mSoundsDisabled) + if (mSounds[type] != nullptr && !mSoundsDisabled) { - MWBase::Environment::get().getSoundManager()->stopSound3D(mPtr, mSoundIds[type]); - mSoundIds[type].clear(); + MWBase::Environment::get().getSoundManager()->stopSound(mSounds[type]); + mSounds[type] = nullptr; } } @@ -838,10 +840,10 @@ bool NpcAnimation::addOrReplaceIndividualPart(ESM::PartReferenceType type, int g MWWorld::ConstContainerStoreIterator csi = inv.getSlot(group < 0 ? MWWorld::InventoryStore::Slot_Helmet : group); if (csi != inv.end()) { - mSoundIds[type] = csi->getClass().getSound(*csi); - if (!mSoundIds[type].empty()) + const auto soundId = csi->getClass().getSound(*csi); + if (!soundId.empty()) { - MWBase::Environment::get().getSoundManager()->playSound3D(mPtr, mSoundIds[type], + mSounds[type] = MWBase::Environment::get().getSoundManager()->playSound3D(mPtr, soundId, 1.0f, 1.0f, MWSound::Type::Sfx, MWSound::PlayMode::Loop ); } diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index 7f8d5434bf..7edf35a5cd 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -8,12 +8,19 @@ #include "actoranimation.hpp" #include "weaponanimation.hpp" +#include + namespace ESM { struct NPC; struct BodyPart; } +namespace MWSound +{ + class Sound; +} + namespace MWRender { @@ -40,7 +47,7 @@ private: // Bounded Parts PartHolderPtr mObjectParts[ESM::PRT_Count]; - std::string mSoundIds[ESM::PRT_Count]; + std::array mSounds; const ESM::NPC *mNpc; std::string mHeadModel; diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index 4810b2385b..fd12ad3677 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -428,7 +428,7 @@ namespace MWRender if (activeGrid) { - OpenThreads::ScopedLock lock(mRefTrackerMutex); + std::lock_guard lock(mRefTrackerMutex); for (auto ref : getRefTracker().mBlacklist) refs.erase(ref); } @@ -464,7 +464,7 @@ namespace MWRender float dSqr = (viewPoint - pos).length2(); if (!activeGrid) { - OpenThreads::ScopedLock lock(mSizeCacheMutex); + std::lock_guard lock(mSizeCacheMutex); SizeCache::iterator found = mSizeCache.find(pair.first); if (found != mSizeCache.end() && found->second < dSqr*minSize*minSize) continue; @@ -501,7 +501,7 @@ namespace MWRender } { - OpenThreads::ScopedLock lock(mRefTrackerMutex); + std::lock_guard lock(mRefTrackerMutex); if (getRefTracker().mDisabled.count(pair.first)) continue; } @@ -509,7 +509,7 @@ namespace MWRender float radius2 = cnode->getBound().radius2() * ref.mScale*ref.mScale; if (radius2 < dSqr*minSize*minSize && !activeGrid) { - OpenThreads::ScopedLock lock(mSizeCacheMutex); + std::lock_guard lock(mSizeCacheMutex); mSizeCache[pair.first] = radius2; continue; } @@ -685,7 +685,7 @@ namespace MWRender return false; { - OpenThreads::ScopedLock lock(mRefTrackerMutex); + std::lock_guard lock(mRefTrackerMutex); if (enabled && !getWritableRefTracker().mDisabled.erase(refnum)) return false; if (!enabled && !getWritableRefTracker().mDisabled.insert(refnum).second) return false; if (mRefTrackerLocked) return false; @@ -706,7 +706,7 @@ namespace MWRender return false; { - OpenThreads::ScopedLock lock(mRefTrackerMutex); + std::lock_guard lock(mRefTrackerMutex); if (!getWritableRefTracker().mBlacklist.insert(refnum).second) return false; if (mRefTrackerLocked) return false; } @@ -724,7 +724,7 @@ namespace MWRender void ObjectPaging::clear() { - OpenThreads::ScopedLock lock(mRefTrackerMutex); + std::lock_guard lock(mRefTrackerMutex); mRefTrackerNew.mDisabled.clear(); mRefTrackerNew.mBlacklist.clear(); mRefTrackerLocked = true; @@ -734,7 +734,7 @@ namespace MWRender { if (!mRefTrackerLocked) return false; { - OpenThreads::ScopedLock lock(mRefTrackerMutex); + std::lock_guard lock(mRefTrackerMutex); mRefTrackerLocked = false; if (mRefTracker == mRefTrackerNew) return false; diff --git a/apps/openmw/mwrender/objectpaging.hpp b/apps/openmw/mwrender/objectpaging.hpp index 9c2c54f655..2ca3020386 100644 --- a/apps/openmw/mwrender/objectpaging.hpp +++ b/apps/openmw/mwrender/objectpaging.hpp @@ -5,7 +5,7 @@ #include #include -#include +#include namespace Resource { @@ -58,7 +58,7 @@ namespace MWRender float mMinSizeMergeFactor; float mMinSizeCostMultiplier; - OpenThreads::Mutex mRefTrackerMutex; + std::mutex mRefTrackerMutex; struct RefTracker { std::set mDisabled; @@ -72,7 +72,7 @@ namespace MWRender const RefTracker& getRefTracker() const { return mRefTracker; } RefTracker& getWritableRefTracker() { return mRefTrackerLocked ? mRefTrackerNew : mRefTracker; } - OpenThreads::Mutex mSizeCacheMutex; + std::mutex mSizeCacheMutex; typedef std::map SizeCache; SizeCache mSizeCache; }; diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index fc78b6ebbc..d7549fc54e 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -2,6 +2,8 @@ #include #include +#include +#include #include #include @@ -709,24 +711,24 @@ namespace MWRender virtual void operator () (osg::RenderInfo& renderInfo) const { - OpenThreads::ScopedLock lock(mMutex); + std::lock_guard lock(mMutex); if (renderInfo.getState()->getFrameStamp()->getFrameNumber() >= mFrame) { mDone = true; - mCondition.signal(); + mCondition.notify_one(); } } void waitTillDone() { - OpenThreads::ScopedLock lock(mMutex); + std::unique_lock lock(mMutex); if (mDone) return; - mCondition.wait(&mMutex); + mCondition.wait(lock); } - mutable OpenThreads::Condition mCondition; - mutable OpenThreads::Mutex mMutex; + mutable std::condition_variable mCondition; + mutable std::mutex mMutex; mutable bool mDone; unsigned int mFrame; }; @@ -1338,7 +1340,7 @@ namespace MWRender if(mCamera->isNearest() && dist > 0.f) mCamera->toggleViewMode(); else if (override) - mCamera->setBaseCameraDistance(-dist / 120.f * 10, adjust); + mCamera->updateBaseCameraDistance(-dist / 120.f * 10, adjust); else mCamera->setCameraDistance(-dist / 120.f * 10, adjust); } @@ -1346,7 +1348,7 @@ namespace MWRender { mCamera->toggleViewMode(); if (override) - mCamera->setBaseCameraDistance(0.f, false); + mCamera->updateBaseCameraDistance(0.f, false); else mCamera->setCameraDistance(0.f, false); } @@ -1395,7 +1397,7 @@ namespace MWRender void RenderingManager::changeVanityModeScale(float factor) { if(mCamera->isVanityOrPreviewModeEnabled()) - mCamera->setBaseCameraDistance(-factor/120.f*10, true); + mCamera->updateBaseCameraDistance(-factor/120.f*10, true); } void RenderingManager::overrideFieldOfView(float val) diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index 5955187261..1606b29797 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -4,6 +4,10 @@ #include #include #include +#include +#include +#include +#include #include @@ -11,11 +15,6 @@ #include #include -#include -#include -#include -#include - #include "openal_output.hpp" #include "sound_decoder.hpp" #include "sound.hpp" @@ -309,31 +308,33 @@ const ALfloat OpenAL_SoundStream::sBufferLength = 0.125f; // // A background streaming thread (keeps active streams processed) // -struct OpenAL_Output::StreamThread : public OpenThreads::Thread { +struct OpenAL_Output::StreamThread +{ typedef std::vector StreamVec; StreamVec mStreams; std::atomic mQuitNow; - OpenThreads::Mutex mMutex; - OpenThreads::Condition mCondVar; + std::mutex mMutex; + std::condition_variable mCondVar; + std::thread mThread; StreamThread() : mQuitNow(false) + , mThread([this] { run(); }) { - start(); } ~StreamThread() { mQuitNow = true; mMutex.lock(); mMutex.unlock(); - mCondVar.broadcast(); - join(); + mCondVar.notify_all(); + mThread.join(); } // thread entry point - virtual void run() + void run() { - OpenThreads::ScopedLock lock(mMutex); + std::unique_lock lock(mMutex); while(!mQuitNow) { StreamVec::iterator iter = mStreams.begin(); @@ -345,30 +346,30 @@ struct OpenAL_Output::StreamThread : public OpenThreads::Thread { ++iter; } - mCondVar.wait(&mMutex, 50); + mCondVar.wait_for(lock, std::chrono::milliseconds(50)); } } void add(OpenAL_SoundStream *stream) { - OpenThreads::ScopedLock lock(mMutex); + std::lock_guard lock(mMutex); if(std::find(mStreams.begin(), mStreams.end(), stream) == mStreams.end()) { mStreams.push_back(stream); - mCondVar.broadcast(); + mCondVar.notify_all(); } } void remove(OpenAL_SoundStream *stream) { - OpenThreads::ScopedLock lock(mMutex); + std::lock_guard lock(mMutex); StreamVec::iterator iter = std::find(mStreams.begin(), mStreams.end(), stream); if(iter != mStreams.end()) mStreams.erase(iter); } void removeAll() { - OpenThreads::ScopedLock lock(mMutex); + std::lock_guard lock(mMutex); mStreams.clear(); } @@ -1341,7 +1342,7 @@ double OpenAL_Output::getStreamOffset(Stream *sound) { if(!sound->mHandle) return 0.0; OpenAL_SoundStream *stream = reinterpret_cast(sound->mHandle); - OpenThreads::ScopedLock lock(mStreamThread->mMutex); + std::lock_guard lock(mStreamThread->mMutex); return stream->getStreamOffset(); } @@ -1349,7 +1350,7 @@ float OpenAL_Output::getStreamLoudness(Stream *sound) { if(!sound->mHandle) return 0.0; OpenAL_SoundStream *stream = reinterpret_cast(sound->mHandle); - OpenThreads::ScopedLock lock(mStreamThread->mMutex); + std::lock_guard lock(mStreamThread->mMutex); return stream->getCurrentLoudness(); } @@ -1357,7 +1358,7 @@ bool OpenAL_Output::isStreamPlaying(Stream *sound) { if(!sound->mHandle) return false; OpenAL_SoundStream *stream = reinterpret_cast(sound->mHandle); - OpenThreads::ScopedLock lock(mStreamThread->mMutex); + std::lock_guard lock(mStreamThread->mMutex); return stream->isPlaying(); } diff --git a/apps/openmw/mwsound/regionsoundselector.cpp b/apps/openmw/mwsound/regionsoundselector.cpp new file mode 100644 index 0000000000..ac5f66721d --- /dev/null +++ b/apps/openmw/mwsound/regionsoundselector.cpp @@ -0,0 +1,71 @@ +#include "regionsoundselector.hpp" + +#include + +#include +#include + +#include "../mwbase/world.hpp" +#include "../mwworld/esmstore.hpp" + +namespace MWSound +{ + namespace + { + int addChance(int result, const ESM::Region::SoundRef &v) + { + return result + v.mChance; + } + } + + boost::optional RegionSoundSelector::getNextRandom(float duration, const std::string& regionName, + const MWBase::World& world) + { + mTimePassed += duration; + + if (mTimePassed < mTimeToNextEnvSound) + return {}; + + const float a = Misc::Rng::rollClosedProbability(); + // NOTE: We should use the "Minimum Time Between Environmental Sounds" and + // "Maximum Time Between Environmental Sounds" fallback settings here. + mTimeToNextEnvSound = 5.0f * a + 15.0f * (1.0f - a); + mTimePassed = 0; + + if (mLastRegionName != regionName) + { + mLastRegionName = regionName; + mSumChance = 0; + } + + const ESM::Region* const region = world.getStore().get().search(mLastRegionName); + + if (region == nullptr) + return {}; + + if (mSumChance == 0) + { + mSumChance = std::accumulate(region->mSoundList.begin(), region->mSoundList.end(), 0, addChance); + if (mSumChance == 0) + return {}; + } + + const int r = Misc::Rng::rollDice(mSumChance); + int pos = 0; + + const auto isSelected = [&] (const ESM::Region::SoundRef& sound) + { + if (r - pos < sound.mChance) + return true; + pos += sound.mChance; + return false; + }; + + const auto it = std::find_if(region->mSoundList.begin(), region->mSoundList.end(), isSelected); + + if (it == region->mSoundList.end()) + return {}; + + return it->mSound; + } +} diff --git a/apps/openmw/mwsound/regionsoundselector.hpp b/apps/openmw/mwsound/regionsoundselector.hpp new file mode 100644 index 0000000000..00a2d5ca85 --- /dev/null +++ b/apps/openmw/mwsound/regionsoundselector.hpp @@ -0,0 +1,29 @@ +#ifndef GAME_SOUND_REGIONSOUNDSELECTOR_H +#define GAME_SOUND_REGIONSOUNDSELECTOR_H + +#include + +#include + +namespace MWBase +{ + class World; +} + +namespace MWSound +{ + class RegionSoundSelector + { + public: + boost::optional getNextRandom(float duration, const std::string& regionName, + const MWBase::World& world); + + private: + float mTimeToNextEnvSound = 0.0f; + int mSumChance = 0; + std::string mLastRegionName; + float mTimePassed = 0.0; + }; +} + +#endif diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 162c176e6d..a4241fef22 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -30,22 +30,34 @@ namespace MWSound { + namespace + { + constexpr float sMinUpdateInterval = 1.0f / 30.0f; + + 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); } SoundManager::SoundManager(const VFS::Manager* vfs, bool useSound) : mVFS(vfs) , mOutput(new DEFAULT_OUTPUT(*this)) - , mMasterVolume(1.0f) - , mSFXVolume(1.0f) - , mMusicVolume(1.0f) - , mVoiceVolume(1.0f) - , mFootstepsVolume(1.0f) + , mWaterSoundUpdater(makeWaterSoundUpdaterSettings()) , mSoundBuffers(new SoundBufferList::element_type()) , mBufferCacheSize(0) - , mSounds(new std::deque()) - , mStreams(new std::deque()) - , mMusic(nullptr) , mListenerUnderwater(false) , mListenerPos(0,0,0) , mListenerDir(1,0,0) @@ -54,24 +66,6 @@ namespace MWSound , mNearWaterSound(nullptr) , mPlaybackPaused(false) { - mMasterVolume = Settings::Manager::getFloat("master volume", "Sound"); - mMasterVolume = std::min(std::max(mMasterVolume, 0.0f), 1.0f); - mSFXVolume = Settings::Manager::getFloat("sfx volume", "Sound"); - mSFXVolume = std::min(std::max(mSFXVolume, 0.0f), 1.0f); - mMusicVolume = Settings::Manager::getFloat("music volume", "Sound"); - mMusicVolume = std::min(std::max(mMusicVolume, 0.0f), 1.0f); - mVoiceVolume = Settings::Manager::getFloat("voice volume", "Sound"); - mVoiceVolume = std::min(std::max(mVoiceVolume, 0.0f), 1.0f); - 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; @@ -271,39 +265,17 @@ namespace MWSound return nullptr; } - Sound *SoundManager::getSoundRef() + SoundPtr SoundManager::getSoundRef() { - Sound *ret; - if(!mUnusedSounds.empty()) - { - ret = mUnusedSounds.back(); - mUnusedSounds.pop_back(); - } - else - { - mSounds->emplace_back(); - ret = &mSounds->back(); - } - return ret; + return mSounds.get(); } - Stream *SoundManager::getStreamRef() + StreamPtr SoundManager::getStreamRef() { - Stream *ret; - if(!mUnusedStreams.empty()) - { - ret = mUnusedStreams.back(); - mUnusedStreams.pop_back(); - } - else - { - mStreams->emplace_back(); - ret = &mStreams->back(); - } - return ret; + return mStreams.get(); } - Stream *SoundManager::playVoice(DecoderPtr decoder, const osg::Vec3f &pos, bool playlocal) + StreamPtr SoundManager::playVoice(DecoderPtr decoder, const osg::Vec3f &pos, bool playlocal) { MWBase::World* world = MWBase::Environment::get().getWorld(); static const float fAudioMinDistanceMult = world->getStore().get().find("fAudioMinDistanceMult")->mValue.getFloat(); @@ -315,57 +287,34 @@ namespace MWSound bool played; float basevol = volumeFromType(Type::Voice); - Stream *sound = getStreamRef(); + StreamPtr sound = getStreamRef(); if(playlocal) { sound->init(1.0f, basevol, 1.0f, PlayMode::NoEnv|Type::Voice|Play_2D); - played = mOutput->streamSound(decoder, sound, true); + played = mOutput->streamSound(decoder, sound.get(), true); } else { sound->init(pos, 1.0f, basevol, 1.0f, minDistance, maxDistance, PlayMode::Normal|Type::Voice|Play_3D); - played = mOutput->streamSound3D(decoder, sound, true); + played = mOutput->streamSound3D(decoder, sound.get(), true); } if(!played) - { - mUnusedStreams.push_back(sound); return nullptr; - } return sound; } // Gets the combined volume settings for the given sound type float SoundManager::volumeFromType(Type type) const { - float volume = mMasterVolume; - switch(type) - { - case Type::Sfx: - volume *= mSFXVolume; - break; - case Type::Voice: - volume *= mVoiceVolume; - break; - case Type::Foot: - volume *= mFootstepsVolume; - break; - case Type::Music: - volume *= mMusicVolume; - break; - case Type::Movie: - case Type::Mask: - break; - } - return volume; + return mVolumeSettings.getVolumeFromType(type); } void SoundManager::stopMusic() { if(mMusic) { - mOutput->finishStream(mMusic); - mUnusedStreams.push_back(mMusic); + mOutput->finishStream(mMusic.get()); mMusic = nullptr; } } @@ -385,7 +334,7 @@ namespace MWSound mMusic = getStreamRef(); mMusic->init(1.0f, volumeFromType(Type::Music), 1.0f, PlayMode::NoEnv|Type::Music|Play_2D); - mOutput->streamSound(decoder, mMusic); + mOutput->streamSound(decoder, mMusic.get()); } void SoundManager::advanceMusic(const std::string& filename) @@ -435,7 +384,7 @@ namespace MWSound bool SoundManager::isMusicPlaying() { - return mMusic && mOutput->isStreamPlaying(mMusic); + return mMusic && mOutput->isStreamPlaying(mMusic.get()); } void SoundManager::playPlaylist(const std::string &playlist) @@ -518,10 +467,10 @@ namespace MWSound const osg::Vec3f pos = world->getActorHeadTransform(ptr).getTrans(); stopSay(ptr); - Stream *sound = playVoice(decoder, pos, (ptr == MWMechanics::getPlayer())); + StreamPtr sound = playVoice(decoder, pos, (ptr == MWMechanics::getPlayer())); if(!sound) return; - mSaySoundsQueue.emplace(ptr, sound); + mSaySoundsQueue.emplace(ptr, std::move(sound)); } float SoundManager::getSaySoundLoudness(const MWWorld::ConstPtr &ptr) const @@ -529,7 +478,7 @@ namespace MWSound SaySoundMap::const_iterator snditer = mActiveSaySounds.find(ptr); if(snditer != mActiveSaySounds.end()) { - Stream *sound = snditer->second; + Stream *sound = snditer->second.get(); return mOutput->getStreamLoudness(sound); } @@ -549,10 +498,10 @@ namespace MWSound return; stopSay(MWWorld::ConstPtr()); - Stream *sound = playVoice(decoder, osg::Vec3f(), true); + StreamPtr sound = playVoice(decoder, osg::Vec3f(), true); if(!sound) return; - mActiveSaySounds.insert(std::make_pair(MWWorld::ConstPtr(), sound)); + mActiveSaySounds.emplace(MWWorld::ConstPtr(), std::move(sound)); } bool SoundManager::sayDone(const MWWorld::ConstPtr &ptr) const @@ -560,7 +509,7 @@ namespace MWSound SaySoundMap::const_iterator snditer = mActiveSaySounds.find(ptr); if(snditer != mActiveSaySounds.end()) { - if(mOutput->isStreamPlaying(snditer->second)) + if(mOutput->isStreamPlaying(snditer->second.get())) return false; return true; } @@ -572,7 +521,7 @@ namespace MWSound SaySoundMap::const_iterator snditer = mSaySoundsQueue.find(ptr); if(snditer != mSaySoundsQueue.end()) { - if(mOutput->isStreamPlaying(snditer->second)) + if(mOutput->isStreamPlaying(snditer->second.get())) return true; return false; } @@ -580,7 +529,7 @@ namespace MWSound snditer = mActiveSaySounds.find(ptr); if(snditer != mActiveSaySounds.end()) { - if(mOutput->isStreamPlaying(snditer->second)) + if(mOutput->isStreamPlaying(snditer->second.get())) return true; return false; } @@ -593,16 +542,14 @@ namespace MWSound SaySoundMap::iterator snditer = mSaySoundsQueue.find(ptr); if(snditer != mSaySoundsQueue.end()) { - mOutput->finishStream(snditer->second); - mUnusedStreams.push_back(snditer->second); + mOutput->finishStream(snditer->second.get()); mSaySoundsQueue.erase(snditer); } snditer = mActiveSaySounds.find(ptr); if(snditer != mActiveSaySounds.end()) { - mOutput->finishStream(snditer->second); - mUnusedStreams.push_back(snditer->second); + mOutput->finishStream(snditer->second.get()); mActiveSaySounds.erase(snditer); } } @@ -613,27 +560,24 @@ namespace MWSound if(!mOutput->isInitialized()) return nullptr; - Stream *track = getStreamRef(); + StreamPtr track = getStreamRef(); track->init(1.0f, volumeFromType(type), 1.0f, PlayMode::NoEnv|type|Play_2D); - if(!mOutput->streamSound(decoder, track)) - { - mUnusedStreams.push_back(track); + if(!mOutput->streamSound(decoder, track.get())) return nullptr; - } - mActiveTracks.insert( - std::lower_bound(mActiveTracks.begin(), mActiveTracks.end(), track), track - ); - return track; + Stream* result = track.get(); + const auto it = std::lower_bound(mActiveTracks.begin(), mActiveTracks.end(), track); + mActiveTracks.insert(it, std::move(track)); + return result; } void SoundManager::stopTrack(Stream *stream) { mOutput->finishStream(stream); - TrackList::iterator iter = std::lower_bound(mActiveTracks.begin(), mActiveTracks.end(), stream); - if(iter != mActiveTracks.end() && *iter == stream) + TrackList::iterator iter = std::lower_bound(mActiveTracks.begin(), mActiveTracks.end(), stream, + [] (const StreamPtr& lhs, Stream* rhs) { return lhs.get() < rhs; }); + if(iter != mActiveTracks.end() && iter->get() == stream) mActiveTracks.erase(iter); - mUnusedStreams.push_back(stream); } double SoundManager::getTrackTimeDelay(Stream *stream) @@ -642,7 +586,7 @@ namespace MWSound } - Sound *SoundManager::playSound(const std::string& soundId, float volume, float pitch, Type type, PlayMode mode, float offset) + Sound* SoundManager::playSound(const std::string& soundId, float volume, float pitch, Type type, PlayMode mode, float offset) { if(!mOutput->isInitialized()) return nullptr; @@ -653,13 +597,10 @@ namespace MWSound // Only one copy of given sound can be played at time, so stop previous copy stopSound(sfx, MWWorld::ConstPtr()); - Sound *sound = getSoundRef(); + SoundPtr sound = getSoundRef(); sound->init(volume * sfx->mVolume, volumeFromType(type), pitch, mode|type|Play_2D); - if(!mOutput->playSound(sound, sfx->mHandle, offset)) - { - mUnusedSounds.push_back(sound); + if(!mOutput->playSound(sound.get(), sfx->mHandle, offset)) return nullptr; - } if(sfx->mUses++ == 0) { @@ -667,8 +608,9 @@ namespace MWSound if(iter != mUnusedBuffers.end()) mUnusedBuffers.erase(iter); } - mActiveSounds[MWWorld::ConstPtr()].push_back(std::make_pair(sound, sfx)); - return sound; + Sound* result = sound.get(); + mActiveSounds[MWWorld::ConstPtr()].emplace_back(std::move(sound), sfx); + return result; } Sound *SoundManager::playSound3D(const MWWorld::ConstPtr &ptr, const std::string& soundId, @@ -678,35 +620,32 @@ namespace MWSound if(!mOutput->isInitialized()) return nullptr; + const osg::Vec3f objpos(ptr.getRefData().getPosition().asVec3()); + if ((mode & PlayMode::RemoveAtDistance) && (mListenerPos - objpos).length2() > 2000 * 2000) + return nullptr; + // Look up the sound in the ESM data Sound_Buffer *sfx = loadSound(Misc::StringUtils::lowerCase(soundId)); if(!sfx) return nullptr; - const osg::Vec3f objpos(ptr.getRefData().getPosition().asVec3()); - if((mode&PlayMode::RemoveAtDistance) && (mListenerPos-objpos).length2() > 2000*2000) - return nullptr; - // Only one copy of given sound can be played at time on ptr, so stop previous copy stopSound(sfx, ptr); bool played; - Sound *sound = getSoundRef(); + SoundPtr sound = getSoundRef(); if(!(mode&PlayMode::NoPlayerLocal) && ptr == MWMechanics::getPlayer()) { sound->init(volume * sfx->mVolume, volumeFromType(type), pitch, mode|type|Play_2D); - played = mOutput->playSound(sound, sfx->mHandle, offset); + played = mOutput->playSound(sound.get(), sfx->mHandle, offset); } else { sound->init(objpos, volume * sfx->mVolume, volumeFromType(type), pitch, sfx->mMinDist, sfx->mMaxDist, mode|type|Play_3D); - played = mOutput->playSound3D(sound, sfx->mHandle, offset); + played = mOutput->playSound3D(sound.get(), sfx->mHandle, offset); } if(!played) - { - mUnusedSounds.push_back(sound); return nullptr; - } if(sfx->mUses++ == 0) { @@ -714,8 +653,9 @@ namespace MWSound if(iter != mUnusedBuffers.end()) mUnusedBuffers.erase(iter); } - mActiveSounds[ptr].push_back(std::make_pair(sound, sfx)); - return sound; + Sound* result = sound.get(); + mActiveSounds[ptr].emplace_back(std::move(sound), sfx); + return result; } Sound *SoundManager::playSound3D(const osg::Vec3f& initialPos, const std::string& soundId, @@ -729,14 +669,11 @@ namespace MWSound Sound_Buffer *sfx = loadSound(Misc::StringUtils::lowerCase(soundId)); if(!sfx) return nullptr; - Sound *sound = getSoundRef(); + SoundPtr sound = getSoundRef(); sound->init(initialPos, volume * sfx->mVolume, volumeFromType(type), pitch, sfx->mMinDist, sfx->mMaxDist, mode|type|Play_3D); - if(!mOutput->playSound3D(sound, sfx->mHandle, offset)) - { - mUnusedSounds.push_back(sound); + if(!mOutput->playSound3D(sound.get(), sfx->mHandle, offset)) return nullptr; - } if(sfx->mUses++ == 0) { @@ -744,8 +681,9 @@ namespace MWSound if(iter != mUnusedBuffers.end()) mUnusedBuffers.erase(iter); } - mActiveSounds[MWWorld::ConstPtr()].push_back(std::make_pair(sound, sfx)); - return sound; + Sound* result = sound.get(); + mActiveSounds[MWWorld::ConstPtr()].emplace_back(std::move(sound), sfx); + return result; } void SoundManager::stopSound(Sound *sound) @@ -762,28 +700,17 @@ namespace MWSound for(SoundBufferRefPair &snd : snditer->second) { if(snd.second == sfx) - mOutput->finishSound(snd.first); + mOutput->finishSound(snd.first.get()); } } } - void SoundManager::stopSound(const std::string& soundId) - { - if(!mOutput->isInitialized()) - return; - - Sound_Buffer *sfx = loadSound(Misc::StringUtils::lowerCase(soundId)); - if (!sfx) return; - - stopSound(sfx, MWWorld::ConstPtr()); - } - void SoundManager::stopSound3D(const MWWorld::ConstPtr &ptr, const std::string& soundId) { if(!mOutput->isInitialized()) return; - Sound_Buffer *sfx = loadSound(Misc::StringUtils::lowerCase(soundId)); + Sound_Buffer *sfx = lookupSound(Misc::StringUtils::lowerCase(soundId)); if (!sfx) return; stopSound(sfx, ptr); @@ -795,14 +722,14 @@ namespace MWSound if(snditer != mActiveSounds.end()) { for(SoundBufferRefPair &snd : snditer->second) - mOutput->finishSound(snd.first); + mOutput->finishSound(snd.first.get()); } SaySoundMap::iterator sayiter = mSaySoundsQueue.find(ptr); if(sayiter != mSaySoundsQueue.end()) - mOutput->finishStream(sayiter->second); + mOutput->finishStream(sayiter->second.get()); sayiter = mActiveSaySounds.find(ptr); if(sayiter != mActiveSaySounds.end()) - mOutput->finishStream(sayiter->second); + mOutput->finishStream(sayiter->second.get()); } void SoundManager::stopSound(const MWWorld::CellStore *cell) @@ -812,20 +739,20 @@ namespace MWSound if(!snd.first.isEmpty() && snd.first != MWMechanics::getPlayer() && snd.first.getCell() == cell) { for(SoundBufferRefPair &sndbuf : snd.second) - mOutput->finishSound(sndbuf.first); + mOutput->finishSound(sndbuf.first.get()); } } for(SaySoundMap::value_type &snd : mSaySoundsQueue) { if(!snd.first.isEmpty() && snd.first != MWMechanics::getPlayer() && snd.first.getCell() == cell) - mOutput->finishStream(snd.second); + mOutput->finishStream(snd.second.get()); } for(SaySoundMap::value_type &snd : mActiveSaySounds) { if(!snd.first.isEmpty() && snd.first != MWMechanics::getPlayer() && snd.first.getCell() == cell) - mOutput->finishStream(snd.second); + mOutput->finishStream(snd.second.get()); } } @@ -835,7 +762,9 @@ namespace MWSound SoundMap::iterator snditer = mActiveSounds.find(ptr); if(snditer != mActiveSounds.end()) { - Sound_Buffer *sfx = loadSound(Misc::StringUtils::lowerCase(soundId)); + Sound_Buffer *sfx = lookupSound(Misc::StringUtils::lowerCase(soundId)); + if (sfx == nullptr) + return; for(SoundBufferRefPair &sndbuf : snditer->second) { if(sndbuf.second == sfx) @@ -852,7 +781,7 @@ namespace MWSound Sound_Buffer *sfx = lookupSound(Misc::StringUtils::lowerCase(soundId)); return std::find_if(snditer->second.cbegin(), snditer->second.cend(), [this,sfx](const SoundBufferRefPair &snd) -> bool - { return snd.second == sfx && mOutput->isSoundPlaying(snd.first); } + { return snd.second == sfx && mOutput->isSoundPlaying(snd.first.get()); } ) != snditer->second.cend(); } return false; @@ -907,151 +836,85 @@ namespace MWSound void SoundManager::updateRegionSound(float duration) { - static float sTimeToNextEnvSound = 0.0f; - static int total = 0; - static std::string regionName = ""; - static float sTimePassed = 0.0; MWBase::World *world = MWBase::Environment::get().getWorld(); const MWWorld::ConstPtr player = world->getPlayerPtr(); const ESM::Cell *cell = player.getCell()->getCell(); - sTimePassed += duration; - if(!cell->isExterior() || sTimePassed < sTimeToNextEnvSound) + if (!cell->isExterior()) return; - float a = Misc::Rng::rollClosedProbability(); - // NOTE: We should use the "Minimum Time Between Environmental Sounds" and - // "Maximum Time Between Environmental Sounds" fallback settings here. - sTimeToNextEnvSound = 5.0f*a + 15.0f*(1.0f-a); - sTimePassed = 0; - - if(regionName != cell->mRegion) - { - regionName = cell->mRegion; - total = 0; - } - - const ESM::Region *regn = world->getStore().get().search(regionName); - if(regn == nullptr) - return; - - if(total == 0) - { - for(const ESM::Region::SoundRef &sndref : regn->mSoundList) - total += (int)sndref.mChance; - if(total == 0) - return; - } - - int r = Misc::Rng::rollDice(total); - int pos = 0; - - for(const ESM::Region::SoundRef &sndref : regn->mSoundList) - { - if(r - pos < sndref.mChance) - { - playSound(sndref.mSound, 1.0f, 1.0f); - break; - } - pos += sndref.mChance; - } + if (const auto next = mRegionSoundSelector.getNextRandom(duration, cell->mRegion, *world)) + playSound(*next, 1.0f, 1.0f); } - void SoundManager::updateWaterSound(float /*duration*/) + void SoundManager::updateWaterSound() { - 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(); + const auto update = mWaterSoundUpdater.update(player, *world); - float volume = 0.0f; - const std::string& soundId = player.getCell()->isExterior() ? mNearWaterOutdoorID : mNearWaterIndoorID; + WaterSoundAction action; + Sound_Buffer* sfx; + std::tie(action, sfx) = getWaterSoundAction(update, curcell); - if (!mListenerUnderwater) + switch (action) { - 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); - - if (mNearWaterSound) - { - if (volume == 0.0f) - { + case WaterSoundAction::DoNothing: + break; + case WaterSoundAction::SetVolume: + mNearWaterSound->setVolume(update.mVolume * sfx->mVolume); + break; + case WaterSoundAction::FinishSound: mOutput->finishSound(mNearWaterSound); mNearWaterSound = nullptr; - } - else - { - bool soundIdChanged = false; - - Sound_Buffer *sfx = lookupSound(soundId); - if(LastCell != curcell) - { - LastCell = curcell; - SoundMap::const_iterator snditer = mActiveSounds.find(MWWorld::Ptr()); - if(snditer != mActiveSounds.end()) - { - SoundBufferRefPairList::const_iterator pairiter = std::find_if( - snditer->second.begin(), snditer->second.end(), - [this](const SoundBufferRefPairList::value_type &item) -> bool - { return mNearWaterSound == item.first; } - ); - if (pairiter != snditer->second.end() && pairiter->second != sfx) - soundIdChanged = true; - } - } - - if(soundIdChanged) - { + break; + case WaterSoundAction::PlaySound: + if (mNearWaterSound) mOutput->finishSound(mNearWaterSound); - mNearWaterSound = playSound(soundId, volume, 1.0f, Type::Sfx, PlayMode::Loop); - } - else if (sfx) - mNearWaterSound->setVolume(volume * sfx->mVolume); - } + mNearWaterSound = playSound(update.mId, update.mVolume, 1.0f, Type::Sfx, PlayMode::Loop); + break; } - else if (volume > 0.0f) + + mLastCell = curcell; + } + + std::pair SoundManager::getWaterSoundAction( + const WaterSoundUpdate& update, const ESM::Cell* cell) const + { + if (mNearWaterSound) { - LastCell = curcell; - mNearWaterSound = playSound(soundId, volume, 1.0f, Type::Sfx, PlayMode::Loop); + if (update.mVolume == 0.0f) + return {WaterSoundAction::FinishSound, nullptr}; + + bool soundIdChanged = false; + + Sound_Buffer* sfx = lookupSound(update.mId); + if (mLastCell != cell) + { + const auto snditer = mActiveSounds.find(MWWorld::ConstPtr()); + if (snditer != mActiveSounds.end()) + { + const auto pairiter = std::find_if( + snditer->second.begin(), snditer->second.end(), + [this](const SoundBufferRefPairList::value_type &item) -> bool + { return mNearWaterSound == item.first.get(); } + ); + if (pairiter != snditer->second.end() && pairiter->second != sfx) + soundIdChanged = true; + } + } + + if (soundIdChanged) + return {WaterSoundAction::PlaySound, nullptr}; + + if (sfx) + return {WaterSoundAction::SetVolume, sfx}; } + else if (update.mVolume > 0.0f) + return {WaterSoundAction::PlaySound, nullptr}; + + return {WaterSoundAction::DoNothing, nullptr}; } void SoundManager::updateSounds(float duration) @@ -1061,17 +924,19 @@ namespace MWSound SaySoundMap::iterator queuesayiter = mSaySoundsQueue.begin(); while (queuesayiter != mSaySoundsQueue.end()) { - mActiveSaySounds[queuesayiter->first] = queuesayiter->second; + const auto dst = mActiveSaySounds.find(queuesayiter->first); + if (dst == mActiveSaySounds.end()) + mActiveSaySounds.emplace(queuesayiter->first, std::move(queuesayiter->second)); + else + dst->second = std::move(queuesayiter->second); mSaySoundsQueue.erase(queuesayiter++); } - static float timePassed = 0.0; - - timePassed += duration; - if(timePassed < (1.0f/30.0f)) + mTimePassed += duration; + if (mTimePassed < sMinUpdateInterval) return; - duration = timePassed; - timePassed = 0.0f; + duration = mTimePassed; + mTimePassed = 0.0f; // Make sure music is still playing if(!isMusicPlaying() && !mCurrentPlaylist.empty()) @@ -1104,10 +969,9 @@ namespace MWSound SoundBufferRefPairList::iterator sndidx = snditer->second.begin(); while(sndidx != snditer->second.end()) { - Sound *sound; - Sound_Buffer *sfx; + Sound *sound = sndidx->first.get(); + Sound_Buffer *sfx = sndidx->second; - std::tie(sound, sfx) = *sndidx; if(!ptr.isEmpty() && sound->getIs3D()) { const ESM::Position &pos = ptr.getRefData().getPosition(); @@ -1124,10 +988,9 @@ namespace MWSound if(!mOutput->isSoundPlaying(sound)) { mOutput->finishSound(sound); - mUnusedSounds.push_back(sound); - if(sound == mUnderwaterSound) + if (sound == mUnderwaterSound) mUnderwaterSound = nullptr; - if(sound == mNearWaterSound) + if (sound == mNearWaterSound) mNearWaterSound = nullptr; if(sfx->mUses-- == 1) mUnusedBuffers.push_front(sfx); @@ -1151,7 +1014,7 @@ namespace MWSound while(sayiter != mActiveSaySounds.end()) { MWWorld::ConstPtr ptr = sayiter->first; - Stream *sound = sayiter->second; + Stream *sound = sayiter->second.get(); if(!ptr.isEmpty() && sound->getIs3D()) { MWBase::World *world = MWBase::Environment::get().getWorld(); @@ -1168,7 +1031,6 @@ namespace MWSound if(!mOutput->isStreamPlaying(sound)) { mOutput->finishStream(sound); - mUnusedStreams.push_back(sound); mActiveSaySounds.erase(sayiter++); } else @@ -1183,7 +1045,7 @@ namespace MWSound TrackList::iterator trkiter = mActiveTracks.begin(); for(;trkiter != mActiveTracks.end();++trkiter) { - Stream *sound = *trkiter; + Stream *sound = trkiter->get(); if(!mOutput->isStreamPlaying(sound)) { mOutput->finishStream(sound); @@ -1214,8 +1076,8 @@ namespace MWSound { mMusic->updateFade(duration); - mOutput->updateStream(mMusic); - + mOutput->updateStream(mMusic.get()); + if (mMusic->getRealVolume() <= 0.f) { streamMusicFull(mNextMusic); @@ -1235,18 +1097,14 @@ namespace MWSound MWBase::StateManager::State_NoGame) { updateRegionSound(duration); - updateWaterSound(duration); + updateWaterSound(); } } void SoundManager::processChangedSettings(const Settings::CategorySettingVector& settings) { - mMasterVolume = Settings::Manager::getFloat("master volume", "Sound"); - mMusicVolume = Settings::Manager::getFloat("music volume", "Sound"); - mSFXVolume = Settings::Manager::getFloat("sfx volume", "Sound"); - mFootstepsVolume = Settings::Manager::getFloat("footsteps volume", "Sound"); - mVoiceVolume = Settings::Manager::getFloat("voice volume", "Sound"); + mVolumeSettings.update(); if(!mOutput->isInitialized()) return; @@ -1255,32 +1113,32 @@ namespace MWSound { for(SoundBufferRefPair &sndbuf : snd.second) { - Sound *sound = sndbuf.first; + Sound *sound = sndbuf.first.get(); sound->setBaseVolume(volumeFromType(sound->getPlayType())); mOutput->updateSound(sound); } } for(SaySoundMap::value_type &snd : mActiveSaySounds) { - Stream *sound = snd.second; + Stream *sound = snd.second.get(); sound->setBaseVolume(volumeFromType(sound->getPlayType())); mOutput->updateStream(sound); } for(SaySoundMap::value_type &snd : mSaySoundsQueue) { - Stream *sound = snd.second; + Stream *sound = snd.second.get(); sound->setBaseVolume(volumeFromType(sound->getPlayType())); mOutput->updateStream(sound); } - for(Stream *sound : mActiveTracks) + for (const StreamPtr& sound : mActiveTracks) { sound->setBaseVolume(volumeFromType(sound->getPlayType())); - mOutput->updateStream(sound); + mOutput->updateStream(sound.get()); } if(mMusic) { mMusic->setBaseVolume(volumeFromType(mMusic->getPlayType())); - mOutput->updateStream(mMusic); + mOutput->updateStream(mMusic.get()); } mOutput->finishUpdate(); } @@ -1292,6 +1150,8 @@ namespace MWSound mListenerUp = up; mListenerUnderwater = underwater; + + mWaterSoundUpdater.setUnderwater(underwater); } void SoundManager::updatePtr(const MWWorld::ConstPtr &old, const MWWorld::ConstPtr &updated) @@ -1307,17 +1167,17 @@ namespace MWSound SaySoundMap::iterator sayiter = mSaySoundsQueue.find(old); if(sayiter != mSaySoundsQueue.end()) { - Stream *stream = sayiter->second; + StreamPtr stream = std::move(sayiter->second); mSaySoundsQueue.erase(sayiter); - mSaySoundsQueue.emplace(updated, stream); + mSaySoundsQueue.emplace(updated, std::move(stream)); } sayiter = mActiveSaySounds.find(old); if(sayiter != mActiveSaySounds.end()) { - Stream *stream = sayiter->second; + StreamPtr stream = std::move(sayiter->second); mActiveSaySounds.erase(sayiter); - mActiveSaySounds.emplace(updated, stream); + mActiveSaySounds.emplace(updated, std::move(stream)); } } @@ -1394,8 +1254,7 @@ namespace MWSound { for(SoundBufferRefPair &sndbuf : snd.second) { - mOutput->finishSound(sndbuf.first); - mUnusedSounds.push_back(sndbuf.first); + mOutput->finishSound(sndbuf.first.get()); Sound_Buffer *sfx = sndbuf.second; if(sfx->mUses-- == 1) mUnusedBuffers.push_front(sfx); @@ -1406,24 +1265,15 @@ namespace MWSound mNearWaterSound = nullptr; for(SaySoundMap::value_type &snd : mSaySoundsQueue) - { - mOutput->finishStream(snd.second); - mUnusedStreams.push_back(snd.second); - } + mOutput->finishStream(snd.second.get()); mSaySoundsQueue.clear(); for(SaySoundMap::value_type &snd : mActiveSaySounds) - { - mOutput->finishStream(snd.second); - mUnusedStreams.push_back(snd.second); - } + mOutput->finishStream(snd.second.get()); mActiveSaySounds.clear(); - for(Stream *sound : mActiveTracks) - { - mOutput->finishStream(sound); - mUnusedStreams.push_back(sound); - } + for(StreamPtr& sound : mActiveTracks) + mOutput->finishStream(sound.get()); mActiveTracks.clear(); mPlaybackPaused = false; std::fill(std::begin(mPausedSoundTypes), std::end(mPausedSoundTypes), 0); diff --git a/apps/openmw/mwsound/soundmanagerimp.hpp b/apps/openmw/mwsound/soundmanagerimp.hpp index 55588d06fa..b3f612d7d6 100644 --- a/apps/openmw/mwsound/soundmanagerimp.hpp +++ b/apps/openmw/mwsound/soundmanagerimp.hpp @@ -9,11 +9,16 @@ #include #include - +#include #include #include "../mwbase/soundmanager.hpp" +#include "regionsoundselector.hpp" +#include "watersoundupdater.hpp" +#include "type.hpp" +#include "volumesettings.hpp" + namespace VFS { class Manager; @@ -22,6 +27,7 @@ namespace VFS namespace ESM { struct Sound; + struct Cell; } namespace MWSound @@ -42,6 +48,9 @@ namespace MWSound Play_3D = 1<<31 }; + using SoundPtr = Misc::ObjectPtr; + using StreamPtr = Misc::ObjectPtr; + class SoundManager : public MWBase::SoundManager { const VFS::Manager* mVFS; @@ -53,18 +62,10 @@ namespace MWSound std::unordered_map> mMusicToPlay; // A list with music files not yet played std::string mLastPlayedMusic; // The music file that was last played - float mMasterVolume; - float mSFXVolume; - float mMusicVolume; - float mVoiceVolume; - float mFootstepsVolume; + VolumeSettings mVolumeSettings; + + WaterSoundUpdater mWaterSoundUpdater; - int mNearWaterRadius; - int mNearWaterPoints; - float mNearWaterIndoorTolerance; - float mNearWaterOutdoorTolerance; - std::string mNearWaterIndoorID; - std::string mNearWaterOutdoorID; 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 @@ -81,25 +82,23 @@ namespace MWSound typedef std::deque SoundList; SoundList mUnusedBuffers; - std::unique_ptr> mSounds; - std::vector mUnusedSounds; + Misc::ObjectPool mSounds; - std::unique_ptr> mStreams; - std::vector mUnusedStreams; + Misc::ObjectPool mStreams; - typedef std::pair SoundBufferRefPair; + typedef std::pair SoundBufferRefPair; typedef std::vector SoundBufferRefPairList; typedef std::map SoundMap; SoundMap mActiveSounds; - typedef std::map SaySoundMap; + typedef std::map SaySoundMap; SaySoundMap mSaySoundsQueue; SaySoundMap mActiveSaySounds; - typedef std::vector TrackList; + typedef std::vector TrackList; TrackList mActiveTracks; - Stream *mMusic; + StreamPtr mMusic; std::string mCurrentPlaylist; bool mListenerUnderwater; @@ -115,6 +114,12 @@ namespace MWSound std::string mNextMusic; bool mPlaybackPaused; + RegionSoundSelector mRegionSoundSelector; + + float mTimePassed = 0; + + const ESM::Cell *mLastCell = nullptr; + Sound_Buffer *insertSound(const std::string &soundId, const ESM::Sound *sound); Sound_Buffer *lookupSound(const std::string &soundId) const; @@ -123,10 +128,10 @@ namespace MWSound // returns a decoder to start streaming, or nullptr if the sound was not found DecoderPtr loadVoice(const std::string &voicefile); - Sound *getSoundRef(); - Stream *getStreamRef(); + SoundPtr getSoundRef(); + StreamPtr getStreamRef(); - Stream *playVoice(DecoderPtr decoder, const osg::Vec3f &pos, bool playlocal); + StreamPtr playVoice(DecoderPtr decoder, const osg::Vec3f &pos, bool playlocal); void streamMusicFull(const std::string& filename); void advanceMusic(const std::string& filename); @@ -134,11 +139,22 @@ namespace MWSound void updateSounds(float duration); void updateRegionSound(float duration); - void updateWaterSound(float duration); + void updateWaterSound(); void updateMusic(float duration); float volumeFromType(Type type) const; + enum class WaterSoundAction + { + DoNothing, + SetVolume, + FinishSound, + PlaySound, + }; + + std::pair getWaterSoundAction(const WaterSoundUpdate& update, + const ESM::Cell* cell) const; + SoundManager(const SoundManager &rhs); SoundManager& operator=(const SoundManager &rhs); @@ -233,9 +249,6 @@ namespace MWSound virtual void stopSound(const MWWorld::CellStore *cell); ///< Stop all sounds for the given cell. - virtual void stopSound(const std::string& soundId); - ///< Stop a non-3d looping sound - virtual void fadeOutSound3D(const MWWorld::ConstPtr &reference, const std::string& soundId, float duration); ///< Fade out given sound (that is already playing) of given object ///< @param reference Reference to object, whose sound is faded out diff --git a/apps/openmw/mwsound/type.hpp b/apps/openmw/mwsound/type.hpp new file mode 100644 index 0000000000..9f95bfa401 --- /dev/null +++ b/apps/openmw/mwsound/type.hpp @@ -0,0 +1,17 @@ +#ifndef GAME_SOUND_TYPE_H +#define GAME_SOUND_TYPE_H + +namespace MWSound +{ + enum class Type + { + Sfx = 1 << 4, /* Normal SFX sound */ + Voice = 1 << 5, /* Voice sound */ + Foot = 1 << 6, /* Footstep sound */ + Music = 1 << 7, /* Music track */ + Movie = 1 << 8, /* Movie audio track */ + Mask = Sfx | Voice | Foot | Music | Movie + }; +} + +#endif diff --git a/apps/openmw/mwsound/volumesettings.cpp b/apps/openmw/mwsound/volumesettings.cpp new file mode 100644 index 0000000000..cc4eac3d6d --- /dev/null +++ b/apps/openmw/mwsound/volumesettings.cpp @@ -0,0 +1,56 @@ +#include "volumesettings.hpp" + +#include + +#include + +namespace MWSound +{ + namespace + { + float clamp(float value) + { + return std::max(0.0f, std::min(1.0f, value)); + } + } + + VolumeSettings::VolumeSettings() + : mMasterVolume(clamp(Settings::Manager::getFloat("master volume", "Sound"))), + mSFXVolume(clamp(Settings::Manager::getFloat("sfx volume", "Sound"))), + mMusicVolume(clamp(Settings::Manager::getFloat("music volume", "Sound"))), + mVoiceVolume(clamp(Settings::Manager::getFloat("voice volume", "Sound"))), + mFootstepsVolume(clamp(Settings::Manager::getFloat("footsteps volume", "Sound"))) + { + } + + float VolumeSettings::getVolumeFromType(Type type) const + { + float volume = mMasterVolume; + + switch(type) + { + case Type::Sfx: + volume *= mSFXVolume; + break; + case Type::Voice: + volume *= mVoiceVolume; + break; + case Type::Foot: + volume *= mFootstepsVolume; + break; + case Type::Music: + volume *= mMusicVolume; + break; + case Type::Movie: + case Type::Mask: + break; + } + + return volume; + } + + void VolumeSettings::update() + { + *this = VolumeSettings(); + } +} diff --git a/apps/openmw/mwsound/volumesettings.hpp b/apps/openmw/mwsound/volumesettings.hpp new file mode 100644 index 0000000000..eec5f5c1bf --- /dev/null +++ b/apps/openmw/mwsound/volumesettings.hpp @@ -0,0 +1,26 @@ +#ifndef GAME_SOUND_VOLUMESETTINGS_H +#define GAME_SOUND_VOLUMESETTINGS_H + +#include "type.hpp" + +namespace MWSound +{ + class VolumeSettings + { + public: + VolumeSettings(); + + float getVolumeFromType(Type type) const; + + void update(); + + private: + float mMasterVolume; + float mSFXVolume; + float mMusicVolume; + float mVoiceVolume; + float mFootstepsVolume; + }; +} + +#endif diff --git a/apps/openmw/mwsound/watersoundupdater.cpp b/apps/openmw/mwsound/watersoundupdater.cpp new file mode 100644 index 0000000000..b1646c404f --- /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 0000000000..b20b9db6c0 --- /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 diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 42fe1a960c..64316789fe 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -1,6 +1,8 @@ #include "scene.hpp" #include +#include +#include #include #include @@ -741,15 +743,12 @@ namespace MWWorld player.getClass().adjustPosition(player, true); } - MWBase::MechanicsManager *mechMgr = - MWBase::Environment::get().getMechanicsManager(); - - mechMgr->updateCell(old, player); - mechMgr->watchActor(player); + MWBase::Environment::get().getMechanicsManager()->updateCell(old, player); + MWBase::Environment::get().getWindowManager()->watchActor(player); mPhysics->updatePtr(old, player); - MWBase::Environment::get().getWorld()->adjustSky(); + world->adjustSky(); mLastPlayerPos = player.getRefData().getPosition().asVec3(); } @@ -1143,7 +1142,7 @@ namespace MWWorld } else loadingListener->setProgress(0); - OpenThreads::Thread::microSleep(5000); + std::this_thread::sleep_for(std::chrono::milliseconds(5)); } } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 6788a7fa29..fd934b8a8e 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1390,6 +1390,9 @@ namespace MWWorld { if(ptr.getRefData().getBaseNode() != 0) { + mRendering->pagingBlacklistObject(mStore.find(ptr.getCellRef().getRefId()), ptr); + mWorldScene->removeFromPagedRefs(ptr); + mRendering->rotateObject(ptr, rotate); mPhysics->updateRotation(ptr); @@ -2453,7 +2456,7 @@ namespace MWWorld rotateObject(player, 0.f, 0.f, 0.f, MWBase::RotationFlag_inverseOrder | MWBase::RotationFlag_adjust); MWBase::Environment::get().getMechanicsManager()->add(getPlayerPtr()); - MWBase::Environment::get().getMechanicsManager()->watchActor(getPlayerPtr()); + MWBase::Environment::get().getWindowManager()->watchActor(getPlayerPtr()); std::string model = getPlayerPtr().getClass().getModel(getPlayerPtr()); model = Misc::ResourceHelpers::correctActorModelPath(model, mResourceSystem->getVFS()); diff --git a/cmake/FindBullet.cmake b/cmake/FindBullet.cmake deleted file mode 100644 index 6d68bed707..0000000000 --- a/cmake/FindBullet.cmake +++ /dev/null @@ -1,73 +0,0 @@ -# - Try to find the Bullet physics engine -# -# This module accepts the following env variables -# BULLET_ROOT - Can be set to bullet install path or Windows build path -# -# Once done this will define -# Bullet_FOUND - System has the all required components. -# Bullet_INCLUDE_DIRS - Include directory necessary for using the required components headers. -# Bullet_LIBRARIES - Link these to use the required bullet components. -# Bullet_VERSION - Version of libbullet -# -# For each of the components -# - LinearMath -# - BulletCollision -# - BulletSoftBody -# - BulletDynamics -# -# Copyright (c) 2009, Philip Lowman -# Modified for OpenMW to parse BT_BULLET_VERSION. -# -# Redistribution AND use is allowed according to the terms of the New -# BSD license. -# For details see the accompanying COPYING-CMAKE-SCRIPTS file. - -include(LibFindMacros) - -# Macro: _internal_find_bullet_library -# Checks for the given component by invoking pkgconfig etc. -macro(_internal_find_bullet_library _lib) - libfind_pkg_detect(Bullet_${_lib} bullet - FIND_LIBRARY ${_lib} - HINTS $ENV{BULLET_ROOT} - PATH_SUFFIXES lib - ) - libfind_process(Bullet_${_lib}) -endmacro() - -set(_known_components LinearMath BulletCollision BulletSoftBody BulletDynamics) - -# Check if the required components were found and add their stuff to the Bullet_* vars. -foreach (_component ${Bullet_FIND_COMPONENTS}) - list(FIND _known_components ${_component} _known_component) - if (_known_component EQUAL -1) - message(FATAL_ERROR "Unknown component '${_component}'") - endif() - - set(Bullet_${_component}_Debug_FIND_QUIETLY TRUE) # don't spam messages with optional Debug component - _internal_find_bullet_library(${_component}) - _internal_find_bullet_library(${_component}_Debug) - - if (Bullet_${_component}_Debug_FOUND) - set(Bullet_LIBRARIES ${Bullet_LIBRARIES} optimized ${Bullet_${_component}_LIBRARIES} debug ${Bullet_${_component}_Debug_LIBRARIES}) - else() - set(Bullet_LIBRARIES ${Bullet_LIBRARIES} ${Bullet_${_component}_LIBRARIES}) - endif() -endforeach() - -libfind_pkg_detect(Bullet bullet - FIND_PATH btBulletCollisionCommon.h - HINTS $ENV{BULLET_ROOT} - PATH_SUFFIXES include/bullet -) -set(Bullet_INCLUDE_DIRS ${Bullet_INCLUDE_DIR}) -libfind_version_header(Bullet LinearMath/btScalar.h BT_BULLET_VERSION) - -FIND_PACKAGE_HANDLE_STANDARD_ARGS(Bullet - FOUND_VAR Bullet_FOUND - VERSION_VAR Bullet_VERSION - HANDLE_COMPONENTS - REQUIRED_VARS - Bullet_LIBRARIES - Bullet_INCLUDE_DIR -) diff --git a/cmake/FindFreetype.cmake b/cmake/FindFreetype.cmake deleted file mode 100644 index 3b75868356..0000000000 --- a/cmake/FindFreetype.cmake +++ /dev/null @@ -1,91 +0,0 @@ -#------------------------------------------------------------------- -# This file is part of the CMake build system for OGRE -# (Object-oriented Graphics Rendering Engine) -# For the latest info, see https://www.ogre3d.org/ -# -# The contents of this file are placed in the public domain. Feel -# free to make use of it in any way you like. -#------------------------------------------------------------------- - -# - Try to find FreeType -# -# This module accepts the following env variable -# FREETYPE_DIR - Can be set to custom install path -# -# Once done, this will define -# -# Freetype_FOUND - system has FreeType -# Freetype_INCLUDE_DIRS - the FreeType include directories -# Freetype_LIBRARIES - link these to use FreeType -# Freetype_VERSION - version of FreeType -# -# libfreetype internals: -# -# ====================================== -# new versions (2.5.2) -# -# file structure: -# /include/freetype2/ft2build.h -# /include/freetype2/freetype.h -# used as: -# #include -# #include -# requires: -# -I /include/freetype2/ -# -# ====================================== -# old versions (2.4.8, 2.3.5) -# -# file structure: -# /include/ft2build.h -# /include/freetype2/freetype/freetype.h -# used as: -# #include -# #include -# requires: -# -I /include/ -I /include/freetype2/ -# -# ====================================== - -include(LibFindMacros) - -set(_REGULAR_INSTALL_PATHS - /usr/X11R6 - /usr/local/X11R6 - /usr/local/X11 - /usr/freeware - ENV GTKMM_BASEPATH - [HKEY_CURRENT_USER\\SOFTWARE\\gtkmm\\2.4;Path] - [HKEY_LOCAL_MACHINE\\SOFTWARE\\gtkmm\\2.4;Path] -) - -libfind_pkg_detect(Freetype freetype2 - FIND_PATH ft2build.h - HINTS $ENV{FREETYPE_DIR} - PATHS ${_REGULAR_INSTALL_PATHS} - PATH_SUFFIXES include freetype2 - FIND_LIBRARY freetype freetype2311 freetype239 freetype238 freetype235 freetype219 - HINTS $ENV{FREETYPE_DIR} - PATHS ${_REGULAR_INSTALL_PATHS} - PATH_SUFFIXES lib -) -find_path(Freetype_OLD_INCLUDE_DIR - # in new versions of freetype old_include_dir equals to include_dir - # see explanation above - NAMES freetype/freetype.h freetype.h - PATHS ${Freetype_INCLUDE_DIR} - PATH_SUFFIXES freetype2 - NO_DEFAULT_PATH -) -libfind_version_n_header(Freetype - NAMES freetype/freetype.h freetype.h - PATHS Freetype_OLD_INCLUDE_DIR - DEFINES FREETYPE_MAJOR FREETYPE_MINOR FREETYPE_PATCH -) - -set(Freetype_PROCESS_INCLUDES Freetype_OLD_INCLUDE_DIR) -libfind_process(Freetype) - -if (Freetype_INCLUDE_DIRS) - list(REMOVE_DUPLICATES Freetype_INCLUDE_DIRS) -endif() diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 1d4a84ce1f..86d657792e 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -210,7 +210,7 @@ if (CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") endif() endif () -include_directories(${Bullet_INCLUDE_DIRS} ${CMAKE_CURRENT_BINARY_DIR}) +include_directories(${BULLET_INCLUDE_DIRS} ${CMAKE_CURRENT_BINARY_DIR}) add_library(components STATIC ${COMPONENT_FILES} ${MOC_SRCS} ${ESM_UI_HDR}) @@ -229,7 +229,7 @@ target_link_libraries(components ${OSGGA_LIBRARIES} ${OSGSHADOW_LIBRARIES} ${OSGANIMATION_LIBRARIES} - ${Bullet_LIBRARIES} + ${BULLET_LIBRARIES} ${SDL2_LIBRARIES} ${OPENGL_gl_LIBRARY} ${MyGUI_LIBRARIES} diff --git a/components/config/gamesettings.cpp b/components/config/gamesettings.cpp index fc1d813685..b771b7fc8e 100644 --- a/components/config/gamesettings.cpp +++ b/components/config/gamesettings.cpp @@ -2,11 +2,8 @@ #include "launchersettings.hpp" #include -#include #include -#include #include -#include #include @@ -105,9 +102,9 @@ bool Config::GameSettings::readUserFile(QTextStream &stream) return readFile(stream, mUserSettings); } -bool Config::GameSettings::readFile(QTextStream &stream, QMap &settings) +bool Config::GameSettings::readFile(QTextStream &stream, QMultiMap &settings) { - QMap cache; + QMultiMap cache; QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$"); while (!stream.atEnd()) { @@ -151,7 +148,7 @@ bool Config::GameSettings::readFile(QTextStream &stream, QMap values.append(settings.values(key)); if (!values.contains(value)) { - cache.insertMulti(key, value); + cache.insert(key, value); } } } @@ -368,7 +365,7 @@ bool Config::GameSettings::writeFileWithComments(QFile &file) *iter = QString(); // assume no match QString key = settingRegex.cap(1); QString keyVal = settingRegex.cap(1)+"="+settingRegex.cap(2); - QMap::const_iterator i = mUserSettings.find(key); + QMultiMap::const_iterator i = mUserSettings.find(key); while (i != mUserSettings.end() && i.key() == key) { QString settingLine = i.key() + "=" + i.value(); diff --git a/components/config/gamesettings.hpp b/components/config/gamesettings.hpp index eeac893c23..a6876b83d4 100644 --- a/components/config/gamesettings.hpp +++ b/components/config/gamesettings.hpp @@ -5,7 +5,7 @@ #include #include #include -#include +#include #include @@ -31,7 +31,9 @@ namespace Config inline void setValue(const QString &key, const QString &value) { + mSettings.remove(key); mSettings.insert(key, value); + mUserSettings.remove(key); mUserSettings.insert(key, value); } @@ -39,11 +41,11 @@ namespace Config { QStringList values = mSettings.values(key); if (!values.contains(value)) - mSettings.insertMulti(key, value); + mSettings.insert(key, value); values = mUserSettings.values(key); if (!values.contains(value)) - mUserSettings.insertMulti(key, value); + mUserSettings.insert(key, value); } inline void remove(const QString &key) @@ -63,7 +65,7 @@ namespace Config QStringList values(const QString &key, const QStringList &defaultValues = QStringList()) const; bool readFile(QTextStream &stream); - bool readFile(QTextStream &stream, QMap &settings); + bool readFile(QTextStream &stream, QMultiMap &settings); bool readUserFile(QTextStream &stream); bool writeFile(QTextStream &stream); @@ -78,8 +80,8 @@ namespace Config Files::ConfigurationManager &mCfgMgr; void validatePaths(); - QMap mSettings; - QMap mUserSettings; + QMultiMap mSettings; + QMultiMap mUserSettings; QStringList mDataDirs; QString mDataLocal; diff --git a/components/config/launchersettings.cpp b/components/config/launchersettings.cpp index 91bf450f49..ae591ef581 100644 --- a/components/config/launchersettings.cpp +++ b/components/config/launchersettings.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include @@ -22,7 +22,7 @@ Config::LauncherSettings::~LauncherSettings() QStringList Config::LauncherSettings::subKeys(const QString &key) { - QMap settings = SettingsBase::getSettings(); + QMultiMap settings = SettingsBase::getSettings(); QStringList keys = settings.uniqueKeys(); QRegExp keyRe("(.+)/"); @@ -54,7 +54,7 @@ bool Config::LauncherSettings::writeFile(QTextStream &stream) { QString sectionPrefix; QRegExp sectionRe("([^/]+)/(.+)$"); - QMap settings = SettingsBase::getSettings(); + QMultiMap settings = SettingsBase::getSettings(); QMapIterator i(settings); i.toBack(); diff --git a/components/config/launchersettings.hpp b/components/config/launchersettings.hpp index c5eefb22a5..1483052bbc 100644 --- a/components/config/launchersettings.hpp +++ b/components/config/launchersettings.hpp @@ -6,7 +6,7 @@ namespace Config { - class LauncherSettings : public SettingsBase > + class LauncherSettings : public SettingsBase > { public: LauncherSettings(); diff --git a/components/config/settingsbase.hpp b/components/config/settingsbase.hpp index 08cd0bfc6f..1199717167 100644 --- a/components/config/settingsbase.hpp +++ b/components/config/settingsbase.hpp @@ -5,7 +5,7 @@ #include #include #include -#include +#include namespace Config { @@ -33,7 +33,7 @@ namespace Config { QStringList values = mSettings.values(key); if (!values.contains(value)) - mSettings.insertMulti(key, value); + mSettings.insert(key, value); } inline void setMultiValueEnabled(bool enable) @@ -83,8 +83,9 @@ namespace Config if (!values.contains(value)) { if (mMultiValue) { - cache.insertMulti(key, value); + cache.insert(key, value); } else { + cache.remove(key); cache.insert(key, value); } } diff --git a/components/esmterrain/storage.cpp b/components/esmterrain/storage.cpp index fc93706a37..cf27b7128d 100644 --- a/components/esmterrain/storage.cpp +++ b/components/esmterrain/storage.cpp @@ -2,8 +2,6 @@ #include -#include - #include #include @@ -548,7 +546,7 @@ namespace ESMTerrain Terrain::LayerInfo Storage::getLayerInfo(const std::string& texture) { - OpenThreads::ScopedLock lock(mLayerInfoMutex); + std::lock_guard lock(mLayerInfoMutex); // Already have this cached? std::map::iterator found = mLayerInfoMap.find(texture); diff --git a/components/esmterrain/storage.hpp b/components/esmterrain/storage.hpp index a5f62ba48d..c645311401 100644 --- a/components/esmterrain/storage.hpp +++ b/components/esmterrain/storage.hpp @@ -2,8 +2,7 @@ #define COMPONENTS_ESM_TERRAIN_STORAGE_H #include - -#include +#include #include @@ -138,7 +137,7 @@ namespace ESMTerrain std::string getTextureName (UniqueTextureId id); std::map mLayerInfoMap; - OpenThreads::Mutex mLayerInfoMutex; + std::mutex mLayerInfoMutex; std::string mNormalMapPattern; std::string mNormalHeightMapPattern; diff --git a/components/misc/objectpool.hpp b/components/misc/objectpool.hpp new file mode 100644 index 0000000000..b3dae6a4f5 --- /dev/null +++ b/components/misc/objectpool.hpp @@ -0,0 +1,83 @@ +#ifndef OPENMW_COMPONENTS_MISC_OBJECTPOOL_H +#define OPENMW_COMPONENTS_MISC_OBJECTPOOL_H + +#include +#include +#include + +namespace Misc +{ + template + class ObjectPool; + + template + class ObjectPtrDeleter + { + public: + ObjectPtrDeleter(std::nullptr_t) + : mPool(nullptr) {} + + ObjectPtrDeleter(ObjectPool& pool) + : mPool(&pool) {} + + void operator()(T* object) const + { + mPool->recycle(object); + } + + private: + ObjectPool* mPool; + }; + + template + struct ObjectPtr final : std::unique_ptr> + { + using std::unique_ptr>::unique_ptr; + using std::unique_ptr>::operator=; + + ObjectPtr() + : ObjectPtr(nullptr) {} + + ObjectPtr(std::nullptr_t) + : std::unique_ptr>(nullptr, nullptr) {} + }; + + template + class ObjectPool + { + friend class ObjectPtrDeleter; + + public: + ObjectPool() + : mObjects(std::make_unique>()) {} + + ObjectPtr get() + { + T* object; + + if (!mUnused.empty()) + { + object = mUnused.back(); + mUnused.pop_back(); + } + else + { + mObjects->emplace_back(); + object = &mObjects->back(); + } + + return ObjectPtr(object, ObjectPtrDeleter(*this)); + } + + private: + std::unique_ptr> mObjects; + std::vector mUnused; + + void recycle(T* object) + { + mUnused.push_back(object); + } + }; +} + +#endif diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 88e89b4005..8266b5e016 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -1,5 +1,7 @@ #include "nifloader.hpp" +#include + #include #include #include @@ -923,15 +925,18 @@ namespace NifOsg osg::BoundingBox box; int i=0; - for (std::vector::const_iterator it = partctrl->particles.begin(); - iactiveCount && it != partctrl->particles.end(); ++it, ++i) + for (const auto& particle : partctrl->particles) { - const Nif::NiParticleSystemController::Particle& particle = *it; + if (i++ >= particledata->activeCount) + break; + + if (particle.lifespan <= 0) + continue; ParticleAgeSetter particletemplate(std::max(0.f, particle.lifetime)); osgParticle::Particle* created = partsys->createParticle(&particletemplate); - created->setLifeTime(std::max(0.f, particle.lifespan)); + created->setLifeTime(particle.lifespan); // Note this position and velocity is not correct for a particle system with absolute reference frame, // which can not be done in this loader since we are not attached to the scene yet. Will be fixed up post-load in the SceneManager. @@ -970,6 +975,8 @@ namespace NifOsg osgParticle::ConstantRateCounter* counter = new osgParticle::ConstantRateCounter; if (partctrl->emitFlags & Nif::NiParticleSystemController::NoAutoAdjust) counter->setNumberOfParticlesPerSecondToCreate(partctrl->emitRate); + else if (partctrl->lifetime == 0 && partctrl->lifetimeRandom == 0) + counter->setNumberOfParticlesPerSecondToCreate(0); else counter->setNumberOfParticlesPerSecondToCreate(partctrl->numParticles / (partctrl->lifetime + partctrl->lifetimeRandom/2)); @@ -1725,8 +1732,8 @@ namespace NifOsg { typedef std::set, CompareStateAttribute> Cache; static Cache sCache; - static OpenThreads::Mutex sMutex; - OpenThreads::ScopedLock lock(sMutex); + static std::mutex sMutex; + std::lock_guard lock(sMutex); typename Cache::iterator found = sCache.find(attr); if (found == sCache.end()) found = sCache.insert(attr).first; diff --git a/components/nifosg/particle.cpp b/components/nifosg/particle.cpp index 804a8f8ab8..f71dcdd967 100644 --- a/components/nifosg/particle.cpp +++ b/components/nifosg/particle.cpp @@ -125,7 +125,7 @@ void ParticleShooter::shoot(osgParticle::Particle *particle) const particle->setVelocity(dir * vel); // Not supposed to set this here, but there doesn't seem to be a better way of doing it - particle->setLifeTime(mLifetime + mLifetimeRandom * Misc::Rng::rollClosedProbability()); + particle->setLifeTime(std::max(std::numeric_limits::epsilon(), mLifetime + mLifetimeRandom * Misc::Rng::rollClosedProbability())); } GrowFadeAffector::GrowFadeAffector(float growTime, float fadeTime) @@ -184,6 +184,7 @@ ParticleColorAffector::ParticleColorAffector(const ParticleColorAffector ©, void ParticleColorAffector::operate(osgParticle::Particle* particle, double /* dt */) { + assert(particle->getLifeTime() > 0); float time = static_cast(particle->getAge()/particle->getLifeTime()); osg::Vec4f color = mData.interpKey(time); float alpha = color.a(); diff --git a/components/resource/multiobjectcache.cpp b/components/resource/multiobjectcache.cpp index d6639d3aec..9d75d2480a 100644 --- a/components/resource/multiobjectcache.cpp +++ b/components/resource/multiobjectcache.cpp @@ -21,7 +21,7 @@ namespace Resource { std::vector > objectsToRemove; { - OpenThreads::ScopedLock lock(_objectCacheMutex); + std::lock_guard lock(_objectCacheMutex); // Remove unreferenced entries from object cache ObjectCacheMap::iterator oitr = _objectCache.begin(); @@ -45,7 +45,7 @@ namespace Resource void MultiObjectCache::clear() { - OpenThreads::ScopedLock lock(_objectCacheMutex); + std::lock_guard lock(_objectCacheMutex); _objectCache.clear(); } @@ -56,13 +56,13 @@ namespace Resource OSG_ALWAYS << " trying to add NULL object to cache for " << filename << std::endl; return; } - OpenThreads::ScopedLock lock(_objectCacheMutex); + std::lock_guard lock(_objectCacheMutex); _objectCache.insert(std::make_pair(filename, object)); } osg::ref_ptr MultiObjectCache::takeFromObjectCache(const std::string &fileName) { - OpenThreads::ScopedLock lock(_objectCacheMutex); + std::lock_guard lock(_objectCacheMutex); ObjectCacheMap::iterator found = _objectCache.find(fileName); if (found == _objectCache.end()) return osg::ref_ptr(); @@ -76,7 +76,7 @@ namespace Resource void MultiObjectCache::releaseGLObjects(osg::State *state) { - OpenThreads::ScopedLock lock(_objectCacheMutex); + std::lock_guard lock(_objectCacheMutex); for(ObjectCacheMap::iterator itr = _objectCache.begin(); itr != _objectCache.end(); @@ -89,7 +89,7 @@ namespace Resource unsigned int MultiObjectCache::getCacheSize() const { - OpenThreads::ScopedLock lock(_objectCacheMutex); + std::lock_guard lock(_objectCacheMutex); return _objectCache.size(); } diff --git a/components/resource/multiobjectcache.hpp b/components/resource/multiobjectcache.hpp index b73e29d50f..543406ebd8 100644 --- a/components/resource/multiobjectcache.hpp +++ b/components/resource/multiobjectcache.hpp @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -43,7 +44,7 @@ namespace Resource typedef std::multimap > ObjectCacheMap; ObjectCacheMap _objectCache; - mutable OpenThreads::Mutex _objectCacheMutex; + mutable std::mutex _objectCacheMutex; }; diff --git a/components/resource/objectcache.hpp b/components/resource/objectcache.hpp index 24d7d04a97..6e309a7f77 100644 --- a/components/resource/objectcache.hpp +++ b/components/resource/objectcache.hpp @@ -26,6 +26,7 @@ #include #include +#include namespace osg { @@ -53,7 +54,7 @@ class GenericObjectCache : public osg::Referenced void updateTimeStampOfObjectsInCacheWithExternalReferences(double referenceTime) { // look for objects with external references and update their time stamp. - OpenThreads::ScopedLock lock(_objectCacheMutex); + std::lock_guard lock(_objectCacheMutex); for(typename ObjectCacheMap::iterator itr=_objectCache.begin(); itr!=_objectCache.end(); ++itr) { // If ref count is greater than 1, the object has an external reference. @@ -71,7 +72,7 @@ class GenericObjectCache : public osg::Referenced { std::vector > objectsToRemove; { - OpenThreads::ScopedLock lock(_objectCacheMutex); + std::lock_guard lock(_objectCacheMutex); // Remove expired entries from object cache typename ObjectCacheMap::iterator oitr = _objectCache.begin(); while(oitr != _objectCache.end()) @@ -92,21 +93,21 @@ class GenericObjectCache : public osg::Referenced /** Remove all objects in the cache regardless of having external references or expiry times.*/ void clear() { - OpenThreads::ScopedLock lock(_objectCacheMutex); + std::lock_guard lock(_objectCacheMutex); _objectCache.clear(); } /** Add a key,object,timestamp triple to the Registry::ObjectCache.*/ void addEntryToObjectCache(const KeyType& key, osg::Object* object, double timestamp = 0.0) { - OpenThreads::ScopedLock lock(_objectCacheMutex); + std::lock_guard lock(_objectCacheMutex); _objectCache[key]=ObjectTimeStampPair(object,timestamp); } /** Remove Object from cache.*/ void removeFromObjectCache(const KeyType& key) { - OpenThreads::ScopedLock lock(_objectCacheMutex); + std::lock_guard lock(_objectCacheMutex); typename ObjectCacheMap::iterator itr = _objectCache.find(key); if (itr!=_objectCache.end()) _objectCache.erase(itr); } @@ -114,7 +115,7 @@ class GenericObjectCache : public osg::Referenced /** Get an ref_ptr from the object cache*/ osg::ref_ptr getRefFromObjectCache(const KeyType& key) { - OpenThreads::ScopedLock lock(_objectCacheMutex); + std::lock_guard lock(_objectCacheMutex); typename ObjectCacheMap::iterator itr = _objectCache.find(key); if (itr!=_objectCache.end()) return itr->second.first; @@ -124,7 +125,7 @@ class GenericObjectCache : public osg::Referenced /** Check if an object is in the cache, and if it is, update its usage time stamp. */ bool checkInObjectCache(const KeyType& key, double timeStamp) { - OpenThreads::ScopedLock lock(_objectCacheMutex); + std::lock_guard lock(_objectCacheMutex); typename ObjectCacheMap::iterator itr = _objectCache.find(key); if (itr!=_objectCache.end()) { @@ -137,7 +138,7 @@ class GenericObjectCache : public osg::Referenced /** call releaseGLObjects on all objects attached to the object cache.*/ void releaseGLObjects(osg::State* state) { - OpenThreads::ScopedLock lock(_objectCacheMutex); + std::lock_guard lock(_objectCacheMutex); for(typename ObjectCacheMap::iterator itr = _objectCache.begin(); itr != _objectCache.end(); ++itr) { osg::Object* object = itr->second.first.get(); @@ -148,7 +149,7 @@ class GenericObjectCache : public osg::Referenced /** call node->accept(nv); for all nodes in the objectCache. */ void accept(osg::NodeVisitor& nv) { - OpenThreads::ScopedLock lock(_objectCacheMutex); + std::lock_guard lock(_objectCacheMutex); for(typename ObjectCacheMap::iterator itr = _objectCache.begin(); itr != _objectCache.end(); ++itr) { osg::Object* object = itr->second.first.get(); @@ -165,7 +166,7 @@ class GenericObjectCache : public osg::Referenced template void call(Functor& f) { - OpenThreads::ScopedLock lock(_objectCacheMutex); + std::lock_guard lock(_objectCacheMutex); for (typename ObjectCacheMap::iterator it = _objectCache.begin(); it != _objectCache.end(); ++it) f(it->first, it->second.first.get()); } @@ -173,7 +174,7 @@ class GenericObjectCache : public osg::Referenced /** Get the number of objects in the cache. */ unsigned int getCacheSize() const { - OpenThreads::ScopedLock lock(_objectCacheMutex); + std::lock_guard lock(_objectCacheMutex); return _objectCache.size(); } @@ -185,7 +186,7 @@ class GenericObjectCache : public osg::Referenced typedef std::map ObjectCacheMap; ObjectCacheMap _objectCache; - mutable OpenThreads::Mutex _objectCacheMutex; + mutable std::mutex _objectCacheMutex; }; diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index 8264e3b1ee..ca6c7c895e 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -126,7 +126,7 @@ namespace Resource void clearCache() { - OpenThreads::ScopedLock lock(_listMutex); + std::lock_guard lock(_listMutex); _sharedTextureList.clear(); _sharedStateSetList.clear(); } @@ -625,7 +625,7 @@ namespace Resource mShaderManager->releaseGLObjects(state); - OpenThreads::ScopedLock lock(mSharedStateMutex); + std::lock_guard lock(mSharedStateMutex); mSharedStateManager->releaseGLObjects(state); } @@ -717,7 +717,7 @@ namespace Resource if (mIncrementalCompileOperation) { - OpenThreads::ScopedLock lock(*mIncrementalCompileOperation->getToCompiledMutex()); + std::lock_guard lock(*mIncrementalCompileOperation->getToCompiledMutex()); osgUtil::IncrementalCompileOperation::CompileSets& sets = mIncrementalCompileOperation->getToCompile(); for(osgUtil::IncrementalCompileOperation::CompileSets::iterator it = sets.begin(); it != sets.end();) { @@ -738,7 +738,7 @@ namespace Resource { ResourceManager::clearCache(); - OpenThreads::ScopedLock lock(mSharedStateMutex); + std::lock_guard lock(mSharedStateMutex); mSharedStateManager->clearCache(); mInstanceCache->clear(); } @@ -747,12 +747,12 @@ namespace Resource { if (mIncrementalCompileOperation) { - OpenThreads::ScopedLock lock(*mIncrementalCompileOperation->getToCompiledMutex()); + std::lock_guard lock(*mIncrementalCompileOperation->getToCompiledMutex()); stats->setAttribute(frameNumber, "Compiling", mIncrementalCompileOperation->getToCompile().size()); } { - OpenThreads::ScopedLock lock(mSharedStateMutex); + std::lock_guard lock(mSharedStateMutex); stats->setAttribute(frameNumber, "Texture", mSharedStateManager->getNumSharedTextures()); stats->setAttribute(frameNumber, "StateSet", mSharedStateManager->getNumSharedStateSets()); } diff --git a/components/resource/scenemanager.hpp b/components/resource/scenemanager.hpp index bbe88c9a0e..3a72caf6a3 100644 --- a/components/resource/scenemanager.hpp +++ b/components/resource/scenemanager.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -159,7 +160,7 @@ namespace Resource osg::ref_ptr mInstanceCache; osg::ref_ptr mSharedStateManager; - mutable OpenThreads::Mutex mSharedStateMutex; + mutable std::mutex mSharedStateMutex; Resource::ImageManager* mImageManager; Resource::NifFileManager* mNifFileManager; diff --git a/components/resource/stats.cpp b/components/resource/stats.cpp index 2dda2c2dfa..147970add3 100644 --- a/components/resource/stats.cpp +++ b/components/resource/stats.cpp @@ -25,7 +25,7 @@ StatsHandler::StatsHandler(): _statsWidth(1280.0f), _statsHeight(1024.0f), _font(""), - _characterSize(20.0f) + _characterSize(18.0f) { _camera = new osg::Camera; _camera->getOrCreateStateSet()->setGlobalDefaults(); @@ -45,6 +45,8 @@ Profiler::Profiler() else _font = ""; + _characterSize = 18; + setKeyEventTogglesOnScreenStats(osgGA::GUIEventAdapter::KEY_F3); } diff --git a/components/sceneutil/mwshadowtechnique.cpp b/components/sceneutil/mwshadowtechnique.cpp index 98771bbb42..fa38da54e4 100644 --- a/components/sceneutil/mwshadowtechnique.cpp +++ b/components/sceneutil/mwshadowtechnique.cpp @@ -891,7 +891,7 @@ MWShadowTechnique::ViewDependentData* MWShadowTechnique::createViewDependentData MWShadowTechnique::ViewDependentData* MWShadowTechnique::getViewDependentData(osgUtil::CullVisitor* cv) { - OpenThreads::ScopedLock lock(_viewDependentDataMapMutex); + std::lock_guard lock(_viewDependentDataMapMutex); ViewDependentDataMap::iterator itr = _viewDependentDataMap.find(cv); if (itr!=_viewDependentDataMap.end()) return itr->second.get(); @@ -1343,7 +1343,7 @@ void MWShadowTechnique::cull(osgUtil::CullVisitor& cv) std::string validRegionUniformName = "validRegionMatrix" + std::to_string(sm_i); osg::ref_ptr validRegionUniform; - OpenThreads::ScopedLock lock(_accessUniformsAndProgramMutex); + std::lock_guard lock(_accessUniformsAndProgramMutex); for (auto uniform : _uniforms) { @@ -1467,7 +1467,7 @@ void MWShadowTechnique::createShaders() unsigned int _baseTextureUnit = 0; - OpenThreads::ScopedLock lock(_accessUniformsAndProgramMutex); + std::lock_guard lock(_accessUniformsAndProgramMutex); _shadowCastingStateSet = new osg::StateSet; @@ -2980,7 +2980,7 @@ osg::StateSet* MWShadowTechnique::selectStateSetForRenderingShadow(ViewDependent osg::ref_ptr stateset = vdd.getStateSet(); - OpenThreads::ScopedLock lock(_accessUniformsAndProgramMutex); + std::lock_guard lock(_accessUniformsAndProgramMutex); vdd.getStateSet()->clear(); @@ -3057,7 +3057,7 @@ void MWShadowTechnique::resizeGLObjectBuffers(unsigned int /*maxSize*/) void MWShadowTechnique::releaseGLObjects(osg::State* state) const { - OpenThreads::ScopedLock lock(_viewDependentDataMapMutex); + std::lock_guard lock(_viewDependentDataMapMutex); for(ViewDependentDataMap::const_iterator itr = _viewDependentDataMap.begin(); itr != _viewDependentDataMap.end(); ++itr) diff --git a/components/sceneutil/mwshadowtechnique.hpp b/components/sceneutil/mwshadowtechnique.hpp index 85e548b4b3..77d0bd2b26 100644 --- a/components/sceneutil/mwshadowtechnique.hpp +++ b/components/sceneutil/mwshadowtechnique.hpp @@ -19,6 +19,8 @@ #ifndef COMPONENTS_SCENEUTIL_MWSHADOWTECHNIQUE_H #define COMPONENTS_SCENEUTIL_MWSHADOWTECHNIQUE_H 1 +#include + #include #include #include @@ -234,7 +236,7 @@ namespace SceneUtil { virtual ~MWShadowTechnique(); typedef std::map< osgUtil::CullVisitor*, osg::ref_ptr > ViewDependentDataMap; - mutable OpenThreads::Mutex _viewDependentDataMapMutex; + mutable std::mutex _viewDependentDataMapMutex; ViewDependentDataMap _viewDependentDataMap; osg::ref_ptr _shadowRecievingPlaceholderStateSet; @@ -245,7 +247,7 @@ namespace SceneUtil { osg::ref_ptr _fallbackShadowMapTexture; typedef std::vector< osg::ref_ptr > Uniforms; - mutable OpenThreads::Mutex _accessUniformsAndProgramMutex; + mutable std::mutex _accessUniformsAndProgramMutex; Uniforms _uniforms; osg::ref_ptr _program; diff --git a/components/sceneutil/statesetupdater.cpp b/components/sceneutil/statesetupdater.cpp index 08418f3312..9fb9ba496c 100644 --- a/components/sceneutil/statesetupdater.cpp +++ b/components/sceneutil/statesetupdater.cpp @@ -22,7 +22,7 @@ namespace SceneUtil } } - osg::StateSet* stateset = mStateSets[nv->getTraversalNumber()%2]; + osg::ref_ptr stateset = mStateSets[nv->getTraversalNumber()%2]; apply(stateset, nv); if (!isCullVisitor) diff --git a/components/shader/shadermanager.cpp b/components/shader/shadermanager.cpp index 4495e5b2e5..490c9d4383 100644 --- a/components/shader/shadermanager.cpp +++ b/components/shader/shadermanager.cpp @@ -273,7 +273,7 @@ namespace Shader osg::ref_ptr ShaderManager::getShader(const std::string &templateName, const ShaderManager::DefineMap &defines, osg::Shader::Type shaderType) { - OpenThreads::ScopedLock lock(mMutex); + std::lock_guard lock(mMutex); // read the template if we haven't already TemplateMap::iterator templateIt = mShaderTemplates.find(templateName); @@ -323,7 +323,7 @@ namespace Shader osg::ref_ptr ShaderManager::getProgram(osg::ref_ptr vertexShader, osg::ref_ptr fragmentShader) { - OpenThreads::ScopedLock lock(mMutex); + std::lock_guard lock(mMutex); ProgramMap::iterator found = mPrograms.find(std::make_pair(vertexShader, fragmentShader)); if (found == mPrograms.end()) { @@ -362,7 +362,7 @@ namespace Shader void ShaderManager::releaseGLObjects(osg::State *state) { - OpenThreads::ScopedLock lock(mMutex); + std::lock_guard lock(mMutex); for (auto shader : mShaders) { if (shader.second != nullptr) diff --git a/components/shader/shadermanager.hpp b/components/shader/shadermanager.hpp index 8c5d6dfac2..ed5bbc907b 100644 --- a/components/shader/shadermanager.hpp +++ b/components/shader/shadermanager.hpp @@ -3,6 +3,7 @@ #include #include +#include #include @@ -10,8 +11,6 @@ #include -#include - namespace Shader { @@ -63,7 +62,7 @@ namespace Shader typedef std::map, osg::ref_ptr >, osg::ref_ptr > ProgramMap; ProgramMap mPrograms; - OpenThreads::Mutex mMutex; + std::mutex mMutex; const osg::ref_ptr mShadowMapAlphaTestEnableUniform = new osg::Uniform(); const osg::ref_ptr mShadowMapAlphaTestDisableUniform = new osg::Uniform(); diff --git a/components/terrain/buffercache.cpp b/components/terrain/buffercache.cpp index 1734686def..5871b96bc0 100644 --- a/components/terrain/buffercache.cpp +++ b/components/terrain/buffercache.cpp @@ -2,8 +2,6 @@ #include -#include - #include #include "defs.hpp" @@ -180,7 +178,7 @@ namespace Terrain osg::ref_ptr BufferCache::getUVBuffer(unsigned int numVerts) { - OpenThreads::ScopedLock lock(mUvBufferMutex); + std::lock_guard lock(mUvBufferMutex); if (mUvBufferMap.find(numVerts) != mUvBufferMap.end()) { return mUvBufferMap[numVerts]; @@ -210,7 +208,7 @@ namespace Terrain osg::ref_ptr BufferCache::getIndexBuffer(unsigned int numVerts, unsigned int flags) { std::pair id = std::make_pair(numVerts, flags); - OpenThreads::ScopedLock lock(mIndexBufferMutex); + std::lock_guard lock(mIndexBufferMutex); if (mIndexBufferMap.find(id) != mIndexBufferMap.end()) { @@ -234,11 +232,11 @@ namespace Terrain void BufferCache::clearCache() { { - OpenThreads::ScopedLock lock(mIndexBufferMutex); + std::lock_guard lock(mIndexBufferMutex); mIndexBufferMap.clear(); } { - OpenThreads::ScopedLock lock(mUvBufferMutex); + std::lock_guard lock(mUvBufferMutex); mUvBufferMap.clear(); } } @@ -246,12 +244,12 @@ namespace Terrain void BufferCache::releaseGLObjects(osg::State *state) { { - OpenThreads::ScopedLock lock(mIndexBufferMutex); + std::lock_guard lock(mIndexBufferMutex); for (auto indexbuffer : mIndexBufferMap) indexbuffer.second->releaseGLObjects(state); } { - OpenThreads::ScopedLock lock(mUvBufferMutex); + std::lock_guard lock(mUvBufferMutex); for (auto uvbuffer : mUvBufferMap) uvbuffer.second->releaseGLObjects(state); } diff --git a/components/terrain/buffercache.hpp b/components/terrain/buffercache.hpp index 37563d2c67..929dbad6ff 100644 --- a/components/terrain/buffercache.hpp +++ b/components/terrain/buffercache.hpp @@ -6,6 +6,7 @@ #include #include +#include namespace Terrain { @@ -30,10 +31,10 @@ namespace Terrain // Index buffers are shared across terrain batches where possible. There is one index buffer for each // combination of LOD deltas and index buffer LOD we may need. std::map, osg::ref_ptr > mIndexBufferMap; - OpenThreads::Mutex mIndexBufferMutex; + std::mutex mIndexBufferMutex; std::map > mUvBufferMap; - OpenThreads::Mutex mUvBufferMutex; + std::mutex mUvBufferMutex; }; } diff --git a/components/terrain/compositemaprenderer.cpp b/components/terrain/compositemaprenderer.cpp index ee4a66fcde..2a3fa47daa 100644 --- a/components/terrain/compositemaprenderer.cpp +++ b/components/terrain/compositemaprenderer.cpp @@ -1,7 +1,5 @@ #include "compositemaprenderer.hpp" -#include - #include #include #include @@ -50,7 +48,7 @@ void CompositeMapRenderer::drawImplementation(osg::RenderInfo &renderInfo) const if (mWorkQueue) mUnrefQueue->flush(mWorkQueue.get()); - OpenThreads::ScopedLock lock(mMutex); + std::lock_guard lock(mMutex); if (mImmediateCompileSet.empty() && mCompileSet.empty()) return; @@ -177,7 +175,7 @@ void CompositeMapRenderer::setTargetFrameRate(float framerate) void CompositeMapRenderer::addCompositeMap(CompositeMap* compositeMap, bool immediate) { - OpenThreads::ScopedLock lock(mMutex); + std::lock_guard lock(mMutex); if (immediate) mImmediateCompileSet.insert(compositeMap); else @@ -186,7 +184,7 @@ void CompositeMapRenderer::addCompositeMap(CompositeMap* compositeMap, bool imme void CompositeMapRenderer::setImmediate(CompositeMap* compositeMap) { - OpenThreads::ScopedLock lock(mMutex); + std::lock_guard lock(mMutex); CompileSet::iterator found = mCompileSet.find(compositeMap); if (found == mCompileSet.end()) return; @@ -199,7 +197,7 @@ void CompositeMapRenderer::setImmediate(CompositeMap* compositeMap) unsigned int CompositeMapRenderer::getCompileSetSize() const { - OpenThreads::ScopedLock lock(mMutex); + std::lock_guard lock(mMutex); return mCompileSet.size(); } diff --git a/components/terrain/compositemaprenderer.hpp b/components/terrain/compositemaprenderer.hpp index 201130e303..9d5719c233 100644 --- a/components/terrain/compositemaprenderer.hpp +++ b/components/terrain/compositemaprenderer.hpp @@ -3,9 +3,8 @@ #include -#include - #include +#include namespace osg { @@ -76,7 +75,7 @@ namespace Terrain mutable CompileSet mCompileSet; mutable CompileSet mImmediateCompileSet; - mutable OpenThreads::Mutex mMutex; + mutable std::mutex mMutex; osg::ref_ptr mFBO; }; diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index e094330610..ca1dcb08ce 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -453,7 +453,7 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) void QuadTreeWorld::ensureQuadTreeBuilt() { - OpenThreads::ScopedLock lock(mQuadTreeMutex); + std::lock_guard lock(mQuadTreeMutex); if (mQuadTreeBuilt) return; diff --git a/components/terrain/quadtreeworld.hpp b/components/terrain/quadtreeworld.hpp index 47cf461387..4c05efe646 100644 --- a/components/terrain/quadtreeworld.hpp +++ b/components/terrain/quadtreeworld.hpp @@ -4,7 +4,7 @@ #include "world.hpp" #include "terraingrid.hpp" -#include +#include namespace osg { @@ -61,7 +61,7 @@ namespace Terrain std::vector mChunkManagers; - OpenThreads::Mutex mQuadTreeMutex; + std::mutex mQuadTreeMutex; bool mQuadTreeBuilt; float mLodFactor; int mVertexLodMod; diff --git a/docs/source/reference/modding/settings/camera.rst b/docs/source/reference/modding/settings/camera.rst index ecc1bda50f..20349e97fd 100644 --- a/docs/source/reference/modding/settings/camera.rst +++ b/docs/source/reference/modding/settings/camera.rst @@ -114,6 +114,17 @@ while small values can result in the hands not being visible. This setting can only be configured by editing the settings configuration file. +third person camera distance +---------------------------- + +:Type: floating point +:Range: 30-800 +:Default: 192.0 + +Distance from the camera to the character in third person mode. + +This setting can be changed in game using "Zoom In" / "Zoom Out" controls. + view over shoulder ------------------ diff --git a/docs/source/reference/modding/settings/game.rst b/docs/source/reference/modding/settings/game.rst index c27796aec6..8d0b0dfc14 100644 --- a/docs/source/reference/modding/settings/game.rst +++ b/docs/source/reference/modding/settings/game.rst @@ -330,3 +330,29 @@ If disabled then the whole character's body is pointed to the direction of view. If enabled then the character turns lower body to the direction of movement. Upper body is turned partially. Head is always pointed to the direction of view. In combat mode it works only for diagonal movement. In non-combat mode it also changes straight right and straight left movement. This setting can only be configured by editing the settings configuration file. + +swim upward coef +---------------- + +:Type: floating point +:Range: -1.0 to 1.0 +:Default: 0.0 + +Makes player swim a bit upward (or downward in case of negative value) from the line of sight. Intended to make simpler swimming without diving. Recommened range of values is from 0.0 to 0.2. + +This setting can only be configured by editing the settings configuration file. + +trainers training skills based on base skill +-------------------------------------------- + +:Type: boolean +:Range: True/False +:Default: False + +The trainers in Morrowind choose their proposed training skills based on their 3 best attributes. + +If disabled then the 3 best skills of trainers and the training limits take into account fortified/drained trainer skill. + +If enabled then the 3 best skills of trainers and the training limits are based on the trainer base skills. + +This setting can be controlled in Advanced tab of the launcher. diff --git a/docs/source/reference/modding/settings/input.rst b/docs/source/reference/modding/settings/input.rst index 090b279582..8a95686cfc 100644 --- a/docs/source/reference/modding/settings/input.rst +++ b/docs/source/reference/modding/settings/input.rst @@ -135,6 +135,18 @@ camera sensitivity setting. This setting can only be configured by editing the settings configuration file. +joystick dead zone +------------------ + +:Type: floating point +:Range: 0.0 to 0.5 +:Default: 0.1 + +This setting controls the radius of dead zone (where an input is discarded) for joystick axes. +Note that third-party software can provide its own dead zones. In this case OpenmW-specific setting dead zone can be disabled (0.0). + +This setting can only be configured by editing the settings configuration file. + enable gyroscope ---------------- diff --git a/extern/oics/ICSInputControlSystem.cpp b/extern/oics/ICSInputControlSystem.cpp index ccd09a04e7..c03c039a40 100644 --- a/extern/oics/ICSInputControlSystem.cpp +++ b/extern/oics/ICSInputControlSystem.cpp @@ -34,6 +34,7 @@ namespace ICS , DetectingBindingListener* detectingBindingListener , InputControlSystemLog* log, size_t channelCount) : mFileName(file) + , mDeadZone(0.1f) , mLog(log) , mDetectingBindingListener(detectingBindingListener) , mDetectingBindingControl(NULL) diff --git a/extern/oics/ICSInputControlSystem.h b/extern/oics/ICSInputControlSystem.h index 43e659d0a0..c49224dc2b 100644 --- a/extern/oics/ICSInputControlSystem.h +++ b/extern/oics/ICSInputControlSystem.h @@ -79,6 +79,8 @@ namespace ICS void setDetectingBindingListener(DetectingBindingListener* detectingBindingListener){ mDetectingBindingListener = detectingBindingListener; }; DetectingBindingListener* getDetectingBindingListener(){ return mDetectingBindingListener; }; + void setJoystickDeadZone(float deadZone){ mDeadZone = deadZone; }; + // in seconds void update(float timeSinceLastFrame); @@ -180,6 +182,8 @@ namespace ICS std::string mFileName; + float mDeadZone; + typedef std::map ControlsKeyBinderMapType; // typedef std::map ControlsAxisBinderMapType; // typedef std::map ControlsButtonBinderMapType; // diff --git a/extern/oics/ICSInputControlSystem_joystick.cpp b/extern/oics/ICSInputControlSystem_joystick.cpp index ed6e2f6647..38199436ba 100644 --- a/extern/oics/ICSInputControlSystem_joystick.cpp +++ b/extern/oics/ICSInputControlSystem_joystick.cpp @@ -27,8 +27,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "ICSInputControlSystem.h" #define SDL_JOY_AXIS_MIN -32768 -#define SDL_JOY_AXIS_MAX 32767 -#define DEADZONE 0.1f +#define SDL_JOY_AXIS_MAX 32767 namespace ICS { @@ -263,13 +262,13 @@ namespace ICS float axisRange = SDL_JOY_AXIS_MAX - SDL_JOY_AXIS_MIN; float valDisplaced = (float)(evt.value - SDL_JOY_AXIS_MIN); - float percent = valDisplaced / axisRange * (1+DEADZONE*2) - DEADZONE; //Assures all values, 0 through 1, are seen - if(percent > .5-DEADZONE && percent < .5+DEADZONE) //close enough to center + float percent = valDisplaced / axisRange * (1+mDeadZone*2) - mDeadZone; //Assures all values, 0 through 1, are seen + if(percent > .5-mDeadZone && percent < .5+mDeadZone) //close enough to center percent = .5; else if(percent > .5) - percent -= DEADZONE; + percent -= mDeadZone; else - percent += DEADZONE; + percent += mDeadZone; if(joystickBinderItem.direction == Control::INCREASE) { diff --git a/extern/osg-ffmpeg-videoplayer/videostate.cpp b/extern/osg-ffmpeg-videoplayer/videostate.cpp index 014c5c4f55..c176fe0f29 100644 --- a/extern/osg-ffmpeg-videoplayer/videostate.cpp +++ b/extern/osg-ffmpeg-videoplayer/videostate.cpp @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include @@ -95,14 +97,14 @@ void PacketQueue::put(AVPacket *pkt) this->last_pkt = pkt1; this->nb_packets++; this->size += pkt1->pkt.size; - this->cond.signal(); + this->cond.notify_one(); this->mutex.unlock(); } int PacketQueue::get(AVPacket *pkt, VideoState *is) { - OpenThreads::ScopedLock lock(this->mutex); + std::unique_lock lock(this->mutex); while(!is->mQuit) { AVPacketList *pkt1 = this->first_pkt; @@ -122,7 +124,7 @@ int PacketQueue::get(AVPacket *pkt, VideoState *is) if(this->flushing) break; - this->cond.wait(&this->mutex); + this->cond.wait(lock); } return -1; @@ -131,7 +133,7 @@ int PacketQueue::get(AVPacket *pkt, VideoState *is) void PacketQueue::flush() { this->flushing = true; - this->cond.signal(); + this->cond.notify_one(); } void PacketQueue::clear() @@ -226,7 +228,7 @@ void VideoState::video_display(VideoPicture *vp) void VideoState::video_refresh() { - OpenThreads::ScopedLock lock(this->pictq_mutex); + std::lock_guard lock(this->pictq_mutex); if(this->pictq_size == 0) return; @@ -238,7 +240,7 @@ void VideoState::video_refresh() this->pictq_rindex = (pictq_rindex+1) % VIDEO_PICTURE_ARRAY_SIZE; this->frame_last_pts = vp->pts; this->pictq_size--; - this->pictq_cond.signal(); + this->pictq_cond.notify_one(); } else { @@ -268,7 +270,7 @@ void VideoState::video_refresh() // update queue for next picture this->pictq_size--; this->pictq_rindex = (this->pictq_rindex+1) % VIDEO_PICTURE_ARRAY_SIZE; - this->pictq_cond.signal(); + this->pictq_cond.notify_one(); } } @@ -279,9 +281,9 @@ int VideoState::queue_picture(AVFrame *pFrame, double pts) /* wait until we have a new pic */ { - OpenThreads::ScopedLock lock(this->pictq_mutex); + std::unique_lock lock(this->pictq_mutex); while(this->pictq_size >= VIDEO_PICTURE_QUEUE_SIZE && !this->mQuit) - this->pictq_cond.wait(&this->pictq_mutex, 1); + this->pictq_cond.wait_for(lock, std::chrono::milliseconds(1)); } if(this->mQuit) return -1; @@ -340,16 +342,21 @@ double VideoState::synchronize_video(AVFrame *src_frame, double pts) return pts; } -class VideoThread : public OpenThreads::Thread +class VideoThread { public: VideoThread(VideoState* self) : mVideoState(self) + , mThread([this] { run(); }) { - start(); } - virtual void run() + ~VideoThread() + { + mThread.join(); + } + + void run() { VideoState* self = mVideoState; AVPacket pkt1, *packet = &pkt1; @@ -408,18 +415,24 @@ public: private: VideoState* mVideoState; + std::thread mThread; }; -class ParseThread : public OpenThreads::Thread +class ParseThread { public: ParseThread(VideoState* self) : mVideoState(self) + , mThread([this] { run(); }) { - start(); } - virtual void run() + ~ParseThread() + { + mThread.join(); + } + + void run() { VideoState* self = mVideoState; @@ -503,7 +516,7 @@ public: if((self->audio_st && self->audioq.size > MAX_AUDIOQ_SIZE) || (self->video_st && self->videoq.size > MAX_VIDEOQ_SIZE)) { - OpenThreads::Thread::microSleep(10 * 1000); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); continue; } @@ -534,6 +547,7 @@ public: private: VideoState* mVideoState; + std::thread mThread; }; @@ -712,12 +726,10 @@ void VideoState::deinit() if (this->parse_thread.get()) { - this->parse_thread->join(); this->parse_thread.reset(); } if (this->video_thread.get()) { - this->video_thread->join(); this->video_thread.reset(); } @@ -816,7 +828,7 @@ ExternalClock::ExternalClock() void ExternalClock::setPaused(bool paused) { - OpenThreads::ScopedLock lock(mMutex); + std::lock_guard lock(mMutex); if (mPaused == paused) return; if (paused) @@ -830,7 +842,7 @@ void ExternalClock::setPaused(bool paused) uint64_t ExternalClock::get() { - OpenThreads::ScopedLock lock(mMutex); + std::lock_guard lock(mMutex); if (mPaused) return mPausedAt; else @@ -839,7 +851,7 @@ uint64_t ExternalClock::get() void ExternalClock::set(uint64_t time) { - OpenThreads::ScopedLock lock(mMutex); + std::lock_guard lock(mMutex); mTimeBase = av_gettime() - time; mPausedAt = time; } diff --git a/extern/osg-ffmpeg-videoplayer/videostate.hpp b/extern/osg-ffmpeg-videoplayer/videostate.hpp index 54519f428d..984050cd6f 100644 --- a/extern/osg-ffmpeg-videoplayer/videostate.hpp +++ b/extern/osg-ffmpeg-videoplayer/videostate.hpp @@ -6,10 +6,8 @@ #include #include #include - -#include -#include -#include +#include +#include #include namespace osg @@ -64,7 +62,7 @@ struct ExternalClock uint64_t mPausedAt; bool mPaused; - OpenThreads::Mutex mMutex; + std::mutex mMutex; void setPaused(bool paused); uint64_t get(); @@ -83,8 +81,8 @@ struct PacketQueue { std::atomic nb_packets; std::atomic size; - OpenThreads::Mutex mutex; - OpenThreads::Condition cond; + std::mutex mutex; + std::condition_variable cond; void put(AVPacket *pkt); int get(AVPacket *pkt, VideoState *is); @@ -164,8 +162,8 @@ struct VideoState { VideoPicture pictq[VIDEO_PICTURE_ARRAY_SIZE]; AVFrame* rgbaFrame; // used as buffer for the frame converted from its native format to RGBA int pictq_size, pictq_rindex, pictq_windex; - OpenThreads::Mutex pictq_mutex; - OpenThreads::Condition pictq_cond; + std::mutex pictq_mutex; + std::condition_variable pictq_cond; std::unique_ptr parse_thread; std::unique_ptr video_thread; diff --git a/files/settings-default.cfg b/files/settings-default.cfg index bc07463a1a..1e62ff5bff 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -33,6 +33,9 @@ field of view = 60.0 # Best to leave this at the default since vanilla assets are not complete enough to adapt to high FoV's. Too low FoV would clip the hands off screen. first person field of view = 60.0 +# Distance from the camera to the character in third person mode. +third person camera distance = 192 + # If enabled then third person camera is positioned above character's shoulder and crosshair is visible. view over shoulder = false @@ -310,6 +313,12 @@ uncapped damage fatigue = false # Turn lower body to movement direction. 'true' makes diagonal movement more realistic. turn to movement direction = false +# Makes player swim a bit upward (or downward in case of negative value) from the line of sight. +swim upward coef = 0.0 + +# Make the training skills proposed by a trainer based on its base attribute instead of its modified ones +trainers training skills based on base skill = false + [General] # Anisotropy reduces distortion in textures at low angles (e.g. 0 to 16). @@ -418,6 +427,9 @@ enable controller = true # Emulated gamepad cursor sensitivity. gamepad cursor speed = 1.0 +# Set dead zone for joysticks (gamepad or on-screen ones) +joystick dead zone = 0.1 + # Enable gyroscope support. enable gyroscope = false diff --git a/files/ui/advancedpage.ui b/files/ui/advancedpage.ui index fed547b8cd..142cd72594 100644 --- a/files/ui/advancedpage.ui +++ b/files/ui/advancedpage.ui @@ -236,6 +236,16 @@ + + + + <html><head/><body><p>Trainers now only choose which skills to train using their base skill points, allowing mercantile improving effects to be used without making mercantile an offered skill.</p></body></html> + + + Trainers choose their training skills based on their base skill points + + + @@ -442,7 +452,7 @@ - <html><head/><body><p>If this setting is true, melee weapons reach and speed will be showed on item tooltip.</p><p>The default value is false.</p></body></html> + <html><head/><body><p>If this setting is true, melee weapons reach and speed will be shown on item tooltip.</p><p>The default value is false.</p></body></html> Show melee info @@ -452,7 +462,7 @@ - <html><head/><body><p>If this setting is true, damage bonus of arrows and bolts will be showed on item tooltip.</p><p>The default value is false.</p></body></html> + <html><head/><body><p>If this setting is true, damage bonus of arrows and bolts will be shown on item tooltip.</p><p>The default value is false.</p></body></html> Show projectile damage @@ -462,7 +472,7 @@ - <html><head/><body><p>If this setting is true, dialogue topics will have a different color if the topic is specific to the NPC you're talking to or the topic was previously seen. Color can be change in settings.cfg.</p><p>The default value is false.</p></body></html> + <html><head/><body><p>If this setting is true, dialogue topics will have a different color if the topic is specific to the NPC you're talking to or the topic was previously seen. Color can be changed in settings.cfg.</p><p>The default value is false.</p></body></html> Change dialogue topic color