Merge remote-tracking branch 'remotes/origin/master' into openxr_vr

pull/615/head
Mads Buvik Sandvei 5 years ago
commit c9e761eb88

@ -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/
@ -41,26 +46,184 @@ MacOS:
paths:
- build/OpenMW-*.dmg
Windows:
variables: &engine-targets
targets: "openmw,openmw-essimporter,openmw-iniimporter,openmw-launcher,openmw-wizard"
variables: &cs-targets
targets: "openmw-cs,bsatool,esmtool,niftest"
.Windows_Ninja_Base:
tags:
- windows
before_script:
- Import-Module "$env:ChocolateyInstall\helpers\chocolateyProfile.psm1"
- choco install git --force --params "/GitAndUnixToolsOnPath" -y
- choco install 7zip -y
- choco install cmake.install --installargs 'ADD_CMAKE_TO_PATH=System' -y
- choco install vswhere -y
- choco install ninja -y
- choco install python -y
- refreshenv
stage: build
allow_failure: true
script:
- Set-Variable -Name "time" -Value (date -Format "%H:%m")
- $time = (Get-Date -Format "HH:mm:ss")
- echo ${time}
- echo "started by ${GITLAB_USER_NAME}"
# TODO: to anyone wanting to do further work here, we need to figure out how to get the below working
# TODO: on gitlab's new shared windows runners. They currently don't have bash or anything else installed
# TODO: it is currently just a bare windows 10 with powershell.
# - env # turn on for debugging
# - sh %CI_PROJECT_DIR%/CI/before_script.msvc.sh -c Release -p x64 -v 2017 -V
# - SET msBuildLocation="C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\msbuild.exe"
# - call %msBuildLocation% MSVC2017_64\OpenMW.sln /t:Build /p:Configuration=Release /m:%NUMBER_OF_PROCESSORS%
# - 7z a OpenMW_MSVC2017_64_%CI_BUILD_REF_NAME%_%CI_BUILD_ID%.zip %CI_PROJECT_DIR%\MSVC2017_64\Release\
- sh CI/before_script.msvc.sh -c $config -p Win64 -v 2019 -k -V -N
- cd MSVC2019_64_Ninja
- .\ActivateMSVC.ps1
- cmake --build . --config $config --target ($targets.Split(','))
- cd $config
- |
if (Get-ChildItem -Recurse *.pdb) {
7z a -tzip ..\..\OpenMW_MSVC2019_64_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}_symbols.zip '*.pdb'
Get-ChildItem -Recurse *.pdb | Remove-Item
}
- 7z a -tzip ..\..\OpenMW_MSVC2019_64_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}.zip '*'
cache:
key: ninja-v2
paths:
- deps
- MSVC2019_64_Ninja/deps/Qt
artifacts:
when: always
paths:
- "*.zip"
- "*.log"
- MSVC2019_64_Ninja/*.log
- MSVC2019_64_Ninja/*/*.log
- MSVC2019_64_Ninja/*/*/*.log
- MSVC2019_64_Ninja/*/*/*/*.log
- MSVC2019_64_Ninja/*/*/*/*/*.log
- MSVC2019_64_Ninja/*/*/*/*/*/*.log
- MSVC2019_64_Ninja/*/*/*/*/*/*/*.log
- MSVC2019_64_Ninja/*/*/*/*/*/*/*/*.log
Windows_Ninja_Engine_Release:
extends:
- .Windows_Ninja_Base
variables:
<<: *engine-targets
config: "Release"
Windows_Ninja_Engine_Debug:
extends:
- .Windows_Ninja_Base
variables:
<<: *engine-targets
config: "Debug"
Windows_Ninja_Engine_RelWithDebInfo:
extends:
- .Windows_Ninja_Base
variables:
<<: *engine-targets
config: "RelWithDebInfo"
Windows_Ninja_CS_Release:
extends:
- .Windows_Ninja_Base
variables:
<<: *cs-targets
config: "Release"
Windows_Ninja_CS_Debug:
extends:
- .Windows_Ninja_Base
variables:
<<: *cs-targets
config: "Debug"
Windows_Ninja_CS_RelWithDebInfo:
extends:
- .Windows_Ninja_Base
variables:
<<: *cs-targets
config: "RelWithDebInfo"
.Windows_MSBuild_Base:
tags:
- windows
before_script:
- Import-Module "$env:ChocolateyInstall\helpers\chocolateyProfile.psm1"
- choco install git --force --params "/GitAndUnixToolsOnPath" -y
- choco install 7zip -y
- choco install cmake.install --installargs 'ADD_CMAKE_TO_PATH=System' -y
- choco install vswhere -y
- choco install python -y
- refreshenv
stage: build
script:
- $time = (Get-Date -Format "HH:mm:ss")
- echo ${time}
- echo "started by ${GITLAB_USER_NAME}"
- sh CI/before_script.msvc.sh -c $config -p Win64 -v 2019 -k -V
- cd MSVC2019_64
- cmake --build . --config $config --target ($targets.Split(','))
- cd $config
- |
if (Get-ChildItem -Recurse *.pdb) {
7z a -tzip ..\..\OpenMW_MSVC2019_64_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}_symbols.zip '*.pdb'
Get-ChildItem -Recurse *.pdb | Remove-Item
}
- 7z a -tzip ..\..\OpenMW_MSVC2019_64_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}.zip '*'
cache:
key: msbuild-v2
paths:
- deps
- MSVC2019_64/deps/Qt
artifacts:
when: always
paths:
- "*.zip"
- "*.log"
- MSVC2019_64/*.log
- MSVC2019_64/*/*.log
- MSVC2019_64/*/*/*.log
- MSVC2019_64/*/*/*/*.log
- MSVC2019_64/*/*/*/*/*.log
- MSVC2019_64/*/*/*/*/*/*.log
- MSVC2019_64/*/*/*/*/*/*/*.log
- MSVC2019_64/*/*/*/*/*/*/*/*.log
Windows_MSBuild_Engine_Release:
extends:
- .Windows_MSBuild_Base
variables:
<<: *engine-targets
config: "Release"
Windows_MSBuild_Engine_Debug:
extends:
- .Windows_MSBuild_Base
variables:
<<: *engine-targets
config: "Debug"
Windows_MSBuild_Engine_RelWithDebInfo:
extends:
- .Windows_MSBuild_Base
variables:
<<: *engine-targets
config: "RelWithDebInfo"
Windows_MSBuild_CS_Release:
extends:
- .Windows_MSBuild_Base
variables:
<<: *cs-targets
config: "Release"
Windows_MSBuild_CS_Debug:
extends:
- .Windows_MSBuild_Base
variables:
<<: *cs-targets
config: "Debug"
Windows_MSBuild_CS_RelWithDebInfo:
extends:
- .Windows_MSBuild_Base
variables:
<<: *cs-targets
config: "RelWithDebInfo"

@ -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}"

@ -8,6 +8,7 @@
Bug #3714: Savegame fails to load due to conflict between SpellState and MagicEffects
Bug #4021: Attributes and skills are not stored as floats
Bug #4623: Corprus implementation is incorrect
Bug #4764: Data race in osg ParticleSystem
Bug #4774: Guards are ignorant of an invisible player that tries to attack them
Bug #5108: Savegame bloating due to inefficient fog textures format
Bug #5165: Active spells should use real time intead of timestamps
@ -17,6 +18,8 @@
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 #5387: Move/MoveWorld don't update the object's cell properly
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 +32,24 @@
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
Bug #5531: Actors flee using current rotation by axis x
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
Feature #5524: Resume failed script execution after reload
Feature #5525: Search fields tweaks (utf-8)
Task #5480: Drop Qt4 support
0.46.0

@ -39,6 +39,10 @@ originalIFS="$IFS"
IFS=$'\n\r'
for pair in $(cmd //c "set"); do
IFS='=' read -r -a separatedPair <<< "${pair}"
if [ ${#separatedPair[@]} -ne 2 ]; then
echo "Parsed '$pair' as ${#separatedPair[@]} parts, expected 2."
continue
fi
originalCmdEnv["${separatedPair[0]}"]="${separatedPair[1]}"
done
@ -49,6 +53,10 @@ declare -A cmdEnvChanges
for pair in $cmdEnv; do
if [ -n "$pair" ]; then
IFS='=' read -r -a separatedPair <<< "${pair}"
if [ ${#separatedPair[@]} -ne 2 ]; then
echo "Parsed '$pair' as ${#separatedPair[@]} parts, expected 2."
continue
fi
key="${separatedPair[0]}"
value="${separatedPair[1]}"
if ! [ ${originalCmdEnv[$key]+_} ] || [ "${originalCmdEnv[$key]}" != "$value" ]; then

@ -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

@ -621,7 +621,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

@ -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" \
..

@ -312,7 +312,7 @@ include_directories("."
${Boost_INCLUDE_DIR}
${MyGUI_INCLUDE_DIRS}
${OPENAL_INCLUDE_DIR}
${Bullet_INCLUDE_DIRS}
${BULLET_INCLUDE_DIRS}
)
if(BUILD_VR_OPENXR)

@ -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");

@ -216,7 +216,6 @@ endif(APPLE)
target_link_libraries(openmw-cs
${OSG_LIBRARIES}
${OPENTHREADS_LIBRARIES}
${OSGTEXT_LIBRARIES}
${OSGUTIL_LIBRARIES}
${OSGVIEWER_LIBRARIES}

@ -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
{

@ -1,5 +1,8 @@
#include "scenewidget.hpp"
#include <chrono>
#include <thread>
#include <QEvent>
#include <QResizeEvent>
#include <QTimer>
@ -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<double>(minFrameTime - dt));
}
}

@ -20,7 +20,7 @@ source_group(game FILES ${GAME} ${GAME_HEADER})
add_openmw_dir (mwrender
actors objects renderingmanager animation rotatecontroller sky npcanimation vismask
creatureanimation effectmanager util renderinginterface pathgrid rendermode weaponanimation
bulletdebugdraw globalmap characterpreview camera localmap water terrainstorage ripplesimulation
bulletdebugdraw globalmap characterpreview camera viewovershoulder localmap water terrainstorage ripplesimulation
renderbin actoranimation landmanager navmesh actorspaths recastmesh fogmanager objectpaging
)
@ -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
@ -103,7 +103,6 @@ add_openmw_dir (mwbase
set(OPENMW_LINK_TARGETS
${OSG_LIBRARIES}
${OPENTHREADS_LIBRARIES}
${OSGPARTICLE_LIBRARIES}
${OSGUTIL_LIBRARIES}
${OSGDB_LIBRARIES}

@ -2,6 +2,8 @@
#include <iomanip>
#include <fstream>
#include <chrono>
#include <thread>
#include <boost/filesystem/fstream.hpp>
@ -73,6 +75,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 <UserStatsType type>
struct UserStatsValue
{
static const UserStats sValue;
};
template <>
const UserStats UserStatsValue<UserStatsType::Input>::sValue {"Input", "input"};
template <>
const UserStats UserStatsValue<UserStatsType::Sound>::sValue {"Sound", "sound"};
template <>
const UserStats UserStatsValue<UserStatsType::State>::sValue {"State", "state"};
template <>
const UserStats UserStatsValue<UserStatsType::Script>::sValue {"Script", "script"};
template <>
const UserStats UserStatsValue<UserStatsType::Mechanics>::sValue {"Mech", "mechanics"};
template <>
const UserStats UserStatsValue<UserStatsType::Physics>::sValue {"Phys", "physics"};
template <>
const UserStats UserStatsValue<UserStatsType::World>::sValue {"World", "world"};
template <>
const UserStats UserStatsValue<UserStatsType::Gui>::sValue {"Gui", "gui"};
template <UserStatsType type>
struct ForEachUserStatsValue
{
template <class F>
static void apply(F&& f)
{
f(UserStatsValue<type>::sValue);
using Next = ForEachUserStatsValue<static_cast<UserStatsType>(static_cast<std::size_t>(type) + 1)>;
Next::apply(std::forward<F>(f));
}
};
template <>
struct ForEachUserStatsValue<UserStatsType::Number>
{
template <class F>
static void apply(F&&) {}
};
template <class F>
void forEachUserStatsValue(F&& f)
{
ForEachUserStatsValue<static_cast<UserStatsType>(0)>::apply(std::forward<F>(f));
}
template <UserStatsType sType>
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<sType>::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()
@ -93,119 +229,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<UserStatsType::Input> 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<UserStatsType::Sound> 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<UserStatsType::State> 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)
ScopedProfile<UserStatsType::Script> profile(frameStart, frameNumber, *timer, *stats);
if (mEnvironment.getStateManager()->getState() != MWBase::StateManager::State_NoGame)
{
if (mEnvironment.getWorld()->getScriptsEnabled())
if (!paused)
{
// local scripts
executeLocalScripts();
if (mEnvironment.getWorld()->getScriptsEnabled())
{
// local scripts
executeLocalScripts();
// global scripts
mEnvironment.getScriptManager()->getGlobalScripts().run();
}
// 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)
{
double hours = (frametime * mEnvironment.getWorld()->getTimeScaleFactor()) / 3600.0;
mEnvironment.getWorld()->advanceTime(hours, true);
mEnvironment.getWorld()->rechargeItems(frametime, true);
}
}
}
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)
// update mechanics
{
mEnvironment.getMechanicsManager()->update(frametime,
guiActive);
}
osg::Timer_t afterMechanicsTick = osg::Timer::instance()->tick();
ScopedProfile<UserStatsType::Mechanics> profile(frameStart, frameNumber, *timer, *stats);
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_NoGame)
{
mEnvironment.getMechanicsManager()->update(frametime, guiActive);
}
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<UserStatsType::Physics> 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<UserStatsType::World> 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<UserStatsType::Gui> profile(frameStart, frameNumber, *timer, *stats);
mEnvironment.getWindowManager()->update(frametime);
}
if (stats->collectStats("resource"))
{
@ -218,7 +354,6 @@ bool OMW::Engine::frame(float frametime)
mEnvironment.reportStats(frameNumber, *stats);
}
}
catch (const std::exception& e)
{
@ -261,8 +396,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()
@ -724,14 +857,7 @@ void OMW::Engine::go()
// Setup profiler
osg::ref_ptr<Resource::Profiler> 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);
@ -783,7 +909,7 @@ void OMW::Engine::go()
if (!frame(dt))
{
OpenThreads::Thread::microSleep(5000);
std::this_thread::sleep_for(std::chrono::milliseconds(5));
continue;
}
else
@ -803,7 +929,14 @@ void OMW::Engine::go()
if (stats)
{
const auto frameNumber = mViewer->getFrameStamp()->getFrameNumber();
mViewer->getViewerStats()->report(stats, frameNumber);
if (frameNumber >= 2)
{
mViewer->getViewerStats()->report(stats, frameNumber - 2);
osgViewer::Viewer::Cameras cameras;
mViewer->getCameras(cameras);
for (auto camera : cameras)
camera->getStats()->report(stats, frameNumber - 2);
}
}
mEnvironment.limitFrameRate(frameTimer.time_s());

@ -115,8 +115,6 @@ namespace OMW
bool mScriptBlacklistUse;
bool mNewGame;
osg::Timer_t mStartTick;
// not implemented
Engine (const Engine&);
Engine& operator= (const Engine&);

@ -1,8 +1,8 @@
#include "environment.hpp"
#include <cassert>
#include <OpenThreads/Thread>
#include <chrono>
#include <thread>
#include "world.hpp"
#include "scriptmanager.hpp"
@ -99,7 +99,7 @@ void MWBase::Environment::limitFrameRate(double dt) const
double minFrameTime = 1.0 / static_cast<double>(mFrameRateLimit);
if (thisFrameTime < minFrameTime)
{
OpenThreads::Thread::microSleep(1000*1000*(minFrameTime-thisFrameTime));
std::this_thread::sleep_for(std::chrono::duration<double>(minFrameTime - thisFrameTime));
}
}
}

@ -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
///

@ -35,6 +35,8 @@ namespace MWBase
virtual ~ScriptManager() {}
virtual void clear() = 0;
virtual bool run (const std::string& name, Interpreter::Context& interpreterContext) = 0;
///< Run the script with the given name (compile first, if not compiled yet)

@ -6,6 +6,7 @@
#include <set>
#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<int>(a); }
inline int operator&(Type a, Type b) { return static_cast<int>(a) & static_cast<int>(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

@ -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<float>& 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
@ -254,6 +238,8 @@ namespace MWBase
virtual void update (float duration) = 0;
virtual void updateConsoleObjectPtr(const MWWorld::Ptr& currentPtr, const MWWorld::Ptr& newPtr) = 0;
/**
* Fetches a GMST string from the store, if there is no setting with the given
* ID or it is not a string the default string is returned.
@ -360,6 +346,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;
};
}

@ -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<ESM::Class>().find(mGenerateClass);
mPlayerClass = *klass;
MWBase::Environment::get().getWindowManager()->setPlayerClass(mPlayerClass);
updatePlayerHealth();
}

@ -1,13 +1,12 @@
#ifndef CHARACTER_CREATION_HPP
#define CHARACTER_CREATION_HPP
#include <components/esm/loadskil.hpp>
#include <components/esm/loadclas.hpp>
#include <map>
#include <vector>
#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<int> 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<float>& 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<float>& 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);

@ -472,6 +472,12 @@ namespace MWGui
setCoord(10,10, width-10, height/2);
}
void Console::updateSelectedObjectPtr(const MWWorld::Ptr& currentPtr, const MWWorld::Ptr& newPtr)
{
if (mPtr == currentPtr)
mPtr = newPtr;
}
void Console::setSelectedObject(const MWWorld::Ptr& object)
{
if (!object.isEmpty())

@ -58,6 +58,8 @@ namespace MWGui
void executeFile (const std::string& path);
void updateSelectedObjectPtr(const MWWorld::Ptr& currentPtr, const MWWorld::Ptr& newPtr);
void clear();
virtual void resetReference ();

@ -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<float>& value);
void setValue (const std::string& id, const MWMechanics::DynamicStat<float>& 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;
@ -115,8 +114,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();

@ -69,8 +69,8 @@ namespace
return compareType(leftName, rightName);
// compare items by name
leftName = Misc::StringUtils::lowerCase(left.mBase.getClass().getName(left.mBase));
rightName = Misc::StringUtils::lowerCase(right.mBase.getClass().getName(right.mBase));
leftName = Misc::StringUtils::lowerCaseUtf8(left.mBase.getClass().getName(left.mBase));
rightName = Misc::StringUtils::lowerCaseUtf8(right.mBase.getClass().getName(right.mBase));
result = leftName.compare(rightName);
if (result != 0)
@ -213,7 +213,7 @@ namespace MWGui
if (!mNameFilter.empty())
{
const auto itemName = Misc::StringUtils::lowerCase(base.getClass().getName(base));
const auto itemName = Misc::StringUtils::lowerCaseUtf8(base.getClass().getName(base));
return itemName.find(mNameFilter) != std::string::npos;
}
@ -226,7 +226,7 @@ namespace MWGui
for (const auto& effect : effects)
{
const auto ciEffect = Misc::StringUtils::lowerCase(effect);
const auto ciEffect = Misc::StringUtils::lowerCaseUtf8(effect);
if (ciEffect.find(mEffectFilter) != std::string::npos)
return true;
@ -285,7 +285,7 @@ namespace MWGui
return false;
}
std::string compare = Misc::StringUtils::lowerCase(item.mBase.getClass().getName(item.mBase));
std::string compare = Misc::StringUtils::lowerCaseUtf8(item.mBase.getClass().getName(item.mBase));
if(compare.find(mNameFilter) == std::string::npos)
return false;
@ -318,12 +318,12 @@ namespace MWGui
void SortFilterItemModel::setNameFilter (const std::string& filter)
{
mNameFilter = Misc::StringUtils::lowerCase(filter);
mNameFilter = Misc::StringUtils::lowerCaseUtf8(filter);
}
void SortFilterItemModel::setEffectFilter (const std::string& filter)
{
mEffectFilter = Misc::StringUtils::lowerCase(filter);
mEffectFilter = Misc::StringUtils::lowerCaseUtf8(filter);
}
void SortFilterItemModel::update()

@ -0,0 +1,188 @@
#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<ESM::GameSetting>()
.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<ESM::NPC>()->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<ESM::Race>().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<ESM::Class>().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;
}
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<float>& 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<int>& major, const std::vector<int>& minor)
{
for (StatsListener* listener : mListeners)
listener->configureSkills(major, minor);
}
}

@ -0,0 +1,69 @@
#ifndef MWGUI_STATSWATCHER_H
#define MWGUI_STATSWATCHER_H
#include <set>
#include <components/esm/attr.hpp>
#include <components/esm/loadskil.hpp>
#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<float>& 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<int>& major, const std::vector<int>& minor) {}
};
class StatsWatcher
{
MWWorld::Ptr mWatched;
MWMechanics::AttributeValue mWatchedAttributes[ESM::Attribute::Length];
MWMechanics::SkillValue mWatchedSkills[ESM::Skill::Length];
MWMechanics::DynamicStat<float> mWatchedHealth;
MWMechanics::DynamicStat<float> mWatchedMagicka;
MWMechanics::DynamicStat<float> mWatchedFatigue;
std::string mWatchedName;
std::string mWatchedRace;
std::string mWatchedClass;
int mWatchedLevel;
float mWatchedTimeToStartDrowning;
bool mWatchedStatsEmpty;
std::set<StatsListener*> mListeners;
void setValue(const std::string& id, const MWMechanics::AttributeValue& value);
void setValue(const std::string& id, const MWMechanics::DynamicStat<float>& 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<int>& major, const std::vector<int>& 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

@ -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 <components/esm/loadskil.hpp>
namespace MWGui
{
class WindowManager;
class StatsWindow : public WindowPinnableBase, public NoDrop
class StatsWindow : public WindowPinnableBase, public NoDrop, public StatsListener
{
public:
typedef std::map<std::string, int> 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<float>& 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<float>& 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<MyGUI::Window>()); }
virtual void onOpen() override { onWindowResize(mMainWidget->castType<MyGUI::Window>()); }
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

@ -14,6 +14,8 @@
#include "../mwmechanics/npcstats.hpp"
#include "../mwmechanics/actorutil.hpp"
#include <components/settings/settings.hpp>
#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<int, float> > skills;
MWMechanics::NpcStats const& actorStats(actor.getClass().getNpcStats(actor));
for (int i=0; i<ESM::Skill::Length; ++i)
{
float value = actor.getClass().getSkill(actor, i);
float value = getSkillForTraining(actorStats, i);
skills.push_back(std::make_pair(i, value));
}
@ -140,7 +144,7 @@ namespace MWGui
if (price > 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();

@ -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'
};
}

@ -1,6 +1,8 @@
#include "windowmanagerimp.hpp"
#include <cassert>
#include <chrono>
#include <thread>
#include <osgViewer/Viewer>
@ -286,6 +288,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()
@ -480,6 +484,10 @@ namespace MWGui
// Set up visibility
updateVisible();
mStatsWatcher->addListener(mHud);
mStatsWatcher->addListener(mStatsWindow);
mStatsWatcher->addListener(mCharGen);
}
int WindowManager::getFontHeight() const
@ -492,8 +500,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);
@ -503,6 +514,8 @@ namespace MWGui
{
try
{
mStatsWatcher.reset();
mKeyboardNavigation.reset();
MyGUI::LanguageManager::getInstance().eventRequestTag.clear();
@ -575,6 +588,11 @@ namespace MWGui
}
}
void WindowManager::updateConsoleObjectPtr(const MWWorld::Ptr& currentPtr, const MWWorld::Ptr& newPtr)
{
mConsole->updateSelectedObjectPtr(currentPtr, newPtr);
}
void WindowManager::updateVisible()
{
bool loading = (getMode() == GM_Loading || getMode() == GM_LoadingWallpaper);
@ -663,58 +681,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<ESM::Skill::SkillEnum> (parSkill), value);
mCharGen->setValue(static_cast<ESM::Skill::SkillEnum> (parSkill), value);
}
void WindowManager::setValue (const std::string& id, const MWMechanics::DynamicStat<float>& 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)
@ -767,7 +738,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();
@ -920,7 +891,9 @@ namespace MWGui
if (mCharGen)
mCharGen->onFrame(frameDuration);
updateActivatedQuickKey ();
updateActivatedQuickKey();
mStatsWatcher->update();
cleanupGarbage();
}
@ -1820,7 +1793,7 @@ namespace MWGui
if (!mWindowVisible)
{
mVideoWidget->pause();
OpenThreads::Thread::microSleep(5000);
std::this_thread::sleep_for(std::chrono::milliseconds(5));
}
else
{
@ -2246,4 +2219,14 @@ namespace MWGui
for (unsigned int i=0; i<mWindows.size(); ++i)
mWindows[i]->setVisible(visible);
}
void WindowManager::watchActor(const MWWorld::Ptr& ptr)
{
mStatsWatcher->watchActor(ptr);
}
MWWorld::Ptr WindowManager::getWatchedActor() const
{
return mStatsWatcher->getWatchedActor();
}
}

@ -13,13 +13,12 @@
#include "../mwbase/windowmanager.hpp"
#include "../mwworld/ptr.hpp"
#include <components/sdlutil/events.hpp>
#include <components/settings/settings.hpp>
#include <components/to_utf8/to_utf8.hpp>
#include "mapwindow.hpp"
#include "statswatcher.hpp"
#include "textcolours.hpp"
#include <MyGUI_KeyCode.h>
@ -197,22 +196,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<float>& 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);
@ -256,6 +244,8 @@ namespace MWGui
virtual void unsetSelectedSpell();
virtual void unsetSelectedWeapon();
virtual void updateConsoleObjectPtr(const MWWorld::Ptr& currentPtr, const MWWorld::Ptr& newPtr);
virtual void showCrosshair(bool show);
virtual bool getSubtitlesEnabled();
@ -300,6 +290,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; }
@ -403,6 +396,7 @@ namespace MWGui
osgViewer::Viewer* mViewer;
std::unique_ptr<Gui::FontLoader> mFontLoader;
std::unique_ptr<StatsWatcher> mStatsWatcher;
bool mConsoleOnlyScripts;

@ -234,6 +234,11 @@ namespace MWInput
}
}
void BindingsManager::setJoystickDeadZone(float deadZone)
{
mInputBinder->setJoystickDeadZone(deadZone);
}
float BindingsManager::getActionValue (int id) const
{
return mInputBinder->getChannel(id)->getValue();

@ -41,6 +41,8 @@ namespace MWInput
void setPlayerControlsEnabled(bool enabled);
void setJoystickDeadZone(float deadZone);
bool isLeftOrRightButton(int action, bool joystick) const;
bool actionIsActive(int id) const;

@ -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<int>(-zAxis * dt * 1500.0f);
mMouseManager->injectMouseMove(xMove, yMove, mouseWheelMove);
mMouseManager->warpMouse();
MWBase::Environment::get().getWindowManager()->setCursorActive(true);

@ -229,10 +229,10 @@ namespace MWInput
bool MouseManager::injectMouseButtonRelease(Uint8 button)
{
return MyGUI::InputManager::getInstance().injectMousePress(static_cast<int>(mGuiCursorX), static_cast<int>(mGuiCursorY), sdlButtonToMyGUI(button));
return MyGUI::InputManager::getInstance().injectMouseRelease(static_cast<int>(mGuiCursorX), static_cast<int>(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<int>(mGuiCursorX), static_cast<int>(mGuiCursorY), mMouseWheel);
MyGUI::InputManager::getInstance().injectMouseMove(static_cast<int>(mGuiCursorX), static_cast<int>(mGuiCursorY), static_cast<int>(mMouseWheel));
}
void MouseManager::warpMouse()

@ -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; }

@ -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();

@ -344,6 +344,7 @@ namespace MWMechanics
{
storage.mFleeBlindRunTimer += duration;
storage.mMovement.mRotation[0] = -actor.getRefData().getPosition().rot[0];
storage.mMovement.mRotation[2] = osg::PI + getZAngleToDir(target.getRefData().getPosition().asVec3()-actor.getRefData().getPosition().asVec3());
storage.mMovement.mPosition[1] = 1;
updateActorsMovement(actor, duration, storage);

@ -1925,6 +1925,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();
@ -1988,7 +1989,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)
@ -2217,8 +2218,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;
@ -2242,6 +2242,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)
{

@ -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,136 +302,37 @@ 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<ESM::GameSetting>()
.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;
// Note: we should do it here since game mechanics and world updates use these values
MWWorld::Ptr ptr = getPlayer();
MWBase::WindowManager *winMgr = MWBase::Environment::get().getWindowManager();
// 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 equipped weapon icon
MWWorld::InventoryStore& inv = ptr.getClass().getInventoryStore(ptr);
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);
// 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, ptr)));
else
{
const std::string& spell = winMgr->getSelectedSpell();
if (!spell.empty())
winMgr->setSelectedSpell(spell, int(getSpellSuccessChance(spell, mWatched)));
else
winMgr->unsetSelectedSpell();
}
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<ESM::NPC>()->mBase;
const ESM::Race *race =
world->getStore().get<ESM::Race>().find(player->mRace);
const ESM::Class *cls =
world->getStore().get<ESM::Class>().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();
mActors.removeActor(ptr);
mActors.addActor(ptr, true);
}
@ -517,7 +409,7 @@ namespace MWMechanics
int MechanicsManager::getHoursToRest() const
{
return mActors.getHoursToRest(mWatched);
return mActors.getHoursToRest(getPlayer());
}
void MechanicsManager::setPlayerName (const std::string& name)

@ -21,20 +21,6 @@ namespace MWMechanics
{
class MechanicsManager : public MWBase::MechanicsManager
{
MWWorld::Ptr mWatched;
AttributeValue mWatchedAttributes[8];
SkillValue mWatchedSkills[27];
DynamicStat<float> mWatchedHealth;
DynamicStat<float> mWatchedMagicka;
DynamicStat<float> 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
///

@ -624,6 +624,7 @@ namespace MWRender
, mHeadPitchRadians(0.f)
, mUpperBodyYawRadians(0.f)
, mLegsYawRadians(0.f)
, mBodyPitchRadians(0.f)
, mHasMagicEffects(false)
, mAlpha(1.f)
{
@ -1394,11 +1395,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;
}
}
@ -1539,6 +1540,8 @@ namespace MWRender
{
if (mLightListCallback)
mObjectRoot->removeCullCallback(mLightListCallback);
if (mTransparencyUpdater)
mObjectRoot->removeCullCallback(mTransparencyUpdater);
previousStateset = mObjectRoot->getStateSet();
mObjectRoot->getParent(0)->removeChild(mObjectRoot);
}
@ -1630,6 +1633,8 @@ namespace MWRender
if (!mLightListCallback)
mLightListCallback = new SceneUtil::LightListCallback;
mObjectRoot->addCullCallback(mLightListCallback);
if (mTransparencyUpdater)
mObjectRoot->addCullCallback(mTransparencyUpdater);
}
osg::Group* Animation::getObjectRoot()

@ -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; }

@ -3,6 +3,7 @@
#include <osg/Camera>
#include <components/sceneutil/positionattitudetransform.hpp>
#include <components/settings/settings.hpp>
#include <components/sceneutil/visitor.hpp>
#include "../mwbase/environment.hpp"
@ -63,14 +64,21 @@ 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),
mCameraDistance(0.f),
mThirdPersonMode(ThirdPersonViewMode::Standard),
mOverShoulderOffset(osg::Vec2f(30.0f, -10.0f)),
mSmoothTransitionToCombatMode(0.f)
mMaxNextCameraDistance(800.f),
mFocalPointCurrentOffset(osg::Vec2d()),
mFocalPointTargetOffset(osg::Vec2d()),
mFocalPointTransitionSpeedCoef(1.f),
mSkipFocalPointTransition(true),
mPreviousTransitionInfluence(0.f),
mSmoothedSpeed(0.f),
mZoomOutWhenMoveCoef(Settings::Manager::getFloat("zoom out when move coef", "Camera")),
mDynamicCameraDistanceEnabled(false),
mShowCrosshairInThirdPersonMode(false)
{
mVanity.enabled = false;
mVanity.allowed = true;
@ -139,14 +147,11 @@ namespace MWRender
osg::Vec3d Camera::getFocalPointOffset() const
{
osg::Vec3d offset(0, 0, 10.f);
if (mThirdPersonMode == ThirdPersonViewMode::OverShoulder && !mPreviewMode && !mVanity.enabled)
if (!mPreviewMode && !mVanity.enabled)
{
float horizontalOffset = mOverShoulderOffset.x() * (1.f - mSmoothTransitionToCombatMode);
float verticalOffset = mSmoothTransitionToCombatMode * 15.f + (1.f - mSmoothTransitionToCombatMode) * mOverShoulderOffset.y();
offset.x() += horizontalOffset * cos(getYaw());
offset.y() += horizontalOffset * sin(getYaw());
offset.z() += verticalOffset;
offset.x() += mFocalPointCurrentOffset.x() * cos(getYaw());
offset.y() += mFocalPointCurrentOffset.x() * sin(getYaw());
offset.z() += mFocalPointCurrentOffset.y();
}
return offset;
}
@ -238,35 +243,69 @@ namespace MWRender
// only show the crosshair in game mode
MWBase::WindowManager *wm = MWBase::Environment::get().getWindowManager();
wm->showCrosshair(!wm->isGuiMode() && !mVanity.enabled && !mPreviewMode
&& (mFirstPersonView || mThirdPersonMode != ThirdPersonViewMode::Standard));
&& (mFirstPersonView || mShowCrosshairInThirdPersonMode));
if(mVanity.enabled)
{
rotateCamera(0.f, 0.f, osg::DegreesToRadians(3.f * duration), true);
}
updateSmoothTransitionToCombatMode(duration);
updateFocalPointOffset(duration);
float speed = mTrackingPtr.getClass().getSpeed(mTrackingPtr);
speed /= (1.f + speed / 500.f);
float maxDelta = 300.f * duration;
mSmoothedSpeed += osg::clampBetween(speed - mSmoothedSpeed, -maxDelta, maxDelta);
mMaxNextCameraDistance = mCameraDistance + duration * (100.f + mBaseCameraDistance);
}
void Camera::setOverShoulderOffset(float horizontal, float vertical)
void Camera::setFocalPointTargetOffset(osg::Vec2d v)
{
mOverShoulderOffset = osg::Vec2f(horizontal, vertical);
mFocalPointTargetOffset = v;
mPreviousTransitionSpeed = mFocalPointTransitionSpeed;
mPreviousTransitionInfluence = 1.0f;
}
void Camera::updateSmoothTransitionToCombatMode(float duration)
void Camera::updateFocalPointOffset(float duration)
{
bool combatMode = true;
if (mTrackingPtr.getClass().isActor())
combatMode = mTrackingPtr.getClass().getCreatureStats(mTrackingPtr).getDrawState() != MWMechanics::DrawState_Nothing;
float speed = ((combatMode ? 1.f : 0.f) - mSmoothTransitionToCombatMode) * 5;
if (speed != 0)
speed += speed > 0 ? 1 : -1;
if (duration <= 0)
return;
mSmoothTransitionToCombatMode += speed * duration;
if (mSmoothTransitionToCombatMode > 1)
mSmoothTransitionToCombatMode = 1;
if (mSmoothTransitionToCombatMode < 0)
mSmoothTransitionToCombatMode = 0;
if (mSkipFocalPointTransition)
{
mSkipFocalPointTransition = false;
mPreviousExtraOffset = osg::Vec2d();
mPreviousTransitionInfluence = 0.f;
mFocalPointCurrentOffset = mFocalPointTargetOffset;
}
osg::Vec2d oldOffset = mFocalPointCurrentOffset;
if (mPreviousTransitionInfluence > 0)
{
mFocalPointCurrentOffset -= mPreviousExtraOffset;
mPreviousExtraOffset = mPreviousExtraOffset / mPreviousTransitionInfluence + mPreviousTransitionSpeed * duration;
mPreviousTransitionInfluence =
std::max(0.f, mPreviousTransitionInfluence - duration * mFocalPointTransitionSpeedCoef);
mPreviousExtraOffset *= mPreviousTransitionInfluence;
mFocalPointCurrentOffset += mPreviousExtraOffset;
}
osg::Vec2d delta = mFocalPointTargetOffset - mFocalPointCurrentOffset;
if (delta.length2() > 0)
{
float coef = duration * (1.0 + 5.0 / delta.length()) *
mFocalPointTransitionSpeedCoef * (1.0f - mPreviousTransitionInfluence);
mFocalPointCurrentOffset += delta * std::min(coef, 1.0f);
}
else
{
mPreviousExtraOffset = osg::Vec2d();
mPreviousTransitionInfluence = 0.f;
}
mFocalPointTransitionSpeed = (mFocalPointCurrentOffset - oldOffset) / duration;
}
void Camera::toggleViewMode(bool force)
@ -447,7 +486,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;
@ -474,7 +513,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();
}
@ -494,7 +536,15 @@ namespace MWRender
float Camera::getCameraDistanceCorrection() const
{
return mThirdPersonMode != ThirdPersonViewMode::Standard ? std::max(-getPitch(), 0.f) * 50.f : 0;
if (!mDynamicCameraDistanceEnabled)
return 0;
float pitchCorrection = std::max(-getPitch(), 0.f) * 50.f;
float smoothedSpeedSqr = mSmoothedSpeed * mSmoothedSpeed;
float speedCorrection = smoothedSpeedSqr / (smoothedSpeedSqr + 300.f*300.f) * mZoomOutWhenMoveCoef;
return pitchCorrection + speedCorrection;
}
void Camera::setCameraDistance()
@ -502,7 +552,11 @@ namespace MWRender
if (mVanity.enabled || mPreviewMode)
mCameraDistance = mPreviewCam.offset;
else if (!mFirstPersonView)
{
mCameraDistance = mBaseCameraDistance + getCameraDistanceCorrection();
if (mDynamicCameraDistanceEnabled)
mCameraDistance = std::min(mCameraDistance, mMaxNextCameraDistance);
}
mFocalPointAdjustment = osg::Vec3d();
}

@ -23,9 +23,6 @@ namespace MWRender
/// \brief Camera control
class Camera
{
public:
enum class ThirdPersonViewMode {Standard, OverShoulder};
private:
struct CamData {
float roll, pitch, yaw, offset;
@ -57,15 +54,26 @@ namespace MWRender
bool mViewModeToggleQueued;
float mCameraDistance;
float mMaxNextCameraDistance;
ThirdPersonViewMode mThirdPersonMode;
osg::Vec2f mOverShoulderOffset;
osg::Vec3d mFocalPointAdjustment;
// Makes sense only if mThirdPersonMode is OverShoulder. Can be in range [0, 1].
// Used for smooth transition from non-combat camera position (0) to combat camera position (1).
float mSmoothTransitionToCombatMode;
void updateSmoothTransitionToCombatMode(float duration);
osg::Vec2d mFocalPointCurrentOffset;
osg::Vec2d mFocalPointTargetOffset;
float mFocalPointTransitionSpeedCoef;
bool mSkipFocalPointTransition;
// This fields are used to make focal point transition smooth if previous transition was not finished.
float mPreviousTransitionInfluence;
osg::Vec2d mFocalPointTransitionSpeed;
osg::Vec2d mPreviousTransitionSpeed;
osg::Vec2d mPreviousExtraOffset;
float mSmoothedSpeed;
float mZoomOutWhenMoveCoef;
bool mDynamicCameraDistanceEnabled;
bool mShowCrosshairInThirdPersonMode;
void updateFocalPointOffset(float duration);
float getCameraDistanceCorrection() const;
osg::ref_ptr<osg::NodeCallback> mUpdateCallback;
@ -76,8 +84,11 @@ namespace MWRender
MWWorld::Ptr getTrackingPtr() const;
void setThirdPersonViewMode(ThirdPersonViewMode mode) { mThirdPersonMode = mode; }
void setOverShoulderOffset(float horizontal, float vertical);
void setFocalPointTransitionSpeed(float v) { mFocalPointTransitionSpeedCoef = v; }
void setFocalPointTargetOffset(osg::Vec2d v);
void skipFocalPointTransition() { mSkipFocalPointTransition = true; }
void enableDynamicCameraDistance(bool v) { mDynamicCameraDistanceEnabled = v; }
void enableCrosshairInThirdPersonMode(bool v) { mShowCrosshairInThirdPersonMode = v; }
/// Update the view matrix of \a cam
void updateCamera(osg::Camera* cam);
@ -125,7 +136,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.

@ -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;
}

@ -349,6 +349,8 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, osg::ref_ptr<osg::Group> 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
);
}

@ -8,12 +8,19 @@
#include "actoranimation.hpp"
#include "weaponanimation.hpp"
#include <array>
namespace ESM
{
struct NPC;
struct BodyPart;
}
namespace MWSound
{
class Sound;
}
namespace MWRender
{
@ -45,7 +52,7 @@ protected:
// Bounded Parts
PartHolderPtr mObjectParts[ESM::PRT_Count];
std::string mSoundIds[ESM::PRT_Count];
std::array<MWSound::Sound*, ESM::PRT_Count> mSounds;
const ESM::NPC *mNpc;
std::string mHeadModel;

@ -428,7 +428,7 @@ namespace MWRender
if (activeGrid)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mRefTrackerMutex);
std::lock_guard<std::mutex> 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<OpenThreads::Mutex> lock(mSizeCacheMutex);
std::lock_guard<std::mutex> 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<OpenThreads::Mutex> lock(mRefTrackerMutex);
std::lock_guard<std::mutex> 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<OpenThreads::Mutex> lock(mSizeCacheMutex);
std::lock_guard<std::mutex> lock(mSizeCacheMutex);
mSizeCache[pair.first] = radius2;
continue;
}
@ -670,22 +670,33 @@ namespace MWRender
{
if (mActiveGridOnly && !std::get<2>(id)) return false;
pos /= ESM::Land::REAL_SIZE;
clampToCell(pos);
osg::Vec2f center = std::get<0>(id);
float halfSize = std::get<1>(id)/2;
return pos.x() >= center.x()-halfSize && pos.y() >= center.y()-halfSize && pos.x() <= center.x()+halfSize && pos.y() <= center.y()+halfSize;
}
void clampToCell(osg::Vec3f& cellPos)
{
osg::Vec2i min (mCell.x(), mCell.y());
osg::Vec2i max (mCell.x()+1, mCell.y()+1);
if (cellPos.x() < min.x()) cellPos.x() = min.x();
if (cellPos.x() > max.x()) cellPos.x() = max.x();
if (cellPos.y() < min.y()) cellPos.y() = min.y();
if (cellPos.y() > max.y()) cellPos.y() = max.y();
}
osg::Vec3f mPosition;
osg::Vec2i mCell;
std::set<MWRender::ChunkId> mToClear;
bool mActiveGridOnly = false;
};
bool ObjectPaging::enableObject(int type, const ESM::RefNum & refnum, const osg::Vec3f& pos, bool enabled)
bool ObjectPaging::enableObject(int type, const ESM::RefNum & refnum, const osg::Vec3f& pos, const osg::Vec2i& cell, bool enabled)
{
if (!typeFilter(type, false))
return false;
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mRefTrackerMutex);
std::lock_guard<std::mutex> lock(mRefTrackerMutex);
if (enabled && !getWritableRefTracker().mDisabled.erase(refnum)) return false;
if (!enabled && !getWritableRefTracker().mDisabled.insert(refnum).second) return false;
if (mRefTrackerLocked) return false;
@ -693,6 +704,7 @@ namespace MWRender
ClearCacheFunctor ccf;
ccf.mPosition = pos;
ccf.mCell = cell;
mCache->call(ccf);
if (ccf.mToClear.empty()) return false;
for (auto chunk : ccf.mToClear)
@ -700,19 +712,20 @@ namespace MWRender
return true;
}
bool ObjectPaging::blacklistObject(int type, const ESM::RefNum & refnum, const osg::Vec3f& pos)
bool ObjectPaging::blacklistObject(int type, const ESM::RefNum & refnum, const osg::Vec3f& pos, const osg::Vec2i& cell)
{
if (!typeFilter(type, false))
return false;
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mRefTrackerMutex);
std::lock_guard<std::mutex> lock(mRefTrackerMutex);
if (!getWritableRefTracker().mBlacklist.insert(refnum).second) return false;
if (mRefTrackerLocked) return false;
}
ClearCacheFunctor ccf;
ccf.mPosition = pos;
ccf.mCell = cell;
ccf.mActiveGridOnly = true;
mCache->call(ccf);
if (ccf.mToClear.empty()) return false;
@ -724,7 +737,7 @@ namespace MWRender
void ObjectPaging::clear()
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mRefTrackerMutex);
std::lock_guard<std::mutex> lock(mRefTrackerMutex);
mRefTrackerNew.mDisabled.clear();
mRefTrackerNew.mBlacklist.clear();
mRefTrackerLocked = true;
@ -734,7 +747,7 @@ namespace MWRender
{
if (!mRefTrackerLocked) return false;
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mRefTrackerMutex);
std::lock_guard<std::mutex> lock(mRefTrackerMutex);
mRefTrackerLocked = false;
if (mRefTracker == mRefTrackerNew)
return false;

@ -5,7 +5,7 @@
#include <components/resource/resourcemanager.hpp>
#include <components/esm/loadcell.hpp>
#include <OpenThreads/Mutex>
#include <mutex>
namespace Resource
{
@ -34,10 +34,10 @@ namespace MWRender
virtual unsigned int getNodeMask() override;
/// @return true if view needs rebuild
bool enableObject(int type, const ESM::RefNum & refnum, const osg::Vec3f& pos, bool enabled);
bool enableObject(int type, const ESM::RefNum & refnum, const osg::Vec3f& pos, const osg::Vec2i& cell, bool enabled);
/// @return true if view needs rebuild
bool blacklistObject(int type, const ESM::RefNum & refnum, const osg::Vec3f& pos);
bool blacklistObject(int type, const ESM::RefNum & refnum, const osg::Vec3f& pos, const osg::Vec2i& cell);
void clear();
@ -58,7 +58,7 @@ namespace MWRender
float mMinSizeMergeFactor;
float mMinSizeCostMultiplier;
OpenThreads::Mutex mRefTrackerMutex;
std::mutex mRefTrackerMutex;
struct RefTracker
{
std::set<ESM::RefNum> 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<ESM::RefNum, float> SizeCache;
SizeCache mSizeCache;
};

@ -2,6 +2,8 @@
#include <limits>
#include <cstdlib>
#include <condition_variable>
#include <mutex>
#include <osg/Light>
#include <osg/LightModel>
@ -64,6 +66,7 @@
#include "vismask.hpp"
#include "pathgrid.hpp"
#include "camera.hpp"
#include "viewovershoulder.hpp"
#include "water.hpp"
#include "terrainstorage.hpp"
#include "util.hpp"
@ -311,6 +314,8 @@ namespace MWRender
mWater.reset(new Water(mRootNode, sceneRoot, mResourceSystem, mViewer->getIncrementalCompileOperation(), resourcePath));
mCamera.reset(new Camera(mViewer->getCamera()));
if (Settings::Manager::getBool("view over shoulder", "Camera"))
mViewOverShoulderController.reset(new ViewOverShoulderController(mCamera.get()));
mViewer->setLightingMode(osgViewer::View::NO_LIGHT);
@ -371,7 +376,6 @@ namespace MWRender
float firstPersonFov = Settings::Manager::getFloat("first person field of view", "Camera");
mFirstPersonFieldOfView = std::min(std::max(1.f, firstPersonFov), 179.f);
mStateUpdater->setFogEnd(mViewDistance);
updateThirdPersonViewMode();
mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("near", mNearClip));
mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("far", mViewDistance));
@ -387,19 +391,6 @@ namespace MWRender
mWorkQueue = nullptr;
}
void RenderingManager::updateThirdPersonViewMode()
{
if (Settings::Manager::getBool("view over shoulder", "Camera"))
mCamera->setThirdPersonViewMode(Camera::ThirdPersonViewMode::OverShoulder);
else
mCamera->setThirdPersonViewMode(Camera::ThirdPersonViewMode::Standard);
std::stringstream offset(Settings::Manager::getString("view over shoulder offset", "Camera"));
float horizontal = 30.f, vertical = -10.f;
offset >> horizontal >> vertical;
mCamera->setOverShoulderOffset(horizontal, vertical);
}
osgUtil::IncrementalCompileOperation* RenderingManager::getIncrementalCompileOperation()
{
return mViewer->getIncrementalCompileOperation();
@ -635,6 +626,8 @@ namespace MWRender
updateNavMesh();
updateRecastMesh();
if (mViewOverShoulderController)
mViewOverShoulderController->update();
mCamera->update(dt, paused);
osg::Vec3d focal, cameraPos;
@ -717,25 +710,24 @@ namespace MWRender
virtual void operator () (osg::RenderInfo& renderInfo) const
{
Log(Debug::Verbose) << "NotifyDrawCompletedCallback: " << renderInfo.getState()->getFrameStamp()->getFrameNumber() << " >= " << mFrame;
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mMutex);
std::lock_guard<std::mutex> lock(mMutex);
if (renderInfo.getState()->getFrameStamp()->getFrameNumber() >= mFrame)
{
mDone = true;
mCondition.signal();
mCondition.notify_one();
}
}
void waitTillDone()
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mMutex);
std::unique_lock<std::mutex> 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;
};
@ -1384,7 +1376,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);
}
@ -1392,7 +1384,7 @@ namespace MWRender
{
mCamera->toggleViewMode();
if (override)
mCamera->setBaseCameraDistance(0.f, false);
mCamera->updateBaseCameraDistance(0.f, false);
else
mCamera->setCameraDistance(0.f, false);
}
@ -1441,7 +1433,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)
@ -1561,7 +1553,7 @@ namespace MWRender
{
if (!ptr.isInCell() || !ptr.getCell()->isExterior() || !mObjectPaging)
return false;
if (mObjectPaging->enableObject(type, ptr.getCellRef().getRefNum(), ptr.getCellRef().getPosition().asVec3(), enabled))
if (mObjectPaging->enableObject(type, ptr.getCellRef().getRefNum(), ptr.getCellRef().getPosition().asVec3(), osg::Vec2i(ptr.getCell()->getCell()->getGridX(), ptr.getCell()->getCell()->getGridY()), enabled))
{
mTerrain->rebuildViews();
return true;
@ -1574,7 +1566,7 @@ namespace MWRender
return;
const ESM::RefNum & refnum = ptr.getCellRef().getRefNum();
if (!refnum.hasContentFile()) return;
if (mObjectPaging->blacklistObject(type, refnum, ptr.getCellRef().getPosition().asVec3()))
if (mObjectPaging->blacklistObject(type, refnum, ptr.getCellRef().getPosition().asVec3(), osg::Vec2i(ptr.getCell()->getCell()->getGridX(), ptr.getCell()->getCell()->getGridY())))
mTerrain->rebuildViews();
}
bool RenderingManager::pagingUnlockCache()

@ -79,6 +79,7 @@ namespace MWRender
class NpcAnimation;
class Pathgrid;
class Camera;
class ViewOverShoulderController;
class Water;
class TerrainStorage;
class LandManager;
@ -302,6 +303,7 @@ namespace MWRender
osg::ref_ptr<NpcAnimation> mPlayerAnimation;
osg::ref_ptr<SceneUtil::PositionAttitudeTransform> mPlayerNode;
std::unique_ptr<Camera> mCamera;
std::unique_ptr<ViewOverShoulderController> mViewOverShoulderController;
osg::Vec3f mCurrentCameraPos;
osg::ref_ptr<StateUpdater> mStateUpdater;

@ -200,6 +200,7 @@ void RippleSimulation::emitRipple(const osg::Vec3f &pos)
{
if (std::abs(pos.z() - mParticleNode->getPosition().z()) < 20)
{
osgParticle::ParticleSystem::ScopedWriteLock lock(*mParticleSystem->getReadWriteMutex());
osgParticle::Particle* p = mParticleSystem->createParticle(nullptr);
p->setPosition(osg::Vec3f(pos.x(), pos.y(), 0.f));
p->setAngle(osg::Vec3f(0,0, Misc::Rng::rollProbability() * osg::PI * 2 - osg::PI));

@ -0,0 +1,96 @@
#include "viewovershoulder.hpp"
#include <osg/Quat>
#include <components/settings/settings.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/ptr.hpp"
#include "../mwworld/refdata.hpp"
#include "../mwmechanics/drawstate.hpp"
namespace MWRender
{
ViewOverShoulderController::ViewOverShoulderController(Camera* camera) :
mCamera(camera), mMode(Mode::RightShoulder),
mAutoSwitchShoulder(Settings::Manager::getBool("auto switch shoulder", "Camera")),
mOverShoulderHorizontalOffset(30.f), mOverShoulderVerticalOffset(-10.f)
{
std::stringstream offset(Settings::Manager::getString("view over shoulder offset", "Camera"));
offset >> mOverShoulderHorizontalOffset >> mOverShoulderVerticalOffset;
mDefaultShoulderIsRight = mOverShoulderHorizontalOffset >= 0;
mOverShoulderHorizontalOffset = std::abs(mOverShoulderHorizontalOffset);
mCamera->enableDynamicCameraDistance(true);
mCamera->enableCrosshairInThirdPersonMode(true);
mCamera->setFocalPointTargetOffset({mOverShoulderHorizontalOffset, mOverShoulderVerticalOffset});
}
void ViewOverShoulderController::update()
{
if (mCamera->isVanityOrPreviewModeEnabled() || mCamera->isFirstPerson())
return;
Mode oldMode = mMode;
auto ptr = mCamera->getTrackingPtr();
if (ptr.getClass().isActor() && ptr.getClass().getCreatureStats(ptr).getDrawState() != MWMechanics::DrawState_Nothing)
mMode = Mode::Combat;
else if (MWBase::Environment::get().getWorld()->isSwimming(ptr))
mMode = Mode::Swimming;
else if (oldMode == Mode::Combat || oldMode == Mode::Swimming)
mMode = mDefaultShoulderIsRight ? Mode::RightShoulder : Mode::LeftShoulder;
if (mAutoSwitchShoulder && (mMode == Mode::LeftShoulder || mMode == Mode::RightShoulder))
trySwitchShoulder();
if (oldMode == mMode) return;
if (oldMode == Mode::Combat || mMode == Mode::Combat)
mCamera->setFocalPointTransitionSpeed(5.f);
else
mCamera->setFocalPointTransitionSpeed(1.f);
switch (mMode)
{
case Mode::RightShoulder:
mCamera->setFocalPointTargetOffset({mOverShoulderHorizontalOffset, mOverShoulderVerticalOffset});
break;
case Mode::LeftShoulder:
mCamera->setFocalPointTargetOffset({-mOverShoulderHorizontalOffset, mOverShoulderVerticalOffset});
break;
case Mode::Combat:
case Mode::Swimming:
default:
mCamera->setFocalPointTargetOffset({0, 15});
}
}
void ViewOverShoulderController::trySwitchShoulder()
{
const float limitToSwitch = 120; // switch to other shoulder if wall is closer than this limit
const float limitToSwitchBack = 300; // switch back to default shoulder if there is no walls at this distance
auto orient = osg::Quat(mCamera->getYaw(), osg::Vec3d(0,0,1));
osg::Vec3d playerPos = mCamera->getFocalPoint() - mCamera->getFocalPointOffset();
MWBase::World* world = MWBase::Environment::get().getWorld();
osg::Vec3d sideOffset = orient * osg::Vec3d(world->getHalfExtents(mCamera->getTrackingPtr()).x() - 1, 0, 0);
float rayRight = world->getDistToNearestRayHit(
playerPos + sideOffset, orient * osg::Vec3d(1, 1, 0), limitToSwitchBack + 1);
float rayLeft = world->getDistToNearestRayHit(
playerPos - sideOffset, orient * osg::Vec3d(-1, 1, 0), limitToSwitchBack + 1);
float rayForward = world->getDistToNearestRayHit(
playerPos, orient * osg::Vec3d(0, 1, 0), limitToSwitchBack + 1);
if (rayLeft < limitToSwitch && rayRight > limitToSwitchBack)
mMode = Mode::RightShoulder;
else if (rayRight < limitToSwitch && rayLeft > limitToSwitchBack)
mMode = Mode::LeftShoulder;
else if (rayLeft > limitToSwitchBack && rayRight > limitToSwitchBack && rayForward > limitToSwitchBack)
mMode = mDefaultShoulderIsRight ? Mode::RightShoulder : Mode::LeftShoulder;
}
}

@ -0,0 +1,30 @@
#ifndef VIEWOVERSHOULDER_H
#define VIEWOVERSHOULDER_H
#include "camera.hpp"
namespace MWRender
{
class ViewOverShoulderController
{
public:
ViewOverShoulderController(Camera* camera);
void update();
private:
void trySwitchShoulder();
enum class Mode { RightShoulder, LeftShoulder, Combat, Swimming };
Camera* mCamera;
Mode mMode;
bool mAutoSwitchShoulder;
float mOverShoulderHorizontalOffset;
float mOverShoulderVerticalOffset;
bool mDefaultShoulderIsRight;
};
}
#endif // VIEWOVERSHOULDER_H

@ -79,8 +79,8 @@ namespace MWScript
if (Success)
{
std::vector<Interpreter::Type_Code> code;
mParser.getCode (code);
mScripts.insert (std::make_pair (name, std::make_pair (code, mParser.getLocals())));
mParser.getCode(code);
mScripts.emplace(name, CompiledScript(code, mParser.getLocals()));
return true;
}
@ -100,7 +100,7 @@ namespace MWScript
{
// failed -> ignore script from now on.
std::vector<Interpreter::Type_Code> empty;
mScripts.insert (std::make_pair (name, std::make_pair (empty, Compiler::Locals())));
mScripts.emplace(name, CompiledScript(empty, Compiler::Locals()));
return false;
}
@ -109,7 +109,7 @@ namespace MWScript
}
// execute script
if (!iter->second.first.empty())
if (!iter->second.mByteCode.empty() && iter->second.mActive)
try
{
if (!mOpcodesInstalled)
@ -118,7 +118,7 @@ namespace MWScript
mOpcodesInstalled = true;
}
mInterpreter.run (&iter->second.first[0], iter->second.first.size(), interpreterContext);
mInterpreter.run (&iter->second.mByteCode[0], iter->second.mByteCode.size(), interpreterContext);
return true;
}
catch (const MissingImplicitRefError& e)
@ -129,11 +129,21 @@ namespace MWScript
{
Log(Debug::Error) << "Execution of script " << name << " failed: " << e.what();
iter->second.first.clear(); // don't execute again.
iter->second.mActive = false; // don't execute again.
}
return false;
}
void ScriptManager::clear()
{
for (auto& script : mScripts)
{
script.second.mActive = true;
}
mGlobalScripts.clear();
}
std::pair<int, int> ScriptManager::compileAll()
{
int count = 0;
@ -163,7 +173,7 @@ namespace MWScript
ScriptCollection::iterator iter = mScripts.find (name2);
if (iter!=mScripts.end())
return iter->second.second;
return iter->second.mLocals;
}
{

@ -41,7 +41,20 @@ namespace MWScript
Interpreter::Interpreter mInterpreter;
bool mOpcodesInstalled;
typedef std::pair<std::vector<Interpreter::Type_Code>, Compiler::Locals> CompiledScript;
struct CompiledScript
{
std::vector<Interpreter::Type_Code> mByteCode;
Compiler::Locals mLocals;
bool mActive;
CompiledScript(const std::vector<Interpreter::Type_Code>& code, const Compiler::Locals& locals)
{
mByteCode = code;
mLocals = locals;
mActive = true;
}
};
typedef std::map<std::string, CompiledScript> ScriptCollection;
ScriptCollection mScripts;
@ -55,6 +68,8 @@ namespace MWScript
Compiler::Context& compilerContext, int warningsMode,
const std::vector<std::string>& scriptBlacklist);
virtual void clear();
virtual bool run (const std::string& name, Interpreter::Context& interpreterContext);
///< Run the script with the given name (compile first, if not compiled yet)

@ -725,7 +725,8 @@ namespace MWScript
// We should move actors, standing on moving object, too.
// This approach can be used to create elevators.
moveStandingActors(ptr, diff);
MWBase::Environment::get().getWorld()->moveObject(ptr, worldPos.x(), worldPos.y(), worldPos.z());
dynamic_cast<MWScript::InterpreterContext&>(runtime.getContext()).updatePtr(ptr,
MWBase::Environment::get().getWorld()->moveObject(ptr, worldPos.x(), worldPos.y(), worldPos.z()));
}
};
@ -761,7 +762,8 @@ namespace MWScript
// We should move actors, standing on moving object, too.
// This approach can be used to create elevators.
moveStandingActors(ptr, diff);
MWBase::Environment::get().getWorld()->moveObject(ptr, objPos[0]+diff.x(), objPos[1]+diff.y(), objPos[2]+diff.z());
dynamic_cast<MWScript::InterpreterContext&>(runtime.getContext()).updatePtr(ptr,
MWBase::Environment::get().getWorld()->moveObject(ptr, objPos[0]+diff.x(), objPos[1]+diff.y(), objPos[2]+diff.z()));
}
};

@ -4,6 +4,10 @@
#include <memory>
#include <array>
#include <atomic>
#include <condition_variable>
#include <thread>
#include <mutex>
#include <chrono>
#include <stdint.h>
@ -11,11 +15,6 @@
#include <components/misc/constants.hpp>
#include <components/vfs/manager.hpp>
#include <OpenThreads/Thread>
#include <OpenThreads/Condition>
#include <OpenThreads/Mutex>
#include <OpenThreads/ScopedLock>
#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<OpenAL_SoundStream*> StreamVec;
StreamVec mStreams;
std::atomic<bool> 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<OpenThreads::Mutex> lock(mMutex);
std::unique_lock<std::mutex> 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<OpenThreads::Mutex> lock(mMutex);
std::lock_guard<std::mutex> 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<OpenThreads::Mutex> lock(mMutex);
std::lock_guard<std::mutex> lock(mMutex);
StreamVec::iterator iter = std::find(mStreams.begin(), mStreams.end(), stream);
if(iter != mStreams.end()) mStreams.erase(iter);
}
void removeAll()
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mMutex);
std::lock_guard<std::mutex> 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<OpenAL_SoundStream*>(sound->mHandle);
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mStreamThread->mMutex);
std::lock_guard<std::mutex> 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<OpenAL_SoundStream*>(sound->mHandle);
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mStreamThread->mMutex);
std::lock_guard<std::mutex> 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<OpenAL_SoundStream*>(sound->mHandle);
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mStreamThread->mMutex);
std::lock_guard<std::mutex> lock(mStreamThread->mMutex);
return stream->isPlaying();
}

@ -0,0 +1,71 @@
#include "regionsoundselector.hpp"
#include <components/misc/rng.hpp>
#include <algorithm>
#include <numeric>
#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<std::string> 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<ESM::Region>().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;
}
}

@ -0,0 +1,29 @@
#ifndef GAME_SOUND_REGIONSOUNDSELECTOR_H
#define GAME_SOUND_REGIONSOUNDSELECTOR_H
#include <boost/optional.hpp>
#include <string>
namespace MWBase
{
class World;
}
namespace MWSound
{
class RegionSoundSelector
{
public:
boost::optional<std::string> 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

@ -11,85 +11,65 @@ namespace MWSound
inline int operator&(int a, PlayMode b) { return a & static_cast<int>(b); }
inline int operator&(PlayMode a, PlayMode b) { return static_cast<int>(a) & static_cast<int>(b); }
struct SoundParams
{
osg::Vec3f mPos;
float mVolume = 1;
float mBaseVolume = 1;
float mPitch = 1;
float mMinDistance = 1;
float mMaxDistance = 1000;
int mFlags = 0;
float mFadeOutTime = 0;
};
class SoundBase {
SoundBase& operator=(const SoundBase&) = delete;
SoundBase(const SoundBase&) = delete;
SoundBase(SoundBase&&) = delete;
osg::Vec3f mPos;
float mVolume; /* NOTE: Real volume = mVolume*mBaseVolume */
float mBaseVolume;
float mPitch;
float mMinDistance;
float mMaxDistance;
int mFlags;
float mFadeOutTime;
SoundParams mParams;
protected:
Sound_Instance mHandle;
Sound_Instance mHandle = nullptr;
friend class OpenAL_Output;
public:
void setPosition(const osg::Vec3f &pos) { mPos = pos; }
void setVolume(float volume) { mVolume = volume; }
void setBaseVolume(float volume) { mBaseVolume = volume; }
void setFadeout(float duration) { mFadeOutTime = duration; }
void setPosition(const osg::Vec3f &pos) { mParams.mPos = pos; }
void setVolume(float volume) { mParams.mVolume = volume; }
void setBaseVolume(float volume) { mParams.mBaseVolume = volume; }
void setFadeout(float duration) { mParams.mFadeOutTime = duration; }
void updateFade(float duration)
{
if(mFadeOutTime > 0.0f)
if (mParams.mFadeOutTime > 0.0f)
{
float soundDuration = std::min(duration, mFadeOutTime);
mVolume *= (mFadeOutTime-soundDuration) / mFadeOutTime;
mFadeOutTime -= soundDuration;
float soundDuration = std::min(duration, mParams.mFadeOutTime);
mParams.mVolume *= (mParams.mFadeOutTime - soundDuration) / mParams.mFadeOutTime;
mParams.mFadeOutTime -= soundDuration;
}
}
const osg::Vec3f &getPosition() const { return mPos; }
float getRealVolume() const { return mVolume * mBaseVolume; }
float getPitch() const { return mPitch; }
float getMinDistance() const { return mMinDistance; }
float getMaxDistance() const { return mMaxDistance; }
const osg::Vec3f &getPosition() const { return mParams.mPos; }
float getRealVolume() const { return mParams.mVolume * mParams.mBaseVolume; }
float getPitch() const { return mParams.mPitch; }
float getMinDistance() const { return mParams.mMinDistance; }
float getMaxDistance() const { return mParams.mMaxDistance; }
MWSound::Type getPlayType() const
{ return static_cast<MWSound::Type>(mFlags&MWSound::Type::Mask); }
bool getUseEnv() const { return !(mFlags&MWSound::PlayMode::NoEnv); }
bool getIsLooping() const { return mFlags&MWSound::PlayMode::Loop; }
bool getDistanceCull() const { return mFlags&MWSound::PlayMode::RemoveAtDistance; }
bool getIs3D() const { return mFlags&Play_3D; }
void init(const osg::Vec3f& pos, float vol, float basevol, float pitch, float mindist, float maxdist, int flags)
{
mPos = pos;
mVolume = vol;
mBaseVolume = basevol;
mPitch = pitch;
mMinDistance = mindist;
mMaxDistance = maxdist;
mFlags = flags;
mFadeOutTime = 0.0f;
mHandle = nullptr;
}
{ return static_cast<MWSound::Type>(mParams.mFlags & MWSound::Type::Mask); }
bool getUseEnv() const { return !(mParams.mFlags & MWSound::PlayMode::NoEnv); }
bool getIsLooping() const { return mParams.mFlags & MWSound::PlayMode::Loop; }
bool getDistanceCull() const { return mParams.mFlags & MWSound::PlayMode::RemoveAtDistance; }
bool getIs3D() const { return mParams.mFlags & Play_3D; }
void init(float vol, float basevol, float pitch, int flags)
void init(const SoundParams& params)
{
mPos = osg::Vec3f(0.0f, 0.0f, 0.0f);
mVolume = vol;
mBaseVolume = basevol;
mPitch = pitch;
mMinDistance = 1.0f;
mMaxDistance = 1000.0f;
mFlags = flags;
mFadeOutTime = 0.0f;
mParams = params;
mHandle = nullptr;
}
SoundBase()
: mPos(0.0f, 0.0f, 0.0f), mVolume(1.0f), mBaseVolume(1.0f), mPitch(1.0f)
, mMinDistance(1.0f), mMaxDistance(1000.0f), mFlags(0), mFadeOutTime(0.0f)
, mHandle(nullptr)
{ }
SoundBase() = default;
};
class Sound : public SoundBase {

@ -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<int>(a) | static_cast<int>(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<Sound>())
, mStreams(new std::deque<Stream>())
, 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<ESM::GameSetting>().find("fAudioMinDistanceMult")->mValue.getFloat();
@ -315,57 +287,46 @@ 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);
sound->init([&] {
SoundParams params;
params.mBaseVolume = basevol;
params.mFlags = PlayMode::NoEnv | Type::Voice | Play_2D;
return params;
} ());
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);
sound->init([&] {
SoundParams params;
params.mPos = pos;
params.mBaseVolume = basevol;
params.mMinDistance = minDistance;
params.mMaxDistance = maxDistance;
params.mFlags = PlayMode::Normal | Type::Voice | Play_3D;
return params;
} ());
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;
}
}
@ -383,9 +344,13 @@ namespace MWSound
decoder->open(filename);
mMusic = getStreamRef();
mMusic->init(1.0f, volumeFromType(Type::Music), 1.0f,
PlayMode::NoEnv|Type::Music|Play_2D);
mOutput->streamSound(decoder, mMusic);
mMusic->init([&] {
SoundParams params;
params.mBaseVolume = volumeFromType(Type::Music);
params.mFlags = PlayMode::NoEnv | Type::Music | Play_2D;
return params;
} ());
mOutput->streamSound(decoder, mMusic.get());
}
void SoundManager::advanceMusic(const std::string& filename)
@ -435,7 +400,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 +483,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 +494,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 +514,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 +525,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 +537,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 +545,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 +558,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 +576,29 @@ namespace MWSound
if(!mOutput->isInitialized())
return nullptr;
Stream *track = getStreamRef();
track->init(1.0f, volumeFromType(type), 1.0f, PlayMode::NoEnv|type|Play_2D);
if(!mOutput->streamSound(decoder, track))
{
mUnusedStreams.push_back(track);
StreamPtr track = getStreamRef();
track->init([&] {
SoundParams params;
params.mBaseVolume = volumeFromType(type);
params.mFlags = PlayMode::NoEnv | type | Play_2D;
return params;
} ());
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 +607,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 +618,17 @@ namespace MWSound
// Only one copy of given sound can be played at time, so stop previous copy
stopSound(sfx, MWWorld::ConstPtr());
Sound *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);
SoundPtr sound = getSoundRef();
sound->init([&] {
SoundParams params;
params.mVolume = volume * sfx->mVolume;
params.mBaseVolume = volumeFromType(type);
params.mPitch = pitch;
params.mFlags = mode | type | Play_2D;
return params;
} ());
if(!mOutput->playSound(sound.get(), sfx->mHandle, offset))
return nullptr;
}
if(sfx->mUses++ == 0)
{
@ -667,8 +636,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 +648,48 @@ 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);
sound->init([&] {
SoundParams params;
params.mVolume = volume * sfx->mVolume;
params.mBaseVolume = volumeFromType(type);
params.mPitch = pitch;
params.mFlags = mode | type | Play_2D;
return params;
} ());
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);
sound->init([&] {
SoundParams params;
params.mPos = objpos;
params.mVolume = volume * sfx->mVolume;
params.mBaseVolume = volumeFromType(type);
params.mPitch = pitch;
params.mMinDistance = sfx->mMinDist;
params.mMaxDistance = sfx->mMaxDist;
params.mFlags = mode | type | Play_3D;
return params;
} ());
played = mOutput->playSound3D(sound.get(), sfx->mHandle, offset);
}
if(!played)
{
mUnusedSounds.push_back(sound);
return nullptr;
}
if(sfx->mUses++ == 0)
{
@ -714,8 +697,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 +713,20 @@ namespace MWSound
Sound_Buffer *sfx = loadSound(Misc::StringUtils::lowerCase(soundId));
if(!sfx) return nullptr;
Sound *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);
SoundPtr sound = getSoundRef();
sound->init([&] {
SoundParams params;
params.mPos = initialPos;
params.mVolume = volume * sfx->mVolume;
params.mBaseVolume = volumeFromType(type);
params.mPitch = pitch;
params.mMinDistance = sfx->mMinDist;
params.mMaxDistance = sfx->mMaxDist;
params.mFlags = mode | type | Play_3D;
return params;
} ());
if(!mOutput->playSound3D(sound.get(), sfx->mHandle, offset))
return nullptr;
}
if(sfx->mUses++ == 0)
{
@ -744,8 +734,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 +753,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 +775,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 +792,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 +815,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 +834,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 +889,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)
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<ESM::Region>().search(regionName);
if(regn == nullptr)
if (!cell->isExterior())
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;
}
}
case WaterSoundAction::DoNothing:
break;
case WaterSoundAction::SetVolume:
mNearWaterSound->setVolume(update.mVolume * sfx->mVolume);
break;
case WaterSoundAction::FinishSound:
mOutput->finishSound(mNearWaterSound);
mNearWaterSound = nullptr;
break;
case WaterSoundAction::PlaySound:
if (mNearWaterSound)
mOutput->finishSound(mNearWaterSound);
mNearWaterSound = playSound(update.mId, update.mVolume, 1.0f, Type::Sfx, PlayMode::Loop);
break;
}
else
volume = 1.0f;
volume = std::min(volume, 1.0f);
mLastCell = curcell;
}
std::pair<SoundManager::WaterSoundAction, Sound_Buffer*> SoundManager::getWaterSoundAction(
const WaterSoundUpdate& update, const ESM::Cell* cell) const
{
if (mNearWaterSound)
{
if (volume == 0.0f)
{
mOutput->finishSound(mNearWaterSound);
mNearWaterSound = nullptr;
}
else
{
bool soundIdChanged = false;
if (update.mVolume == 0.0f)
return {WaterSoundAction::FinishSound, nullptr};
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;
}
}
bool soundIdChanged = false;
if(soundIdChanged)
Sound_Buffer* sfx = lookupSound(update.mId);
if (mLastCell != cell)
{
const auto snditer = mActiveSounds.find(MWWorld::ConstPtr());
if (snditer != mActiveSounds.end())
{
mOutput->finishSound(mNearWaterSound);
mNearWaterSound = playSound(soundId, volume, 1.0f, Type::Sfx, PlayMode::Loop);
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;
}
else if (sfx)
mNearWaterSound->setVolume(volume * sfx->mVolume);
}
if (soundIdChanged)
return {WaterSoundAction::PlaySound, nullptr};
if (sfx)
return {WaterSoundAction::SetVolume, sfx};
}
else if (volume > 0.0f)
{
LastCell = curcell;
mNearWaterSound = playSound(soundId, volume, 1.0f, Type::Sfx, PlayMode::Loop);
}
else if (update.mVolume > 0.0f)
return {WaterSoundAction::PlaySound, nullptr};
return {WaterSoundAction::DoNothing, nullptr};
}
void SoundManager::updateSounds(float duration)
@ -1061,17 +977,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 +1022,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 +1041,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 +1067,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 +1084,6 @@ namespace MWSound
if(!mOutput->isStreamPlaying(sound))
{
mOutput->finishStream(sound);
mUnusedStreams.push_back(sound);
mActiveSaySounds.erase(sayiter++);
}
else
@ -1183,7 +1098,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 +1129,8 @@ namespace MWSound
{
mMusic->updateFade(duration);
mOutput->updateStream(mMusic);
mOutput->updateStream(mMusic.get());
if (mMusic->getRealVolume() <= 0.f)
{
streamMusicFull(mNextMusic);
@ -1235,18 +1150,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 +1166,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 +1203,8 @@ namespace MWSound
mListenerUp = up;
mListenerUnderwater = underwater;
mWaterSoundUpdater.setUnderwater(underwater);
}
void SoundManager::updatePtr(const MWWorld::ConstPtr &old, const MWWorld::ConstPtr &updated)
@ -1307,17 +1220,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 +1307,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 +1318,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);

@ -9,11 +9,16 @@
#include <unordered_map>
#include <components/settings/settings.hpp>
#include <components/misc/objectpool.hpp>
#include <components/fallback/fallback.hpp>
#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<Sound>;
using StreamPtr = Misc::ObjectPtr<Stream>;
class SoundManager : public MWBase::SoundManager
{
const VFS::Manager* mVFS;
@ -53,18 +62,10 @@ namespace MWSound
std::unordered_map<std::string, std::vector<int>> 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;
int mNearWaterRadius;
int mNearWaterPoints;
float mNearWaterIndoorTolerance;
float mNearWaterOutdoorTolerance;
std::string mNearWaterIndoorID;
std::string mNearWaterOutdoorID;
VolumeSettings mVolumeSettings;
WaterSoundUpdater mWaterSoundUpdater;
typedef std::unique_ptr<std::deque<Sound_Buffer> > 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<Sound_Buffer*> SoundList;
SoundList mUnusedBuffers;
std::unique_ptr<std::deque<Sound>> mSounds;
std::vector<Sound*> mUnusedSounds;
Misc::ObjectPool<Sound> mSounds;
std::unique_ptr<std::deque<Stream>> mStreams;
std::vector<Stream*> mUnusedStreams;
Misc::ObjectPool<Stream> mStreams;
typedef std::pair<MWBase::Sound*,Sound_Buffer*> SoundBufferRefPair;
typedef std::pair<SoundPtr, Sound_Buffer*> SoundBufferRefPair;
typedef std::vector<SoundBufferRefPair> SoundBufferRefPairList;
typedef std::map<MWWorld::ConstPtr,SoundBufferRefPairList> SoundMap;
SoundMap mActiveSounds;
typedef std::map<MWWorld::ConstPtr,Stream*> SaySoundMap;
typedef std::map<MWWorld::ConstPtr, StreamPtr> SaySoundMap;
SaySoundMap mSaySoundsQueue;
SaySoundMap mActiveSaySounds;
typedef std::vector<Stream*> TrackList;
typedef std::vector<StreamPtr> 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<WaterSoundAction, Sound_Buffer*> 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

@ -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

@ -0,0 +1,56 @@
#include "volumesettings.hpp"
#include <components/settings/settings.hpp>
#include <algorithm>
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();
}
}

@ -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

@ -0,0 +1,71 @@
#include "watersoundupdater.hpp"
#include "../mwbase/world.hpp"
#include "../mwworld/cellstore.hpp"
#include "../mwworld/ptr.hpp"
#include <components/esm/loadcell.hpp>
#include <osg/Vec3f>
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;
}
}

@ -0,0 +1,54 @@
#ifndef GAME_SOUND_WATERSOUNDUPDATER_H
#define GAME_SOUND_WATERSOUNDUPDATER_H
#include <string>
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

@ -47,7 +47,7 @@ void MWState::StateManager::cleanup (bool force)
MWBase::Environment::get().getSoundManager()->clear();
MWBase::Environment::get().getDialogueManager()->clear();
MWBase::Environment::get().getJournal()->clear();
MWBase::Environment::get().getScriptManager()->getGlobalScripts().clear();
MWBase::Environment::get().getScriptManager()->clear();
MWBase::Environment::get().getWorld()->clear();
MWBase::Environment::get().getWindowManager()->clear();
MWBase::Environment::get().getInputManager()->clear();

@ -1,6 +1,8 @@
#include "scene.hpp"
#include <limits>
#include <chrono>
#include <thread>
#include <BulletCollision/CollisionDispatch/btCollisionObject.h>
#include <BulletCollision/CollisionShapes/btCompoundShape.h>
@ -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));
}
}

@ -954,6 +954,7 @@ namespace MWWorld
removeContainerScripts(getPlayerPtr());
mWorldScene->changeToInteriorCell(cellName, position, adjustPlayerPos, changeEvent);
addContainerScripts(getPlayerPtr(), getPlayerPtr().getCell());
mRendering->getCamera()->skipFocalPointTransition();
#ifdef USE_OPENXR
auto* xrInput = MWVR::Environment::get().getInputManager();
@ -975,6 +976,7 @@ namespace MWWorld
removeContainerScripts(getPlayerPtr());
mWorldScene->changeToExteriorCell(position, adjustPlayerPos, changeEvent);
addContainerScripts(getPlayerPtr(), getPlayerPtr().getCell());
mRendering->getCamera()->skipFocalPointTransition();
#ifdef USE_OPENXR
auto* xrInput = MWVR::Environment::get().getInputManager();
@ -1226,7 +1228,6 @@ namespace MWWorld
mRendering->updatePtr(ptr, newPtr);
MWBase::Environment::get().getSoundManager()->updatePtr (ptr, newPtr);
mPhysics->updatePtr(ptr, newPtr);
MWBase::Environment::get().getScriptManager()->getGlobalScripts().updatePtrs(ptr, newPtr);
MWBase::MechanicsManager *mechMgr = MWBase::Environment::get().getMechanicsManager();
mechMgr->updateCell(ptr, newPtr);
@ -1242,6 +1243,9 @@ namespace MWWorld
}
}
}
MWBase::Environment::get().getWindowManager()->updateConsoleObjectPtr(ptr, newPtr);
MWBase::Environment::get().getScriptManager()->getGlobalScripts().updatePtrs(ptr, newPtr);
}
if (haveToMove && newPtr.getRefData().getBaseNode())
{
@ -1431,6 +1435,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);
@ -1945,7 +1952,7 @@ namespace MWWorld
std::string enchantId = selectedEnchantItem.getClass().getEnchantment(selectedEnchantItem);
if (!enchantId.empty())
{
const ESM::Enchantment* ench = mStore.get<ESM::Enchantment>().search(selectedEnchantItem.getClass().getEnchantment(selectedEnchantItem));
const ESM::Enchantment* ench = mStore.get<ESM::Enchantment>().search(enchantId);
if (ench)
preloadEffects(&ench->mEffects);
}
@ -2494,7 +2501,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());

@ -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 <philip at yhbt.com>
# 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
)

@ -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:
# <prefix>/include/freetype2/ft2build.h
# <prefix>/include/freetype2/freetype.h
# used as:
# #include <ft2build.h>
# #include <freetype.h>
# requires:
# -I <prefix>/include/freetype2/
#
# ======================================
# old versions (2.4.8, 2.3.5)
#
# file structure:
# <prefix>/include/ft2build.h
# <prefix>/include/freetype2/freetype/freetype.h
# used as:
# #include <ft2build.h>
# #include <freetype/freetype.h>
# requires:
# -I <prefix>/include/ -I <prefix>/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()

@ -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}

@ -2,11 +2,8 @@
#include "launchersettings.hpp"
#include <QTextCodec>
#include <QTextStream>
#include <QDir>
#include <QString>
#include <QRegExp>
#include <QMap>
#include <components/files/configurationmanager.hpp>
@ -105,9 +102,9 @@ bool Config::GameSettings::readUserFile(QTextStream &stream)
return readFile(stream, mUserSettings);
}
bool Config::GameSettings::readFile(QTextStream &stream, QMap<QString, QString> &settings)
bool Config::GameSettings::readFile(QTextStream &stream, QMultiMap<QString, QString> &settings)
{
QMap<QString, QString> cache;
QMultiMap<QString, QString> cache;
QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$");
while (!stream.atEnd()) {
@ -151,7 +148,7 @@ bool Config::GameSettings::readFile(QTextStream &stream, QMap<QString, QString>
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<QString, QString>::const_iterator i = mUserSettings.find(key);
QMultiMap<QString, QString>::const_iterator i = mUserSettings.find(key);
while (i != mUserSettings.end() && i.key() == key)
{
QString settingLine = i.key() + "=" + i.value();

@ -5,7 +5,7 @@
#include <QStringList>
#include <QString>
#include <QFile>
#include <QMap>
#include <QMultiMap>
#include <boost/filesystem/path.hpp>
@ -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<QString, QString> &settings);
bool readFile(QTextStream &stream, QMultiMap<QString, QString> &settings);
bool readUserFile(QTextStream &stream);
bool writeFile(QTextStream &stream);
@ -78,8 +80,8 @@ namespace Config
Files::ConfigurationManager &mCfgMgr;
void validatePaths();
QMap<QString, QString> mSettings;
QMap<QString, QString> mUserSettings;
QMultiMap<QString, QString> mSettings;
QMultiMap<QString, QString> mUserSettings;
QStringList mDataDirs;
QString mDataLocal;

@ -3,7 +3,7 @@
#include <QTextStream>
#include <QString>
#include <QRegExp>
#include <QMap>
#include <QMultiMap>
#include <QDebug>
@ -22,7 +22,7 @@ Config::LauncherSettings::~LauncherSettings()
QStringList Config::LauncherSettings::subKeys(const QString &key)
{
QMap<QString, QString> settings = SettingsBase::getSettings();
QMultiMap<QString, QString> settings = SettingsBase::getSettings();
QStringList keys = settings.uniqueKeys();
QRegExp keyRe("(.+)/");
@ -54,7 +54,7 @@ bool Config::LauncherSettings::writeFile(QTextStream &stream)
{
QString sectionPrefix;
QRegExp sectionRe("([^/]+)/(.+)$");
QMap<QString, QString> settings = SettingsBase::getSettings();
QMultiMap<QString, QString> settings = SettingsBase::getSettings();
QMapIterator<QString, QString> i(settings);
i.toBack();

@ -6,7 +6,7 @@
namespace Config
{
class LauncherSettings : public SettingsBase<QMap<QString, QString> >
class LauncherSettings : public SettingsBase<QMultiMap<QString, QString> >
{
public:
LauncherSettings();

@ -5,7 +5,7 @@
#include <QStringList>
#include <QString>
#include <QRegExp>
#include <QMap>
#include <QMultiMap>
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);
}
}

@ -2,8 +2,6 @@
#include <set>
#include <OpenThreads/ScopedLock>
#include <osg/Image>
#include <osg/Plane>
@ -548,7 +546,7 @@ namespace ESMTerrain
Terrain::LayerInfo Storage::getLayerInfo(const std::string& texture)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mLayerInfoMutex);
std::lock_guard<std::mutex> lock(mLayerInfoMutex);
// Already have this cached?
std::map<std::string, Terrain::LayerInfo>::iterator found = mLayerInfoMap.find(texture);

@ -2,8 +2,7 @@
#define COMPONENTS_ESM_TERRAIN_STORAGE_H
#include <cassert>
#include <OpenThreads/Mutex>
#include <mutex>
#include <components/terrain/storage.hpp>
@ -138,7 +137,7 @@ namespace ESMTerrain
std::string getTextureName (UniqueTextureId id);
std::map<std::string, Terrain::LayerInfo> mLayerInfoMap;
OpenThreads::Mutex mLayerInfoMutex;
std::mutex mLayerInfoMutex;
std::string mNormalMapPattern;
std::string mNormalHeightMapPattern;

@ -0,0 +1,83 @@
#ifndef OPENMW_COMPONENTS_MISC_OBJECTPOOL_H
#define OPENMW_COMPONENTS_MISC_OBJECTPOOL_H
#include <deque>
#include <memory>
#include <vector>
namespace Misc
{
template <class T>
class ObjectPool;
template <class T>
class ObjectPtrDeleter
{
public:
ObjectPtrDeleter(std::nullptr_t)
: mPool(nullptr) {}
ObjectPtrDeleter(ObjectPool<T>& pool)
: mPool(&pool) {}
void operator()(T* object) const
{
mPool->recycle(object);
}
private:
ObjectPool<T>* mPool;
};
template <class T>
struct ObjectPtr final : std::unique_ptr<T, ObjectPtrDeleter<T>>
{
using std::unique_ptr<T, ObjectPtrDeleter<T>>::unique_ptr;
using std::unique_ptr<T, ObjectPtrDeleter<T>>::operator=;
ObjectPtr()
: ObjectPtr(nullptr) {}
ObjectPtr(std::nullptr_t)
: std::unique_ptr<T, ObjectPtrDeleter<T>>(nullptr, nullptr) {}
};
template <class T>
class ObjectPool
{
friend class ObjectPtrDeleter<T>;
public:
ObjectPool()
: mObjects(std::make_unique<std::deque<T>>()) {}
ObjectPtr<T> get()
{
T* object;
if (!mUnused.empty())
{
object = mUnused.back();
mUnused.pop_back();
}
else
{
mObjects->emplace_back();
object = &mObjects->back();
}
return ObjectPtr<T>(object, ObjectPtrDeleter<T>(*this));
}
private:
std::unique_ptr<std::deque<T>> mObjects;
std::vector<T*> mUnused;
void recycle(T* object)
{
mUnused.push_back(object);
}
};
}
#endif

@ -1,5 +1,7 @@
#include "nifloader.hpp"
#include <mutex>
#include <osg/Matrixf>
#include <osg/MatrixTransform>
#include <osg/Geometry>
@ -923,15 +925,18 @@ namespace NifOsg
osg::BoundingBox box;
int i=0;
for (std::vector<Nif::NiParticleSystemController::Particle>::const_iterator it = partctrl->particles.begin();
i<particledata->activeCount && 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<osg::ref_ptr<Attribute>, CompareStateAttribute> Cache;
static Cache sCache;
static OpenThreads::Mutex sMutex;
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(sMutex);
static std::mutex sMutex;
std::lock_guard<std::mutex> lock(sMutex);
typename Cache::iterator found = sCache.find(attr);
if (found == sCache.end())
found = sCache.insert(attr).first;

@ -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<float>::epsilon(), mLifetime + mLifetimeRandom * Misc::Rng::rollClosedProbability()));
}
GrowFadeAffector::GrowFadeAffector(float growTime, float fadeTime)
@ -184,6 +184,7 @@ ParticleColorAffector::ParticleColorAffector(const ParticleColorAffector &copy,
void ParticleColorAffector::operate(osgParticle::Particle* particle, double /* dt */)
{
assert(particle->getLifeTime() > 0);
float time = static_cast<float>(particle->getAge()/particle->getLifeTime());
osg::Vec4f color = mData.interpKey(time);
float alpha = color.a();

@ -21,7 +21,7 @@ namespace Resource
{
std::vector<osg::ref_ptr<osg::Object> > objectsToRemove;
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
std::lock_guard<std::mutex> lock(_objectCacheMutex);
// Remove unreferenced entries from object cache
ObjectCacheMap::iterator oitr = _objectCache.begin();
@ -45,7 +45,7 @@ namespace Resource
void MultiObjectCache::clear()
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
std::lock_guard<std::mutex> 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<OpenThreads::Mutex> lock(_objectCacheMutex);
std::lock_guard<std::mutex> lock(_objectCacheMutex);
_objectCache.insert(std::make_pair(filename, object));
}
osg::ref_ptr<osg::Object> MultiObjectCache::takeFromObjectCache(const std::string &fileName)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
std::lock_guard<std::mutex> lock(_objectCacheMutex);
ObjectCacheMap::iterator found = _objectCache.find(fileName);
if (found == _objectCache.end())
return osg::ref_ptr<osg::Object>();
@ -76,7 +76,7 @@ namespace Resource
void MultiObjectCache::releaseGLObjects(osg::State *state)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
std::lock_guard<std::mutex> lock(_objectCacheMutex);
for(ObjectCacheMap::iterator itr = _objectCache.begin();
itr != _objectCache.end();
@ -89,7 +89,7 @@ namespace Resource
unsigned int MultiObjectCache::getCacheSize() const
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
std::lock_guard<std::mutex> lock(_objectCacheMutex);
return _objectCache.size();
}

@ -3,6 +3,7 @@
#include <map>
#include <string>
#include <mutex>
#include <osg/ref_ptr>
#include <osg/Referenced>
@ -43,7 +44,7 @@ namespace Resource
typedef std::multimap<std::string, osg::ref_ptr<osg::Object> > ObjectCacheMap;
ObjectCacheMap _objectCache;
mutable OpenThreads::Mutex _objectCacheMutex;
mutable std::mutex _objectCacheMutex;
};

@ -26,6 +26,7 @@
#include <string>
#include <map>
#include <mutex>
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<OpenThreads::Mutex> lock(_objectCacheMutex);
std::lock_guard<std::mutex> 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<osg::ref_ptr<osg::Object> > objectsToRemove;
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
std::lock_guard<std::mutex> 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<OpenThreads::Mutex> lock(_objectCacheMutex);
std::lock_guard<std::mutex> 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<OpenThreads::Mutex> lock(_objectCacheMutex);
std::lock_guard<std::mutex> lock(_objectCacheMutex);
_objectCache[key]=ObjectTimeStampPair(object,timestamp);
}
/** Remove Object from cache.*/
void removeFromObjectCache(const KeyType& key)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
std::lock_guard<std::mutex> 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<Object> from the object cache*/
osg::ref_ptr<osg::Object> getRefFromObjectCache(const KeyType& key)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
std::lock_guard<std::mutex> 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<OpenThreads::Mutex> lock(_objectCacheMutex);
std::lock_guard<std::mutex> 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<OpenThreads::Mutex> lock(_objectCacheMutex);
std::lock_guard<std::mutex> 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<OpenThreads::Mutex> lock(_objectCacheMutex);
std::lock_guard<std::mutex> 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 <class Functor>
void call(Functor& f)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
std::lock_guard<std::mutex> 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<OpenThreads::Mutex> lock(_objectCacheMutex);
std::lock_guard<std::mutex> lock(_objectCacheMutex);
return _objectCache.size();
}
@ -185,7 +186,7 @@ class GenericObjectCache : public osg::Referenced
typedef std::map<KeyType, ObjectTimeStampPair > ObjectCacheMap;
ObjectCacheMap _objectCache;
mutable OpenThreads::Mutex _objectCacheMutex;
mutable std::mutex _objectCacheMutex;
};

@ -126,7 +126,7 @@ namespace Resource
void clearCache()
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_listMutex);
std::lock_guard<OpenThreads::Mutex> lock(_listMutex);
_sharedTextureList.clear();
_sharedStateSetList.clear();
}
@ -625,7 +625,7 @@ namespace Resource
mShaderManager->releaseGLObjects(state);
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mSharedStateMutex);
std::lock_guard<std::mutex> lock(mSharedStateMutex);
mSharedStateManager->releaseGLObjects(state);
}
@ -717,7 +717,7 @@ namespace Resource
if (mIncrementalCompileOperation)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(*mIncrementalCompileOperation->getToCompiledMutex());
std::lock_guard<OpenThreads::Mutex> 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<OpenThreads::Mutex> lock(mSharedStateMutex);
std::lock_guard<std::mutex> lock(mSharedStateMutex);
mSharedStateManager->clearCache();
mInstanceCache->clear();
}
@ -747,12 +747,12 @@ namespace Resource
{
if (mIncrementalCompileOperation)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(*mIncrementalCompileOperation->getToCompiledMutex());
std::lock_guard<OpenThreads::Mutex> lock(*mIncrementalCompileOperation->getToCompiledMutex());
stats->setAttribute(frameNumber, "Compiling", mIncrementalCompileOperation->getToCompile().size());
}
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mSharedStateMutex);
std::lock_guard<std::mutex> lock(mSharedStateMutex);
stats->setAttribute(frameNumber, "Texture", mSharedStateManager->getNumSharedTextures());
stats->setAttribute(frameNumber, "StateSet", mSharedStateManager->getNumSharedStateSets());
}

@ -4,6 +4,7 @@
#include <string>
#include <map>
#include <memory>
#include <mutex>
#include <osg/ref_ptr>
#include <osg/Node>
@ -159,7 +160,7 @@ namespace Resource
osg::ref_ptr<MultiObjectCache> mInstanceCache;
osg::ref_ptr<Resource::SharedStateManager> mSharedStateManager;
mutable OpenThreads::Mutex mSharedStateMutex;
mutable std::mutex mSharedStateMutex;
Resource::ImageManager* mImageManager;
Resource::NifFileManager* mNifFileManager;

@ -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);
}

@ -891,7 +891,7 @@ MWShadowTechnique::ViewDependentData* MWShadowTechnique::createViewDependentData
MWShadowTechnique::ViewDependentData* MWShadowTechnique::getViewDependentData(osgUtil::CullVisitor* cv)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_viewDependentDataMapMutex);
std::lock_guard<std::mutex> lock(_viewDependentDataMapMutex);
ViewDependentDataMap::iterator itr = _viewDependentDataMap.find(cv);
if (itr!=_viewDependentDataMap.end()) return itr->second.get();
@ -1435,7 +1435,7 @@ void MWShadowTechnique::cull(osgUtil::CullVisitor& cv)
std::string validRegionUniformName = "validRegionMatrix" + std::to_string(sm_i);
osg::ref_ptr<osg::Uniform> validRegionUniform;
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_accessUniformsAndProgramMutex);
std::lock_guard<std::mutex> lock(_accessUniformsAndProgramMutex);
for (auto uniform : _uniforms)
{
@ -1560,7 +1560,7 @@ void MWShadowTechnique::createShaders()
unsigned int _baseTextureUnit = 0;
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_accessUniformsAndProgramMutex);
std::lock_guard<std::mutex> lock(_accessUniformsAndProgramMutex);
_shadowCastingStateSet = new osg::StateSet;
@ -3073,7 +3073,7 @@ osg::StateSet* MWShadowTechnique::selectStateSetForRenderingShadow(ViewDependent
osg::ref_ptr<osg::StateSet> stateset = vdd.getStateSet();
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_accessUniformsAndProgramMutex);
std::lock_guard<std::mutex> lock(_accessUniformsAndProgramMutex);
vdd.getStateSet()->clear();
@ -3150,7 +3150,7 @@ void MWShadowTechnique::resizeGLObjectBuffers(unsigned int /*maxSize*/)
void MWShadowTechnique::releaseGLObjects(osg::State* state) const
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_viewDependentDataMapMutex);
std::lock_guard<std::mutex> lock(_viewDependentDataMapMutex);
for(ViewDependentDataMap::const_iterator itr = _viewDependentDataMap.begin();
itr != _viewDependentDataMap.end();
++itr)

@ -19,6 +19,8 @@
#ifndef COMPONENTS_SCENEUTIL_MWSHADOWTECHNIQUE_H
#define COMPONENTS_SCENEUTIL_MWSHADOWTECHNIQUE_H 1
#include <mutex>
#include <osg/Camera>
#include <osg/Material>
#include <osg/MatrixTransform>
@ -255,8 +257,8 @@ namespace SceneUtil {
virtual ~MWShadowTechnique();
typedef std::map< osgUtil::CullVisitor*, osg::ref_ptr<ViewDependentData> > ViewDependentDataMap;
mutable std::mutex _viewDependentDataMapMutex;
typedef std::map< std::string, osg::ref_ptr<ViewDependentData> > ViewDependentDataShareMap;
mutable OpenThreads::Mutex _viewDependentDataMapMutex;
ViewDependentDataMap _viewDependentDataMap;
ViewDependentDataShareMap _viewDependentDataShareMap;
@ -268,7 +270,7 @@ namespace SceneUtil {
osg::ref_ptr<osg::Texture2D> _fallbackShadowMapTexture;
typedef std::vector< osg::ref_ptr<osg::Uniform> > Uniforms;
mutable OpenThreads::Mutex _accessUniformsAndProgramMutex;
mutable std::mutex _accessUniformsAndProgramMutex;
Uniforms _uniforms;
osg::ref_ptr<osg::Program> _program;

@ -22,7 +22,7 @@ namespace SceneUtil
}
}
osg::StateSet* stateset = mStateSets[nv->getTraversalNumber()%2];
osg::ref_ptr<osg::StateSet> stateset = mStateSets[nv->getTraversalNumber()%2];
apply(stateset, nv);
if (!isCullVisitor)

@ -273,7 +273,7 @@ namespace Shader
osg::ref_ptr<osg::Shader> ShaderManager::getShader(const std::string &templateName, const ShaderManager::DefineMap &defines, osg::Shader::Type shaderType)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mMutex);
std::lock_guard<std::mutex> 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<osg::Program> ShaderManager::getProgram(osg::ref_ptr<osg::Shader> vertexShader, osg::ref_ptr<osg::Shader> fragmentShader)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mMutex);
std::lock_guard<std::mutex> 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<OpenThreads::Mutex> lock(mMutex);
std::lock_guard<std::mutex> lock(mMutex);
for (auto shader : mShaders)
{
if (shader.second != nullptr)

@ -3,6 +3,7 @@
#include <string>
#include <map>
#include <mutex>
#include <osg/ref_ptr>
@ -10,8 +11,6 @@
#include <osgViewer/Viewer>
#include <OpenThreads/Mutex>
namespace Shader
{
@ -63,7 +62,7 @@ namespace Shader
typedef std::map<std::pair<osg::ref_ptr<osg::Shader>, osg::ref_ptr<osg::Shader> >, osg::ref_ptr<osg::Program> > ProgramMap;
ProgramMap mPrograms;
OpenThreads::Mutex mMutex;
std::mutex mMutex;
const osg::ref_ptr<osg::Uniform> mShadowMapAlphaTestEnableUniform = new osg::Uniform();
const osg::ref_ptr<osg::Uniform> mShadowMapAlphaTestDisableUniform = new osg::Uniform();

@ -2,8 +2,6 @@
#include <cassert>
#include <OpenThreads/ScopedLock>
#include <osg/PrimitiveSet>
#include "defs.hpp"
@ -180,7 +178,7 @@ namespace Terrain
osg::ref_ptr<osg::Vec2Array> BufferCache::getUVBuffer(unsigned int numVerts)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mUvBufferMutex);
std::lock_guard<std::mutex> lock(mUvBufferMutex);
if (mUvBufferMap.find(numVerts) != mUvBufferMap.end())
{
return mUvBufferMap[numVerts];
@ -210,7 +208,7 @@ namespace Terrain
osg::ref_ptr<osg::DrawElements> BufferCache::getIndexBuffer(unsigned int numVerts, unsigned int flags)
{
std::pair<int, int> id = std::make_pair(numVerts, flags);
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mIndexBufferMutex);
std::lock_guard<std::mutex> lock(mIndexBufferMutex);
if (mIndexBufferMap.find(id) != mIndexBufferMap.end())
{
@ -234,11 +232,11 @@ namespace Terrain
void BufferCache::clearCache()
{
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mIndexBufferMutex);
std::lock_guard<std::mutex> lock(mIndexBufferMutex);
mIndexBufferMap.clear();
}
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mUvBufferMutex);
std::lock_guard<std::mutex> lock(mUvBufferMutex);
mUvBufferMap.clear();
}
}
@ -246,12 +244,12 @@ namespace Terrain
void BufferCache::releaseGLObjects(osg::State *state)
{
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mIndexBufferMutex);
std::lock_guard<std::mutex> lock(mIndexBufferMutex);
for (auto indexbuffer : mIndexBufferMap)
indexbuffer.second->releaseGLObjects(state);
}
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mUvBufferMutex);
std::lock_guard<std::mutex> lock(mUvBufferMutex);
for (auto uvbuffer : mUvBufferMap)
uvbuffer.second->releaseGLObjects(state);
}

@ -6,6 +6,7 @@
#include <osg/PrimitiveSet>
#include <map>
#include <mutex>
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<std::pair<int, int>, osg::ref_ptr<osg::DrawElements> > mIndexBufferMap;
OpenThreads::Mutex mIndexBufferMutex;
std::mutex mIndexBufferMutex;
std::map<int, osg::ref_ptr<osg::Vec2Array> > mUvBufferMap;
OpenThreads::Mutex mUvBufferMutex;
std::mutex mUvBufferMutex;
};
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save