mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-03-30 03:06:43 +00:00
Merge remote-tracking branch 'remotes/origin/master' into openxr_vr
This commit is contained in:
commit
c9e761eb88
114 changed files with 2260 additions and 1288 deletions
193
.gitlab-ci.yml
193
.gitlab-ci.yml
|
@ -7,19 +7,24 @@ Debian:
|
|||
- linux
|
||||
image: debian:bullseye
|
||||
cache:
|
||||
key: apt-cache
|
||||
key: cache.002
|
||||
paths:
|
||||
- apt-cache/
|
||||
- ccache/
|
||||
before_script:
|
||||
- export APT_CACHE_DIR=`pwd`/apt-cache && mkdir -pv $APT_CACHE_DIR
|
||||
- apt-get update -yq
|
||||
- apt-get -o dir::cache::archives="$APT_CACHE_DIR" install -y cmake build-essential libboost-filesystem-dev libboost-program-options-dev libboost-system-dev libboost-iostreams-dev libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev libsdl2-dev libqt5opengl5-dev libopenal-dev libopenscenegraph-dev libunshield-dev libtinyxml-dev libmygui-dev libbullet-dev
|
||||
- apt-get -o dir::cache::archives="$APT_CACHE_DIR" install -y cmake build-essential libboost-filesystem-dev libboost-program-options-dev libboost-system-dev libboost-iostreams-dev libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev libsdl2-dev libqt5opengl5-dev libopenal-dev libopenscenegraph-dev libunshield-dev libtinyxml-dev libmygui-dev libbullet-dev ccache
|
||||
stage: build
|
||||
script:
|
||||
script:
|
||||
- export CCACHE_BASEDIR="`pwd`"
|
||||
- export CCACHE_DIR="`pwd`/ccache" && mkdir -pv "$CCACHE_DIR"
|
||||
- ccache -z -M 250M
|
||||
- cores_to_use=$((`nproc`-2)); if (( $cores_to_use < 1 )); then cores_to_use=1; fi
|
||||
- mkdir build; cd build; cmake -DCMAKE_BUILD_TYPE=MinSizeRel ../
|
||||
- mkdir build; cd build; cmake -DCMAKE_BUILD_TYPE=MinSizeRel ../ -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
|
||||
- make -j$cores_to_use
|
||||
- DESTDIR=artifacts make install
|
||||
- ccache -s
|
||||
artifacts:
|
||||
paths:
|
||||
- build/artifacts/
|
||||
|
@ -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}"
|
||||
|
|
10
CHANGELOG.md
10
CHANGELOG.md
|
@ -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)
|
||||
{
|
||||
if (mEnvironment.getWorld()->getScriptsEnabled())
|
||||
{
|
||||
// local scripts
|
||||
executeLocalScripts();
|
||||
ScopedProfile<UserStatsType::Script> profile(frameStart, frameNumber, *timer, *stats);
|
||||
|
||||
// global scripts
|
||||
mEnvironment.getScriptManager()->getGlobalScripts().run();
|
||||
if (mEnvironment.getStateManager()->getState() != MWBase::StateManager::State_NoGame)
|
||||
{
|
||||
if (!paused)
|
||||
{
|
||||
if (mEnvironment.getWorld()->getScriptsEnabled())
|
||||
{
|
||||
// local scripts
|
||||
executeLocalScripts();
|
||||
|
||||
// global scripts
|
||||
mEnvironment.getScriptManager()->getGlobalScripts().run();
|
||||
}
|
||||
|
||||
mEnvironment.getWorld()->markCellAsUnchanged();
|
||||
}
|
||||
|
||||
mEnvironment.getWorld()->markCellAsUnchanged();
|
||||
if (!guiActive)
|
||||
{
|
||||
double hours = (frametime * mEnvironment.getWorld()->getTimeScaleFactor()) / 3600.0;
|
||||
mEnvironment.getWorld()->advanceTime(hours, true);
|
||||
mEnvironment.getWorld()->rechargeItems(frametime, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!guiActive)
|
||||
// update mechanics
|
||||
{
|
||||
ScopedProfile<UserStatsType::Mechanics> profile(frameStart, frameNumber, *timer, *stats);
|
||||
|
||||
if (mEnvironment.getStateManager()->getState() != MWBase::StateManager::State_NoGame)
|
||||
{
|
||||
double hours = (frametime * mEnvironment.getWorld()->getTimeScaleFactor()) / 3600.0;
|
||||
mEnvironment.getWorld()->advanceTime(hours, true);
|
||||
mEnvironment.getWorld()->rechargeItems(frametime, true);
|
||||
mEnvironment.getMechanicsManager()->update(frametime, guiActive);
|
||||
}
|
||||
}
|
||||
osg::Timer_t afterScriptTick = osg::Timer::instance()->tick();
|
||||
|
||||
// update actors
|
||||
osg::Timer_t beforeMechanicsTick = osg::Timer::instance()->tick();
|
||||
if (mEnvironment.getStateManager()->getState()!=
|
||||
MWBase::StateManager::State_NoGame)
|
||||
{
|
||||
mEnvironment.getMechanicsManager()->update(frametime,
|
||||
guiActive);
|
||||
}
|
||||
osg::Timer_t afterMechanicsTick = osg::Timer::instance()->tick();
|
||||
|
||||
if (mEnvironment.getStateManager()->getState()==
|
||||
MWBase::StateManager::State_Running)
|
||||
{
|
||||
MWWorld::Ptr player = mEnvironment.getWorld()->getPlayerPtr();
|
||||
if(!guiActive && player.getClass().getCreatureStats(player).isDead())
|
||||
mEnvironment.getStateManager()->endGame();
|
||||
if (mEnvironment.getStateManager()->getState() == MWBase::StateManager::State_Running)
|
||||
{
|
||||
MWWorld::Ptr player = mEnvironment.getWorld()->getPlayerPtr();
|
||||
if(!guiActive && player.getClass().getCreatureStats(player).isDead())
|
||||
mEnvironment.getStateManager()->endGame();
|
||||
}
|
||||
}
|
||||
|
||||
// update physics
|
||||
osg::Timer_t beforePhysicsTick = osg::Timer::instance()->tick();
|
||||
if (mEnvironment.getStateManager()->getState()!=
|
||||
MWBase::StateManager::State_NoGame)
|
||||
{
|
||||
mEnvironment.getWorld()->updatePhysics(frametime, guiActive);
|
||||
ScopedProfile<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()
|
||||
|
|
188
apps/openmw/mwgui/statswatcher.cpp
Normal file
188
apps/openmw/mwgui/statswatcher.cpp
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
69
apps/openmw/mwgui/statswatcher.hpp
Normal file
69
apps/openmw/mwgui/statswatcher.hpp
Normal file
|
@ -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())
|
||||
// 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 = 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);
|
||||
else
|
||||
{
|
||||
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;
|
||||
|
||||
// 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();
|
||||
const std::string& spell = winMgr->getSelectedSpell();
|
||||
if (!spell.empty())
|
||||
winMgr->setSelectedSpell(spell, int(MWMechanics::getSpellSuccessChance(spell, ptr)));
|
||||
else
|
||||
winMgr->setSelectedWeapon(*weapon);
|
||||
|
||||
// Update the selected spell icon
|
||||
MWWorld::ContainerStoreIterator enchantItem = inv.getSelectedEnchantItem();
|
||||
if (enchantItem != inv.end())
|
||||
winMgr->setSelectedEnchantItem(*enchantItem);
|
||||
else
|
||||
{
|
||||
const std::string& spell = winMgr->getSelectedSpell();
|
||||
if (!spell.empty())
|
||||
winMgr->setSelectedSpell(spell, int(getSpellSuccessChance(spell, mWatched)));
|
||||
else
|
||||
winMgr->unsetSelectedSpell();
|
||||
}
|
||||
|
||||
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;
|
||||
osg::Vec2d mFocalPointCurrentOffset;
|
||||
osg::Vec2d mFocalPointTargetOffset;
|
||||
float mFocalPointTransitionSpeedCoef;
|
||||
bool mSkipFocalPointTransition;
|
||||
|
||||
// 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);
|
||||
// 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));
|
||||
|
|
96
apps/openmw/mwrender/viewovershoulder.cpp
Normal file
96
apps/openmw/mwrender/viewovershoulder.cpp
Normal file
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
30
apps/openmw/mwrender/viewovershoulder.hpp
Normal file
30
apps/openmw/mwrender/viewovershoulder.hpp
Normal file
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
71
apps/openmw/mwsound/regionsoundselector.cpp
Normal file
71
apps/openmw/mwsound/regionsoundselector.cpp
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
29
apps/openmw/mwsound/regionsoundselector.hpp
Normal file
29
apps/openmw/mwsound/regionsoundselector.hpp
Normal file
|
@ -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; }
|
||||
{ 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(const osg::Vec3f& pos, float vol, float basevol, float pitch, float mindist, float maxdist, int flags)
|
||||
void init(const SoundParams& params)
|
||||
{
|
||||
mPos = pos;
|
||||
mVolume = vol;
|
||||
mBaseVolume = basevol;
|
||||
mPitch = pitch;
|
||||
mMinDistance = mindist;
|
||||
mMaxDistance = maxdist;
|
||||
mFlags = flags;
|
||||
mFadeOutTime = 0.0f;
|
||||
mParams = params;
|
||||
mHandle = nullptr;
|
||||
}
|
||||
|
||||
void init(float vol, float basevol, float pitch, int flags)
|
||||
{
|
||||
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;
|
||||
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)
|
||||
if (!cell->isExterior())
|
||||
return;
|
||||
|
||||
float a = Misc::Rng::rollClosedProbability();
|
||||
// NOTE: We should use the "Minimum Time Between Environmental Sounds" and
|
||||
// "Maximum Time Between Environmental Sounds" fallback settings here.
|
||||
sTimeToNextEnvSound = 5.0f*a + 15.0f*(1.0f-a);
|
||||
sTimePassed = 0;
|
||||
|
||||
if(regionName != cell->mRegion)
|
||||
{
|
||||
regionName = cell->mRegion;
|
||||
total = 0;
|
||||
}
|
||||
|
||||
const ESM::Region *regn = world->getStore().get<ESM::Region>().search(regionName);
|
||||
if(regn == nullptr)
|
||||
return;
|
||||
|
||||
if(total == 0)
|
||||
{
|
||||
for(const ESM::Region::SoundRef &sndref : regn->mSoundList)
|
||||
total += (int)sndref.mChance;
|
||||
if(total == 0)
|
||||
return;
|
||||
}
|
||||
|
||||
int r = Misc::Rng::rollDice(total);
|
||||
int pos = 0;
|
||||
|
||||
for(const ESM::Region::SoundRef &sndref : regn->mSoundList)
|
||||
{
|
||||
if(r - pos < sndref.mChance)
|
||||
{
|
||||
playSound(sndref.mSound, 1.0f, 1.0f);
|
||||
break;
|
||||
}
|
||||
pos += sndref.mChance;
|
||||
}
|
||||
if (const auto next = mRegionSoundSelector.getNextRandom(duration, cell->mRegion, *world))
|
||||
playSound(*next, 1.0f, 1.0f);
|
||||
}
|
||||
|
||||
void SoundManager::updateWaterSound(float /*duration*/)
|
||||
void SoundManager::updateWaterSound()
|
||||
{
|
||||
static const ESM::Cell *LastCell;
|
||||
MWBase::World* world = MWBase::Environment::get().getWorld();
|
||||
const MWWorld::ConstPtr player = world->getPlayerPtr();
|
||||
osg::Vec3f pos = player.getRefData().getPosition().asVec3();
|
||||
const ESM::Cell *curcell = player.getCell()->getCell();
|
||||
const auto update = mWaterSoundUpdater.update(player, *world);
|
||||
|
||||
float volume = 0.0f;
|
||||
const std::string& soundId = player.getCell()->isExterior() ? mNearWaterOutdoorID : mNearWaterIndoorID;
|
||||
WaterSoundAction action;
|
||||
Sound_Buffer* sfx;
|
||||
std::tie(action, sfx) = getWaterSoundAction(update, curcell);
|
||||
|
||||
if (!mListenerUnderwater)
|
||||
switch (action)
|
||||
{
|
||||
if (curcell->hasWater())
|
||||
{
|
||||
float dist = std::abs(player.getCell()->getWaterLevel() - pos.z());
|
||||
|
||||
if (player.getCell()->isExterior() && dist < mNearWaterOutdoorTolerance)
|
||||
{
|
||||
volume = (mNearWaterOutdoorTolerance - dist) / mNearWaterOutdoorTolerance;
|
||||
|
||||
if (mNearWaterPoints > 1)
|
||||
{
|
||||
int underwaterPoints = 0;
|
||||
|
||||
float step = mNearWaterRadius * 2.0f / (mNearWaterPoints - 1);
|
||||
|
||||
for (int x = 0; x < mNearWaterPoints; x++)
|
||||
{
|
||||
for (int y = 0; y < mNearWaterPoints; y++)
|
||||
{
|
||||
float height = world->getTerrainHeightAt(
|
||||
osg::Vec3f(pos.x() - mNearWaterRadius + x*step, pos.y() - mNearWaterRadius + y*step, 0.0f));
|
||||
|
||||
if (height < 0)
|
||||
underwaterPoints++;
|
||||
}
|
||||
}
|
||||
|
||||
volume *= underwaterPoints * 2.0f / (mNearWaterPoints*mNearWaterPoints);
|
||||
}
|
||||
}
|
||||
else if (!player.getCell()->isExterior() && dist < mNearWaterIndoorTolerance)
|
||||
{
|
||||
volume = (mNearWaterIndoorTolerance - dist) / mNearWaterIndoorTolerance;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
volume = 1.0f;
|
||||
|
||||
volume = std::min(volume, 1.0f);
|
||||
|
||||
if (mNearWaterSound)
|
||||
{
|
||||
if (volume == 0.0f)
|
||||
{
|
||||
case WaterSoundAction::DoNothing:
|
||||
break;
|
||||
case WaterSoundAction::SetVolume:
|
||||
mNearWaterSound->setVolume(update.mVolume * sfx->mVolume);
|
||||
break;
|
||||
case WaterSoundAction::FinishSound:
|
||||
mOutput->finishSound(mNearWaterSound);
|
||||
mNearWaterSound = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool soundIdChanged = false;
|
||||
|
||||
Sound_Buffer *sfx = lookupSound(soundId);
|
||||
if(LastCell != curcell)
|
||||
{
|
||||
LastCell = curcell;
|
||||
SoundMap::const_iterator snditer = mActiveSounds.find(MWWorld::Ptr());
|
||||
if(snditer != mActiveSounds.end())
|
||||
{
|
||||
SoundBufferRefPairList::const_iterator pairiter = std::find_if(
|
||||
snditer->second.begin(), snditer->second.end(),
|
||||
[this](const SoundBufferRefPairList::value_type &item) -> bool
|
||||
{ return mNearWaterSound == item.first; }
|
||||
);
|
||||
if (pairiter != snditer->second.end() && pairiter->second != sfx)
|
||||
soundIdChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(soundIdChanged)
|
||||
{
|
||||
break;
|
||||
case WaterSoundAction::PlaySound:
|
||||
if (mNearWaterSound)
|
||||
mOutput->finishSound(mNearWaterSound);
|
||||
mNearWaterSound = playSound(soundId, volume, 1.0f, Type::Sfx, PlayMode::Loop);
|
||||
}
|
||||
else if (sfx)
|
||||
mNearWaterSound->setVolume(volume * sfx->mVolume);
|
||||
}
|
||||
mNearWaterSound = playSound(update.mId, update.mVolume, 1.0f, Type::Sfx, PlayMode::Loop);
|
||||
break;
|
||||
}
|
||||
else if (volume > 0.0f)
|
||||
|
||||
mLastCell = curcell;
|
||||
}
|
||||
|
||||
std::pair<SoundManager::WaterSoundAction, Sound_Buffer*> SoundManager::getWaterSoundAction(
|
||||
const WaterSoundUpdate& update, const ESM::Cell* cell) const
|
||||
{
|
||||
if (mNearWaterSound)
|
||||
{
|
||||
LastCell = curcell;
|
||||
mNearWaterSound = playSound(soundId, volume, 1.0f, Type::Sfx, PlayMode::Loop);
|
||||
if (update.mVolume == 0.0f)
|
||||
return {WaterSoundAction::FinishSound, nullptr};
|
||||
|
||||
bool soundIdChanged = false;
|
||||
|
||||
Sound_Buffer* sfx = lookupSound(update.mId);
|
||||
if (mLastCell != cell)
|
||||
{
|
||||
const auto snditer = mActiveSounds.find(MWWorld::ConstPtr());
|
||||
if (snditer != mActiveSounds.end())
|
||||
{
|
||||
const auto pairiter = std::find_if(
|
||||
snditer->second.begin(), snditer->second.end(),
|
||||
[this](const SoundBufferRefPairList::value_type &item) -> bool
|
||||
{ return mNearWaterSound == item.first.get(); }
|
||||
);
|
||||
if (pairiter != snditer->second.end() && pairiter->second != sfx)
|
||||
soundIdChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (soundIdChanged)
|
||||
return {WaterSoundAction::PlaySound, nullptr};
|
||||
|
||||
if (sfx)
|
||||
return {WaterSoundAction::SetVolume, sfx};
|
||||
}
|
||||
else if (update.mVolume > 0.0f)
|
||||
return {WaterSoundAction::PlaySound, nullptr};
|
||||
|
||||
return {WaterSoundAction::DoNothing, nullptr};
|
||||
}
|
||||
|
||||
void SoundManager::updateSounds(float duration)
|
||||
|
@ -1061,17 +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;
|
||||
VolumeSettings mVolumeSettings;
|
||||
|
||||
WaterSoundUpdater mWaterSoundUpdater;
|
||||
|
||||
int mNearWaterRadius;
|
||||
int mNearWaterPoints;
|
||||
float mNearWaterIndoorTolerance;
|
||||
float mNearWaterOutdoorTolerance;
|
||||
std::string mNearWaterIndoorID;
|
||||
std::string mNearWaterOutdoorID;
|
||||
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
|
||||
|
|
17
apps/openmw/mwsound/type.hpp
Normal file
17
apps/openmw/mwsound/type.hpp
Normal file
|
@ -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
|
56
apps/openmw/mwsound/volumesettings.cpp
Normal file
56
apps/openmw/mwsound/volumesettings.cpp
Normal file
|
@ -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();
|
||||
}
|
||||
}
|
26
apps/openmw/mwsound/volumesettings.hpp
Normal file
26
apps/openmw/mwsound/volumesettings.hpp
Normal file
|
@ -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
|
71
apps/openmw/mwsound/watersoundupdater.cpp
Normal file
71
apps/openmw/mwsound/watersoundupdater.cpp
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
54
apps/openmw/mwsound/watersoundupdater.hpp
Normal file
54
apps/openmw/mwsound/watersoundupdater.hpp
Normal file
|
@ -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;
|
||||
|
|
83
components/misc/objectpool.hpp
Normal file
83
components/misc/objectpool.hpp
Normal file
|
@ -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 ©,
|
|||
|
||||
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…
Reference in a new issue