mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-02-06 18:15:32 +00:00
Merge remote-tracking branch 'remotes/origin/master' into openmw-vr
This commit is contained in:
commit
1bee59c2b1
145 changed files with 2835 additions and 630 deletions
|
@ -131,6 +131,7 @@ Programmers
|
|||
Martin Otto (MAtahualpa)
|
||||
Mateusz Kołaczek (PL_kolek)
|
||||
Mateusz Malisz (malice)
|
||||
Max Henzerling (SaintMercury)
|
||||
megaton
|
||||
Michael Hogan (Xethik)
|
||||
Michael Mc Donnell
|
||||
|
|
29
CHANGELOG.md
29
CHANGELOG.md
|
@ -1,7 +1,6 @@
|
|||
0.47.0
|
||||
------
|
||||
|
||||
Bug #832: OpenMW-CS: Handle deleted references
|
||||
Bug #1662: Qt4 and Windows binaries crash if there's a non-ASCII character in a file path/config path
|
||||
Bug #1901: Actors colliding behaviour is different from vanilla
|
||||
Bug #1952: Incorrect particle lighting
|
||||
|
@ -9,7 +8,7 @@
|
|||
Bug #2311: Targeted scripts are not properly supported on non-unique RefIDs
|
||||
Bug #2473: Unable to overstock merchants
|
||||
Bug #2798: Mutable ESM records
|
||||
Bug #2976 [reopened]: Issues combining settings from the command line and both config files
|
||||
Bug #2976: [reopened]: Issues combining settings from the command line and both config files
|
||||
Bug #3137: Walking into a wall prevents jumping
|
||||
Bug #3372: Projectiles and magic bolts go through moving targets
|
||||
Bug #3676: NiParticleColorModifier isn't applied properly
|
||||
|
@ -24,7 +23,7 @@
|
|||
Bug #4201: Projectile-projectile collision
|
||||
Bug #4247: Cannot walk up stairs in Ebonheart docks
|
||||
Bug #4357: OpenMW-CS: TopicInfos index sorting and rearranging isn't fully functional
|
||||
Bug #4363: Editor: Defect in Clone Function for Dialogue Info records
|
||||
Bug #4363: OpenMW-CS: Defect in Clone Function for Dialogue Info records
|
||||
Bug #4447: Actor collision capsule shape allows looking through some walls
|
||||
Bug #4465: Collision shape overlapping causes twitching
|
||||
Bug #4476: Abot Gondoliers: player hangs in air during scenic travel
|
||||
|
@ -34,6 +33,7 @@
|
|||
Bug #4764: Data race in osg ParticleSystem
|
||||
Bug #4765: Data race in ChunkManager -> Array::setBinding
|
||||
Bug #4774: Guards are ignorant of an invisible player that tries to attack them
|
||||
Bug #5026: Data races with rain intensity uniform set by sky and used by water
|
||||
Bug #5101: Hostile followers travel with the player
|
||||
Bug #5108: Savegame bloating due to inefficient fog textures format
|
||||
Bug #5165: Active spells should use real time intead of timestamps
|
||||
|
@ -44,11 +44,11 @@
|
|||
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 #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 #5391: Races Redone 1.2 bodies don't show on the inventory
|
||||
Bug #5397: NPC greeting does not reset if you leave + reenter area
|
||||
Bug #5400: Editor: Verifier checks race of non-skin bodyparts
|
||||
Bug #5400: OpenMW-CS: Verifier checks race of non-skin bodyparts
|
||||
Bug #5403: Enchantment effect doesn't show on an enemy during death animation
|
||||
Bug #5415: Environment maps in ebony cuirass and HiRez Armors Indoril cuirass don't work
|
||||
Bug #5416: Junk non-node records before the root node are not handled gracefully
|
||||
|
@ -57,6 +57,7 @@
|
|||
Bug #5424: Creatures do not headtrack player
|
||||
Bug #5425: Poison effect only appears for one frame
|
||||
Bug #5427: GetDistance unknown ID error is misleading
|
||||
Bug #5431: Physics performance degradation after a specific number of actors on a scene
|
||||
Bug #5435: Enemies can't hurt the player when collision is off
|
||||
Bug #5441: Enemies can't push a player character when in critical strike stance
|
||||
Bug #5451: Magic projectiles don't disappear with the caster
|
||||
|
@ -70,6 +71,7 @@
|
|||
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 #5525: Case-insensitive search in the inventory window does not work with non-ASCII characters
|
||||
Bug #5531: Actors flee using current rotation by axis x
|
||||
Bug #5539: Window resize breaks when going from a lower resolution to full screen resolution
|
||||
Bug #5548: Certain exhausted topics can be highlighted again even though there's no new dialogue
|
||||
|
@ -86,6 +88,7 @@
|
|||
Bug #5656: Sneaking characters block hits while standing
|
||||
Bug #5661: Region sounds don't play at the right interval
|
||||
Bug #5675: OpenMW-cs. FRMR subrecords are saved with the wrong MastIdx
|
||||
Bug #5680: Bull Netches incorrectly aim over the player character's head and always miss
|
||||
Bug #5681: Player character can clip or pass through bridges instead of colliding against them
|
||||
Bug #5687: Bound items covering the same inventory slot expiring at the same time freezes the game
|
||||
Bug #5688: Water shader broken indoors with enable indoor shadows = false
|
||||
|
@ -93,27 +96,37 @@
|
|||
Bug #5703: OpenMW-CS menu system crashing on XFCE
|
||||
Bug #5706: AI sequences stop looping after the saved game is reloaded
|
||||
Bug #5713: OpenMW-CS: Collada models are corrupted in Qt-based scene view
|
||||
Bug #5731: Editor: skirts are invisible on characters
|
||||
Bug #5731: OpenMW-CS: skirts are invisible on characters
|
||||
Bug #5739: Saving and loading the save a second or two before hitting the ground doesn't count fall damage
|
||||
Bug #5758: Paralyzed actors behavior is inconsistent with vanilla
|
||||
Bug #5762: Movement solver is insufficiently robust
|
||||
Bug #5807: Video decoding crash on ARM
|
||||
Bug #5821: NPCs from mods getting removed if mod order was changed
|
||||
Bug #5835: OpenMW doesn't accept negative values for NPC's hello, alarm, fight, and flee
|
||||
Bug #5836: OpenMW dialogue/greeting/voice filter doesn't accept negative Ai values for NPC's hello, alarm, fight, and flee
|
||||
Bug #5838: Local map and other menus become blank in some locations while playing Wizards' Islands mod.
|
||||
Bug #5840: GetSoundPlaying "Health Damage" doesn't play when NPC hits target with shield effect ( vanilla engine behavior )
|
||||
Bug #5841: Can't Cast Zero Cost Spells When Magicka is < 0
|
||||
Bug #5869: Guards can initiate arrest dialogue behind locked doors
|
||||
Bug #5871: The console appears if you type the Russian letter "Ё" in the name of the enchantment
|
||||
Bug #5877: Effects appearing with empty icon
|
||||
Bug #5899: Visible modal windows and dropdowns crashing game on exit
|
||||
Bug #5902: NiZBufferProperty is unable to disable the depth test
|
||||
Bug #5906: Sunglare doesn't work with Mesa drivers and AMD GPUs
|
||||
Bug #5912: ImprovedBound mod doesn't work
|
||||
Bug #5914: BM: The Swimmer can't reach destination
|
||||
Feature #390: 3rd person look "over the shoulder"
|
||||
Feature #832: OpenMW-CS: Handle deleted references
|
||||
Feature #1536: Show more information about level on menu
|
||||
Feature #2386: Distant Statics in the form of Object Paging
|
||||
Feature #2404: Levelled List can not be placed into a container
|
||||
Feature #2686: Timestamps in openmw.log
|
||||
Feature #3171: OpenMW-CS: Instance drag selection
|
||||
Feature #4894: Consider actors as obstacles for pathfinding
|
||||
Feature #4899: Alpha-To-Coverage Anti-Aliasing for alpha testing
|
||||
Feature #4977: Use the "default icon.tga" when an item's icon is not found
|
||||
Feature #5043: Head Bobbing
|
||||
Feature #5199: Improve Scene Colors
|
||||
Feature #5199: OpenMW-CS: Improve scene view colors
|
||||
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
|
||||
|
@ -122,7 +135,6 @@
|
|||
Feature #5486: Fixes trainers to choose their training skills based on their base skill points
|
||||
Feature #5519: Code Patch tab in launcher
|
||||
Feature #5524: Resume failed script execution after reload
|
||||
Feature #5525: Search fields tweaks (utf-8)
|
||||
Feature #5545: Option to allow stealing from an unconscious NPC during combat
|
||||
Feature #5563: Run physics update in background thread
|
||||
Feature #5579: MCP SetAngle enhancement
|
||||
|
@ -135,6 +147,7 @@
|
|||
Feature #5730: Add graphic herbalism option to the launcher and documents
|
||||
Feature #5771: ori command should report where a mesh is loaded from and whether the x version is used.
|
||||
Feature #5813: Instanced groundcover support
|
||||
Feature #5910: Fall back to delta time when physics can't keep up
|
||||
Task #5480: Drop Qt4 support
|
||||
Task #5520: Improve cell name autocompleter implementation
|
||||
Task #5844: Update 'toggle sneak' documentation
|
||||
|
|
|
@ -1,13 +1,19 @@
|
|||
#!/bin/sh -e
|
||||
#!/bin/sh -ex
|
||||
|
||||
# workaround python issue on travis
|
||||
HOMEBREW_NO_AUTO_UPDATE=1 brew uninstall --ignore-dependencies python@3.8 || true
|
||||
HOMEBREW_NO_AUTO_UPDATE=1 brew uninstall --ignore-dependencies python@3.9 || true
|
||||
HOMEBREW_NO_AUTO_UPDATE=1 brew uninstall --ignore-dependencies qt@6 || true
|
||||
|
||||
# Some of these tools can come from places other than brew, so check before installing
|
||||
command -v ccache >/dev/null 2>&1 || brew install ccache
|
||||
command -v cmake >/dev/null 2>&1 || brew install cmake
|
||||
command -v qmake >/dev/null 2>&1 || brew install qt
|
||||
command -v qmake >/dev/null 2>&1 || brew install qt@5
|
||||
export PATH="/usr/local/opt/qt@5/bin:$PATH" # needed to use qmake in none default path as qt now points to qt6
|
||||
|
||||
ccache --version
|
||||
cmake --version
|
||||
qmake --version
|
||||
|
||||
curl -fSL -R -J https://downloads.openmw.org/osx/dependencies/openmw-deps-f8918dd.zip -o ~/openmw-deps.zip
|
||||
unzip -o ~/openmw-deps.zip -d /private/tmp/openmw-deps > /dev/null
|
||||
|
|
|
@ -329,6 +329,16 @@ add_qt_platform_dlls() {
|
|||
QT_PLATFORMS[$CONFIG]="${QT_PLATFORMS[$CONFIG]} $@"
|
||||
}
|
||||
|
||||
declare -A QT_STYLES
|
||||
QT_STYLES["Release"]=""
|
||||
QT_STYLES["Debug"]=""
|
||||
QT_STYLES["RelWithDebInfo"]=""
|
||||
add_qt_style_dlls() {
|
||||
local CONFIG=$1
|
||||
shift
|
||||
QT_STYLES[$CONFIG]="${QT_STYLES[$CONFIG]} $@"
|
||||
}
|
||||
|
||||
if [ -z $PLATFORM ]; then
|
||||
PLATFORM="$(uname -m)"
|
||||
fi
|
||||
|
@ -842,9 +852,11 @@ fi
|
|||
wrappedExit 1
|
||||
fi
|
||||
|
||||
if ! [ -e "aqt-venv/${VENV_BIN_DIR}/aqt" ]; then
|
||||
# check version
|
||||
aqt-venv/${VENV_BIN_DIR}/pip list | grep 'aqtinstall\s*1.1.3' || [ $? -ne 0 ]
|
||||
if [ $? -eq 0 ]; then
|
||||
echo " Installing aqt wheel into virtualenv..."
|
||||
run_cmd "aqt-venv/${VENV_BIN_DIR}/pip" install aqtinstall==0.9.2
|
||||
run_cmd "aqt-venv/${VENV_BIN_DIR}/pip" install aqtinstall==1.1.3
|
||||
fi
|
||||
popd > /dev/null
|
||||
|
||||
|
@ -872,6 +884,7 @@ fi
|
|||
fi
|
||||
add_runtime_dlls $CONFIGURATION "$(pwd)/bin/Qt5"{Core,Gui,Network,OpenGL,Widgets}${DLLSUFFIX}.dll
|
||||
add_qt_platform_dlls $CONFIGURATION "$(pwd)/plugins/platforms/qwindows${DLLSUFFIX}.dll"
|
||||
add_qt_style_dlls $CONFIGURATION "$(pwd)/plugins/styles/qwindowsvistastyle${DLLSUFFIX}.dll"
|
||||
done
|
||||
echo Done.
|
||||
else
|
||||
|
@ -887,6 +900,7 @@ fi
|
|||
DIR=$(windowsPathAsUnix "${QT_SDK}")
|
||||
add_runtime_dlls $CONFIGURATION "${DIR}/bin/Qt5"{Core,Gui,Network,OpenGL,Widgets}${DLLSUFFIX}.dll
|
||||
add_qt_platform_dlls $CONFIGURATION "${DIR}/plugins/platforms/qwindows${DLLSUFFIX}.dll"
|
||||
add_qt_style_dlls $CONFIGURATION "${DIR}/plugins/styles/qwindowsvistastyle${DLLSUFFIX}.dll"
|
||||
done
|
||||
echo Done.
|
||||
fi
|
||||
|
@ -1082,6 +1096,13 @@ fi
|
|||
cp "$DLL" "${DLL_PREFIX}platforms"
|
||||
done
|
||||
echo
|
||||
echo "- Qt Style DLLs..."
|
||||
mkdir -p ${DLL_PREFIX}styles
|
||||
for DLL in ${QT_STYLES[$CONFIGURATION]}; do
|
||||
echo " $(basename $DLL)"
|
||||
cp "$DLL" "${DLL_PREFIX}styles"
|
||||
done
|
||||
echo
|
||||
done
|
||||
#fi
|
||||
|
||||
|
@ -1089,7 +1110,13 @@ if [ -n "$ACTIVATE_MSVC" ]; then
|
|||
echo -n "- Activating MSVC in the current shell... "
|
||||
command -v vswhere >/dev/null 2>&1 || { echo "Error: vswhere is not on the path."; wrappedExit 1; }
|
||||
|
||||
MSVC_INSTALLATION_PATH=$(vswhere -products '*' -version "[$MSVC_REAL_VER,$(awk "BEGIN { print $MSVC_REAL_VER + 1; exit }"))" -property installationPath)
|
||||
# There are so many arguments now that I'm going to document them:
|
||||
# * products: allow Visual Studio or standalone build tools
|
||||
# * version: obvious. Awk helps make a version range by adding one.
|
||||
# * property installationPath: only give the installation path.
|
||||
# * latest: return only one result if several candidates exist. Prefer the last installed/updated
|
||||
# * requires: make sure it's got the MSVC compiler instead of, for example, just the .NET compiler. The .x86.x64 suffix means it's for either, not that it's the x64 on x86 cross compiler as you always get both
|
||||
MSVC_INSTALLATION_PATH=$(vswhere -products '*' -version "[$MSVC_REAL_VER,$(awk "BEGIN { print $MSVC_REAL_VER + 1; exit }"))" -property installationPath -latest -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64)
|
||||
if [ -z "$MSVC_INSTALLATION_PATH" ]; then
|
||||
echo "vswhere was unable to find MSVC $MSVC_DISPLAY_YEAR"
|
||||
wrappedExit 1
|
||||
|
|
|
@ -4,7 +4,7 @@ export CXX=clang++
|
|||
export CC=clang
|
||||
|
||||
DEPENDENCIES_ROOT="/private/tmp/openmw-deps/openmw-deps"
|
||||
QT_PATH=$(brew --prefix qt)
|
||||
QT_PATH=$(brew --prefix qt@5)
|
||||
CCACHE_EXECUTABLE=$(brew --prefix ccache)/bin/ccache
|
||||
mkdir build
|
||||
cd build
|
||||
|
|
|
@ -24,6 +24,7 @@ declare -rA GROUPED_DEPS=(
|
|||
libbullet-dev liblz4-dev libpng-dev libjpeg-dev
|
||||
libxcb-glx0-dev libx11-dev
|
||||
"
|
||||
# TODO: add librecastnavigation-dev when debian is ready
|
||||
|
||||
# These dependencies can alternatively be built and linked statically.
|
||||
[openmw-deps-dynamic]="libmygui-dev libopenscenegraph-dev"
|
||||
|
|
|
@ -16,6 +16,11 @@ endif()
|
|||
# Detect OS
|
||||
include(cmake/OSIdentity.cmake)
|
||||
|
||||
option(OPENMW_GL4ES_MANUAL_INIT "Manually initialize gl4es. This is more reliable on platforms without a windowing system. Requires gl4es to be configured with -DNOEGL=ON -DNO_LOADER=ON -DNO_INIT_CONSTRUCTOR=ON." OFF)
|
||||
if(OPENMW_GL4ES_MANUAL_INIT)
|
||||
add_definitions(-DOPENMW_GL4ES_MANUAL_INIT)
|
||||
endif()
|
||||
|
||||
# Apps and tools
|
||||
option(BUILD_OPENMW "Build OpenMW" ON)
|
||||
option(BUILD_LAUNCHER "Build Launcher" ON)
|
||||
|
@ -111,6 +116,12 @@ option(SDL2_STATIC "Link static build of SDL into the binaries" FALSE)
|
|||
option(QT_STATIC "Link static build of QT into the binaries" FALSE)
|
||||
|
||||
option(OPENMW_USE_SYSTEM_BULLET "Use system provided bullet physics library" ON)
|
||||
if(OPENMW_USE_SYSTEM_BULLET)
|
||||
set(_bullet_static_default OFF)
|
||||
else()
|
||||
set(_bullet_static_default ON)
|
||||
endif()
|
||||
option(BULLET_STATIC "Link static build of Bullet into the binaries" ${_bullet_static_default})
|
||||
|
||||
option(OPENMW_USE_SYSTEM_OSG "Use system provided OpenSceneGraph libraries" ON)
|
||||
if(OPENMW_USE_SYSTEM_OSG)
|
||||
|
@ -131,6 +142,7 @@ option(MYGUI_STATIC "Link static build of Mygui into the binaries" ${_mygui_stat
|
|||
option(OPENMW_USE_SYSTEM_RECASTNAVIGATION "Use system provided recastnavigation library" OFF)
|
||||
if(OPENMW_USE_SYSTEM_RECASTNAVIGATION)
|
||||
set(_recastnavigation_static_default OFF)
|
||||
find_package(RecastNavigation REQUIRED)
|
||||
else()
|
||||
set(_recastnavigation_static_default ON)
|
||||
endif()
|
||||
|
@ -345,7 +357,9 @@ set(BOOST_COMPONENTS system filesystem program_options iostreams)
|
|||
if(WIN32)
|
||||
set(BOOST_COMPONENTS ${BOOST_COMPONENTS} locale)
|
||||
if(MSVC)
|
||||
set(BOOST_COMPONENTS ${BOOST_COMPONENTS} zlib)
|
||||
# boost-zlib is not present (nor needed) in vcpkg version of boost.
|
||||
# there, it is part of boost-iostreams instead.
|
||||
set(BOOST_OPTIONAL_COMPONENTS zlib)
|
||||
endif(MSVC)
|
||||
endif(WIN32)
|
||||
|
||||
|
@ -355,7 +369,7 @@ endif()
|
|||
|
||||
set(Boost_NO_BOOST_CMAKE ON)
|
||||
|
||||
find_package(Boost 1.6.2 REQUIRED COMPONENTS ${BOOST_COMPONENTS})
|
||||
find_package(Boost 1.6.2 REQUIRED COMPONENTS ${BOOST_COMPONENTS} OPTIONAL_COMPONENTS ${BOOST_OPTIONAL_COMPONENTS})
|
||||
if(OPENMW_USE_SYSTEM_MYGUI)
|
||||
find_package(MyGUI 3.2.2 REQUIRED)
|
||||
endif()
|
||||
|
|
|
@ -215,12 +215,16 @@ if(APPLE)
|
|||
endif(APPLE)
|
||||
|
||||
target_link_libraries(openmw-cs
|
||||
${OSG_LIBRARIES}
|
||||
${OSGTEXT_LIBRARIES}
|
||||
${OSGUTIL_LIBRARIES}
|
||||
# CMake's built-in OSG finder does not use pkgconfig, so we have to
|
||||
# manually ensure the order is correct for inter-library dependencies.
|
||||
# This only makes a difference with `-DOPENMW_USE_SYSTEM_OSG=ON -DOSG_STATIC=ON`.
|
||||
# https://gitlab.kitware.com/cmake/cmake/-/issues/21701
|
||||
${OSGVIEWER_LIBRARIES}
|
||||
${OSGGA_LIBRARIES}
|
||||
${OSGFX_LIBRARIES}
|
||||
${OSGGA_LIBRARIES}
|
||||
${OSGUTIL_LIBRARIES}
|
||||
${OSGTEXT_LIBRARIES}
|
||||
${OSG_LIBRARIES}
|
||||
${EXTERN_OSGQT_LIBRARY}
|
||||
${Boost_SYSTEM_LIBRARY}
|
||||
${Boost_FILESYSTEM_LIBRARY}
|
||||
|
@ -233,17 +237,24 @@ if(OSG_STATIC)
|
|||
add_library(openmw_cs_osg_plugins INTERFACE)
|
||||
foreach(_plugin ${USED_OSG_PLUGINS})
|
||||
string(TOUPPER ${_plugin} _plugin_uc)
|
||||
if (${_plugin_uc}_LIBRARY MATCHES "[/.]")
|
||||
if(OPENMW_USE_SYSTEM_OSG)
|
||||
list(APPEND _osg_plugins_static_files ${${_plugin_uc}_LIBRARY})
|
||||
else()
|
||||
list(APPEND _osg_plugins_static_files $<TARGET_FILE:${${_plugin_uc}_LIBRARY}>)
|
||||
target_link_libraries(openmw_cs_osg_plugins INTERFACE $<TARGET_PROPERTY:${${_plugin_uc}_LIBRARY},LINK_LIBRARIES>)
|
||||
add_dependencies(openmw_cs_osg_plugins ${${_plugin_uc}_LIBRARY})
|
||||
endif()
|
||||
target_link_libraries(openmw_cs_osg_plugins INTERFACE ${${_plugin_uc}_LIBRARY})
|
||||
endforeach()
|
||||
# We use --whole-archive because OSG plugins use registration.
|
||||
get_whole_archive_options(_opts ${_osg_plugins_static_files})
|
||||
target_link_options(openmw_cs_osg_plugins INTERFACE ${_opts})
|
||||
target_link_libraries(openmw-cs openmw_cs_osg_plugins)
|
||||
|
||||
if(OPENMW_USE_SYSTEM_OSG)
|
||||
# OSG plugin pkgconfig files are missing these dependencies.
|
||||
# https://github.com/openscenegraph/OpenSceneGraph/issues/1052
|
||||
target_link_libraries(openmw freetype jpeg png)
|
||||
endif()
|
||||
endif(OSG_STATIC)
|
||||
|
||||
target_link_libraries(openmw-cs Qt5::Widgets Qt5::Core Qt5::Network Qt5::OpenGL)
|
||||
|
|
|
@ -34,7 +34,7 @@ namespace CSMPrefs
|
|||
void storeValue(const QKeySequence& sequence);
|
||||
void resetState();
|
||||
|
||||
static const int MaxKeys = 4;
|
||||
static constexpr int MaxKeys = 4;
|
||||
|
||||
QPushButton* mButton;
|
||||
|
||||
|
|
|
@ -102,13 +102,18 @@ add_openmw_dir (mwbase
|
|||
# Main executable
|
||||
|
||||
set(OPENMW_LINK_TARGETS
|
||||
${OSG_LIBRARIES}
|
||||
# CMake's built-in OSG finder does not use pkgconfig, so we have to
|
||||
# manually ensure the order is correct for inter-library dependencies.
|
||||
# This only makes a difference with `-DOPENMW_USE_SYSTEM_OSG=ON -DOSG_STATIC=ON`.
|
||||
# https://gitlab.kitware.com/cmake/cmake/-/issues/21701
|
||||
${OSGPARTICLE_LIBRARIES}
|
||||
${OSGUTIL_LIBRARIES}
|
||||
${OSGDB_LIBRARIES}
|
||||
${OSGVIEWER_LIBRARIES}
|
||||
${OSGGA_LIBRARIES}
|
||||
${OSGSHADOW_LIBRARIES}
|
||||
${OSGDB_LIBRARIES}
|
||||
${OSGUTIL_LIBRARIES}
|
||||
${OSG_LIBRARIES}
|
||||
|
||||
${Boost_SYSTEM_LIBRARY}
|
||||
${Boost_THREAD_LIBRARY}
|
||||
${Boost_FILESYSTEM_LIBRARY}
|
||||
|
@ -138,18 +143,26 @@ if(OSG_STATIC)
|
|||
add_library(openmw_osg_plugins INTERFACE)
|
||||
foreach(_plugin ${USED_OSG_PLUGINS})
|
||||
string(TOUPPER ${_plugin} _plugin_uc)
|
||||
if (${_plugin_uc}_LIBRARY MATCHES "[/.]")
|
||||
if(OPENMW_USE_SYSTEM_OSG)
|
||||
list(APPEND _osg_plugins_static_files ${${_plugin_uc}_LIBRARY})
|
||||
else()
|
||||
list(APPEND _osg_plugins_static_files $<TARGET_FILE:${${_plugin_uc}_LIBRARY}>)
|
||||
target_link_libraries(openmw_osg_plugins INTERFACE $<TARGET_PROPERTY:${${_plugin_uc}_LIBRARY},LINK_LIBRARIES>)
|
||||
add_dependencies(openmw_osg_plugins ${${_plugin_uc}_LIBRARY})
|
||||
endif()
|
||||
target_link_libraries(openmw_osg_plugins INTERFACE ${${_plugin_uc}_LIBRARY})
|
||||
endforeach()
|
||||
# We use --whole-archive because OSG plugins use registration.
|
||||
get_whole_archive_options(_opts ${_osg_plugins_static_files})
|
||||
target_link_options(openmw_osg_plugins INTERFACE ${_opts})
|
||||
set(OPENMW_LINK_TARGETS ${OPENMW_LINK_TARGETS}
|
||||
openmw_osg_plugins)
|
||||
|
||||
if(OPENMW_USE_SYSTEM_OSG)
|
||||
# OSG plugin pkgconfig files are missing these dependencies.
|
||||
# https://github.com/openscenegraph/OpenSceneGraph/issues/1052
|
||||
set(OPENMW_LINK_TARGETS ${OPENMW_LINK_TARGETS}
|
||||
freetype jpeg png)
|
||||
endif()
|
||||
endif(OSG_STATIC)
|
||||
|
||||
if (ANDROID)
|
||||
|
|
|
@ -552,6 +552,7 @@ namespace MWBase
|
|||
virtual void launchMagicBolt (const std::string& spellId, const MWWorld::Ptr& caster, const osg::Vec3f& fallbackDirection) = 0;
|
||||
virtual void launchProjectile (MWWorld::Ptr& actor, MWWorld::Ptr& projectile,
|
||||
const osg::Vec3f& worldPos, const osg::Quat& orient, MWWorld::Ptr& bow, float speed, float attackStrength) = 0;
|
||||
virtual void updateProjectilesCasters() = 0;
|
||||
|
||||
virtual void applyLoopingParticles(const MWWorld::Ptr& ptr) = 0;
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@ namespace MWGui
|
|||
{
|
||||
static const int fontHeight = MWBase::Environment::get().getWindowManager()->getFontHeight();
|
||||
|
||||
MyGUI::GlyphInfo* gi = font->getGlyphInfo(ch);
|
||||
const MyGUI::GlyphInfo* gi = font->getGlyphInfo(ch);
|
||||
if (gi)
|
||||
{
|
||||
const float scale = font->getDefaultHeight() / (float) fontHeight;
|
||||
|
|
|
@ -42,6 +42,7 @@ namespace MWGui
|
|||
|
||||
void Layout::shutdown()
|
||||
{
|
||||
setVisible(false);
|
||||
MyGUI::Gui::getInstance().destroyWidget(mMainWidget);
|
||||
mListWindowRoot.clear();
|
||||
}
|
||||
|
|
|
@ -35,20 +35,20 @@ namespace MWGui
|
|||
bool onDropItem(const MWWorld::Ptr &item, int count) override;
|
||||
bool onTakeItem(const MWWorld::Ptr &item, int count) override;
|
||||
|
||||
static const int Category_Weapon = (1<<1);
|
||||
static const int Category_Apparel = (1<<2);
|
||||
static const int Category_Misc = (1<<3);
|
||||
static const int Category_Magic = (1<<4);
|
||||
static const int Category_All = 255;
|
||||
static constexpr int Category_Weapon = (1<<1);
|
||||
static constexpr int Category_Apparel = (1<<2);
|
||||
static constexpr int Category_Misc = (1<<3);
|
||||
static constexpr int Category_Magic = (1<<4);
|
||||
static constexpr int Category_All = 255;
|
||||
|
||||
static const int Filter_OnlyIngredients = (1<<0);
|
||||
static const int Filter_OnlyEnchanted = (1<<1);
|
||||
static const int Filter_OnlyEnchantable = (1<<2);
|
||||
static const int Filter_OnlyChargedSoulstones = (1<<3);
|
||||
static const int Filter_OnlyUsableItems = (1<<4); // Only items with a Use action
|
||||
static const int Filter_OnlyRepairable = (1<<5);
|
||||
static const int Filter_OnlyRechargable = (1<<6);
|
||||
static const int Filter_OnlyRepairTools = (1<<7);
|
||||
static constexpr int Filter_OnlyIngredients = (1<<0);
|
||||
static constexpr int Filter_OnlyEnchanted = (1<<1);
|
||||
static constexpr int Filter_OnlyEnchantable = (1<<2);
|
||||
static constexpr int Filter_OnlyChargedSoulstones = (1<<3);
|
||||
static constexpr int Filter_OnlyUsableItems = (1<<4); // Only items with a Use action
|
||||
static constexpr int Filter_OnlyRepairable = (1<<5);
|
||||
static constexpr int Filter_OnlyRechargable = (1<<6);
|
||||
static constexpr int Filter_OnlyRepairTools = (1<<7);
|
||||
|
||||
|
||||
private:
|
||||
|
|
|
@ -187,7 +187,9 @@ namespace MWGui
|
|||
}
|
||||
else if (mWidgetMap.find(effectId) != mWidgetMap.end())
|
||||
{
|
||||
mWidgetMap[effectId]->setVisible(false);
|
||||
MyGUI::ImageBox* image = mWidgetMap[effectId];
|
||||
image->setVisible(false);
|
||||
image->setAlpha(1.f);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -63,6 +63,8 @@ namespace MWGui
|
|||
|
||||
void watchActor(const MWWorld::Ptr& ptr);
|
||||
MWWorld::Ptr getWatchedActor() const { return mWatched; }
|
||||
|
||||
void forceUpdate() { mWatchedStatsEmpty = true; }
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -268,7 +268,7 @@ namespace MWGui
|
|||
void initialiseOverride() override;
|
||||
|
||||
private:
|
||||
static const int sIconOffset = 24;
|
||||
static constexpr int sIconOffset = 24;
|
||||
|
||||
void updateWidgets();
|
||||
|
||||
|
|
|
@ -183,7 +183,7 @@ namespace MWGui
|
|||
, mWerewolfOverlayEnabled(Settings::Manager::getBool ("werewolf overlay", "GUI"))
|
||||
, mHudEnabled(true)
|
||||
, mCursorVisible(true)
|
||||
, mCursorActive(false)
|
||||
, mCursorActive(true)
|
||||
, mVideoEnabled(false)
|
||||
, mPlayerBounty(-1)
|
||||
, mGui(nullptr)
|
||||
|
@ -201,7 +201,7 @@ namespace MWGui
|
|||
{
|
||||
float uiScale = Settings::Manager::getFloat("scaling factor", "GUI");
|
||||
mGuiPlatform = new osgMyGUI::Platform(viewer, guiRoot, resourceSystem->getImageManager(), uiScale);
|
||||
mGuiPlatform->initialise(resourcePath, logpath);
|
||||
mGuiPlatform->initialise(resourcePath, (boost::filesystem::path(logpath) / "MyGUI.log").generic_string());
|
||||
|
||||
|
||||
#ifdef USE_OPENXR
|
||||
|
@ -530,6 +530,8 @@ namespace MWGui
|
|||
}
|
||||
else
|
||||
allow(GW_ALL);
|
||||
|
||||
mStatsWatcher->forceUpdate();
|
||||
}
|
||||
|
||||
WindowManager::~WindowManager()
|
||||
|
@ -538,8 +540,6 @@ namespace MWGui
|
|||
{
|
||||
mStatsWatcher.reset();
|
||||
|
||||
mKeyboardNavigation.reset();
|
||||
|
||||
MyGUI::LanguageManager::getInstance().eventRequestTag.clear();
|
||||
MyGUI::PointerManager::getInstance().eventChangeMousePointer.clear();
|
||||
MyGUI::InputManager::getInstance().eventChangeKeyFocus.clear();
|
||||
|
@ -558,6 +558,8 @@ namespace MWGui
|
|||
delete mCursorManager;
|
||||
delete mToolTips;
|
||||
|
||||
mKeyboardNavigation.reset();
|
||||
|
||||
cleanupGarbage();
|
||||
|
||||
mFontLoader.reset();
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
|
||||
#include "../mwmechanics/actorutil.hpp"
|
||||
|
||||
#include <components/misc/timer.hpp>
|
||||
|
||||
namespace MWRender
|
||||
{
|
||||
class Animation;
|
||||
|
@ -41,12 +43,18 @@ namespace MWMechanics
|
|||
bool isTurningToPlayer() const;
|
||||
void setTurningToPlayer(bool turning);
|
||||
|
||||
Misc::TimerStatus updateEngageCombatTimer(float duration)
|
||||
{
|
||||
return mEngageCombat.update(duration);
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<CharacterController> mCharacterController;
|
||||
int mGreetingTimer{0};
|
||||
float mTargetAngleRadians{0.f};
|
||||
GreetingState mGreetingState{Greet_None};
|
||||
bool mIsTurningToPlayer{false};
|
||||
Misc::DeviatingPeriodicTimer mEngageCombat{1.0f, 0.25f, Misc::Rng::deviate(0, 0.25f)};
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -346,7 +346,11 @@ namespace MWMechanics
|
|||
if (actor != MWMechanics::getPlayer())
|
||||
return;
|
||||
|
||||
MWWorld::Ptr newItem = *store.getSlot(slot);
|
||||
MWWorld::Ptr newItem;
|
||||
auto it = store.getSlot(slot);
|
||||
// Equip can fail because beast races cannot equip boots/helmets
|
||||
if(it != store.end())
|
||||
newItem = *it;
|
||||
|
||||
if (newItem.isEmpty() || boundPtr != newItem)
|
||||
return;
|
||||
|
@ -1901,14 +1905,11 @@ namespace MWMechanics
|
|||
{
|
||||
if(!paused)
|
||||
{
|
||||
static float timerUpdateAITargets = 0;
|
||||
static float timerUpdateHeadTrack = 0;
|
||||
static float timerUpdateEquippedLight = 0;
|
||||
static float timerUpdateHello = 0;
|
||||
const float updateEquippedLightInterval = 1.0f;
|
||||
|
||||
// target lists get updated once every 1.0 sec
|
||||
if (timerUpdateAITargets >= 1.0f) timerUpdateAITargets = 0;
|
||||
if (timerUpdateHeadTrack >= 0.3f) timerUpdateHeadTrack = 0;
|
||||
if (timerUpdateHello >= 0.25f) timerUpdateHello = 0;
|
||||
if (mTimerDisposeSummonsCorpses >= 0.2f) mTimerDisposeSummonsCorpses = 0;
|
||||
|
@ -1961,6 +1962,8 @@ namespace MWMechanics
|
|||
|
||||
iter->first.getClass().getCreatureStats(iter->first).getActiveSpells().update(duration);
|
||||
|
||||
const Misc::TimerStatus engageCombatTimerStatus = iter->second->updateEngageCombatTimer(duration);
|
||||
|
||||
// For dead actors we need to update looping spell particles
|
||||
if (iter->first.getClass().getCreatureStats(iter->first).isDead())
|
||||
{
|
||||
|
@ -1989,7 +1992,7 @@ namespace MWMechanics
|
|||
}
|
||||
if (aiActive && inProcessingRange)
|
||||
{
|
||||
if (timerUpdateAITargets == 0)
|
||||
if (engageCombatTimerStatus == Misc::TimerStatus::Elapsed)
|
||||
{
|
||||
if (!isPlayer)
|
||||
adjustCommandedActor(iter->first);
|
||||
|
@ -2076,7 +2079,6 @@ namespace MWMechanics
|
|||
if (avoidCollisions)
|
||||
predictAndAvoidCollisions();
|
||||
|
||||
timerUpdateAITargets += duration;
|
||||
timerUpdateHeadTrack += duration;
|
||||
timerUpdateEquippedLight += duration;
|
||||
timerUpdateHello += duration;
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#include "aicast.hpp"
|
||||
|
||||
#include <components/misc/constants.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/mechanicsmanager.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
|
@ -54,12 +56,12 @@ bool MWMechanics::AiCast::execute(const MWWorld::Ptr& actor, MWMechanics::Charac
|
|||
if (target != actor && target.getClass().isActor())
|
||||
{
|
||||
osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getHalfExtents(target);
|
||||
targetPos.z() += halfExtents.z() * 2 * 0.75f;
|
||||
targetPos.z() += halfExtents.z() * 2 * Constants::TorsoHeight;
|
||||
}
|
||||
|
||||
osg::Vec3f actorPos = actor.getRefData().getPosition().asVec3();
|
||||
osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getHalfExtents(actor);
|
||||
actorPos.z() += halfExtents.z() * 2 * 0.75f;
|
||||
actorPos.z() += halfExtents.z() * 2 * Constants::TorsoHeight;
|
||||
|
||||
osg::Vec3f dir = targetPos - actorPos;
|
||||
|
||||
|
|
|
@ -146,19 +146,10 @@ namespace MWMechanics
|
|||
}
|
||||
storage.mActionCooldown -= duration;
|
||||
|
||||
float& timerReact = storage.mTimerReact;
|
||||
if (timerReact < AI_REACTION_TIME)
|
||||
{
|
||||
timerReact += duration;
|
||||
}
|
||||
else
|
||||
{
|
||||
timerReact = 0;
|
||||
if (attack(actor, target, storage, characterController))
|
||||
return true;
|
||||
}
|
||||
if (storage.mReaction.update(duration) == Misc::TimerStatus::Waiting)
|
||||
return false;
|
||||
|
||||
return false;
|
||||
return attack(actor, target, storage, characterController);
|
||||
}
|
||||
|
||||
bool AiCombat::attack(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, AiCombatStorage& storage, CharacterController& characterController)
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "pathfinding.hpp"
|
||||
#include "movement.hpp"
|
||||
#include "obstacle.hpp"
|
||||
#include "aitimer.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
@ -27,7 +28,7 @@ namespace MWMechanics
|
|||
struct AiCombatStorage : AiTemporaryBase
|
||||
{
|
||||
float mAttackCooldown;
|
||||
float mTimerReact;
|
||||
AiReactionTimer mReaction;
|
||||
float mTimerCombatMove;
|
||||
bool mReadyToAttack;
|
||||
bool mAttack;
|
||||
|
@ -60,7 +61,6 @@ namespace MWMechanics
|
|||
|
||||
AiCombatStorage():
|
||||
mAttackCooldown(0.0f),
|
||||
mTimerReact(AI_REACTION_TIME),
|
||||
mTimerCombatMove(0.0f),
|
||||
mReadyToAttack(false),
|
||||
mAttack(false),
|
||||
|
|
|
@ -73,23 +73,25 @@ namespace MWMechanics
|
|||
const MWWorld::Ptr follower = MWBase::Environment::get().getWorld()->getPtr(mTargetActorRefId, false);
|
||||
const osg::Vec3f leaderPos = actor.getRefData().getPosition().asVec3();
|
||||
const osg::Vec3f followerPos = follower.getRefData().getPosition().asVec3();
|
||||
const osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getHalfExtents(actor);
|
||||
const float maxHalfExtent = std::max(halfExtents.x(), std::max(halfExtents.y(), halfExtents.z()));
|
||||
|
||||
if ((leaderPos - followerPos).length2() <= mMaxDist * mMaxDist)
|
||||
{
|
||||
const osg::Vec3f dest(mX, mY, mZ);
|
||||
if (pathTo(actor, dest, duration)) //Returns true on path complete
|
||||
if (pathTo(actor, dest, duration, maxHalfExtent)) //Returns true on path complete
|
||||
{
|
||||
mRemainingDuration = mDuration;
|
||||
return true;
|
||||
}
|
||||
mMaxDist = 450;
|
||||
mMaxDist = maxHalfExtent + 450.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Stop moving if the player is too far away
|
||||
MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle3", 0, 1);
|
||||
actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
|
||||
mMaxDist = 250;
|
||||
mMaxDist = maxHalfExtent + 250.0f;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
@ -28,7 +28,6 @@
|
|||
MWMechanics::AiPackage::AiPackage(AiPackageTypeId typeId, const Options& options) :
|
||||
mTypeId(typeId),
|
||||
mOptions(options),
|
||||
mTimer(AI_REACTION_TIME + 1.0f), // to force initial pathbuild
|
||||
mTargetActorRefId(""),
|
||||
mTargetActorId(-1),
|
||||
mRotateOnTheRunChecks(0),
|
||||
|
@ -64,7 +63,7 @@ MWWorld::Ptr MWMechanics::AiPackage::getTarget() const
|
|||
void MWMechanics::AiPackage::reset()
|
||||
{
|
||||
// reset all members
|
||||
mTimer = AI_REACTION_TIME + 1.0f;
|
||||
mReaction.reset();
|
||||
mIsShortcutting = false;
|
||||
mShortcutProhibited = false;
|
||||
mShortcutFailPos = osg::Vec3f();
|
||||
|
@ -75,7 +74,7 @@ void MWMechanics::AiPackage::reset()
|
|||
|
||||
bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f& dest, float duration, float destTolerance)
|
||||
{
|
||||
mTimer += duration; //Update timer
|
||||
const Misc::TimerStatus timerStatus = mReaction.update(duration);
|
||||
|
||||
const osg::Vec3f position = actor.getRefData().getPosition().asVec3(); //position of the actor
|
||||
MWBase::World* world = MWBase::Environment::get().getWorld();
|
||||
|
@ -98,7 +97,7 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f&
|
|||
const bool isDestReached = (distToTarget <= destTolerance);
|
||||
const bool actorCanMoveByZ = canActorMoveByZAxis(actor);
|
||||
|
||||
if (!isDestReached && mTimer > AI_REACTION_TIME)
|
||||
if (!isDestReached && timerStatus == Misc::TimerStatus::Elapsed)
|
||||
{
|
||||
if (actor.getClass().isBipedal(actor))
|
||||
openDoors(actor);
|
||||
|
@ -115,7 +114,7 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f&
|
|||
if (wasShortcutting || doesPathNeedRecalc(dest, actor)) // if need to rebuild path
|
||||
{
|
||||
const auto pathfindingHalfExtents = world->getPathfindingHalfExtents(actor);
|
||||
mPathFinder.buildPath(actor, position, dest, actor.getCell(), getPathGridGraph(actor.getCell()),
|
||||
mPathFinder.buildLimitedPath(actor, position, dest, actor.getCell(), getPathGridGraph(actor.getCell()),
|
||||
pathfindingHalfExtents, getNavigatorFlags(actor), getAreaCosts(actor));
|
||||
mRotateOnTheRunChecks = 3;
|
||||
|
||||
|
@ -142,8 +141,6 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f&
|
|||
mPathFinder.addPointToPath(dest); //Adds the final destination to the path, to try to get to where you want to go
|
||||
}
|
||||
}
|
||||
|
||||
mTimer = 0;
|
||||
}
|
||||
|
||||
const float actorTolerance = 2 * actor.getClass().getMaxSpeed(actor) * duration
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "obstacle.hpp"
|
||||
#include "aistate.hpp"
|
||||
#include "aipackagetypeid.hpp"
|
||||
#include "aitimer.hpp"
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
|
@ -28,8 +29,6 @@ namespace ESM
|
|||
|
||||
namespace MWMechanics
|
||||
{
|
||||
const float AI_REACTION_TIME = 0.25f;
|
||||
|
||||
class CharacterController;
|
||||
class PathgridGraph;
|
||||
|
||||
|
@ -158,7 +157,7 @@ namespace MWMechanics
|
|||
PathFinder mPathFinder;
|
||||
ObstacleCheck mObstacleCheck;
|
||||
|
||||
float mTimer;
|
||||
AiReactionTimer mReaction;
|
||||
|
||||
std::string mTargetActorRefId;
|
||||
mutable int mTargetActorId;
|
||||
|
|
|
@ -53,9 +53,14 @@ bool AiPursue::execute (const MWWorld::Ptr& actor, CharacterController& characte
|
|||
|
||||
const float pathTolerance = 100.f;
|
||||
|
||||
if (pathTo(actor, dest, duration, pathTolerance) &&
|
||||
std::abs(dest.z() - actorPos.z()) < pathTolerance) // check the true distance in case the target is far away in Z-direction
|
||||
// check the true distance in case the target is far away in Z-direction
|
||||
bool reached = pathTo(actor, dest, duration, pathTolerance) &&
|
||||
std::abs(dest.z() - actorPos.z()) < pathTolerance;
|
||||
|
||||
if (reached)
|
||||
{
|
||||
if (!MWBase::Environment::get().getWorld()->getLOS(target, actor))
|
||||
return false;
|
||||
MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Dialogue, actor); //Arrest player when reached
|
||||
return true;
|
||||
}
|
||||
|
|
26
apps/openmw/mwmechanics/aitimer.hpp
Normal file
26
apps/openmw/mwmechanics/aitimer.hpp
Normal file
|
@ -0,0 +1,26 @@
|
|||
#ifndef OPENMW_MECHANICS_AITIMER_H
|
||||
#define OPENMW_MECHANICS_AITIMER_H
|
||||
|
||||
#include <components/misc/rng.hpp>
|
||||
#include <components/misc/timer.hpp>
|
||||
|
||||
namespace MWMechanics
|
||||
{
|
||||
constexpr float AI_REACTION_TIME = 0.25f;
|
||||
|
||||
class AiReactionTimer
|
||||
{
|
||||
public:
|
||||
static constexpr float sDeviation = 0.1f;
|
||||
|
||||
Misc::TimerStatus update(float duration) { return mImpl.update(duration); }
|
||||
|
||||
void reset() { mImpl.reset(Misc::Rng::deviate(0, sDeviation)); }
|
||||
|
||||
private:
|
||||
Misc::DeviatingPeriodicTimer mImpl {AI_REACTION_TIME, sDeviation,
|
||||
Misc::Rng::deviate(0, sDeviation)};
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -223,15 +223,10 @@ namespace MWMechanics
|
|||
|
||||
doPerFrameActionsForState(actor, duration, storage);
|
||||
|
||||
float& lastReaction = storage.mReaction;
|
||||
lastReaction += duration;
|
||||
if (AI_REACTION_TIME <= lastReaction)
|
||||
{
|
||||
lastReaction = 0;
|
||||
return reactionTimeActions(actor, storage, pos);
|
||||
}
|
||||
else
|
||||
if (storage.mReaction.update(duration) == Misc::TimerStatus::Waiting)
|
||||
return false;
|
||||
|
||||
return reactionTimeActions(actor, storage, pos);
|
||||
}
|
||||
|
||||
bool AiWander::reactionTimeActions(const MWWorld::Ptr& actor, AiWanderStorage& storage, ESM::Position& pos)
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "pathfinding.hpp"
|
||||
#include "obstacle.hpp"
|
||||
#include "aistate.hpp"
|
||||
#include "aitimer.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
@ -25,7 +26,7 @@ namespace MWMechanics
|
|||
/// \brief This class holds the variables AiWander needs which are deleted if the package becomes inactive.
|
||||
struct AiWanderStorage : AiTemporaryBase
|
||||
{
|
||||
float mReaction; // update some actions infrequently
|
||||
AiReactionTimer mReaction;
|
||||
|
||||
// AiWander states
|
||||
enum WanderState
|
||||
|
@ -57,7 +58,6 @@ namespace MWMechanics
|
|||
int mStuckCount;
|
||||
|
||||
AiWanderStorage():
|
||||
mReaction(0),
|
||||
mState(Wander_ChooseAction),
|
||||
mIsWanderingManually(false),
|
||||
mCanWanderAlongPathGrid(true),
|
||||
|
|
|
@ -2943,8 +2943,8 @@ void CharacterController::updateHeadTracking(float duration)
|
|||
if (!head)
|
||||
return;
|
||||
|
||||
float zAngleRadians = 0.f;
|
||||
float xAngleRadians = 0.f;
|
||||
double zAngleRadians = 0.f;
|
||||
double xAngleRadians = 0.f;
|
||||
|
||||
if (!mHeadTrackTarget.isEmpty())
|
||||
{
|
||||
|
@ -2977,15 +2977,16 @@ void CharacterController::updateHeadTracking(float duration)
|
|||
const osg::Vec3f actorDirection = mPtr.getRefData().getBaseNode()->getAttitude() * osg::Vec3f(0,1,0);
|
||||
|
||||
zAngleRadians = std::atan2(actorDirection.x(), actorDirection.y()) - std::atan2(direction.x(), direction.y());
|
||||
zAngleRadians = Misc::normalizeAngle(zAngleRadians - mAnimation->getHeadYaw()) + mAnimation->getHeadYaw();
|
||||
zAngleRadians *= (1 - direction.z() * direction.z());
|
||||
xAngleRadians = std::asin(direction.z());
|
||||
}
|
||||
|
||||
const double xLimit = osg::DegreesToRadians(40.0);
|
||||
const double zLimit = osg::DegreesToRadians(30.0);
|
||||
double zLimitOffset = mAnimation->getUpperBodyYawRadians();
|
||||
xAngleRadians = osg::clampBetween(Misc::normalizeAngle(xAngleRadians), -xLimit, xLimit);
|
||||
zAngleRadians = osg::clampBetween(Misc::normalizeAngle(zAngleRadians),
|
||||
-zLimit + zLimitOffset, zLimit + zLimitOffset);
|
||||
xAngleRadians = osg::clampBetween(xAngleRadians, -xLimit, xLimit);
|
||||
zAngleRadians = osg::clampBetween(zAngleRadians, -zLimit + zLimitOffset, zLimit + zLimitOffset);
|
||||
|
||||
float factor = duration*5;
|
||||
factor = std::min(factor, 1.f);
|
||||
|
|
|
@ -24,7 +24,7 @@ namespace MWMechanics
|
|||
{
|
||||
struct CorprusStats
|
||||
{
|
||||
static const int sWorseningPeriod = 24;
|
||||
static constexpr int sWorseningPeriod = 24;
|
||||
|
||||
int mWorsenings[ESM::Attribute::Length];
|
||||
MWWorld::TimeStamp mNextWorsening;
|
||||
|
|
|
@ -12,7 +12,7 @@ namespace MWMechanics
|
|||
{
|
||||
struct Movement;
|
||||
|
||||
static const int NUM_EVADE_DIRECTIONS = 4;
|
||||
static constexpr int NUM_EVADE_DIRECTIONS = 4;
|
||||
|
||||
/// tests actor's proximity to a closed door by default
|
||||
bool proximityToDoor(const MWWorld::Ptr& actor, float minDist);
|
||||
|
|
|
@ -442,4 +442,21 @@ namespace MWMechanics
|
|||
|
||||
std::copy(prePath.rbegin(), prePath.rend(), std::front_inserter(mPath));
|
||||
}
|
||||
|
||||
void PathFinder::buildLimitedPath(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, const osg::Vec3f& endPoint,
|
||||
const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph, const osg::Vec3f& halfExtents,
|
||||
const DetourNavigator::Flags flags, const DetourNavigator::AreaCosts& areaCosts)
|
||||
{
|
||||
const auto navigator = MWBase::Environment::get().getWorld()->getNavigator();
|
||||
const auto maxDistance = std::min(
|
||||
navigator->getMaxNavmeshAreaRealRadius(),
|
||||
static_cast<float>(Constants::CellSizeInUnits)
|
||||
);
|
||||
const auto startToEnd = endPoint - startPoint;
|
||||
const auto distance = startToEnd.length();
|
||||
if (distance <= maxDistance)
|
||||
return buildPath(actor, startPoint, endPoint, cell, pathgridGraph, halfExtents, flags, areaCosts);
|
||||
const auto end = startPoint + startToEnd * maxDistance / distance;
|
||||
buildPath(actor, startPoint, end, cell, pathgridGraph, halfExtents, flags, areaCosts);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -101,6 +101,10 @@ namespace MWMechanics
|
|||
void buildPathByNavMeshToNextPoint(const MWWorld::ConstPtr& actor, const osg::Vec3f& halfExtents,
|
||||
const DetourNavigator::Flags flags, const DetourNavigator::AreaCosts& areaCosts);
|
||||
|
||||
void buildLimitedPath(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, const osg::Vec3f& endPoint,
|
||||
const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph, const osg::Vec3f& halfExtents,
|
||||
const DetourNavigator::Flags flags, const DetourNavigator::AreaCosts& areaCosts);
|
||||
|
||||
/// Remove front point if exist and within tolerance
|
||||
void update(const osg::Vec3f& position, float pointTolerance, float destinationTolerance,
|
||||
bool shortenIfAlmostStraight, bool canMoveByZ);
|
||||
|
|
|
@ -24,6 +24,14 @@ namespace MWMechanics
|
|||
{
|
||||
}
|
||||
|
||||
Spells::Spells(const Spells& spells) : mSpellList(spells.mSpellList), mSpells(spells.mSpells),
|
||||
mSelectedSpell(spells.mSelectedSpell), mUsedPowers(spells.mUsedPowers),
|
||||
mSpellsChanged(spells.mSpellsChanged), mEffects(spells.mEffects), mSourcedEffects(spells.mSourcedEffects)
|
||||
{
|
||||
if(mSpellList)
|
||||
mSpellList->addListener(this);
|
||||
}
|
||||
|
||||
std::map<const ESM::Spell*, SpellParams>::const_iterator Spells::begin() const
|
||||
{
|
||||
return mSpells.begin();
|
||||
|
|
|
@ -57,6 +57,10 @@ namespace MWMechanics
|
|||
|
||||
Spells();
|
||||
|
||||
Spells(const Spells&);
|
||||
|
||||
Spells(const Spells&&) = delete;
|
||||
|
||||
~Spells();
|
||||
|
||||
static bool hasCorprusEffect(const ESM::Spell *spell);
|
||||
|
|
|
@ -145,6 +145,12 @@ namespace MWMechanics
|
|||
|
||||
for (std::map<ESM::SummonKey, int>::iterator it = creatureMap.begin(); it != creatureMap.end(); )
|
||||
{
|
||||
if(it->second == -1)
|
||||
{
|
||||
// Keep the spell effect active if we failed to spawn anything
|
||||
it++;
|
||||
continue;
|
||||
}
|
||||
MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(it->second);
|
||||
if (ptr.isEmpty() || (ptr.getClass().getCreatureStats(ptr).isDead() && ptr.getClass().getCreatureStats(ptr).isDeathAnimationFinished()))
|
||||
{
|
||||
|
|
|
@ -22,6 +22,7 @@ namespace MWPhysics
|
|||
Actor::Actor(const MWWorld::Ptr& ptr, const Resource::BulletShape* shape, PhysicsTaskScheduler* scheduler)
|
||||
: mStandingOnPtr(nullptr), mCanWaterWalk(false), mWalkingOnWater(false)
|
||||
, mCollisionObject(nullptr), mMeshTranslation(shape->mCollisionBox.center), mHalfExtents(shape->mCollisionBox.extents)
|
||||
, mStuckFrames(0), mLastStuckPosition{0, 0, 0}
|
||||
, mForce(0.f, 0.f, 0.f), mOnGround(true), mOnSlope(false)
|
||||
, mInternalCollisionMode(true)
|
||||
, mExternalCollisionMode(true)
|
||||
|
@ -174,6 +175,9 @@ osg::Vec3f Actor::getCollisionObjectPosition() const
|
|||
bool Actor::setPosition(const osg::Vec3f& position)
|
||||
{
|
||||
std::scoped_lock lock(mPositionMutex);
|
||||
// position is being forced, ignore simulation results until we sync up
|
||||
if (mSkipSimulation)
|
||||
return false;
|
||||
bool hasChanged = mPosition != position || mPositionOffset.length() != 0 || mWorldPositionChanged;
|
||||
mPreviousPosition = mPosition + mPositionOffset;
|
||||
mPosition = position + mPositionOffset;
|
||||
|
@ -187,6 +191,17 @@ void Actor::adjustPosition(const osg::Vec3f& offset)
|
|||
mPositionOffset += offset;
|
||||
}
|
||||
|
||||
void Actor::applyOffsetChange()
|
||||
{
|
||||
if (mPositionOffset.length() == 0)
|
||||
return;
|
||||
mWorldPosition += mPositionOffset;
|
||||
mPosition += mPositionOffset;
|
||||
mPreviousPosition += mPositionOffset;
|
||||
mPositionOffset = osg::Vec3f();
|
||||
mWorldPositionChanged = true;
|
||||
}
|
||||
|
||||
osg::Vec3f Actor::getPosition() const
|
||||
{
|
||||
return mPosition;
|
||||
|
|
|
@ -96,9 +96,16 @@ namespace MWPhysics
|
|||
* Returns true if the new position is different.
|
||||
*/
|
||||
bool setPosition(const osg::Vec3f& position);
|
||||
|
||||
// force set actor position to be as in Ptr::RefData
|
||||
void updatePosition();
|
||||
|
||||
// register a position offset that will be applied during simulation.
|
||||
void adjustPosition(const osg::Vec3f& offset);
|
||||
|
||||
// apply position offset. Can't be called during simulation
|
||||
void applyOffsetChange();
|
||||
|
||||
osg::Vec3f getPosition() const;
|
||||
|
||||
osg::Vec3f getPreviousPosition() const;
|
||||
|
@ -152,6 +159,24 @@ namespace MWPhysics
|
|||
MWWorld::Ptr getStandingOnPtr() const;
|
||||
void setStandingOnPtr(const MWWorld::Ptr& ptr);
|
||||
|
||||
unsigned int getStuckFrames() const
|
||||
{
|
||||
return mStuckFrames;
|
||||
}
|
||||
void setStuckFrames(unsigned int frames)
|
||||
{
|
||||
mStuckFrames = frames;
|
||||
}
|
||||
|
||||
const osg::Vec3f &getLastStuckPosition() const
|
||||
{
|
||||
return mLastStuckPosition;
|
||||
}
|
||||
void setLastStuckPosition(osg::Vec3f position)
|
||||
{
|
||||
mLastStuckPosition = position;
|
||||
}
|
||||
|
||||
private:
|
||||
MWWorld::Ptr mStandingOnPtr;
|
||||
/// Removes then re-adds the collision object to the dynamics world
|
||||
|
@ -185,6 +210,9 @@ namespace MWPhysics
|
|||
btTransform mLocalTransform;
|
||||
mutable std::mutex mPositionMutex;
|
||||
|
||||
unsigned int mStuckFrames;
|
||||
osg::Vec3f mLastStuckPosition;
|
||||
|
||||
osg::Vec3f mForce;
|
||||
std::atomic<bool> mOnGround;
|
||||
std::atomic<bool> mOnSlope;
|
||||
|
|
|
@ -3,24 +3,24 @@
|
|||
|
||||
namespace MWPhysics
|
||||
{
|
||||
static const float sStepSizeUp = 34.0f;
|
||||
static const float sStepSizeDown = 62.0f;
|
||||
static constexpr float sStepSizeUp = 34.0f;
|
||||
static constexpr float sStepSizeDown = 62.0f;
|
||||
|
||||
static const float sMinStep = 10.0f; // hack to skip over tiny unwalkable slopes
|
||||
static const float sMinStep2 = 20.0f; // hack to skip over shorter but longer/wider/further unwalkable slopes
|
||||
static constexpr float sMinStep = 10.0f; // hack to skip over tiny unwalkable slopes
|
||||
static constexpr float sMinStep2 = 20.0f; // hack to skip over shorter but longer/wider/further unwalkable slopes
|
||||
// whether to do the above stairstepping logic hacks to work around bad morrowind assets - disabling causes problems but improves performance
|
||||
static const bool sDoExtraStairHacks = true;
|
||||
static constexpr bool sDoExtraStairHacks = true;
|
||||
|
||||
static const float sGroundOffset = 1.0f;
|
||||
static const float sMaxSlope = 49.0f;
|
||||
static constexpr float sGroundOffset = 1.0f;
|
||||
static constexpr float sMaxSlope = 49.0f;
|
||||
|
||||
// Arbitrary number. To prevent infinite loops. They shouldn't happen but it's good to be prepared.
|
||||
static const int sMaxIterations = 8;
|
||||
static constexpr int sMaxIterations = 8;
|
||||
// Allows for more precise movement solving without getting stuck or snagging too easily.
|
||||
static const float sCollisionMargin = 0.1;
|
||||
static constexpr float sCollisionMargin = 0.1;
|
||||
// Allow for a small amount of penetration to prevent numerical precision issues from causing the "unstuck"ing code to run unnecessarily
|
||||
// Currently set to 0 because having the "unstuck"ing code run whenever possible prevents some glitchy snagging issues
|
||||
static const float sAllowedPenetration = 0.0;
|
||||
static constexpr float sAllowedPenetration = 0.0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -10,6 +10,12 @@
|
|||
|
||||
#include <type_traits>
|
||||
|
||||
#if BT_BULLET_VERSION < 310
|
||||
// Older Bullet versions only support `btScalar` heightfields.
|
||||
// Our heightfield data is `float`.
|
||||
//
|
||||
// These functions handle conversion from `float` to `double` when
|
||||
// `btScalar` is `double` (`BT_USE_DOUBLE_PRECISION`).
|
||||
namespace
|
||||
{
|
||||
template <class T>
|
||||
|
@ -40,14 +46,18 @@ namespace
|
|||
return btScalarHeights.data();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace MWPhysics
|
||||
{
|
||||
HeightField::HeightField(const float* heights, int x, int y, float triSize, float sqrtVerts, float minH, float maxH, const osg::Object* holdObject, PhysicsTaskScheduler* scheduler)
|
||||
: mHoldObject(holdObject)
|
||||
#if BT_BULLET_VERSION < 310
|
||||
, mHeights(makeHeights(heights, sqrtVerts))
|
||||
#endif
|
||||
, mTaskScheduler(scheduler)
|
||||
{
|
||||
#if BT_BULLET_VERSION < 310
|
||||
mShape = std::make_unique<btHeightfieldTerrainShape>(
|
||||
sqrtVerts, sqrtVerts,
|
||||
getHeights(heights, mHeights),
|
||||
|
@ -55,6 +65,10 @@ namespace MWPhysics
|
|||
minH, maxH, 2,
|
||||
PHY_FLOAT, false
|
||||
);
|
||||
#else
|
||||
mShape = std::make_unique<btHeightfieldTerrainShape>(
|
||||
sqrtVerts, sqrtVerts, heights, minH, maxH, 2, false);
|
||||
#endif
|
||||
mShape->setUseDiamondSubdivision(true);
|
||||
mShape->setLocalScaling(btVector3(triSize, triSize, 1));
|
||||
|
||||
|
|
|
@ -34,7 +34,9 @@ namespace MWPhysics
|
|||
std::unique_ptr<btHeightfieldTerrainShape> mShape;
|
||||
std::unique_ptr<btCollisionObject> mCollisionObject;
|
||||
osg::ref_ptr<const osg::Object> mHoldObject;
|
||||
#if BT_BULLET_VERSION < 310
|
||||
std::vector<btScalar> mHeights;
|
||||
#endif
|
||||
|
||||
PhysicsTaskScheduler* mTaskScheduler;
|
||||
|
||||
|
|
|
@ -338,7 +338,7 @@ namespace MWPhysics
|
|||
osg::Vec3f lastSlideNormalFallback(0,0,1);
|
||||
bool forceGroundTest = false;
|
||||
|
||||
for (int iterations = 0; iterations < sMaxIterations && remainingTime > 0.01f; ++iterations)
|
||||
for (int iterations = 0; iterations < sMaxIterations && remainingTime > 0.0001f; ++iterations)
|
||||
{
|
||||
osg::Vec3f nextpos = newPosition + velocity * remainingTime;
|
||||
|
||||
|
@ -528,6 +528,12 @@ namespace MWPhysics
|
|||
isOnGround = false;
|
||||
}
|
||||
}
|
||||
// forcibly treat stuck actors as if they're on flat ground because buggy collisions when inside of things can/will break ground detection
|
||||
if(physicActor->getStuckFrames() > 0)
|
||||
{
|
||||
isOnGround = true;
|
||||
isOnSlope = false;
|
||||
}
|
||||
}
|
||||
|
||||
if((isOnGround && !isOnSlope) || newPosition.z() < swimlevel || actor.mFlying)
|
||||
|
@ -571,6 +577,17 @@ namespace MWPhysics
|
|||
auto* collisionObject = physicActor->getCollisionObject();
|
||||
auto tempPosition = actor.mPosition;
|
||||
|
||||
if(physicActor->getStuckFrames() >= 10)
|
||||
{
|
||||
if((physicActor->getLastStuckPosition() - actor.mPosition).length2() < 100)
|
||||
return;
|
||||
else
|
||||
{
|
||||
physicActor->setStuckFrames(0);
|
||||
physicActor->setLastStuckPosition({0, 0, 0});
|
||||
}
|
||||
}
|
||||
|
||||
// use vanilla-accurate collision hull position hack (do same hitbox offset hack as movement solver)
|
||||
// if vanilla compatibility didn't matter, the "correct" collision hull position would be physicActor->getScaledMeshTranslation()
|
||||
const auto verticalHalfExtent = osg::Vec3f(0.0, 0.0, physicActor->getHalfExtents().z());
|
||||
|
@ -604,6 +621,8 @@ namespace MWPhysics
|
|||
auto contactCallback = gatherContacts({0.0, 0.0, 0.0});
|
||||
if(contactCallback.mDistance < -sAllowedPenetration)
|
||||
{
|
||||
physicActor->setStuckFrames(physicActor->getStuckFrames() + 1);
|
||||
physicActor->setLastStuckPosition(actor.mPosition);
|
||||
// we are; try moving it out of the world
|
||||
auto positionDelta = contactCallback.mContactSum;
|
||||
// limit rejection delta to the largest known individual rejections
|
||||
|
@ -636,6 +655,11 @@ namespace MWPhysics
|
|||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
physicActor->setStuckFrames(0);
|
||||
physicActor->setLastStuckPosition({0, 0, 0});
|
||||
}
|
||||
|
||||
collisionObject->setWorldTransform(oldTransform);
|
||||
actor.mPosition = tempPosition;
|
||||
|
|
|
@ -101,7 +101,7 @@ namespace
|
|||
|
||||
osg::Vec3f interpolateMovements(MWPhysics::ActorFrameData& actorData, float timeAccum, float physicsDt)
|
||||
{
|
||||
const float interpolationFactor = timeAccum / physicsDt;
|
||||
const float interpolationFactor = std::clamp(timeAccum / physicsDt, 0.0f, 1.0f);
|
||||
return actorData.mPosition * interpolationFactor + actorData.mActorRaw->getPreviousPosition() * (1.f - interpolationFactor);
|
||||
}
|
||||
|
||||
|
@ -138,7 +138,8 @@ namespace
|
|||
namespace MWPhysics
|
||||
{
|
||||
PhysicsTaskScheduler::PhysicsTaskScheduler(float physicsDt, std::shared_ptr<btCollisionWorld> collisionWorld)
|
||||
: mPhysicsDt(physicsDt)
|
||||
: mDefaultPhysicsDt(physicsDt)
|
||||
, mPhysicsDt(physicsDt)
|
||||
, mTimeAccum(0.f)
|
||||
, mCollisionWorld(std::move(collisionWorld))
|
||||
, mNumJobs(0)
|
||||
|
@ -152,6 +153,11 @@ namespace MWPhysics
|
|||
, mNextLOS(0)
|
||||
, mFrameNumber(0)
|
||||
, mTimer(osg::Timer::instance())
|
||||
, mPrevStepCount(1)
|
||||
, mBudget(physicsDt)
|
||||
, mAsyncBudget(0.0f)
|
||||
, mBudgetCursor(0)
|
||||
, mAsyncStartTime(0)
|
||||
, mTimeBegin(0)
|
||||
, mTimeEnd(0)
|
||||
, mFrameStart(0)
|
||||
|
@ -186,9 +192,11 @@ namespace MWPhysics
|
|||
mPostStepBarrier = std::make_unique<Misc::Barrier>(mNumThreads, [&]()
|
||||
{
|
||||
if (mRemainingSteps)
|
||||
{
|
||||
--mRemainingSteps;
|
||||
updateActorsPositions();
|
||||
}
|
||||
mNextJob.store(0, std::memory_order_release);
|
||||
updateActorsPositions();
|
||||
});
|
||||
|
||||
mPostSimBarrier = std::make_unique<Misc::Barrier>(mNumThreads, [&]()
|
||||
|
@ -218,13 +226,61 @@ namespace MWPhysics
|
|||
thread.join();
|
||||
}
|
||||
|
||||
const std::vector<MWWorld::Ptr>& PhysicsTaskScheduler::moveActors(int numSteps, float timeAccum, std::vector<ActorFrameData>&& actorsData, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats)
|
||||
std::tuple<int, float> PhysicsTaskScheduler::calculateStepConfig(float timeAccum) const
|
||||
{
|
||||
int maxAllowedSteps = 2;
|
||||
int numSteps = timeAccum / mDefaultPhysicsDt;
|
||||
|
||||
// adjust maximum step count based on whether we're likely physics bottlenecked or not
|
||||
// if maxAllowedSteps ends up higher than numSteps, we will not invoke delta time
|
||||
// if it ends up lower than numSteps, but greater than 1, we will run a number of true delta time physics steps that we expect to be within budget
|
||||
// if it ends up lower than numSteps and also 1, we will run a single delta time physics step
|
||||
// if we did not do this, and had a fixed step count limit,
|
||||
// we would have an unnecessarily low render framerate if we were only physics bottlenecked,
|
||||
// and we would be unnecessarily invoking true delta time if we were only render bottlenecked
|
||||
|
||||
// get physics timing stats
|
||||
float budgetMeasurement = std::max(mBudget.get(), mAsyncBudget.get());
|
||||
// time spent per step in terms of the intended physics framerate
|
||||
budgetMeasurement /= mDefaultPhysicsDt;
|
||||
// ensure sane minimum value
|
||||
budgetMeasurement = std::max(0.00001f, budgetMeasurement);
|
||||
// we're spending almost or more than realtime per physics frame; limit to a single step
|
||||
if (budgetMeasurement > 0.95)
|
||||
maxAllowedSteps = 1;
|
||||
// physics is fairly cheap; limit based on expense
|
||||
if (budgetMeasurement < 0.5)
|
||||
maxAllowedSteps = std::ceil(1.0/budgetMeasurement);
|
||||
// limit to a reasonable amount
|
||||
maxAllowedSteps = std::min(10, maxAllowedSteps);
|
||||
|
||||
// fall back to delta time for this frame if fixed timestep physics would fall behind
|
||||
float actualDelta = mDefaultPhysicsDt;
|
||||
if (numSteps > maxAllowedSteps)
|
||||
{
|
||||
numSteps = maxAllowedSteps;
|
||||
// ensure that we do not simulate a frame ahead when doing delta time; this reduces stutter and latency
|
||||
// this causes interpolation to 100% use the most recent physics result when true delta time is happening
|
||||
// and we deliberately simulate up to exactly the timestamp that we want to render
|
||||
actualDelta = timeAccum/float(numSteps+1);
|
||||
// actually: if this results in a per-step delta less than the target physics steptime, clamp it
|
||||
// this might reintroduce some stutter, but only comes into play in obscure cases
|
||||
// (because numSteps is originally based on mDefaultPhysicsDt, this won't cause us to overrun)
|
||||
actualDelta = std::max(actualDelta, mDefaultPhysicsDt);
|
||||
}
|
||||
|
||||
return std::make_tuple(numSteps, actualDelta);
|
||||
}
|
||||
|
||||
const std::vector<MWWorld::Ptr>& PhysicsTaskScheduler::moveActors(float & timeAccum, std::vector<ActorFrameData>&& actorsData, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats)
|
||||
{
|
||||
// This function run in the main thread.
|
||||
// While the mSimulationMutex is held, background physics threads can't run.
|
||||
|
||||
std::unique_lock lock(mSimulationMutex);
|
||||
|
||||
double timeStart = mTimer->tick();
|
||||
|
||||
mMovedActors.clear();
|
||||
|
||||
// start by finishing previous background computation
|
||||
|
@ -249,14 +305,21 @@ namespace MWPhysics
|
|||
mMovedActors.emplace_back(data.mActorRaw->getPtr());
|
||||
}
|
||||
}
|
||||
if(mAdvanceSimulation)
|
||||
mAsyncBudget.update(mTimer->delta_s(mAsyncStartTime, mTimeEnd), mPrevStepCount, mBudgetCursor);
|
||||
updateStats(frameStart, frameNumber, stats);
|
||||
}
|
||||
|
||||
auto [numSteps, newDelta] = calculateStepConfig(timeAccum);
|
||||
timeAccum -= numSteps*newDelta;
|
||||
|
||||
// init
|
||||
for (auto& data : actorsData)
|
||||
data.updatePosition();
|
||||
mPrevStepCount = numSteps;
|
||||
mRemainingSteps = numSteps;
|
||||
mTimeAccum = timeAccum;
|
||||
mPhysicsDt = newDelta;
|
||||
mActorsFrameData = std::move(actorsData);
|
||||
mAdvanceSimulation = (mRemainingSteps != 0);
|
||||
mNewFrame = true;
|
||||
|
@ -267,20 +330,30 @@ namespace MWPhysics
|
|||
if (mAdvanceSimulation)
|
||||
mWorldFrameData = std::make_unique<WorldFrameData>();
|
||||
|
||||
if (mAdvanceSimulation)
|
||||
mBudgetCursor += 1;
|
||||
|
||||
if (mNumThreads == 0)
|
||||
{
|
||||
syncComputation();
|
||||
if(mAdvanceSimulation)
|
||||
mBudget.update(mTimer->delta_s(timeStart, mTimer->tick()), numSteps, mBudgetCursor);
|
||||
return mMovedActors;
|
||||
}
|
||||
|
||||
mAsyncStartTime = mTimer->tick();
|
||||
lock.unlock();
|
||||
mHasJob.notify_all();
|
||||
if (mAdvanceSimulation)
|
||||
mBudget.update(mTimer->delta_s(timeStart, mTimer->tick()), 1, mBudgetCursor);
|
||||
return mMovedActors;
|
||||
}
|
||||
|
||||
const std::vector<MWWorld::Ptr>& PhysicsTaskScheduler::resetSimulation(const ActorMap& actors)
|
||||
{
|
||||
std::unique_lock lock(mSimulationMutex);
|
||||
mBudget.reset(mDefaultPhysicsDt);
|
||||
mAsyncBudget.reset(0.0f);
|
||||
mMovedActors.clear();
|
||||
mActorsFrameData.clear();
|
||||
for (const auto& [_, actor] : actors)
|
||||
|
@ -492,6 +565,7 @@ namespace MWPhysics
|
|||
if (actor->setPosition(actorData.mPosition))
|
||||
{
|
||||
std::scoped_lock lock(mCollisionWorldMutex);
|
||||
actorData.mPosition = actor->getPosition(); // account for potential position change made by script
|
||||
actor->updateCollisionObjectPosition();
|
||||
mCollisionWorld->updateSingleAabb(actor->getCollisionObject());
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
#include "physicssystem.hpp"
|
||||
#include "ptrholder.hpp"
|
||||
#include "components/misc/budgetmeasurement.hpp"
|
||||
|
||||
namespace Misc
|
||||
{
|
||||
|
@ -32,7 +33,7 @@ namespace MWPhysics
|
|||
/// @param timeAccum accumulated time from previous run to interpolate movements
|
||||
/// @param actorsData per actor data needed to compute new positions
|
||||
/// @return new position of each actor
|
||||
const std::vector<MWWorld::Ptr>& moveActors(int numSteps, float timeAccum, std::vector<ActorFrameData>&& actorsData, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats);
|
||||
const std::vector<MWWorld::Ptr>& moveActors(float & timeAccum, std::vector<ActorFrameData>&& actorsData, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats);
|
||||
|
||||
const std::vector<MWWorld::Ptr>& resetSimulation(const ActorMap& actors);
|
||||
|
||||
|
@ -58,11 +59,13 @@ namespace MWPhysics
|
|||
void updateAabbs();
|
||||
void updatePtrAabb(const std::weak_ptr<PtrHolder>& ptr);
|
||||
void updateStats(osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats);
|
||||
std::tuple<int, float> calculateStepConfig(float timeAccum) const;
|
||||
|
||||
std::unique_ptr<WorldFrameData> mWorldFrameData;
|
||||
std::vector<ActorFrameData> mActorsFrameData;
|
||||
std::vector<MWWorld::Ptr> mMovedActors;
|
||||
const float mPhysicsDt;
|
||||
float mDefaultPhysicsDt;
|
||||
float mPhysicsDt;
|
||||
float mTimeAccum;
|
||||
std::shared_ptr<btCollisionWorld> mCollisionWorld;
|
||||
std::vector<LOSRequest> mLOSCache;
|
||||
|
@ -94,6 +97,12 @@ namespace MWPhysics
|
|||
|
||||
unsigned int mFrameNumber;
|
||||
const osg::Timer* mTimer;
|
||||
|
||||
int mPrevStepCount;
|
||||
Misc::BudgetMeasurement mBudget;
|
||||
Misc::BudgetMeasurement mAsyncBudget;
|
||||
unsigned int mBudgetCursor;
|
||||
osg::Timer_t mAsyncStartTime;
|
||||
osg::Timer_t mTimeBegin;
|
||||
osg::Timer_t mTimeEnd;
|
||||
osg::Timer_t mFrameStart;
|
||||
|
|
|
@ -604,7 +604,9 @@ namespace MWPhysics
|
|||
return object->getCollisionObject();
|
||||
return nullptr;
|
||||
}();
|
||||
assert(caster);
|
||||
|
||||
if (caster == nullptr)
|
||||
Log(Debug::Warning) << "No caster for projectile " << projectileId;
|
||||
|
||||
ProjectileConvexCallback resultCallback(caster, btFrom, btTo, projectile);
|
||||
resultCallback.m_collisionFilterMask = 0xff;
|
||||
|
@ -695,6 +697,15 @@ namespace MWPhysics
|
|||
return mProjectileId;
|
||||
}
|
||||
|
||||
void PhysicsSystem::setCaster(int projectileId, const MWWorld::Ptr& caster)
|
||||
{
|
||||
const auto foundProjectile = mProjectiles.find(projectileId);
|
||||
assert(foundProjectile != mProjectiles.end());
|
||||
auto* projectile = foundProjectile->second.get();
|
||||
|
||||
projectile->setCaster(caster);
|
||||
}
|
||||
|
||||
bool PhysicsSystem::toggleCollisionMode()
|
||||
{
|
||||
ActorMap::iterator found = mActors.find(MWMechanics::getPlayer());
|
||||
|
@ -733,19 +744,14 @@ namespace MWPhysics
|
|||
{
|
||||
mTimeAccum += dt;
|
||||
|
||||
const int maxAllowedSteps = 20;
|
||||
int numSteps = mTimeAccum / mPhysicsDt;
|
||||
numSteps = std::min(numSteps, maxAllowedSteps);
|
||||
|
||||
mTimeAccum -= numSteps * mPhysicsDt;
|
||||
|
||||
if (skipSimulation)
|
||||
return mTaskScheduler->resetSimulation(mActors);
|
||||
|
||||
return mTaskScheduler->moveActors(numSteps, mTimeAccum, prepareFrameData(numSteps), frameStart, frameNumber, stats);
|
||||
// modifies mTimeAccum
|
||||
return mTaskScheduler->moveActors(mTimeAccum, prepareFrameData(mTimeAccum >= mPhysicsDt), frameStart, frameNumber, stats);
|
||||
}
|
||||
|
||||
std::vector<ActorFrameData> PhysicsSystem::prepareFrameData(int numSteps)
|
||||
std::vector<ActorFrameData> PhysicsSystem::prepareFrameData(bool willSimulate)
|
||||
{
|
||||
std::vector<ActorFrameData> actorsFrameData;
|
||||
actorsFrameData.reserve(mMovementQueue.size());
|
||||
|
@ -785,7 +791,7 @@ namespace MWPhysics
|
|||
|
||||
// Ue current value only if we don't advance the simulation. Otherwise we might get a stale value.
|
||||
MWWorld::Ptr standingOn;
|
||||
if (numSteps == 0)
|
||||
if (!willSimulate)
|
||||
standingOn = physicActor->getStandingOnPtr();
|
||||
|
||||
actorsFrameData.emplace_back(std::move(physicActor), standingOn, moveToWaterSurface, movement, slowFall, waterlevel);
|
||||
|
@ -950,6 +956,10 @@ namespace MWPhysics
|
|||
void ActorFrameData::updatePosition()
|
||||
{
|
||||
mActorRaw->updateWorldPosition();
|
||||
// If physics runs "fast enough", position are interpolated without simulation
|
||||
// By calling this here, we are sure that offsets are applied at least once per frame,
|
||||
// regardless of simulation speed.
|
||||
mActorRaw->applyOffsetChange();
|
||||
mPosition = mActorRaw->getPosition();
|
||||
if (mMoveToWaterSurface)
|
||||
{
|
||||
|
|
|
@ -125,6 +125,7 @@ namespace MWPhysics
|
|||
void addActor (const MWWorld::Ptr& ptr, const std::string& mesh);
|
||||
|
||||
int addProjectile(const MWWorld::Ptr& caster, const osg::Vec3f& position, const std::string& mesh, bool computeRadius, bool canTraverseWater);
|
||||
void setCaster(int projectileId, const MWWorld::Ptr& caster);
|
||||
void updateProjectile(const int projectileId, const osg::Vec3f &position) const;
|
||||
void removeProjectile(const int projectileId);
|
||||
|
||||
|
@ -251,7 +252,7 @@ namespace MWPhysics
|
|||
|
||||
void updateWater();
|
||||
|
||||
std::vector<ActorFrameData> prepareFrameData(int numSteps);
|
||||
std::vector<ActorFrameData> prepareFrameData(bool willSimulate);
|
||||
|
||||
osg::ref_ptr<SceneUtil::UnrefQueue> mUnrefQueue;
|
||||
|
||||
|
|
|
@ -102,7 +102,7 @@ public:
|
|||
BlendMask_All = BlendMask_LowerBody | BlendMask_UpperBody
|
||||
};
|
||||
/* This is the number of *discrete* blend masks. */
|
||||
static const size_t sNumBlendMasks = 4;
|
||||
static constexpr size_t sNumBlendMasks = 4;
|
||||
|
||||
/// Holds an animation priority value for each BoneGroup.
|
||||
struct AnimPriority
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <osg/PositionAttitudeTransform>
|
||||
#include <osg/LightModel>
|
||||
#include <osg/LightSource>
|
||||
#include <osg/ValueObject>
|
||||
#include <osgUtil/IntersectionVisitor>
|
||||
#include <osgUtil/LineSegmentIntersector>
|
||||
|
||||
|
@ -20,6 +21,7 @@
|
|||
#include <components/resource/resourcesystem.hpp>
|
||||
#include <components/sceneutil/lightmanager.hpp>
|
||||
#include <components/sceneutil/shadow.hpp>
|
||||
#include <components/settings/settings.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
|
@ -84,7 +86,8 @@ namespace MWRender
|
|||
};
|
||||
|
||||
|
||||
// Set up alpha blending to Additive mode to avoid issues caused by transparent objects writing onto the alpha value of the FBO
|
||||
// Set up alpha blending mode to avoid issues caused by transparent objects writing onto the alpha value of the FBO
|
||||
// This makes the RTT have premultiplied alpha, though, so the source blend factor must be GL_ONE when it's applied
|
||||
class SetUpBlendVisitor : public osg::NodeVisitor
|
||||
{
|
||||
public:
|
||||
|
@ -94,22 +97,40 @@ namespace MWRender
|
|||
|
||||
void apply(osg::Node& node) override
|
||||
{
|
||||
if (osg::StateSet* stateset = node.getStateSet())
|
||||
if (osg::ref_ptr<osg::StateSet> stateset = node.getStateSet())
|
||||
{
|
||||
osg::ref_ptr<osg::StateSet> newStateSet;
|
||||
if (stateset->getAttribute(osg::StateAttribute::BLENDFUNC) || stateset->getBinNumber() == osg::StateSet::TRANSPARENT_BIN)
|
||||
{
|
||||
osg::ref_ptr<osg::StateSet> newStateSet = new osg::StateSet(*stateset, osg::CopyOp::SHALLOW_COPY);
|
||||
osg::BlendFunc* blendFunc = static_cast<osg::BlendFunc*>(stateset->getAttribute(osg::StateAttribute::BLENDFUNC));
|
||||
osg::ref_ptr<osg::BlendFunc> newBlendFunc = blendFunc ? new osg::BlendFunc(*blendFunc) : new osg::BlendFunc;
|
||||
newBlendFunc->setDestinationAlpha(osg::BlendFunc::ONE);
|
||||
newStateSet->setAttribute(newBlendFunc, osg::StateAttribute::ON);
|
||||
node.setStateSet(newStateSet);
|
||||
|
||||
if (blendFunc)
|
||||
{
|
||||
newStateSet = new osg::StateSet(*stateset, osg::CopyOp::SHALLOW_COPY);
|
||||
node.setStateSet(newStateSet);
|
||||
osg::ref_ptr<osg::BlendFunc> newBlendFunc = new osg::BlendFunc(*blendFunc);
|
||||
newStateSet->setAttribute(newBlendFunc, osg::StateAttribute::ON);
|
||||
// I *think* (based on some by-hand maths) that the RGB and dest alpha factors are unchanged, and only dest determines source alpha factor
|
||||
// This has the benefit of being idempotent if we assume nothing used glBlendFuncSeparate before we touched it
|
||||
if (blendFunc->getDestination() == osg::BlendFunc::ONE_MINUS_SRC_ALPHA)
|
||||
newBlendFunc->setSourceAlpha(osg::BlendFunc::ONE);
|
||||
else if (blendFunc->getDestination() == osg::BlendFunc::ONE)
|
||||
newBlendFunc->setSourceAlpha(osg::BlendFunc::ZERO);
|
||||
// Other setups barely exist in the wild and aren't worth supporting as they're not equippable gear
|
||||
else
|
||||
Log(Debug::Info) << "Unable to adjust blend mode for character preview. Source factor 0x" << std::hex << blendFunc->getSource() << ", destination factor 0x" << blendFunc->getDestination() << std::dec;
|
||||
}
|
||||
}
|
||||
if (stateset->getMode(GL_BLEND) & osg::StateAttribute::ON)
|
||||
{
|
||||
if (!newStateSet)
|
||||
{
|
||||
newStateSet = new osg::StateSet(*stateset, osg::CopyOp::SHALLOW_COPY);
|
||||
node.setStateSet(newStateSet);
|
||||
}
|
||||
// Disable noBlendAlphaEnv
|
||||
stateset->setTextureMode(7, GL_TEXTURE_2D, osg::StateAttribute::OFF);
|
||||
stateset->addUniform(mNoAlphaUniform);
|
||||
newStateSet->setTextureMode(7, GL_TEXTURE_2D, osg::StateAttribute::OFF);
|
||||
newStateSet->addUniform(mNoAlphaUniform);
|
||||
}
|
||||
}
|
||||
traverse(node);
|
||||
|
@ -134,6 +155,7 @@ namespace MWRender
|
|||
mTexture->setInternalFormat(GL_RGBA);
|
||||
mTexture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
|
||||
mTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
|
||||
mTexture->setUserValue("premultiplied alpha", true);
|
||||
|
||||
mCamera = new osg::Camera;
|
||||
// hints that the camera is not relative to the master camera
|
||||
|
@ -145,7 +167,7 @@ namespace MWRender
|
|||
mCamera->setProjectionMatrixAsPerspective(fovYDegrees, sizeX/static_cast<float>(sizeY), 0.1f, 10000.f); // zNear and zFar are autocomputed
|
||||
mCamera->setViewport(0, 0, sizeX, sizeY);
|
||||
mCamera->setRenderOrder(osg::Camera::PRE_RENDER);
|
||||
mCamera->attach(osg::Camera::COLOR_BUFFER, mTexture);
|
||||
mCamera->attach(osg::Camera::COLOR_BUFFER, mTexture, 0, 0, false, Settings::Manager::getInt("antialiasing", "Video"));
|
||||
mCamera->setName("CharacterPreview");
|
||||
mCamera->setComputeNearFarMode(osg::Camera::COMPUTE_NEAR_FAR_USING_BOUNDING_VOLUMES);
|
||||
mCamera->setCullMask(~(Mask_UpdateVisitor));
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "groundcover.hpp"
|
||||
|
||||
#include <osg/AlphaFunc>
|
||||
#include <osg/Geometry>
|
||||
#include <osg/VertexAttribDivisor>
|
||||
|
||||
|
@ -258,11 +259,16 @@ namespace MWRender
|
|||
// Keep link to original mesh to keep it in cache
|
||||
group->getOrCreateUserDataContainer()->addUserObject(new Resource::TemplateRef(temp));
|
||||
|
||||
mSceneManager->reinstateRemovedState(node);
|
||||
|
||||
InstancingVisitor visitor(pair.second, worldCenter);
|
||||
node->accept(visitor);
|
||||
group->addChild(node);
|
||||
}
|
||||
|
||||
// Force a unified alpha handling instead of data from meshes
|
||||
osg::ref_ptr<osg::AlphaFunc> alpha = new osg::AlphaFunc(osg::AlphaFunc::GEQUAL, 128.f / 255.f);
|
||||
group->getOrCreateStateSet()->setAttributeAndModes(alpha.get(), osg::StateAttribute::ON);
|
||||
group->getBound();
|
||||
group->setNodeMask(Mask_Groundcover);
|
||||
mSceneManager->recreateShaders(group, "groundcover", false, true);
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <components/settings/settings.hpp>
|
||||
#include <components/sceneutil/visitor.hpp>
|
||||
#include <components/sceneutil/shadow.hpp>
|
||||
#include <components/sceneutil/util.hpp>
|
||||
#include <components/files/memorystream.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
|
@ -237,7 +238,7 @@ void LocalMap::setupRenderToTexture(osg::ref_ptr<osg::Camera> camera, int x, int
|
|||
texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
|
||||
texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
|
||||
|
||||
camera->attach(osg::Camera::COLOR_BUFFER, texture);
|
||||
SceneUtil::attachAlphaToCoverageFriendlyFramebufferToCamera(camera, osg::Camera::COLOR_BUFFER, texture);
|
||||
|
||||
camera->addChild(mSceneRoot);
|
||||
mRoot->addChild(camera);
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
#include <limits>
|
||||
#include <cstdlib>
|
||||
|
||||
#include <osg/AlphaFunc>
|
||||
#include <osg/Light>
|
||||
#include <osg/LightModel>
|
||||
#include <osg/Fog>
|
||||
|
@ -27,6 +26,7 @@
|
|||
#include <components/resource/scenemanager.hpp>
|
||||
#include <components/resource/keyframemanager.hpp>
|
||||
|
||||
#include <components/shader/removedalphafunc.hpp>
|
||||
#include <components/shader/shadermanager.hpp>
|
||||
|
||||
#include <components/settings/settings.hpp>
|
||||
|
@ -223,6 +223,7 @@ namespace MWRender
|
|||
resourceSystem->getSceneManager()->setAutoUseSpecularMaps(Settings::Manager::getBool("auto use object specular maps", "Shaders"));
|
||||
resourceSystem->getSceneManager()->setSpecularMapPattern(Settings::Manager::getString("specular map pattern", "Shaders"));
|
||||
resourceSystem->getSceneManager()->setApplyLightingToEnvMaps(Settings::Manager::getBool("apply lighting to environment maps", "Shaders"));
|
||||
resourceSystem->getSceneManager()->setConvertAlphaTestToAlphaToCoverage(Settings::Manager::getBool("antialias alpha test", "Shaders") && Settings::Manager::getInt("antialiasing", "Video") > 1);
|
||||
|
||||
osg::ref_ptr<SceneUtil::LightManager> sceneRoot = new SceneUtil::LightManager;
|
||||
sceneRoot->setLightingMask(Mask_Lighting);
|
||||
|
@ -255,6 +256,7 @@ namespace MWRender
|
|||
globalDefines["clamp"] = Settings::Manager::getBool("clamp lighting", "Shaders") ? "1" : "0";
|
||||
globalDefines["preLightEnv"] = Settings::Manager::getBool("apply lighting to environment maps", "Shaders") ? "1" : "0";
|
||||
globalDefines["radialFog"] = Settings::Manager::getBool("radial fog", "Shaders") ? "1" : "0";
|
||||
globalDefines["useGPUShader4"] = "0";
|
||||
|
||||
float groundcoverDistance = (Constants::CellSizeInUnits * std::max(1, Settings::Manager::getInt("distance", "Groundcover")) - 1024) * 0.93;
|
||||
globalDefines["groundcoverFadeStart"] = std::to_string(groundcoverDistance * 0.9f);
|
||||
|
@ -321,10 +323,6 @@ namespace MWRender
|
|||
groundcoverRoot->setName("Groundcover Root");
|
||||
sceneRoot->addChild(groundcoverRoot);
|
||||
|
||||
// Force a unified alpha handling instead of data from meshes
|
||||
osg::ref_ptr<osg::AlphaFunc> alpha = new osg::AlphaFunc(osg::AlphaFunc::GEQUAL, 128.f/255.f);
|
||||
groundcoverRoot->getOrCreateStateSet()->setAttributeAndModes(alpha.get(), osg::StateAttribute::ON);
|
||||
|
||||
mGroundcoverUpdater = new GroundcoverUpdater;
|
||||
groundcoverRoot->addUpdateCallback(mGroundcoverUpdater);
|
||||
|
||||
|
@ -381,7 +379,6 @@ namespace MWRender
|
|||
|
||||
mSky.reset(new SkyManager(sceneRoot, resourceSystem->getSceneManager()));
|
||||
mSky->setCamera(mViewer->getCamera());
|
||||
mSky->setRainIntensityUniform(mWater->getRainIntensityUniform());
|
||||
|
||||
source->setStateSetModes(*mRootNode->getOrCreateStateSet(), osg::StateAttribute::ON);
|
||||
|
||||
|
@ -424,6 +421,11 @@ namespace MWRender
|
|||
mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("far", mViewDistance));
|
||||
mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("simpleWater", false));
|
||||
|
||||
// Hopefully, anything genuinely requiring the default alpha func of GL_ALWAYS explicitly sets it
|
||||
mRootNode->getOrCreateStateSet()->setAttribute(Shader::RemovedAlphaFunc::getInstance(GL_ALWAYS));
|
||||
// The transparent renderbin sets alpha testing on because that was faster on old GPUs. It's now slower and breaks things.
|
||||
mRootNode->getOrCreateStateSet()->setMode(GL_ALPHA_TEST, osg::StateAttribute::OFF);
|
||||
|
||||
mUniformNear = mRootNode->getOrCreateStateSet()->getUniform("near");
|
||||
mUniformFar = mRootNode->getOrCreateStateSet()->getUniform("far");
|
||||
|
||||
|
@ -676,6 +678,9 @@ namespace MWRender
|
|||
|
||||
mUnrefQueue->flush(mWorkQueue.get());
|
||||
|
||||
float rainIntensity = mSky->getPrecipitationAlpha();
|
||||
mWater->setRainIntensity(rainIntensity);
|
||||
|
||||
if (!paused)
|
||||
{
|
||||
mEffectManager->update(dt);
|
||||
|
|
|
@ -497,8 +497,6 @@ public:
|
|||
// Disable writing to the color buffer. We are using this geometry for visibility tests only.
|
||||
osg::ref_ptr<osg::ColorMask> colormask (new osg::ColorMask(0, 0, 0, 0));
|
||||
stateset->setAttributeAndModes(colormask, osg::StateAttribute::ON);
|
||||
osg::ref_ptr<osg::PolygonOffset> po (new osg::PolygonOffset( -1., -1. ));
|
||||
stateset->setAttributeAndModes(po, osg::StateAttribute::ON);
|
||||
|
||||
mTransform->addChild(queryNode);
|
||||
|
||||
|
@ -595,7 +593,7 @@ private:
|
|||
if (queryVisible)
|
||||
{
|
||||
osg::ref_ptr<osg::Depth> depth (new osg::Depth);
|
||||
depth->setFunction(osg::Depth::LESS);
|
||||
depth->setFunction(osg::Depth::LEQUAL);
|
||||
// This is a trick to make fragments written by the query always use the maximum depth value,
|
||||
// without having to retrieve the current far clipping distance.
|
||||
// We want the sun glare to be "infinitely" far away.
|
||||
|
@ -1113,7 +1111,6 @@ private:
|
|||
SkyManager::SkyManager(osg::Group* parentNode, Resource::SceneManager* sceneManager)
|
||||
: mSceneManager(sceneManager)
|
||||
, mCamera(nullptr)
|
||||
, mRainIntensityUniform(nullptr)
|
||||
, mAtmosphereNightRoll(0.f)
|
||||
, mCreated(false)
|
||||
, mIsStorm(false)
|
||||
|
@ -1139,7 +1136,7 @@ SkyManager::SkyManager(osg::Group* parentNode, Resource::SceneManager* sceneMana
|
|||
, mBaseWindSpeed(0.f)
|
||||
, mEnabled(true)
|
||||
, mSunEnabled(true)
|
||||
, mEffectFade(0.f)
|
||||
, mPrecipitationAlpha(0.f)
|
||||
{
|
||||
osg::ref_ptr<CameraRelativeTransform> skyroot (new CameraRelativeTransform);
|
||||
skyroot->setName("Sky Root");
|
||||
|
@ -1163,11 +1160,6 @@ SkyManager::SkyManager(osg::Group* parentNode, Resource::SceneManager* sceneMana
|
|||
mUnderwaterSwitch = new UnderwaterSwitchCallback(skyroot);
|
||||
}
|
||||
|
||||
void SkyManager::setRainIntensityUniform(osg::Uniform *uniform)
|
||||
{
|
||||
mRainIntensityUniform = uniform;
|
||||
}
|
||||
|
||||
void SkyManager::create()
|
||||
{
|
||||
assert(!mCreated);
|
||||
|
@ -1522,7 +1514,7 @@ void SkyManager::createRain()
|
|||
|
||||
osg::ref_ptr<osgParticle::ModularProgram> program (new osgParticle::ModularProgram);
|
||||
program->addOperator(new WrapAroundOperator(mCamera,rainRange));
|
||||
program->addOperator(new WeatherAlphaOperator(mEffectFade, true));
|
||||
program->addOperator(new WeatherAlphaOperator(mPrecipitationAlpha, true));
|
||||
program->setParticleSystem(mRainParticleSystem);
|
||||
mRainNode->addChild(program);
|
||||
|
||||
|
@ -1576,29 +1568,23 @@ bool SkyManager::isEnabled()
|
|||
return mEnabled;
|
||||
}
|
||||
|
||||
bool SkyManager::hasRain()
|
||||
bool SkyManager::hasRain() const
|
||||
{
|
||||
return mRainNode != nullptr;
|
||||
}
|
||||
|
||||
float SkyManager::getPrecipitationAlpha() const
|
||||
{
|
||||
if (mEnabled && !mIsStorm && (hasRain() || mParticleNode))
|
||||
return mPrecipitationAlpha;
|
||||
|
||||
return 0.f;
|
||||
}
|
||||
|
||||
void SkyManager::update(float duration)
|
||||
{
|
||||
if (!mEnabled)
|
||||
{
|
||||
if (mRainIntensityUniform)
|
||||
mRainIntensityUniform->set(0.f);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (mRainIntensityUniform)
|
||||
{
|
||||
float rainIntensity = 0.f;
|
||||
if (!mIsStorm && (hasRain() || mParticleNode))
|
||||
rainIntensity = mEffectFade;
|
||||
|
||||
mRainIntensityUniform->set(rainIntensity);
|
||||
}
|
||||
|
||||
switchUnderwaterRain();
|
||||
|
||||
|
@ -1737,7 +1723,7 @@ void SkyManager::setWeather(const WeatherResult& weather)
|
|||
SceneUtil::AssignControllerSourcesVisitor assignVisitor(std::shared_ptr<SceneUtil::ControllerSource>(new SceneUtil::FrameTimeSource));
|
||||
mParticleEffect->accept(assignVisitor);
|
||||
|
||||
AlphaFader::SetupVisitor alphaFaderSetupVisitor(mEffectFade);
|
||||
AlphaFader::SetupVisitor alphaFaderSetupVisitor(mPrecipitationAlpha);
|
||||
|
||||
mParticleEffect->accept(alphaFaderSetupVisitor);
|
||||
|
||||
|
@ -1754,7 +1740,7 @@ void SkyManager::setWeather(const WeatherResult& weather)
|
|||
osg::ref_ptr<osgParticle::ModularProgram> program (new osgParticle::ModularProgram);
|
||||
if (!mIsStorm)
|
||||
program->addOperator(new WrapAroundOperator(mCamera,osg::Vec3(1024,1024,800)));
|
||||
program->addOperator(new WeatherAlphaOperator(mEffectFade, false));
|
||||
program->addOperator(new WeatherAlphaOperator(mPrecipitationAlpha, false));
|
||||
program->setParticleSystem(ps);
|
||||
mParticleNode->addChild(program);
|
||||
}
|
||||
|
@ -1843,7 +1829,7 @@ void SkyManager::setWeather(const WeatherResult& weather)
|
|||
|
||||
mAtmosphereNightNode->setNodeMask(weather.mNight ? ~0 : 0);
|
||||
|
||||
mEffectFade = weather.mEffectFade;
|
||||
mPrecipitationAlpha = weather.mPrecipitationAlpha;
|
||||
}
|
||||
|
||||
float SkyManager::getBaseWindSpeed() const
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
#include <osg/ref_ptr>
|
||||
#include <osg/Vec4f>
|
||||
#include <osg/Uniform>
|
||||
|
||||
namespace osg
|
||||
{
|
||||
|
@ -88,7 +87,7 @@ namespace MWRender
|
|||
|
||||
std::string mParticleEffect;
|
||||
std::string mRainEffect;
|
||||
float mEffectFade;
|
||||
float mPrecipitationAlpha;
|
||||
|
||||
float mRainDiameter;
|
||||
float mRainMinHeight;
|
||||
|
@ -157,7 +156,9 @@ namespace MWRender
|
|||
|
||||
bool isEnabled();
|
||||
|
||||
bool hasRain();
|
||||
bool hasRain() const;
|
||||
|
||||
float getPrecipitationAlpha() const;
|
||||
|
||||
void setRainSpeed(float speed);
|
||||
|
||||
|
@ -180,8 +181,6 @@ namespace MWRender
|
|||
|
||||
void setCamera(osg::Camera *camera);
|
||||
|
||||
void setRainIntensityUniform(osg::Uniform *uniform);
|
||||
|
||||
float getBaseWindSpeed() const;
|
||||
|
||||
private:
|
||||
|
@ -196,7 +195,6 @@ namespace MWRender
|
|||
Resource::SceneManager* mSceneManager;
|
||||
|
||||
osg::Camera *mCamera;
|
||||
osg::Uniform *mRainIntensityUniform;
|
||||
|
||||
osg::ref_ptr<osg::Group> mRootNode;
|
||||
osg::ref_ptr<osg::Group> mEarlyRenderBinRoot;
|
||||
|
@ -271,7 +269,7 @@ namespace MWRender
|
|||
bool mEnabled;
|
||||
bool mSunEnabled;
|
||||
|
||||
float mEffectFade;
|
||||
float mPrecipitationAlpha;
|
||||
|
||||
osg::Vec4f mMoonScriptColor;
|
||||
};
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
|
||||
#include <components/sceneutil/rtt.hpp>
|
||||
#include <components/sceneutil/shadow.hpp>
|
||||
#include <components/sceneutil/util.hpp>
|
||||
#include <components/sceneutil/waterutil.hpp>
|
||||
|
||||
#include <components/misc/constants.hpp>
|
||||
|
@ -213,6 +214,37 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
class RainIntensityUpdater : public SceneUtil::StateSetUpdater
|
||||
{
|
||||
public:
|
||||
RainIntensityUpdater()
|
||||
: mRainIntensity(0.f)
|
||||
{
|
||||
}
|
||||
|
||||
void setRainIntensity(float rainIntensity)
|
||||
{
|
||||
mRainIntensity = rainIntensity;
|
||||
}
|
||||
|
||||
protected:
|
||||
void setDefaults(osg::StateSet* stateset) override
|
||||
{
|
||||
osg::ref_ptr<osg::Uniform> rainIntensityUniform = new osg::Uniform("rainIntensity", 0.0f);
|
||||
stateset->addUniform(rainIntensityUniform.get());
|
||||
}
|
||||
|
||||
void apply(osg::StateSet* stateset, osg::NodeVisitor* /*nv*/) override
|
||||
{
|
||||
osg::ref_ptr<osg::Uniform> rainIntensityUniform = stateset->getUniform("rainIntensity");
|
||||
if (rainIntensityUniform != nullptr)
|
||||
rainIntensityUniform->set(mRainIntensity);
|
||||
}
|
||||
|
||||
private:
|
||||
float mRainIntensity;
|
||||
};
|
||||
|
||||
osg::ref_ptr<osg::Image> readPngImage (const std::string& file)
|
||||
{
|
||||
// use boost in favor of osgDB::readImage, to handle utf-8 path issues on Windows
|
||||
|
@ -396,7 +428,8 @@ public:
|
|||
|
||||
Water::Water(osg::Group *parent, osg::Group* sceneRoot, Resource::ResourceSystem *resourceSystem,
|
||||
osgUtil::IncrementalCompileOperation *ico, const std::string& resourcePath)
|
||||
: mParent(parent)
|
||||
: mRainIntensityUpdater(nullptr)
|
||||
, mParent(parent)
|
||||
, mSceneRoot(sceneRoot)
|
||||
, mResourceSystem(resourceSystem)
|
||||
, mResourcePath(resourcePath)
|
||||
|
@ -429,8 +462,6 @@ Water::Water(osg::Group *parent, osg::Group* sceneRoot, Resource::ResourceSystem
|
|||
|
||||
setHeight(mTop);
|
||||
|
||||
mRainIntensityUniform = new osg::Uniform("rainIntensity",(float) 0.0);
|
||||
|
||||
updateWaterMaterial();
|
||||
|
||||
if (ico)
|
||||
|
@ -460,11 +491,6 @@ void Water::setCullCallback(osg::Callback* callback)
|
|||
}
|
||||
}
|
||||
|
||||
osg::Uniform *Water::getRainIntensityUniform()
|
||||
{
|
||||
return mRainIntensityUniform.get();
|
||||
}
|
||||
|
||||
void Water::updateWaterMaterial()
|
||||
{
|
||||
if (mShaderWaterStateSetUpdater)
|
||||
|
@ -531,6 +557,8 @@ void Water::createSimpleWaterStateSet(osg::Node* node, float alpha)
|
|||
osg::ref_ptr<osg::StateSet> stateset = SceneUtil::createSimpleWaterStateSet(alpha, MWRender::RenderBin_Water);
|
||||
|
||||
node->setStateSet(stateset);
|
||||
node->setUpdateCallback(nullptr);
|
||||
mRainIntensityUpdater = nullptr;
|
||||
|
||||
// Add animated textures
|
||||
std::vector<osg::ref_ptr<osg::Texture2D> > textures;
|
||||
|
@ -583,7 +611,6 @@ public:
|
|||
stateset->addUniform(new osg::Uniform("normalMap", 0));
|
||||
stateset->setTextureAttributeAndModes(0, mNormalMap, osg::StateAttribute::ON);
|
||||
stateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
|
||||
stateset->addUniform(mWater->getRainIntensityUniform());
|
||||
stateset->setAttributeAndModes(mProgram, osg::StateAttribute::ON);
|
||||
|
||||
stateset->addUniform(new osg::Uniform("reflectionMap", 1));
|
||||
|
@ -648,6 +675,9 @@ void Water::createShaderWaterStateSet(osg::Node* node, Reflection* reflection, R
|
|||
normalMap->setMaxAnisotropy(16);
|
||||
normalMap->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_LINEAR);
|
||||
normalMap->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
|
||||
|
||||
mRainIntensityUpdater = new RainIntensityUpdater();
|
||||
node->setUpdateCallback(mRainIntensityUpdater);
|
||||
|
||||
mShaderWaterStateSetUpdater = new ShaderWaterStateSetUpdater(this, mReflection, mRefraction, program, normalMap);
|
||||
node->addCullCallback(mShaderWaterStateSetUpdater);
|
||||
|
@ -731,6 +761,12 @@ void Water::setHeight(const float height)
|
|||
mRefraction->setWaterLevel(mTop);
|
||||
}
|
||||
|
||||
void Water::setRainIntensity(float rainIntensity)
|
||||
{
|
||||
if (mRainIntensityUpdater)
|
||||
mRainIntensityUpdater->setRainIntensity(rainIntensity);
|
||||
}
|
||||
|
||||
void Water::update(float dt)
|
||||
{
|
||||
mSimulation->update(dt);
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
#include <osg/ref_ptr>
|
||||
#include <osg/Vec3f>
|
||||
#include <osg/Uniform>
|
||||
#include <osg/Camera>
|
||||
|
||||
#include <components/settings/settings.hpp>
|
||||
|
@ -46,11 +45,12 @@ namespace MWRender
|
|||
class Refraction;
|
||||
class Reflection;
|
||||
class RippleSimulation;
|
||||
class RainIntensityUpdater;
|
||||
|
||||
/// Water rendering
|
||||
class Water
|
||||
{
|
||||
osg::ref_ptr<osg::Uniform> mRainIntensityUniform;
|
||||
osg::ref_ptr<RainIntensityUpdater> mRainIntensityUpdater;
|
||||
|
||||
osg::ref_ptr<osg::Group> mParent;
|
||||
osg::ref_ptr<osg::Group> mSceneRoot;
|
||||
|
@ -113,6 +113,7 @@ namespace MWRender
|
|||
|
||||
void changeCell(const MWWorld::CellStore* store);
|
||||
void setHeight(const float height);
|
||||
void setRainIntensity(const float rainIntensity);
|
||||
|
||||
void update(float dt);
|
||||
|
||||
|
@ -120,8 +121,6 @@ namespace MWRender
|
|||
osg::Node* getRefractionNode();
|
||||
|
||||
void processChangedSettings(const Settings::CategorySettingVector& settings);
|
||||
|
||||
osg::Uniform *getRainIntensityUniform();
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ namespace MWScript
|
|||
{
|
||||
struct ExplicitRef
|
||||
{
|
||||
static const bool implicit = false;
|
||||
static constexpr bool implicit = false;
|
||||
|
||||
MWWorld::Ptr operator() (Interpreter::Runtime& runtime, bool required = true,
|
||||
bool activeOnly = false) const;
|
||||
|
@ -22,7 +22,7 @@ namespace MWScript
|
|||
|
||||
struct ImplicitRef
|
||||
{
|
||||
static const bool implicit = true;
|
||||
static constexpr bool implicit = true;
|
||||
|
||||
MWWorld::Ptr operator() (Interpreter::Runtime& runtime, bool required = true,
|
||||
bool activeOnly = false) const;
|
||||
|
|
|
@ -270,20 +270,17 @@ namespace MWScript
|
|||
Interpreter::Type_Float pos = runtime[0].mFloat;
|
||||
runtime.pop();
|
||||
|
||||
float ax = ptr.getRefData().getPosition().pos[0];
|
||||
float ay = ptr.getRefData().getPosition().pos[1];
|
||||
float az = ptr.getRefData().getPosition().pos[2];
|
||||
|
||||
// Note: SetPos does not skip weather transitions in vanilla engine, so we do not call setTeleported(true) here.
|
||||
|
||||
MWWorld::Ptr updated = ptr;
|
||||
const auto curPos = ptr.getRefData().getPosition().asVec3();
|
||||
auto newPos = curPos;
|
||||
if(axis == "x")
|
||||
{
|
||||
updated = MWBase::Environment::get().getWorld()->moveObject(ptr,pos,ay,az,true);
|
||||
newPos[0] = pos;
|
||||
}
|
||||
else if(axis == "y")
|
||||
{
|
||||
updated = MWBase::Environment::get().getWorld()->moveObject(ptr,ax,pos,az,true);
|
||||
newPos[1] = pos;
|
||||
}
|
||||
else if(axis == "z")
|
||||
{
|
||||
|
@ -292,20 +289,21 @@ namespace MWScript
|
|||
{
|
||||
float terrainHeight = -std::numeric_limits<float>::max();
|
||||
if (ptr.getCell()->isExterior())
|
||||
terrainHeight = MWBase::Environment::get().getWorld()->getTerrainHeightAt(osg::Vec3f(ax, ay, az));
|
||||
terrainHeight = MWBase::Environment::get().getWorld()->getTerrainHeightAt(curPos);
|
||||
|
||||
if (pos < terrainHeight)
|
||||
pos = terrainHeight;
|
||||
}
|
||||
|
||||
updated = MWBase::Environment::get().getWorld()->moveObject(ptr,ax,ay,pos,true);
|
||||
newPos[2] = pos;
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
dynamic_cast<MWScript::InterpreterContext&>(runtime.getContext()).updatePtr(ptr,updated);
|
||||
dynamic_cast<MWScript::InterpreterContext&>(runtime.getContext()).updatePtr(ptr,
|
||||
MWBase::Environment::get().getWorld()->moveObjectBy(ptr, newPos - curPos));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -287,9 +287,9 @@ void FFmpeg_Decoder::close()
|
|||
mStream = nullptr;
|
||||
|
||||
av_packet_unref(&mPacket);
|
||||
av_freep(&mFrame);
|
||||
swr_free(&mSwr);
|
||||
av_freep(&mDataBuf);
|
||||
av_frame_free(&mFrame);
|
||||
swr_free(&mSwr);
|
||||
|
||||
if(mFormatCtx)
|
||||
{
|
||||
|
@ -302,11 +302,13 @@ void FFmpeg_Decoder::close()
|
|||
//
|
||||
if (mFormatCtx->pb->buffer != nullptr)
|
||||
{
|
||||
av_free(mFormatCtx->pb->buffer);
|
||||
mFormatCtx->pb->buffer = nullptr;
|
||||
av_freep(&mFormatCtx->pb->buffer);
|
||||
}
|
||||
av_free(mFormatCtx->pb);
|
||||
mFormatCtx->pb = nullptr;
|
||||
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 80, 100)
|
||||
avio_context_free(&mFormatCtx->pb);
|
||||
#else
|
||||
av_freep(&mFormatCtx->pb);
|
||||
#endif
|
||||
}
|
||||
avformat_close_input(&mFormatCtx);
|
||||
}
|
||||
|
|
|
@ -69,15 +69,13 @@ namespace MWSound
|
|||
FFmpeg_Decoder& operator=(const FFmpeg_Decoder &rhs);
|
||||
FFmpeg_Decoder(const FFmpeg_Decoder &rhs);
|
||||
|
||||
FFmpeg_Decoder(const VFS::Manager* vfs);
|
||||
public:
|
||||
explicit FFmpeg_Decoder(const VFS::Manager* vfs);
|
||||
|
||||
virtual ~FFmpeg_Decoder();
|
||||
|
||||
friend class SoundManager;
|
||||
};
|
||||
#ifndef DEFAULT_DECODER
|
||||
#define DEFAULT_DECODER (::MWSound::FFmpeg_Decoder)
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -98,9 +98,6 @@ namespace MWSound
|
|||
OpenAL_Output(SoundManager &mgr);
|
||||
virtual ~OpenAL_Output();
|
||||
};
|
||||
#ifndef DEFAULT_OUTPUT
|
||||
#define DEFAULT_OUTPUT(x) ::MWSound::OpenAL_Output((x))
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -54,7 +54,7 @@ namespace MWSound
|
|||
|
||||
SoundManager::SoundManager(const VFS::Manager* vfs, bool useSound)
|
||||
: mVFS(vfs)
|
||||
, mOutput(new DEFAULT_OUTPUT(*this))
|
||||
, mOutput(new OpenAL_Output(*this))
|
||||
, mWaterSoundUpdater(makeWaterSoundUpdaterSettings())
|
||||
, mSoundBuffers(*vfs, *mOutput)
|
||||
, mListenerUnderwater(false)
|
||||
|
@ -109,7 +109,7 @@ namespace MWSound
|
|||
|
||||
SoundManager::~SoundManager()
|
||||
{
|
||||
clear();
|
||||
SoundManager::clear();
|
||||
mSoundBuffers.clear();
|
||||
mOutput.reset();
|
||||
}
|
||||
|
@ -117,7 +117,7 @@ namespace MWSound
|
|||
// Return a new decoder instance, used as needed by the output implementations
|
||||
DecoderPtr SoundManager::getDecoder()
|
||||
{
|
||||
return DecoderPtr(new DEFAULT_DECODER (mVFS));
|
||||
return std::make_shared<FFmpeg_Decoder>(mVFS);
|
||||
}
|
||||
|
||||
DecoderPtr SoundManager::loadVoice(const std::string &voicefile)
|
||||
|
|
|
@ -539,6 +539,8 @@ void MWState::StateManager::loadGame (const Character *character, const std::str
|
|||
MWBase::Environment::get().getWorld()->changeToCell(cell->getCell()->getCellId(), pos, true, false);
|
||||
}
|
||||
|
||||
MWBase::Environment::get().getWorld()->updateProjectilesCasters();
|
||||
|
||||
// Vanilla MW will restart startup scripts when a save game is loaded. This is unintuitive,
|
||||
// but some mods may be using it as a reload detector.
|
||||
MWBase::Environment::get().getScriptManager()->getGlobalScripts().addStartup();
|
||||
|
|
|
@ -104,6 +104,45 @@ namespace
|
|||
}
|
||||
}
|
||||
|
||||
template<class RecordType, class T>
|
||||
void fixRestockingImpl(const T* base, RecordType& state)
|
||||
{
|
||||
// Workaround for old saves not containing negative quantities
|
||||
for(const auto& baseItem : base->mInventory.mList)
|
||||
{
|
||||
if(baseItem.mCount < 0)
|
||||
{
|
||||
for(auto& item : state.mInventory.mItems)
|
||||
{
|
||||
if(item.mCount > 0 && Misc::StringUtils::ciEqual(baseItem.mItem, item.mRef.mRefID))
|
||||
item.mCount = -item.mCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<class RecordType, class T>
|
||||
void fixRestocking(const T* base, RecordType& state)
|
||||
{}
|
||||
|
||||
template<>
|
||||
void fixRestocking<>(const ESM::Creature* base, ESM::CreatureState& state)
|
||||
{
|
||||
fixRestockingImpl(base, state);
|
||||
}
|
||||
|
||||
template<>
|
||||
void fixRestocking<>(const ESM::NPC* base, ESM::NpcState& state)
|
||||
{
|
||||
fixRestockingImpl(base, state);
|
||||
}
|
||||
|
||||
template<>
|
||||
void fixRestocking<>(const ESM::Container* base, ESM::ContainerState& state)
|
||||
{
|
||||
fixRestockingImpl(base, state);
|
||||
}
|
||||
|
||||
template<typename RecordType, typename T>
|
||||
void readReferenceCollection (ESM::ESMReader& reader,
|
||||
MWWorld::CellRefList<T>& collection, const ESM::CellRef& cref, const std::map<int, int>& contentFileMap, MWWorld::CellStore* cellstore)
|
||||
|
@ -134,6 +173,9 @@ namespace
|
|||
if (!record)
|
||||
return;
|
||||
|
||||
if (state.mVersion < 15)
|
||||
fixRestocking(record, state);
|
||||
|
||||
if (state.mRef.mRefNum.hasContentFile())
|
||||
{
|
||||
for (typename MWWorld::CellRefList<T>::List::iterator iter (collection.mList.begin());
|
||||
|
|
|
@ -73,22 +73,22 @@ namespace MWWorld
|
|||
{
|
||||
public:
|
||||
|
||||
static const int Type_Potion = 0x0001;
|
||||
static const int Type_Apparatus = 0x0002;
|
||||
static const int Type_Armor = 0x0004;
|
||||
static const int Type_Book = 0x0008;
|
||||
static const int Type_Clothing = 0x0010;
|
||||
static const int Type_Ingredient = 0x0020;
|
||||
static const int Type_Light = 0x0040;
|
||||
static const int Type_Lockpick = 0x0080;
|
||||
static const int Type_Miscellaneous = 0x0100;
|
||||
static const int Type_Probe = 0x0200;
|
||||
static const int Type_Repair = 0x0400;
|
||||
static const int Type_Weapon = 0x0800;
|
||||
static constexpr int Type_Potion = 0x0001;
|
||||
static constexpr int Type_Apparatus = 0x0002;
|
||||
static constexpr int Type_Armor = 0x0004;
|
||||
static constexpr int Type_Book = 0x0008;
|
||||
static constexpr int Type_Clothing = 0x0010;
|
||||
static constexpr int Type_Ingredient = 0x0020;
|
||||
static constexpr int Type_Light = 0x0040;
|
||||
static constexpr int Type_Lockpick = 0x0080;
|
||||
static constexpr int Type_Miscellaneous = 0x0100;
|
||||
static constexpr int Type_Probe = 0x0200;
|
||||
static constexpr int Type_Repair = 0x0400;
|
||||
static constexpr int Type_Weapon = 0x0800;
|
||||
|
||||
static const int Type_Last = Type_Weapon;
|
||||
static constexpr int Type_Last = Type_Weapon;
|
||||
|
||||
static const int Type_All = 0xffff;
|
||||
static constexpr int Type_All = 0xffff;
|
||||
|
||||
static const std::string sGoldId;
|
||||
|
||||
|
@ -265,13 +265,13 @@ namespace MWWorld
|
|||
template<class From, class To, class Dummy>
|
||||
struct IsConvertible
|
||||
{
|
||||
static const bool value = true;
|
||||
static constexpr bool value = true;
|
||||
};
|
||||
|
||||
template<class Dummy>
|
||||
struct IsConvertible<ConstPtr, Ptr, Dummy>
|
||||
{
|
||||
static const bool value = false;
|
||||
static constexpr bool value = false;
|
||||
};
|
||||
|
||||
template<class T, class U>
|
||||
|
|
|
@ -42,29 +42,29 @@ namespace MWWorld
|
|||
{
|
||||
public:
|
||||
|
||||
static const int Slot_Helmet = 0;
|
||||
static const int Slot_Cuirass = 1;
|
||||
static const int Slot_Greaves = 2;
|
||||
static const int Slot_LeftPauldron = 3;
|
||||
static const int Slot_RightPauldron = 4;
|
||||
static const int Slot_LeftGauntlet = 5;
|
||||
static const int Slot_RightGauntlet = 6;
|
||||
static const int Slot_Boots = 7;
|
||||
static const int Slot_Shirt = 8;
|
||||
static const int Slot_Pants = 9;
|
||||
static const int Slot_Skirt = 10;
|
||||
static const int Slot_Robe = 11;
|
||||
static const int Slot_LeftRing = 12;
|
||||
static const int Slot_RightRing = 13;
|
||||
static const int Slot_Amulet = 14;
|
||||
static const int Slot_Belt = 15;
|
||||
static const int Slot_CarriedRight = 16;
|
||||
static const int Slot_CarriedLeft = 17;
|
||||
static const int Slot_Ammunition = 18;
|
||||
static constexpr int Slot_Helmet = 0;
|
||||
static constexpr int Slot_Cuirass = 1;
|
||||
static constexpr int Slot_Greaves = 2;
|
||||
static constexpr int Slot_LeftPauldron = 3;
|
||||
static constexpr int Slot_RightPauldron = 4;
|
||||
static constexpr int Slot_LeftGauntlet = 5;
|
||||
static constexpr int Slot_RightGauntlet = 6;
|
||||
static constexpr int Slot_Boots = 7;
|
||||
static constexpr int Slot_Shirt = 8;
|
||||
static constexpr int Slot_Pants = 9;
|
||||
static constexpr int Slot_Skirt = 10;
|
||||
static constexpr int Slot_Robe = 11;
|
||||
static constexpr int Slot_LeftRing = 12;
|
||||
static constexpr int Slot_RightRing = 13;
|
||||
static constexpr int Slot_Amulet = 14;
|
||||
static constexpr int Slot_Belt = 15;
|
||||
static constexpr int Slot_CarriedRight = 16;
|
||||
static constexpr int Slot_CarriedLeft = 17;
|
||||
static constexpr int Slot_Ammunition = 18;
|
||||
|
||||
static const int Slots = 19;
|
||||
static constexpr int Slots = 19;
|
||||
|
||||
static const int Slot_NoSlot = -1;
|
||||
static constexpr int Slot_NoSlot = -1;
|
||||
|
||||
private:
|
||||
|
||||
|
|
|
@ -270,9 +270,8 @@ namespace MWWorld
|
|||
osg::Vec3f pos = caster.getRefData().getPosition().asVec3();
|
||||
if (caster.getClass().isActor())
|
||||
{
|
||||
// Spawn at 0.75 * ActorHeight
|
||||
// Note: we ignore the collision box offset, this is required to make some flying creatures work as intended.
|
||||
pos.z() += mPhysics->getRenderingHalfExtents(caster).z() * 2 * 0.75;
|
||||
pos.z() += mPhysics->getRenderingHalfExtents(caster).z() * 2 * Constants::TorsoHeight;
|
||||
}
|
||||
|
||||
if (MWBase::Environment::get().getWorld()->isUnderwater(caster.getCell(), pos)) // Underwater casting not possible
|
||||
|
@ -366,6 +365,29 @@ namespace MWWorld
|
|||
mProjectiles.push_back(state);
|
||||
}
|
||||
|
||||
void ProjectileManager::updateCasters()
|
||||
{
|
||||
for (auto& state : mProjectiles)
|
||||
mPhysics->setCaster(state.mProjectileId, state.getCaster());
|
||||
|
||||
for (auto& state : mMagicBolts)
|
||||
{
|
||||
// casters are identified by actor id in the savegame. objects doesn't have one so they can't be identified back.
|
||||
// TODO: should object-type caster be restored from savegame?
|
||||
if (state.mActorId == -1)
|
||||
continue;
|
||||
|
||||
auto caster = state.getCaster();
|
||||
if (caster.isEmpty())
|
||||
{
|
||||
Log(Debug::Error) << "Couldn't find caster with ID " << state.mActorId;
|
||||
cleanupMagicBolt(state);
|
||||
continue;
|
||||
}
|
||||
mPhysics->setCaster(state.mProjectileId, caster);
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectileManager::update(float dt)
|
||||
{
|
||||
periodicCleanup(dt);
|
||||
|
|
|
@ -54,6 +54,8 @@ namespace MWWorld
|
|||
void launchProjectile (MWWorld::Ptr actor, MWWorld::ConstPtr projectile,
|
||||
const osg::Vec3f& pos, const osg::Quat& orient, MWWorld::Ptr bow, float speed, float attackStrength);
|
||||
|
||||
void updateCasters();
|
||||
|
||||
void update(float dt);
|
||||
|
||||
void processHits();
|
||||
|
|
|
@ -447,11 +447,17 @@ namespace MWWorld
|
|||
mPhysics->disableWater();
|
||||
|
||||
const auto player = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
||||
|
||||
// By default the player is grounded, with the scene fully loaded, we validate and correct this.
|
||||
if (player.mCell == cell) // Only run once, during initial cell load.
|
||||
{
|
||||
mPhysics->traceDown(player, player.getRefData().getPosition().asVec3(), 10.f);
|
||||
}
|
||||
|
||||
navigator->update(player.getRefData().getPosition().asVec3());
|
||||
|
||||
if (!cell->isExterior() && !(cell->getCell()->mData.mFlags & ESM::Cell::QuasiEx))
|
||||
{
|
||||
|
||||
mRendering.configureAmbient(cell->getCell());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1129,7 +1129,7 @@ inline void WeatherManager::calculateResult(const int weatherID, const float gam
|
|||
mResult.mGlareView = current.mGlareView;
|
||||
mResult.mAmbientLoopSoundID = current.mAmbientLoopSoundID;
|
||||
mResult.mAmbientSoundVolume = 1.f;
|
||||
mResult.mEffectFade = 1.f;
|
||||
mResult.mPrecipitationAlpha = 1.f;
|
||||
|
||||
mResult.mIsStorm = current.mIsStorm;
|
||||
|
||||
|
@ -1236,7 +1236,7 @@ inline void WeatherManager::calculateTransitionResult(const float factor, const
|
|||
mResult.mRainSpeed = current.mRainSpeed;
|
||||
mResult.mRainEntranceSpeed = current.mRainEntranceSpeed;
|
||||
mResult.mAmbientSoundVolume = 1 - factor / threshold;
|
||||
mResult.mEffectFade = mResult.mAmbientSoundVolume;
|
||||
mResult.mPrecipitationAlpha = mResult.mAmbientSoundVolume;
|
||||
mResult.mAmbientLoopSoundID = current.mAmbientLoopSoundID;
|
||||
mResult.mRainDiameter = current.mRainDiameter;
|
||||
mResult.mRainMinHeight = current.mRainMinHeight;
|
||||
|
@ -1251,7 +1251,7 @@ inline void WeatherManager::calculateTransitionResult(const float factor, const
|
|||
mResult.mRainSpeed = other.mRainSpeed;
|
||||
mResult.mRainEntranceSpeed = other.mRainEntranceSpeed;
|
||||
mResult.mAmbientSoundVolume = (factor - threshold) / (1 - threshold);
|
||||
mResult.mEffectFade = mResult.mAmbientSoundVolume;
|
||||
mResult.mPrecipitationAlpha = mResult.mAmbientSoundVolume;
|
||||
mResult.mAmbientLoopSoundID = other.mAmbientLoopSoundID;
|
||||
|
||||
mResult.mRainDiameter = other.mRainDiameter;
|
||||
|
|
|
@ -3240,6 +3240,11 @@ namespace MWWorld
|
|||
mProjectileManager->launchMagicBolt(spellId, caster, fallbackDirection);
|
||||
}
|
||||
|
||||
void World::updateProjectilesCasters()
|
||||
{
|
||||
mProjectileManager->updateCasters();
|
||||
}
|
||||
|
||||
class ApplyLoopingParticlesVisitor : public MWMechanics::EffectSourceVisitor
|
||||
{
|
||||
private:
|
||||
|
@ -3921,11 +3926,14 @@ namespace MWWorld
|
|||
return false;
|
||||
}
|
||||
|
||||
osg::Vec3f World::aimToTarget(const ConstPtr &actor, const MWWorld::ConstPtr& target)
|
||||
osg::Vec3f World::aimToTarget(const ConstPtr &actor, const ConstPtr &target)
|
||||
{
|
||||
osg::Vec3f weaponPos = actor.getRefData().getPosition().asVec3();
|
||||
weaponPos.z() += mPhysics->getHalfExtents(actor).z();
|
||||
osg::Vec3f targetPos = mPhysics->getCollisionObjectPosition(target);
|
||||
osg::Vec3f weaponHalfExtents = mPhysics->getHalfExtents(actor);
|
||||
osg::Vec3f targetPos = target.getRefData().getPosition().asVec3();
|
||||
osg::Vec3f targetHalfExtents = mPhysics->getHalfExtents(target);
|
||||
weaponPos.z() += weaponHalfExtents.z() * 2 * Constants::TorsoHeight;
|
||||
targetPos.z() += targetHalfExtents.z();
|
||||
return (targetPos - weaponPos);
|
||||
}
|
||||
|
||||
|
|
|
@ -649,6 +649,7 @@ namespace MWWorld
|
|||
void launchMagicBolt (const std::string& spellId, const MWWorld::Ptr& caster, const osg::Vec3f& fallbackDirection) override;
|
||||
void launchProjectile (MWWorld::Ptr& actor, MWWorld::Ptr& projectile,
|
||||
const osg::Vec3f& worldPos, const osg::Quat& orient, MWWorld::Ptr& bow, float speed, float attackStrength) override;
|
||||
void updateProjectilesCasters() override;
|
||||
|
||||
void applyLoopingParticles(const MWWorld::Ptr& ptr) override;
|
||||
|
||||
|
|
211
cmake/FindRecastNavigation.cmake
Normal file
211
cmake/FindRecastNavigation.cmake
Normal file
|
@ -0,0 +1,211 @@
|
|||
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||
# file Copyright.txt or https://cmake.org/licensing for details.
|
||||
# Copyright 2021 Bret Curtis for OpenMW
|
||||
#[=======================================================================[.rst:
|
||||
FindRecastNavigation
|
||||
-------
|
||||
|
||||
Find the RecastNavigation include directory and library.
|
||||
|
||||
Use this module by invoking find_package with the form::
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
find_package(RecastNavigation
|
||||
[version] # Minimum version e.g. 1.8.0
|
||||
[REQUIRED] # Fail with error if RECAST is not found
|
||||
)
|
||||
|
||||
Imported targets
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
This module defines the following :prop_tgt:`IMPORTED` targets:
|
||||
|
||||
.. variable:: RecastNavigation::Recast
|
||||
|
||||
Imported target for using the RECAST library, if found.
|
||||
|
||||
Result variables
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
.. variable:: RECAST_FOUND
|
||||
|
||||
Set to true if RECAST library found, otherwise false or undefined.
|
||||
|
||||
.. variable:: RECAST_INCLUDE_DIRS
|
||||
|
||||
Paths to include directories listed in one variable for use by RECAST client.
|
||||
|
||||
.. variable:: RECAST_LIBRARIES
|
||||
|
||||
Paths to libraries to linked against to use RECAST.
|
||||
|
||||
.. variable:: RECAST_VERSION
|
||||
|
||||
The version string of RECAST found.
|
||||
|
||||
Cache variables
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
For users who wish to edit and control the module behavior, this module
|
||||
reads hints about search locations from the following variables::
|
||||
|
||||
.. variable:: RECAST_INCLUDE_DIR
|
||||
|
||||
Path to RECAST include directory with ``Recast.h`` header.
|
||||
|
||||
.. variable:: RECAST_LIBRARY
|
||||
|
||||
Path to RECAST library to be linked.
|
||||
|
||||
NOTE: The variables above should not usually be used in CMakeLists.txt files!
|
||||
|
||||
#]=======================================================================]
|
||||
|
||||
### Find libraries ##############################################################
|
||||
|
||||
if(NOT RECAST_LIBRARY)
|
||||
find_library(RECAST_LIBRARY_RELEASE NAMES Recast HINTS ${RECASTNAVIGATION_ROOT} PATH_SUFFIXES lib)
|
||||
find_library(RECAST_LIBRARY_DEBUG NAMES Recast-d HINTS ${RECASTNAVIGATION_ROOT} PATH_SUFFIXES lib)
|
||||
include(SelectLibraryConfigurations)
|
||||
select_library_configurations(RECAST)
|
||||
mark_as_advanced(RECAST_LIBRARY_RELEASE RECAST_LIBRARY_DEBUG)
|
||||
else()
|
||||
file(TO_CMAKE_PATH "${RECAST_LIBRARY}" RECAST_LIBRARY)
|
||||
endif()
|
||||
|
||||
if(NOT DETOUR_LIBRARY)
|
||||
find_library(DETOUR_LIBRARY_RELEASE NAMES Detour HINTS ${RECASTNAVIGATION_ROOT} PATH_SUFFIXES lib)
|
||||
find_library(DETOUR_LIBRARY_DEBUG NAMES Detour-d HINTS ${RECASTNAVIGATION_ROOT} PATH_SUFFIXES lib)
|
||||
include(SelectLibraryConfigurations)
|
||||
select_library_configurations(DETOUR)
|
||||
mark_as_advanced(DETOUR_LIBRARY_RELEASE DETOUR_LIBRARY_DEBUG)
|
||||
else()
|
||||
file(TO_CMAKE_PATH "${DETOUR_LIBRARY}" DETOUR_LIBRARY)
|
||||
endif()
|
||||
|
||||
if(NOT DEBUGUTILS_LIBRARY)
|
||||
find_library(DEBUGUTILS_LIBRARY_RELEASE NAMES DebugUtils HINTS ${RECASTNAVIGATION_ROOT} PATH_SUFFIXES lib)
|
||||
find_library(DEBUGUTILS_LIBRARY_DEBUG NAMES DebugUtils-d HINTS ${RECASTNAVIGATION_ROOT} PATH_SUFFIXES lib)
|
||||
include(SelectLibraryConfigurations)
|
||||
select_library_configurations(DEBUGUTILS)
|
||||
mark_as_advanced(DEBUGUTILS_LIBRARY_RELEASE DEBUGUTILS_LIBRARY_DEBUG)
|
||||
else()
|
||||
file(TO_CMAKE_PATH "${DEBUGUTILS_LIBRARY}" DEBUGUTILS_LIBRARY)
|
||||
endif()
|
||||
|
||||
### Find include directory ####################################################
|
||||
find_path(RECAST_INCLUDE_DIR NAMES Recast.h HINTS ${RECASTNAVIGATION_ROOT} PATH_SUFFIXES include RECAST include/recastnavigation)
|
||||
mark_as_advanced(RECAST_INCLUDE_DIR)
|
||||
|
||||
if(RECAST_INCLUDE_DIR AND EXISTS "${RECAST_INCLUDE_DIR}/Recast.h")
|
||||
file(STRINGS "${RECAST_INCLUDE_DIR}/Recast.h" _Recast_h_contents
|
||||
REGEX "#define RECAST_VERSION_[A-Z]+[ ]+[0-9]+")
|
||||
string(REGEX REPLACE "#define RECAST_VERSION_MAJOR[ ]+([0-9]+).+" "\\1"
|
||||
RECAST_VERSION_MAJOR "${_Recast_h_contents}")
|
||||
string(REGEX REPLACE ".+#define RECAST_VERSION_MINOR[ ]+([0-9]+).+" "\\1"
|
||||
RECAST_VERSION_MINOR "${_Recast_h_contents}")
|
||||
string(REGEX REPLACE ".+#define RECAST_VERSION_RELEASE[ ]+([0-9]+).*" "\\1"
|
||||
RECAST_VERSION_RELEASE "${_Recast_h_contents}")
|
||||
set(RECAST_VERSION "${RECAST_VERSION_MAJOR}.${RECAST_VERSION_MINOR}.${RECAST_VERSION_RELEASE}")
|
||||
unset(_Recast_h_contents)
|
||||
endif()
|
||||
|
||||
#TODO: they don't include a version yet
|
||||
set(RECAST_VERSION "1.5.1")
|
||||
|
||||
### Set result variables ######################################################
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(RecastNavigation DEFAULT_MSG
|
||||
RECAST_LIBRARY RECAST_INCLUDE_DIR RECAST_VERSION)
|
||||
|
||||
set(RECAST_LIBRARIES ${RECAST_LIBRARY})
|
||||
set(RECAST_INCLUDE_DIRS ${RECAST_INCLUDE_DIR})
|
||||
|
||||
set(DETOUR_LIBRARIES ${DETOUR_LIBRARY})
|
||||
set(DETOUR_INCLUDE_DIRS ${RECAST_INCLUDE_DIR})
|
||||
|
||||
set(DEBUGUTILS_LIBRARIES ${DEBUGUTILS_LIBRARY})
|
||||
set(DEBUGUTILS_INCLUDE_DIRS ${RECAST_INCLUDE_DIR})
|
||||
|
||||
### Import targets ############################################################
|
||||
if(RecastNavigation_FOUND)
|
||||
if(NOT TARGET RecastNavigation::Recast)
|
||||
add_library(RecastNavigation::Recast UNKNOWN IMPORTED)
|
||||
set_target_properties(RecastNavigation::Recast PROPERTIES
|
||||
IMPORTED_LINK_INTERFACE_LANGUAGES "C"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${RECAST_INCLUDE_DIR}")
|
||||
|
||||
if(RECAST_LIBRARY_RELEASE)
|
||||
set_property(TARGET RecastNavigation::Recast APPEND PROPERTY
|
||||
IMPORTED_CONFIGURATIONS RELEASE)
|
||||
set_target_properties(RecastNavigation::Recast PROPERTIES
|
||||
IMPORTED_LOCATION_RELEASE "${RECAST_LIBRARY_RELEASE}")
|
||||
endif()
|
||||
|
||||
if(RECAST_LIBRARY_DEBUG)
|
||||
set_property(TARGET RecastNavigation::Recast APPEND PROPERTY
|
||||
IMPORTED_CONFIGURATIONS DEBUG)
|
||||
set_target_properties(RecastNavigation::Recast PROPERTIES
|
||||
IMPORTED_LOCATION_DEBUG "${RECAST_LIBRARY_DEBUG}")
|
||||
endif()
|
||||
|
||||
if(NOT RECAST_LIBRARY_RELEASE AND NOT RECAST_LIBRARY_DEBUG)
|
||||
set_property(TARGET RecastNavigation::Recast APPEND PROPERTY
|
||||
IMPORTED_LOCATION "${RECAST_LIBRARY}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT TARGET RecastNavigation::Detour)
|
||||
add_library(RecastNavigation::Detour UNKNOWN IMPORTED)
|
||||
set_target_properties(RecastNavigation::Detour PROPERTIES
|
||||
IMPORTED_LINK_INTERFACE_LANGUAGES "C"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${DETOUR_INCLUDE_DIR}")
|
||||
|
||||
if(DETOUR_LIBRARY_RELEASE)
|
||||
set_property(TARGET RecastNavigation::Detour APPEND PROPERTY
|
||||
IMPORTED_CONFIGURATIONS RELEASE)
|
||||
set_target_properties(RecastNavigation::Detour PROPERTIES
|
||||
IMPORTED_LOCATION_RELEASE "${DETOUR_LIBRARY_RELEASE}")
|
||||
endif()
|
||||
|
||||
if(DETOUR_LIBRARY_DEBUG)
|
||||
set_property(TARGET RecastNavigation::Detour APPEND PROPERTY
|
||||
IMPORTED_CONFIGURATIONS DEBUG)
|
||||
set_target_properties(RecastNavigation::Detour PROPERTIES
|
||||
IMPORTED_LOCATION_DEBUG "${DETOUR_LIBRARY_DEBUG}")
|
||||
endif()
|
||||
|
||||
if(NOT DETOUR_LIBRARY_RELEASE AND NOT DETOUR_LIBRARY_DEBUG)
|
||||
set_property(TARGET RecastNavigation::Detour APPEND PROPERTY
|
||||
IMPORTED_LOCATION "${DETOUR_LIBRARY}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT TARGET RecastNavigation::DebugUtils)
|
||||
add_library(RecastNavigation::DebugUtils UNKNOWN IMPORTED)
|
||||
set_target_properties(RecastNavigation::DebugUtils PROPERTIES
|
||||
IMPORTED_LINK_INTERFACE_LANGUAGES "C"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${DEBUGUTILS_INCLUDE_DIR}")
|
||||
|
||||
if(DEBUGUTILS_LIBRARY_RELEASE)
|
||||
set_property(TARGET RecastNavigation::DebugUtils APPEND PROPERTY
|
||||
IMPORTED_CONFIGURATIONS RELEASE)
|
||||
set_target_properties(RecastNavigation::DebugUtils PROPERTIES
|
||||
IMPORTED_LOCATION_RELEASE "${DEBUGUTILS_LIBRARY_RELEASE}")
|
||||
endif()
|
||||
|
||||
if(DEBUGUTILS_LIBRARY_DEBUG)
|
||||
set_property(TARGET RecastNavigation::DebugUtils APPEND PROPERTY
|
||||
IMPORTED_CONFIGURATIONS DEBUG)
|
||||
set_target_properties(RecastNavigation::DebugUtils PROPERTIES
|
||||
IMPORTED_LOCATION_DEBUG "${DEBUGUTILS_LIBRARY_DEBUG}")
|
||||
endif()
|
||||
|
||||
if(NOT DEBUGUTILS_LIBRARY_RELEASE AND NOT DEBUGUTILS_LIBRARY_DEBUG)
|
||||
set_property(TARGET RecastNavigation::DebugUtils APPEND PROPERTY
|
||||
IMPORTED_LOCATION "${DEBUGUTILS_LIBRARY}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
endif()
|
|
@ -46,7 +46,7 @@ add_component_dir (resource
|
|||
)
|
||||
|
||||
add_component_dir (shader
|
||||
shadermanager shadervisitor
|
||||
shadermanager shadervisitor removedalphafunc
|
||||
)
|
||||
|
||||
add_component_dir (sceneutil
|
||||
|
@ -140,7 +140,7 @@ add_component_dir (fontloader
|
|||
)
|
||||
|
||||
add_component_dir (sdlutil
|
||||
sdlgraphicswindow imagetosurface sdlinputwrapper sdlvideowrapper events sdlcursormanager
|
||||
gl4es_init sdlgraphicswindow imagetosurface sdlinputwrapper sdlvideowrapper events sdlcursormanager
|
||||
)
|
||||
|
||||
add_component_dir (version
|
||||
|
@ -223,20 +223,26 @@ include_directories(${BULLET_INCLUDE_DIRS} ${CMAKE_CURRENT_BINARY_DIR})
|
|||
add_library(components STATIC ${COMPONENT_FILES} ${MOC_SRCS} ${ESM_UI_HDR})
|
||||
|
||||
target_link_libraries(components
|
||||
# CMake's built-in OSG finder does not use pkgconfig, so we have to
|
||||
# manually ensure the order is correct for inter-library dependencies.
|
||||
# This only makes a difference with `-DOPENMW_USE_SYSTEM_OSG=ON -DOSG_STATIC=ON`.
|
||||
# https://gitlab.kitware.com/cmake/cmake/-/issues/21701
|
||||
${OSGPARTICLE_LIBRARIES}
|
||||
${OSGVIEWER_LIBRARIES}
|
||||
${OSGSHADOW_LIBRARIES}
|
||||
${OSGANIMATION_LIBRARIES}
|
||||
${OSGGA_LIBRARIES}
|
||||
${OSGTEXT_LIBRARIES}
|
||||
${OSGDB_LIBRARIES}
|
||||
${OSGUTIL_LIBRARIES}
|
||||
${OSG_LIBRARIES}
|
||||
${OPENTHREADS_LIBRARIES}
|
||||
|
||||
${Boost_SYSTEM_LIBRARY}
|
||||
${Boost_FILESYSTEM_LIBRARY}
|
||||
${Boost_PROGRAM_OPTIONS_LIBRARY}
|
||||
${Boost_IOSTREAMS_LIBRARY}
|
||||
${OSG_LIBRARIES}
|
||||
${OPENTHREADS_LIBRARIES}
|
||||
${OSGPARTICLE_LIBRARIES}
|
||||
${OSGUTIL_LIBRARIES}
|
||||
${OSGDB_LIBRARIES}
|
||||
${OSGVIEWER_LIBRARIES}
|
||||
${OSGTEXT_LIBRARIES}
|
||||
${OSGGA_LIBRARIES}
|
||||
${OSGSHADOW_LIBRARIES}
|
||||
${OSGANIMATION_LIBRARIES}
|
||||
|
||||
${SDL2_LIBRARIES}
|
||||
${OPENGL_gl_LIBRARY}
|
||||
${MyGUI_LIBRARIES}
|
||||
|
|
|
@ -28,12 +28,11 @@
|
|||
#include <boost/filesystem/path.hpp>
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace Bsa;
|
||||
|
||||
|
||||
/// Error handling
|
||||
void BSAFile::fail(const string &msg)
|
||||
void BSAFile::fail(const std::string &msg)
|
||||
{
|
||||
throw std::runtime_error("BSA Error: " + msg + "\nArchive: " + mFilename);
|
||||
}
|
||||
|
@ -160,7 +159,7 @@ int BSAFile::getIndex(const char *str) const
|
|||
}
|
||||
|
||||
/// Open an archive file.
|
||||
void BSAFile::open(const string &file)
|
||||
void BSAFile::open(const std::string &file)
|
||||
{
|
||||
mFilename = file;
|
||||
readHeader();
|
||||
|
@ -171,7 +170,7 @@ Files::IStreamPtr BSAFile::getFile(const char *file)
|
|||
assert(file);
|
||||
int i = getIndex(file);
|
||||
if(i == -1)
|
||||
fail("File not found: " + string(file));
|
||||
fail("File not found: " + std::string(file));
|
||||
|
||||
const FileStruct &fs = mFiles[i];
|
||||
|
||||
|
|
|
@ -32,133 +32,246 @@ either expressed or implied, of the FreeBSD Project.
|
|||
#include "gldebug.hpp"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
|
||||
#include <components/debug/debuglog.hpp>
|
||||
|
||||
// OpenGL constants not provided by OSG:
|
||||
#include <SDL_opengl_glext.h>
|
||||
|
||||
void debugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *userParam)
|
||||
namespace Debug
|
||||
{
|
||||
|
||||
void debugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *userParam)
|
||||
{
|
||||
#ifdef GL_DEBUG_OUTPUT
|
||||
std::string srcStr;
|
||||
switch (source)
|
||||
{
|
||||
case GL_DEBUG_SOURCE_API:
|
||||
srcStr = "API";
|
||||
break;
|
||||
case GL_DEBUG_SOURCE_WINDOW_SYSTEM:
|
||||
srcStr = "WINDOW_SYSTEM";
|
||||
break;
|
||||
case GL_DEBUG_SOURCE_SHADER_COMPILER:
|
||||
srcStr = "SHADER_COMPILER";
|
||||
break;
|
||||
case GL_DEBUG_SOURCE_THIRD_PARTY:
|
||||
srcStr = "THIRD_PARTY";
|
||||
break;
|
||||
case GL_DEBUG_SOURCE_APPLICATION:
|
||||
srcStr = "APPLICATION";
|
||||
break;
|
||||
case GL_DEBUG_SOURCE_OTHER:
|
||||
srcStr = "OTHER";
|
||||
break;
|
||||
default:
|
||||
srcStr = "UNDEFINED";
|
||||
break;
|
||||
}
|
||||
std::string srcStr;
|
||||
switch (source)
|
||||
{
|
||||
case GL_DEBUG_SOURCE_API:
|
||||
srcStr = "API";
|
||||
break;
|
||||
case GL_DEBUG_SOURCE_WINDOW_SYSTEM:
|
||||
srcStr = "WINDOW_SYSTEM";
|
||||
break;
|
||||
case GL_DEBUG_SOURCE_SHADER_COMPILER:
|
||||
srcStr = "SHADER_COMPILER";
|
||||
break;
|
||||
case GL_DEBUG_SOURCE_THIRD_PARTY:
|
||||
srcStr = "THIRD_PARTY";
|
||||
break;
|
||||
case GL_DEBUG_SOURCE_APPLICATION:
|
||||
srcStr = "APPLICATION";
|
||||
break;
|
||||
case GL_DEBUG_SOURCE_OTHER:
|
||||
srcStr = "OTHER";
|
||||
break;
|
||||
default:
|
||||
srcStr = "UNDEFINED";
|
||||
break;
|
||||
}
|
||||
|
||||
std::string typeStr;
|
||||
std::string typeStr;
|
||||
|
||||
Debug::Level logSeverity = Debug::Warning;
|
||||
switch (type)
|
||||
{
|
||||
case GL_DEBUG_TYPE_ERROR:
|
||||
typeStr = "ERROR";
|
||||
logSeverity = Debug::Error;
|
||||
break;
|
||||
case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
|
||||
typeStr = "DEPRECATED_BEHAVIOR";
|
||||
break;
|
||||
case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
|
||||
typeStr = "UNDEFINED_BEHAVIOR";
|
||||
break;
|
||||
case GL_DEBUG_TYPE_PORTABILITY:
|
||||
typeStr = "PORTABILITY";
|
||||
break;
|
||||
case GL_DEBUG_TYPE_PERFORMANCE:
|
||||
typeStr = "PERFORMANCE";
|
||||
break;
|
||||
case GL_DEBUG_TYPE_OTHER:
|
||||
typeStr = "OTHER";
|
||||
break;
|
||||
default:
|
||||
typeStr = "UNDEFINED";
|
||||
break;
|
||||
}
|
||||
Level logSeverity = Warning;
|
||||
switch (type)
|
||||
{
|
||||
case GL_DEBUG_TYPE_ERROR:
|
||||
typeStr = "ERROR";
|
||||
logSeverity = Error;
|
||||
break;
|
||||
case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
|
||||
typeStr = "DEPRECATED_BEHAVIOR";
|
||||
break;
|
||||
case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
|
||||
typeStr = "UNDEFINED_BEHAVIOR";
|
||||
break;
|
||||
case GL_DEBUG_TYPE_PORTABILITY:
|
||||
typeStr = "PORTABILITY";
|
||||
break;
|
||||
case GL_DEBUG_TYPE_PERFORMANCE:
|
||||
typeStr = "PERFORMANCE";
|
||||
break;
|
||||
case GL_DEBUG_TYPE_OTHER:
|
||||
typeStr = "OTHER";
|
||||
break;
|
||||
default:
|
||||
typeStr = "UNDEFINED";
|
||||
break;
|
||||
}
|
||||
|
||||
Log(logSeverity) << "OpenGL " << typeStr << " [" << srcStr << "]: " << message;
|
||||
Log(logSeverity) << "OpenGL " << typeStr << " [" << srcStr << "]: " << message;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void enableGLDebugExtension(unsigned int contextID)
|
||||
{
|
||||
class PushDebugGroup
|
||||
{
|
||||
public:
|
||||
static std::unique_ptr<PushDebugGroup> sInstance;
|
||||
|
||||
void (GL_APIENTRY * glPushDebugGroup) (GLenum source, GLuint id, GLsizei length, const GLchar * message);
|
||||
|
||||
void (GL_APIENTRY * glPopDebugGroup) (void);
|
||||
|
||||
bool valid()
|
||||
{
|
||||
return glPushDebugGroup && glPopDebugGroup;
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<PushDebugGroup> PushDebugGroup::sInstance{ std::make_unique<PushDebugGroup>() };
|
||||
|
||||
EnableGLDebugOperation::EnableGLDebugOperation() : osg::GraphicsOperation("EnableGLDebugOperation", false)
|
||||
{
|
||||
}
|
||||
|
||||
void EnableGLDebugOperation::operator()(osg::GraphicsContext* graphicsContext)
|
||||
{
|
||||
#ifdef GL_DEBUG_OUTPUT
|
||||
typedef void (GL_APIENTRY *DEBUGPROC)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *userParam);
|
||||
typedef void (GL_APIENTRY *GLDebugMessageControlFunction)(GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled);
|
||||
typedef void (GL_APIENTRY *GLDebugMessageCallbackFunction)(DEBUGPROC, const void* userParam);
|
||||
|
||||
GLDebugMessageControlFunction glDebugMessageControl = nullptr;
|
||||
GLDebugMessageCallbackFunction glDebugMessageCallback = nullptr;
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mMutex);
|
||||
|
||||
if (osg::isGLExtensionSupported(contextID, "GL_KHR_debug"))
|
||||
{
|
||||
osg::setGLExtensionFuncPtr(glDebugMessageCallback, "glDebugMessageCallback");
|
||||
osg::setGLExtensionFuncPtr(glDebugMessageControl, "glDebugMessageControl");
|
||||
}
|
||||
else if (osg::isGLExtensionSupported(contextID, "GL_ARB_debug_output"))
|
||||
{
|
||||
osg::setGLExtensionFuncPtr(glDebugMessageCallback, "glDebugMessageCallbackARB");
|
||||
osg::setGLExtensionFuncPtr(glDebugMessageControl, "glDebugMessageControlARB");
|
||||
}
|
||||
else if (osg::isGLExtensionSupported(contextID, "GL_AMD_debug_output"))
|
||||
{
|
||||
osg::setGLExtensionFuncPtr(glDebugMessageCallback, "glDebugMessageCallbackAMD");
|
||||
osg::setGLExtensionFuncPtr(glDebugMessageControl, "glDebugMessageControlAMD");
|
||||
}
|
||||
unsigned int contextID = graphicsContext->getState()->getContextID();
|
||||
|
||||
if (glDebugMessageCallback && glDebugMessageControl)
|
||||
{
|
||||
glEnable(GL_DEBUG_OUTPUT);
|
||||
glDebugMessageControl(GL_DONT_CARE, GL_DEBUG_TYPE_ERROR, GL_DEBUG_SEVERITY_MEDIUM, 0, nullptr, true);
|
||||
glDebugMessageCallback(debugCallback, nullptr);
|
||||
typedef void (GL_APIENTRY *DEBUGPROC)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *userParam);
|
||||
typedef void (GL_APIENTRY *GLDebugMessageControlFunction)(GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled);
|
||||
typedef void (GL_APIENTRY *GLDebugMessageCallbackFunction)(DEBUGPROC, const void* userParam);
|
||||
|
||||
Log(Debug::Info) << "OpenGL debug callback attached.";
|
||||
}
|
||||
else
|
||||
GLDebugMessageControlFunction glDebugMessageControl = nullptr;
|
||||
GLDebugMessageCallbackFunction glDebugMessageCallback = nullptr;
|
||||
|
||||
if (osg::isGLExtensionSupported(contextID, "GL_KHR_debug"))
|
||||
{
|
||||
osg::setGLExtensionFuncPtr(glDebugMessageCallback, "glDebugMessageCallback");
|
||||
osg::setGLExtensionFuncPtr(glDebugMessageControl, "glDebugMessageControl");
|
||||
osg::setGLExtensionFuncPtr(PushDebugGroup::sInstance->glPushDebugGroup, "glPushDebugGroup");
|
||||
osg::setGLExtensionFuncPtr(PushDebugGroup::sInstance->glPopDebugGroup, "glPopDebugGroup");
|
||||
}
|
||||
else if (osg::isGLExtensionSupported(contextID, "GL_ARB_debug_output"))
|
||||
{
|
||||
osg::setGLExtensionFuncPtr(glDebugMessageCallback, "glDebugMessageCallbackARB");
|
||||
osg::setGLExtensionFuncPtr(glDebugMessageControl, "glDebugMessageControlARB");
|
||||
}
|
||||
else if (osg::isGLExtensionSupported(contextID, "GL_AMD_debug_output"))
|
||||
{
|
||||
osg::setGLExtensionFuncPtr(glDebugMessageCallback, "glDebugMessageCallbackAMD");
|
||||
osg::setGLExtensionFuncPtr(glDebugMessageControl, "glDebugMessageControlAMD");
|
||||
}
|
||||
|
||||
if (glDebugMessageCallback && glDebugMessageControl)
|
||||
{
|
||||
glEnable(GL_DEBUG_OUTPUT);
|
||||
glDebugMessageControl(GL_DONT_CARE, GL_DEBUG_TYPE_ERROR, GL_DEBUG_SEVERITY_MEDIUM, 0, nullptr, true);
|
||||
glDebugMessageCallback(debugCallback, nullptr);
|
||||
|
||||
Log(Info) << "OpenGL debug callback attached.";
|
||||
}
|
||||
else
|
||||
#endif
|
||||
Log(Debug::Error) << "Unable to attach OpenGL debug callback.";
|
||||
}
|
||||
|
||||
Debug::EnableGLDebugOperation::EnableGLDebugOperation() : osg::GraphicsOperation("EnableGLDebugOperation", false)
|
||||
{
|
||||
}
|
||||
|
||||
void Debug::EnableGLDebugOperation::operator()(osg::GraphicsContext* graphicsContext)
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mMutex);
|
||||
|
||||
unsigned int contextID = graphicsContext->getState()->getContextID();
|
||||
enableGLDebugExtension(contextID);
|
||||
}
|
||||
|
||||
bool Debug::shouldDebugOpenGL()
|
||||
{
|
||||
const char* env = std::getenv("OPENMW_DEBUG_OPENGL");
|
||||
if (!env)
|
||||
return false;
|
||||
std::string str(env);
|
||||
if (str.length() == 0)
|
||||
return true;
|
||||
|
||||
return str.find("OFF") == std::string::npos && str.find("0") == std::string::npos && str.find("NO") == std::string::npos;
|
||||
Log(Error) << "Unable to attach OpenGL debug callback.";
|
||||
}
|
||||
|
||||
bool shouldDebugOpenGL()
|
||||
{
|
||||
const char* env = std::getenv("OPENMW_DEBUG_OPENGL");
|
||||
if (!env)
|
||||
return false;
|
||||
std::string str(env);
|
||||
if (str.length() == 0)
|
||||
return true;
|
||||
|
||||
return str.find("OFF") == std::string::npos && str.find("0") == std::string::npos && str.find("NO") == std::string::npos;
|
||||
}
|
||||
|
||||
DebugGroup::DebugGroup(const std::string & message, GLuint id)
|
||||
#ifdef GL_DEBUG_OUTPUT
|
||||
: mSource(GL_DEBUG_SOURCE_APPLICATION)
|
||||
#else
|
||||
: mSource(0x824A)
|
||||
#endif
|
||||
, mId(id)
|
||||
, mMessage(message)
|
||||
{
|
||||
}
|
||||
|
||||
DebugGroup::DebugGroup(const DebugGroup & debugGroup, const osg::CopyOp & copyop)
|
||||
: osg::StateAttribute(debugGroup, copyop)
|
||||
, mSource(debugGroup.mSource)
|
||||
, mId(debugGroup.mId)
|
||||
, mMessage(debugGroup.mMessage)
|
||||
{
|
||||
}
|
||||
|
||||
void DebugGroup::apply(osg::State & state) const
|
||||
{
|
||||
if (!PushDebugGroup::sInstance->valid())
|
||||
{
|
||||
Log(Error) << "OpenGL debug groups not supported on this system, or OPENMW_DEBUG_OPENGL environment variable not set.";
|
||||
return;
|
||||
}
|
||||
|
||||
auto& attributeVec = state.getAttributeVec(this);
|
||||
auto& lastAppliedStack = sLastAppliedStack[state.getContextID()];
|
||||
|
||||
size_t firstNonMatch = 0;
|
||||
while (firstNonMatch < lastAppliedStack.size()
|
||||
&& ((firstNonMatch < attributeVec.size() && lastAppliedStack[firstNonMatch] == attributeVec[firstNonMatch].first)
|
||||
|| lastAppliedStack[firstNonMatch] == this))
|
||||
firstNonMatch++;
|
||||
|
||||
for (size_t i = lastAppliedStack.size(); i > firstNonMatch; --i)
|
||||
lastAppliedStack[i - 1]->pop(state);
|
||||
lastAppliedStack.resize(firstNonMatch);
|
||||
|
||||
lastAppliedStack.reserve(attributeVec.size());
|
||||
for (size_t i = firstNonMatch; i < attributeVec.size(); ++i)
|
||||
{
|
||||
const DebugGroup* group = static_cast<const DebugGroup*>(attributeVec[i].first);
|
||||
group->push(state);
|
||||
lastAppliedStack.push_back(group);
|
||||
}
|
||||
if (!(lastAppliedStack.back() == this))
|
||||
{
|
||||
push(state);
|
||||
lastAppliedStack.push_back(this);
|
||||
}
|
||||
}
|
||||
|
||||
int DebugGroup::compare(const StateAttribute & sa) const
|
||||
{
|
||||
COMPARE_StateAttribute_Types(DebugGroup, sa);
|
||||
|
||||
COMPARE_StateAttribute_Parameter(mSource);
|
||||
COMPARE_StateAttribute_Parameter(mId);
|
||||
COMPARE_StateAttribute_Parameter(mMessage);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DebugGroup::releaseGLObjects(osg::State * state) const
|
||||
{
|
||||
if (state)
|
||||
sLastAppliedStack.erase(state->getContextID());
|
||||
else
|
||||
sLastAppliedStack.clear();
|
||||
}
|
||||
|
||||
bool DebugGroup::isValid() const
|
||||
{
|
||||
return mSource || mId || mMessage.length();
|
||||
}
|
||||
|
||||
void DebugGroup::push(osg::State & state) const
|
||||
{
|
||||
if (isValid())
|
||||
PushDebugGroup::sInstance->glPushDebugGroup(mSource, mId, mMessage.size(), mMessage.c_str());
|
||||
}
|
||||
|
||||
void DebugGroup::pop(osg::State & state) const
|
||||
{
|
||||
if (isValid())
|
||||
PushDebugGroup::sInstance->glPopDebugGroup();
|
||||
}
|
||||
|
||||
std::map<unsigned int, std::vector<const DebugGroup *>> DebugGroup::sLastAppliedStack{};
|
||||
|
||||
}
|
||||
|
|
|
@ -17,5 +17,65 @@ namespace Debug
|
|||
};
|
||||
|
||||
bool shouldDebugOpenGL();
|
||||
|
||||
|
||||
/*
|
||||
Debug groups allow rendering to be annotated, making debugging via APITrace/CodeXL/NSight etc. much clearer.
|
||||
|
||||
Because I've not thought of a quick and clean way of doing it without incurring a small performance cost,
|
||||
there are no uses of this class checked in. For now, add annotations locally when you need them.
|
||||
|
||||
To use this class, add it to a StateSet just like any other StateAttribute. Prefer the string-only constructor.
|
||||
You'll need OPENMW_DEBUG_OPENGL set to true, or shouldDebugOpenGL() redefined to just return true as otherwise
|
||||
the extension function pointers won't get set up. That can maybe be cleaned up in the future.
|
||||
|
||||
Beware that consecutive identical debug groups (i.e. pointers match) won't always get applied due to OSG thinking
|
||||
it's already applied them. Either avoid nesting the same object, add dummy groups so they're not consecutive, or
|
||||
ensure the leaf group isn't identical to its parent.
|
||||
*/
|
||||
class DebugGroup : public osg::StateAttribute
|
||||
{
|
||||
public:
|
||||
DebugGroup()
|
||||
: mSource(0)
|
||||
, mId(0)
|
||||
, mMessage("")
|
||||
{}
|
||||
|
||||
DebugGroup(GLenum source, GLuint id, const std::string& message)
|
||||
: mSource(source)
|
||||
, mId(id)
|
||||
, mMessage(message)
|
||||
{}
|
||||
|
||||
DebugGroup(const std::string& message, GLuint id = 0);
|
||||
|
||||
DebugGroup(const DebugGroup& debugGroup, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY);
|
||||
|
||||
META_StateAttribute(Debug, DebugGroup, osg::StateAttribute::Type(101));
|
||||
|
||||
void apply(osg::State& state) const override;
|
||||
|
||||
int compare(const StateAttribute& sa) const override;
|
||||
|
||||
void releaseGLObjects(osg::State* state = nullptr) const override;
|
||||
|
||||
virtual bool isValid() const;
|
||||
|
||||
protected:
|
||||
virtual ~DebugGroup() = default;
|
||||
|
||||
virtual void push(osg::State& state) const;
|
||||
|
||||
virtual void pop(osg::State& state) const;
|
||||
|
||||
GLenum mSource;
|
||||
GLuint mId;
|
||||
std::string mMessage;
|
||||
|
||||
static std::map<unsigned int, std::vector<const DebugGroup *>> sLastAppliedStack;
|
||||
|
||||
friend EnableGLDebugOperation;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -235,6 +235,8 @@ namespace DetourNavigator
|
|||
const osg::Vec3f& end, const Flags includeFlags) const;
|
||||
|
||||
virtual RecastMeshTiles getRecastMeshTiles() = 0;
|
||||
|
||||
virtual float getMaxNavmeshAreaRealRadius() const = 0;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,8 @@ namespace DetourNavigator
|
|||
|
||||
void NavigatorImpl::addAgent(const osg::Vec3f& agentHalfExtents)
|
||||
{
|
||||
if(agentHalfExtents.length2() <= 0)
|
||||
return;
|
||||
++mAgents[agentHalfExtents];
|
||||
mNavMeshManager.addAgent(agentHalfExtents);
|
||||
}
|
||||
|
@ -211,4 +213,10 @@ namespace DetourNavigator
|
|||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
float NavigatorImpl::getMaxNavmeshAreaRealRadius() const
|
||||
{
|
||||
const auto& settings = getSettings();
|
||||
return getRealTileSize(settings) * getMaxNavmeshAreaRadius(settings);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,6 +60,8 @@ namespace DetourNavigator
|
|||
|
||||
RecastMeshTiles getRecastMeshTiles() override;
|
||||
|
||||
float getMaxNavmeshAreaRealRadius() const override;
|
||||
|
||||
private:
|
||||
Settings mSettings;
|
||||
NavMeshManager mNavMeshManager;
|
||||
|
|
|
@ -92,6 +92,11 @@ namespace DetourNavigator
|
|||
return {};
|
||||
}
|
||||
|
||||
float getMaxNavmeshAreaRealRadius() const override
|
||||
{
|
||||
return std::numeric_limits<float>::max();
|
||||
}
|
||||
|
||||
private:
|
||||
Settings mDefaultSettings {};
|
||||
SharedNavMeshCacheItem mEmptyNavMeshCacheItem;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_SETTINGSUTILS_H
|
||||
#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_SETTINGSUTILS_H
|
||||
#define OPENMW_COMPONENTS_DETOURNAVIGATOR_SETTINGSUTILS_H
|
||||
|
||||
#include "settings.hpp"
|
||||
|
@ -89,6 +89,16 @@ namespace DetourNavigator
|
|||
transform.getOrigin() + btVector3(0, 0, getSwimLevel(settings, agentHalfExtentsZ) - agentHalfExtentsZ)
|
||||
);
|
||||
}
|
||||
|
||||
inline float getRealTileSize(const Settings& settings)
|
||||
{
|
||||
return settings.mTileSize * settings.mCellSize / settings.mRecastScaleFactor;
|
||||
}
|
||||
|
||||
inline float getMaxNavmeshAreaRadius(const Settings& settings)
|
||||
{
|
||||
return std::floor(std::sqrt(settings.mMaxTilesNumber / osg::PI)) - 1;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -357,16 +357,14 @@ std::string ESMReader::getString(int size)
|
|||
|
||||
void ESMReader::fail(const std::string &msg)
|
||||
{
|
||||
using namespace std;
|
||||
|
||||
stringstream ss;
|
||||
std::stringstream ss;
|
||||
|
||||
ss << "ESM Error: " << msg;
|
||||
ss << "\n File: " << mCtx.filename;
|
||||
ss << "\n Record: " << mCtx.recName.toString();
|
||||
ss << "\n Subrecord: " << mCtx.subName.toString();
|
||||
if (mEsm.get())
|
||||
ss << "\n Offset: 0x" << hex << mEsm->tellg();
|
||||
ss << "\n Offset: 0x" << std::hex << mEsm->tellg();
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
|
||||
|
|
|
@ -62,10 +62,31 @@ ESM::Variant& ESM::Variant::operator= (const Variant& variant)
|
|||
return *this;
|
||||
}
|
||||
|
||||
ESM::Variant& ESM::Variant::operator= (Variant&& variant)
|
||||
{
|
||||
if (&variant!=this)
|
||||
{
|
||||
delete mData;
|
||||
|
||||
mType = variant.mType;
|
||||
mData = variant.mData;
|
||||
|
||||
variant.mData = nullptr;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
ESM::Variant::Variant (const Variant& variant)
|
||||
: mType (variant.mType), mData (variant.mData ? variant.mData->clone() : nullptr)
|
||||
{}
|
||||
|
||||
ESM::Variant::Variant(Variant&& variant)
|
||||
: mType (variant.mType), mData (variant.mData)
|
||||
{
|
||||
variant.mData = nullptr;
|
||||
}
|
||||
|
||||
ESM::VarType ESM::Variant::getType() const
|
||||
{
|
||||
return mType;
|
||||
|
|
|
@ -46,8 +46,10 @@ namespace ESM
|
|||
~Variant();
|
||||
|
||||
Variant& operator= (const Variant& variant);
|
||||
Variant& operator= (Variant && variant);
|
||||
|
||||
Variant (const Variant& variant);
|
||||
Variant (Variant&& variant);
|
||||
|
||||
VarType getType() const;
|
||||
|
||||
|
|
42
components/misc/budgetmeasurement.hpp
Normal file
42
components/misc/budgetmeasurement.hpp
Normal file
|
@ -0,0 +1,42 @@
|
|||
#ifndef OPENMW_COMPONENTS_MISC_BUDGETMEASUREMENT_H
|
||||
#define OPENMW_COMPONENTS_MISC_BUDGETMEASUREMENT_H
|
||||
|
||||
|
||||
namespace Misc
|
||||
{
|
||||
|
||||
class BudgetMeasurement
|
||||
{
|
||||
std::array<float, 4> mBudgetHistory;
|
||||
std::array<unsigned int, 4> mBudgetStepCount;
|
||||
|
||||
public:
|
||||
BudgetMeasurement(const float default_expense)
|
||||
{
|
||||
mBudgetHistory = {default_expense, default_expense, default_expense, default_expense};
|
||||
mBudgetStepCount = {1, 1, 1, 1};
|
||||
}
|
||||
|
||||
void reset(const float default_expense)
|
||||
{
|
||||
mBudgetHistory = {default_expense, default_expense, default_expense, default_expense};
|
||||
mBudgetStepCount = {1, 1, 1, 1};
|
||||
}
|
||||
|
||||
void update(double delta, unsigned int stepCount, size_t cursor)
|
||||
{
|
||||
mBudgetHistory[cursor%4] = delta;
|
||||
mBudgetStepCount[cursor%4] = stepCount;
|
||||
}
|
||||
|
||||
double get() const
|
||||
{
|
||||
float sum = (mBudgetHistory[0] + mBudgetHistory[1] + mBudgetHistory[2] + mBudgetHistory[3]);
|
||||
unsigned int stepCountSum = (mBudgetStepCount[0] + mBudgetStepCount[1] + mBudgetStepCount[2] + mBudgetStepCount[3]);
|
||||
return sum/float(stepCountSum);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -33,6 +33,9 @@ const std::string NightDayLabel = "NightDaySwitch";
|
|||
// A label to mark visual switches for herbalism feature
|
||||
const std::string HerbalismLabel = "HerbalismSwitch";
|
||||
|
||||
// Percentage height at which projectiles are spawned from an actor
|
||||
const float TorsoHeight = 0.75f;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -47,4 +47,9 @@ namespace Misc
|
|||
{
|
||||
return static_cast<unsigned int>(std::chrono::high_resolution_clock::now().time_since_epoch().count());
|
||||
}
|
||||
|
||||
float Rng::deviate(float mean, float deviation, Seed& seed)
|
||||
{
|
||||
return std::uniform_real_distribution<float>(mean - deviation, mean + deviation)(seed.mGenerator);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,6 +42,8 @@ public:
|
|||
|
||||
/// returns default seed for RNG
|
||||
static unsigned int generateDefaultSeed();
|
||||
|
||||
static float deviate(float mean, float deviation, Seed& seed = getSeed());
|
||||
};
|
||||
|
||||
}
|
||||
|
|
42
components/misc/timer.hpp
Normal file
42
components/misc/timer.hpp
Normal file
|
@ -0,0 +1,42 @@
|
|||
#ifndef OPENMW_COMPONENTS_MISC_TIMER_H
|
||||
#define OPENMW_COMPONENTS_MISC_TIMER_H
|
||||
|
||||
#include "rng.hpp"
|
||||
|
||||
namespace Misc
|
||||
{
|
||||
enum class TimerStatus
|
||||
{
|
||||
Waiting,
|
||||
Elapsed,
|
||||
};
|
||||
|
||||
class DeviatingPeriodicTimer
|
||||
{
|
||||
public:
|
||||
explicit DeviatingPeriodicTimer(float period, float deviation, float timeLeft)
|
||||
: mPeriod(period), mDeviation(deviation), mTimeLeft(timeLeft)
|
||||
{}
|
||||
|
||||
TimerStatus update(float duration)
|
||||
{
|
||||
if (mTimeLeft > 0)
|
||||
{
|
||||
mTimeLeft -= duration;
|
||||
return TimerStatus::Waiting;
|
||||
}
|
||||
|
||||
mTimeLeft = Rng::deviate(mPeriod, mDeviation);
|
||||
return TimerStatus::Elapsed;
|
||||
}
|
||||
|
||||
void reset(float timeLeft) { mTimeLeft = timeLeft; }
|
||||
|
||||
private:
|
||||
const float mPeriod;
|
||||
const float mDeviation;
|
||||
float mTimeLeft;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
12
components/myguiplatform/myguicompat.h
Normal file
12
components/myguiplatform/myguicompat.h
Normal file
|
@ -0,0 +1,12 @@
|
|||
#ifndef OPENMW_COMPONENTS_MYGUIPLATFORM_MYGUICOMPAT_H
|
||||
#define OPENMW_COMPONENTS_MYGUIPLATFORM_MYGUICOMPAT_H
|
||||
|
||||
#include <MyGUI_Prerequest.h>
|
||||
|
||||
#if MYGUI_VERSION > MYGUI_DEFINE_VERSION(3, 4, 0)
|
||||
#define OPENMW_MYGUI_CONST_GETTER_3_4_1 const
|
||||
#else
|
||||
#define OPENMW_MYGUI_CONST_GETTER_3_4_1
|
||||
#endif
|
||||
|
||||
#endif // OPENMW_COMPONENTS_MYGUIPLATFORM_MYGUICOMPAT_H
|
|
@ -15,7 +15,7 @@ void DataManager::setResourcePath(const std::string &path)
|
|||
mResourcePath = path;
|
||||
}
|
||||
|
||||
MyGUI::IDataStream *DataManager::getData(const std::string &name)
|
||||
MyGUI::IDataStream *DataManager::getData(const std::string &name) OPENMW_MYGUI_CONST_GETTER_3_4_1
|
||||
{
|
||||
std::string fullpath = getDataPath(name);
|
||||
std::unique_ptr<boost::filesystem::ifstream> stream;
|
||||
|
@ -34,13 +34,13 @@ void DataManager::freeData(MyGUI::IDataStream *data)
|
|||
delete data;
|
||||
}
|
||||
|
||||
bool DataManager::isDataExist(const std::string &name)
|
||||
bool DataManager::isDataExist(const std::string &name) OPENMW_MYGUI_CONST_GETTER_3_4_1
|
||||
{
|
||||
std::string fullpath = mResourcePath + "/" + name;
|
||||
return boost::filesystem::exists(fullpath);
|
||||
}
|
||||
|
||||
const MyGUI::VectorString &DataManager::getDataListNames(const std::string &pattern)
|
||||
const MyGUI::VectorString &DataManager::getDataListNames(const std::string &pattern) OPENMW_MYGUI_CONST_GETTER_3_4_1
|
||||
{
|
||||
// TODO: pattern matching (unused?)
|
||||
static MyGUI::VectorString strings;
|
||||
|
@ -49,7 +49,7 @@ const MyGUI::VectorString &DataManager::getDataListNames(const std::string &patt
|
|||
return strings;
|
||||
}
|
||||
|
||||
const std::string &DataManager::getDataPath(const std::string &name)
|
||||
const std::string &DataManager::getDataPath(const std::string &name) OPENMW_MYGUI_CONST_GETTER_3_4_1
|
||||
{
|
||||
static std::string result;
|
||||
result.clear();
|
||||
|
|
|
@ -3,10 +3,11 @@
|
|||
|
||||
#include <MyGUI_DataManager.h>
|
||||
|
||||
#include "myguicompat.h"
|
||||
|
||||
namespace osgMyGUI
|
||||
{
|
||||
|
||||
|
||||
class DataManager : public MyGUI::DataManager
|
||||
{
|
||||
public:
|
||||
|
@ -18,7 +19,7 @@ public:
|
|||
/** Get data stream from specified resource name.
|
||||
@param _name Resource name (usually file name).
|
||||
*/
|
||||
MyGUI::IDataStream* getData(const std::string& _name) override;
|
||||
MyGUI::IDataStream* getData(const std::string& _name) OPENMW_MYGUI_CONST_GETTER_3_4_1 override;
|
||||
|
||||
/** Free data stream.
|
||||
@param _data Data stream.
|
||||
|
@ -28,18 +29,18 @@ public:
|
|||
/** Is data with specified name exist.
|
||||
@param _name Resource name.
|
||||
*/
|
||||
bool isDataExist(const std::string& _name) override;
|
||||
bool isDataExist(const std::string& _name) OPENMW_MYGUI_CONST_GETTER_3_4_1 override;
|
||||
|
||||
/** Get all data names with names that matches pattern.
|
||||
@param _pattern Pattern to match (for example "*.layout").
|
||||
*/
|
||||
const MyGUI::VectorString& getDataListNames(const std::string& _pattern) override;
|
||||
const MyGUI::VectorString& getDataListNames(const std::string& _pattern) OPENMW_MYGUI_CONST_GETTER_3_4_1 override;
|
||||
|
||||
/** Get full path to data.
|
||||
@param _name Resource name.
|
||||
@return Return full path to specified data.
|
||||
*/
|
||||
const std::string& getDataPath(const std::string& _name) override;
|
||||
const std::string& getDataPath(const std::string& _name) OPENMW_MYGUI_CONST_GETTER_3_4_1 override;
|
||||
|
||||
private:
|
||||
std::string mResourcePath;
|
||||
|
|
|
@ -2,11 +2,15 @@
|
|||
|
||||
#include <iomanip>
|
||||
|
||||
#include <components/debug/debuglog.hpp>
|
||||
|
||||
namespace osgMyGUI
|
||||
{
|
||||
void CustomLogListener::open()
|
||||
{
|
||||
mStream.open(boost::filesystem::path(mFileName), std::ios_base::out);
|
||||
if (!mStream.is_open())
|
||||
Log(Debug::Error) << "Unable to create MyGUI log with path " << mFileName;
|
||||
}
|
||||
|
||||
void CustomLogListener::close()
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <osg/BlendFunc>
|
||||
#include <osg/Texture2D>
|
||||
#include <osg/TexMat>
|
||||
#include <osg/ValueObject>
|
||||
|
||||
#include <osgViewer/Viewer>
|
||||
|
||||
|
@ -19,6 +20,9 @@
|
|||
|
||||
#include <components/resource/imagemanager.hpp>
|
||||
|
||||
#include <components/debug/debuglog.hpp>
|
||||
|
||||
#include "myguicompat.h"
|
||||
#include "myguitexture.hpp"
|
||||
|
||||
#define MYGUI_PLATFORM_LOG_SECTION "Platform"
|
||||
|
@ -274,7 +278,7 @@ public:
|
|||
osg::VertexBufferObject* getVertexBuffer();
|
||||
|
||||
void setVertexCount(size_t count) override;
|
||||
size_t getVertexCount() override;
|
||||
size_t getVertexCount() OPENMW_MYGUI_CONST_GETTER_3_4_1 override;
|
||||
|
||||
MyGUI::Vertex *lock() override;
|
||||
void unlock() override;
|
||||
|
@ -301,7 +305,7 @@ void OSGVertexBuffer::setVertexCount(size_t count)
|
|||
mNeedVertexCount = count;
|
||||
}
|
||||
|
||||
size_t OSGVertexBuffer::getVertexCount()
|
||||
size_t OSGVertexBuffer::getVertexCount() OPENMW_MYGUI_CONST_GETTER_3_4_1
|
||||
{
|
||||
return mNeedVertexCount;
|
||||
}
|
||||
|
@ -502,15 +506,24 @@ void GUICamera::doRender(MyGUI::IVertexBuffer *buffer, MyGUI::ITexture *texture,
|
|||
batch.mVertexBuffer = static_cast<OSGVertexBuffer*>(buffer)->getVertexBuffer();
|
||||
batch.mArray = static_cast<OSGVertexBuffer*>(buffer)->getVertexArray();
|
||||
static_cast<OSGVertexBuffer*>(buffer)->markUsed();
|
||||
bool premultipliedAlpha = false;
|
||||
if (texture)
|
||||
{
|
||||
batch.mTexture = static_cast<OSGTexture*>(texture)->getTexture();
|
||||
if (batch.mTexture->getDataVariance() == osg::Object::DYNAMIC)
|
||||
mDrawable->setDataVariance(osg::Object::DYNAMIC); // only for this frame, reset in begin()
|
||||
batch.mTexture->getUserValue("premultiplied alpha", premultipliedAlpha);
|
||||
}
|
||||
|
||||
if (mInjectState)
|
||||
batch.mStateSet = mInjectState;
|
||||
else if (premultipliedAlpha)
|
||||
{
|
||||
// This is hacky, but MyGUI made it impossible to use a custom layer for a nested node, so state couldn't be injected 'properly'
|
||||
osg::ref_ptr<osg::StateSet> stateSet = new osg::StateSet();
|
||||
stateSet->setAttribute(new osg::BlendFunc(osg::BlendFunc::ONE, osg::BlendFunc::ONE_MINUS_SRC_ALPHA));
|
||||
batch.mStateSet = stateSet;
|
||||
}
|
||||
|
||||
mDrawable->addBatch(batch);
|
||||
}
|
||||
|
@ -676,4 +689,14 @@ bool RenderManager::checkTexture(MyGUI::ITexture* _texture)
|
|||
return true;
|
||||
}
|
||||
|
||||
#if MYGUI_VERSION > MYGUI_DEFINE_VERSION(3, 4, 0)
|
||||
void RenderManager::registerShader(
|
||||
const std::string& _shaderName,
|
||||
const std::string& _vertexProgramFile,
|
||||
const std::string& _fragmentProgramFile)
|
||||
{
|
||||
MYGUI_PLATFORM_LOG(Warning, "osgMyGUI::RenderManager::registerShader is not implemented");
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
#include <osg/ref_ptr>
|
||||
#include <set>
|
||||
|
||||
#include "myguicompat.h"
|
||||
|
||||
namespace Resource
|
||||
{
|
||||
class ImageManager;
|
||||
|
@ -85,7 +87,8 @@ public:
|
|||
const MyGUI::IntSize& getViewSize() const override { return mViewSize; }
|
||||
|
||||
/** @see RenderManager::getVertexFormat */
|
||||
MyGUI::VertexColourType getVertexFormat() override { return mVertexFormat; }
|
||||
MyGUI::VertexColourType getVertexFormat() OPENMW_MYGUI_CONST_GETTER_3_4_1 override
|
||||
{ return mVertexFormat; }
|
||||
|
||||
/** @see RenderManager::isFormatSupported */
|
||||
bool isFormatSupported(MyGUI::PixelFormat format, MyGUI::TextureUsage usage) override;
|
||||
|
@ -108,12 +111,17 @@ public:
|
|||
bool checkTexture(MyGUI::ITexture* _texture);
|
||||
|
||||
// setViewSize() is a part of MyGUI::RenderManager interface since 3.4.0 release
|
||||
#if MYGUI_VERSION < MYGUI_DEFINE_VERSION(3,4,0)
|
||||
#if MYGUI_VERSION < MYGUI_DEFINE_VERSION(3, 4, 0)
|
||||
void setViewSize(int width, int height);
|
||||
#else
|
||||
void setViewSize(int width, int height) override;
|
||||
#endif
|
||||
|
||||
// registerShader() is a part of MyGUI::RenderManager interface since 3.4.1 release
|
||||
#if MYGUI_VERSION > MYGUI_DEFINE_VERSION(3, 4, 0)
|
||||
void registerShader(const std::string& _shaderName, const std::string& _vertexProgramFile, const std::string& _fragmentProgramFile) override;
|
||||
#endif
|
||||
|
||||
/*internal:*/
|
||||
|
||||
void collectDrawCalls();
|
||||
|
|
|
@ -115,16 +115,6 @@ namespace osgMyGUI
|
|||
Log(Debug::Warning) << "Would save image to file " << fname;
|
||||
}
|
||||
|
||||
int OSGTexture::getWidth()
|
||||
{
|
||||
return mWidth;
|
||||
}
|
||||
|
||||
int OSGTexture::getHeight()
|
||||
{
|
||||
return mHeight;
|
||||
}
|
||||
|
||||
void *OSGTexture::lock(MyGUI::TextureUsage /*access*/)
|
||||
{
|
||||
if (!mTexture.valid())
|
||||
|
@ -167,15 +157,14 @@ namespace osgMyGUI
|
|||
mLockedImage = nullptr;
|
||||
}
|
||||
|
||||
bool OSGTexture::isLocked()
|
||||
{
|
||||
return mLockedImage.valid();
|
||||
}
|
||||
|
||||
// Render-to-texture not currently implemented.
|
||||
MyGUI::IRenderTarget* OSGTexture::getRenderTarget()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#if MYGUI_VERSION > MYGUI_DEFINE_VERSION(3, 4, 0)
|
||||
void OSGTexture::setShader(const std::string& _shaderName)
|
||||
{ Log(Debug::Warning) << "OSGTexture::setShader is not implemented"; }
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -5,6 +5,12 @@
|
|||
|
||||
#include <osg/ref_ptr>
|
||||
|
||||
#if MYGUI_VERSION > MYGUI_DEFINE_VERSION(3, 4, 0)
|
||||
#define OPENMW_MYGUI_CONST_GETTER_3_4_1 const
|
||||
#else
|
||||
#define OPENMW_MYGUI_CONST_GETTER_3_4_1
|
||||
#endif
|
||||
|
||||
namespace osg
|
||||
{
|
||||
class Image;
|
||||
|
@ -47,17 +53,22 @@ namespace osgMyGUI
|
|||
|
||||
void* lock(MyGUI::TextureUsage access) override;
|
||||
void unlock() override;
|
||||
bool isLocked() override;
|
||||
bool isLocked() OPENMW_MYGUI_CONST_GETTER_3_4_1 override { return mLockedImage.valid(); }
|
||||
|
||||
int getWidth() override;
|
||||
int getHeight() override;
|
||||
int getWidth() OPENMW_MYGUI_CONST_GETTER_3_4_1 override { return mWidth; }
|
||||
int getHeight() OPENMW_MYGUI_CONST_GETTER_3_4_1 override { return mHeight; }
|
||||
|
||||
MyGUI::PixelFormat getFormat() override { return mFormat; }
|
||||
MyGUI::TextureUsage getUsage() override { return mUsage; }
|
||||
size_t getNumElemBytes() override { return mNumElemBytes; }
|
||||
MyGUI::PixelFormat getFormat() OPENMW_MYGUI_CONST_GETTER_3_4_1 override { return mFormat; }
|
||||
MyGUI::TextureUsage getUsage() OPENMW_MYGUI_CONST_GETTER_3_4_1 override { return mUsage; }
|
||||
size_t getNumElemBytes() OPENMW_MYGUI_CONST_GETTER_3_4_1 override { return mNumElemBytes; }
|
||||
|
||||
MyGUI::IRenderTarget *getRenderTarget() override;
|
||||
|
||||
// setShader() is a part of MyGUI::RenderManager interface since 3.4.1 release
|
||||
#if MYGUI_VERSION > MYGUI_DEFINE_VERSION(3, 4, 0)
|
||||
void setShader(const std::string& _shaderName) override;
|
||||
#endif
|
||||
|
||||
/*internal:*/
|
||||
osg::Texture2D *getTexture() const { return mTexture.get(); }
|
||||
};
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
#include <MyGUI_RenderManager.h>
|
||||
#include <algorithm>
|
||||
|
||||
#include "myguicompat.h"
|
||||
|
||||
namespace osgMyGUI
|
||||
{
|
||||
|
||||
|
@ -37,7 +39,7 @@ namespace osgMyGUI
|
|||
mTarget->doRender(_buffer, _texture, _count);
|
||||
}
|
||||
|
||||
const MyGUI::RenderTargetInfo& getInfo() override
|
||||
const MyGUI::RenderTargetInfo& getInfo() OPENMW_MYGUI_CONST_GETTER_3_4_1 override
|
||||
{
|
||||
mInfo = mTarget->getInfo();
|
||||
mInfo.hOffset = mHOffset;
|
||||
|
@ -51,7 +53,7 @@ namespace osgMyGUI
|
|||
MyGUI::IRenderTarget* mTarget;
|
||||
MyGUI::IntSize mViewSize;
|
||||
float mHOffset, mVOffset;
|
||||
MyGUI::RenderTargetInfo mInfo;
|
||||
mutable MyGUI::RenderTargetInfo mInfo;
|
||||
};
|
||||
|
||||
MyGUI::ILayerItem *ScalingLayer::getLayerItemByPoint(int _left, int _top) const
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue