1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-02-06 16:45:32 +00:00

Add OpenMW commits up to 22 Jun 2020

# Conflicts:
#	.travis.yml
#	CMakeLists.txt
This commit is contained in:
David Cernat 2020-06-22 20:50:55 +03:00
commit 3b2eb6f62c
180 changed files with 3144 additions and 1629 deletions

View file

@ -5,7 +5,7 @@ Debian:
tags: tags:
- docker - docker
- linux - linux
image: gcc image: debian:bullseye
cache: cache:
key: apt-cache key: apt-cache
paths: paths:
@ -13,7 +13,7 @@ Debian:
before_script: before_script:
- export APT_CACHE_DIR=`pwd`/apt-cache && mkdir -pv $APT_CACHE_DIR - export APT_CACHE_DIR=`pwd`/apt-cache && mkdir -pv $APT_CACHE_DIR
- apt-get update -yq - apt-get update -yq
- apt-get -o dir::cache::archives="$APT_CACHE_DIR" install -y cmake libboost-filesystem-dev libboost-program-options-dev libboost-system-dev libboost-iostreams-dev libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev libsdl2-dev libqt4-dev libopenal-dev libopenscenegraph-3.4-dev libunshield-dev libtinyxml-dev libmygui-dev libbullet-dev - apt-get -o dir::cache::archives="$APT_CACHE_DIR" install -y cmake build-essential libboost-filesystem-dev libboost-program-options-dev libboost-system-dev libboost-iostreams-dev libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev libsdl2-dev libqt5opengl5-dev libopenal-dev libopenscenegraph-dev libunshield-dev libtinyxml-dev libmygui-dev libbullet-dev
stage: build stage: build
script: script:
- cores_to_use=$((`nproc`-2)); if (( $cores_to_use < 1 )); then cores_to_use=1; fi - cores_to_use=$((`nproc`-2)); if (( $cores_to_use < 1 )); then cores_to_use=1; fi

View file

@ -1,6 +1,7 @@
0.47.0 0.47.0
------ ------
Bug #1662: Qt4 and Windows binaries crash if there's a non-ASCII character in a file path/config path
Bug #1952: Incorrect particle lighting Bug #1952: Incorrect particle lighting
Bug #2311: Targeted scripts are not properly supported on non-unique RefIDs Bug #2311: Targeted scripts are not properly supported on non-unique RefIDs
Bug #3676: NiParticleColorModifier isn't applied properly Bug #3676: NiParticleColorModifier isn't applied properly
@ -28,8 +29,10 @@
Bug #5441: Enemies can't push a player character when in critical strike stance Bug #5441: Enemies can't push a player character when in critical strike stance
Bug #5451: Magic projectiles don't disappear with the caster Bug #5451: Magic projectiles don't disappear with the caster
Bug #5452: Autowalk is being included in savegames Bug #5452: Autowalk is being included in savegames
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 #5362: Show the soul gems' trapped soul in count dialog
Feature #5445: Handle NiLines Feature #5445: Handle NiLines
Task #5480: Drop Qt4 support
0.46.0 0.46.0
------ ------
@ -245,6 +248,7 @@
Bug #5350: An attempt to launch magic bolt causes "AL error invalid value" error Bug #5350: An attempt to launch magic bolt causes "AL error invalid value" error
Bug #5352: Light source items' duration is decremented while they aren't visible Bug #5352: Light source items' duration is decremented while they aren't visible
Feature #1724: Handle AvoidNode Feature #1724: Handle AvoidNode
Feature #2159: "Graying out" exhausted dialogue topics
Feature #2229: Improve pathfinding AI Feature #2229: Improve pathfinding AI
Feature #3025: Analogue gamepad movement controls Feature #3025: Analogue gamepad movement controls
Feature #3442: Default values for fallbacks from ini file Feature #3442: Default values for fallbacks from ini file

View file

@ -1,11 +1,13 @@
& "${env:COMSPEC}" /c ActivateMSVC.bat "&&" set | ForEach-Object { & "${env:COMSPEC}" /c ActivateMSVC.bat "&&" set | ForEach-Object {
$name, $value = $_ -split '=', 2 if ($_.Contains("=")) {
Set-Content env:\"$name" $value $name, $value = $_ -split '=', 2
Set-Content env:\"$name" $value
}
} }
$MissingTools = $false $MissingTools = $false
$tools = "cl", "link", "rc", "mt", "awooga" $tools = "cl", "link", "rc", "mt"
$descriptions = "MSVC Compiler", "MSVC Linker", "MS Windows Resource Compiler", "MS Windows Manifest Tool", "A made up command" $descriptions = "MSVC Compiler", "MSVC Linker", "MS Windows Resource Compiler", "MS Windows Manifest Tool"
for ($i = 0; $i -lt $tools.Length; $i++) { for ($i = 0; $i -lt $tools.Length; $i++) {
$present = $true $present = $true
try { try {

View file

@ -38,7 +38,6 @@ ${ANALYZE} cmake .. \
-DBUILD_MASTER=ON \ -DBUILD_MASTER=ON \
-DBUILD_UNITTESTS=1 \ -DBUILD_UNITTESTS=1 \
-DUSE_SYSTEM_TINYXML=1 \ -DUSE_SYSTEM_TINYXML=1 \
-DDESIRED_QT_VERSION=5 \
-DCMAKE_INSTALL_PREFIX=/usr \ -DCMAKE_INSTALL_PREFIX=/usr \
-DBINDIR=/usr/games \ -DBINDIR=/usr/games \
-DCMAKE_BUILD_TYPE="None" \ -DCMAKE_BUILD_TYPE="None" \

View file

@ -161,7 +161,7 @@ Options:
Build unit tests / Google test Build unit tests / Google test
-u -u
Configure for unity builds. Configure for unity builds.
-v <2013/2015/2017/2019> -v <2017/2019>
Choose the Visual Studio version to use. Choose the Visual Studio version to use.
-n -n
Produce NMake makefiles instead of a Visual Studio solution. Cannout be used with -N. Produce NMake makefiles instead of a Visual Studio solution. Cannout be used with -N.
@ -213,8 +213,8 @@ run_cmd() {
shift shift
if [ -z $VERBOSE ]; then if [ -z $VERBOSE ]; then
eval $CMD $@ > output.log 2>&1 RET=0
RET=$? eval $CMD $@ > output.log 2>&1 || RET=$?
if [ $RET -ne 0 ]; then if [ $RET -ne 0 ]; then
if [ -z $APPVEYOR ]; then if [ -z $APPVEYOR ]; then
@ -230,8 +230,9 @@ run_cmd() {
return $RET return $RET
else else
eval $CMD $@ RET=0
return $? eval $CMD $@ || RET=$?
return $RET
fi fi
} }
@ -256,15 +257,16 @@ download() {
printf " Downloading $FILE... " printf " Downloading $FILE... "
if [ -z $VERBOSE ]; then if [ -z $VERBOSE ]; then
curl --silent --retry 10 -kLy 5 -o $FILE $URL RET=0
RET=$? curl --silent --retry 10 -kLy 5 -o $FILE $URL || RET=$?
else else
curl --retry 10 -kLy 5 -o $FILE $URL RET=0
RET=$? curl --retry 10 -kLy 5 -o $FILE $URL || RET=$?
fi fi
if [ $RET -ne 0 ]; then if [ $RET -ne 0 ]; then
echo "Failed!" echo "Failed!"
wrappedExit $RET
else else
echo "Done." echo "Done."
fi fi
@ -346,21 +348,13 @@ case $VS_VERSION in
;; ;;
14|14.0|2015 ) 14|14.0|2015 )
GENERATOR="Visual Studio 14 2015" echo "Visual Studio 2015 is no longer supported"
TOOLSET="vc140" wrappedExit 1
MSVC_REAL_VER="14"
MSVC_VER="14.0"
MSVC_YEAR="2015"
MSVC_REAL_YEAR="2015"
MSVC_DISPLAY_YEAR="2015"
BOOST_VER="1.67.0"
BOOST_VER_URL="1_67_0"
BOOST_VER_SDK="106700"
;; ;;
12|12.0|2013 ) 12|12.0|2013 )
echo "Visual Studio 2013 is no longer supported" echo "Visual Studio 2013 is no longer supported"
exit 1 wrappedExit 1
;; ;;
esac esac
@ -505,11 +499,6 @@ if [ -z $SKIP_DOWNLOAD ]; then
# Qt # Qt
if [ -z $APPVEYOR ]; then if [ -z $APPVEYOR ]; then
if [ "${MSVC_REAL_YEAR}" = "2015" ] && [ "${BITS}" = "32" ]; then
echo "Qt no longer provides MSVC2015 Win32 packages, switch to 64-bit or a newer Visual Studio. Sorry."
exit 1
fi
download "AQt installer" \ download "AQt installer" \
"https://files.pythonhosted.org/packages/f3/bb/aee972f08deecca31bfc46b5aedfad1ce6c7f3aaf1288d685e4a914b53ac/aqtinstall-0.8-py2.py3-none-any.whl" \ "https://files.pythonhosted.org/packages/f3/bb/aee972f08deecca31bfc46b5aedfad1ce6c7f3aaf1288d685e4a914b53ac/aqtinstall-0.8-py2.py3-none-any.whl" \
"aqtinstall-0.8-py2.py3-none-any.whl" "aqtinstall-0.8-py2.py3-none-any.whl"
@ -604,14 +593,8 @@ fi
# Appveyor has all the boost we need already # Appveyor has all the boost we need already
BOOST_SDK="c:/Libraries/boost_${BOOST_VER_URL}" BOOST_SDK="c:/Libraries/boost_${BOOST_VER_URL}"
if [ $MSVC_REAL_VER -ge 15 ]; then
LIB_SUFFIX="1"
else
LIB_SUFFIX="0"
fi
add_cmake_opts -DBOOST_ROOT="$BOOST_SDK" \ add_cmake_opts -DBOOST_ROOT="$BOOST_SDK" \
-DBOOST_LIBRARYDIR="${BOOST_SDK}/lib${BITS}-msvc-${MSVC_VER}.${LIB_SUFFIX}" -DBOOST_LIBRARYDIR="${BOOST_SDK}/lib${BITS}-msvc-${MSVC_VER}.1"
add_cmake_opts -DBoost_COMPILER="-${TOOLSET}" add_cmake_opts -DBoost_COMPILER="-${TOOLSET}"
echo Done. echo Done.
@ -793,8 +776,7 @@ fi
fi fi
cd $QT_SDK cd $QT_SDK
add_cmake_opts -DDESIRED_QT_VERSION=5 \ add_cmake_opts -DQT_QMAKE_EXECUTABLE="${QT_SDK}/bin/qmake.exe" \
-DQT_QMAKE_EXECUTABLE="${QT_SDK}/bin/qmake.exe" \
-DCMAKE_PREFIX_PATH="$QT_SDK" -DCMAKE_PREFIX_PATH="$QT_SDK"
if [ $CONFIGURATION == "Debug" ]; then if [ $CONFIGURATION == "Debug" ]; then
SUFFIX="d" SUFFIX="d"
@ -806,8 +788,7 @@ fi
echo Done. echo Done.
else else
QT_SDK="C:/Qt/5.13/msvc2017${SUFFIX}" QT_SDK="C:/Qt/5.13/msvc2017${SUFFIX}"
add_cmake_opts -DDESIRED_QT_VERSION=5 \ add_cmake_opts -DQT_QMAKE_EXECUTABLE="${QT_SDK}/bin/qmake.exe" \
-DQT_QMAKE_EXECUTABLE="${QT_SDK}/bin/qmake.exe" \
-DCMAKE_PREFIX_PATH="$QT_SDK" -DCMAKE_PREFIX_PATH="$QT_SDK"
if [ $CONFIGURATION == "Debug" ]; then if [ $CONFIGURATION == "Debug" ]; then
SUFFIX="d" SUFFIX="d"
@ -957,30 +938,18 @@ fi
echo echo
#fi #fi
if ! [ -z $ACTIVATE_MSVC ]; then if [ -n "$ACTIVATE_MSVC" ]; then
echo -n "- Activating MSVC in the current shell... " 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; } command -v vswhere >/dev/null 2>&1 || { echo "Error: vswhere is not on the path."; wrappedExit 1; }
MSVC_INSTALLATION_PATH=$(vswhere -legacy -version "[$MSVC_VER,$(awk "BEGIN { print $MSVC_REAL_VER + 1; exit }"))" -property installationPath) MSVC_INSTALLATION_PATH=$(vswhere -legacy -products '*' -version "[$MSVC_VER,$(awk "BEGIN { print $MSVC_REAL_VER + 1; exit }"))" -property installationPath)
if [ $MSVC_REAL_VER -ge 15 ]; then if [ -z "$MSVC_INSTALLATION_PATH" ]; then
echo "@\"${MSVC_INSTALLATION_PATH}\Common7\Tools\VsDevCmd.bat\" -no_logo -arch=$([ $BITS -eq 64 ] && echo "amd64" || echo "x86") -host_arch=$([ $(uname -m) == 'x86_64' ] && echo "amd64" || echo "x86")" > ActivateMSVC.bat echo "vswhere was unable to find MSVC $MSVC_DISPLAY_YEAR"
else wrappedExit 1
if [ $(uname -m) == 'x86_64' ]; then
if [ $BITS -eq 64 ]; then
compiler=amd64
else
compiler=amd64_x86
fi
else
if [ $BITS -eq 64 ]; then
compiler=x86_amd64
else
compiler=x86
fi
fi
echo "@\"${MSVC_INSTALLATION_PATH}\VC\vcvarsall.bat\" $compiler" > ActivateMSVC.bat
fi fi
echo "@\"${MSVC_INSTALLATION_PATH}\Common7\Tools\VsDevCmd.bat\" -no_logo -arch=$([ $BITS -eq 64 ] && echo "amd64" || echo "x86") -host_arch=$([ $(uname -m) == 'x86_64' ] && echo "amd64" || echo "x86")" > ActivateMSVC.bat
cp "../CI/activate_msvc.sh" . cp "../CI/activate_msvc.sh" .
sed -i "s/\$MSVC_DISPLAY_YEAR/$MSVC_DISPLAY_YEAR/g" activate_msvc.sh sed -i "s/\$MSVC_DISPLAY_YEAR/$MSVC_DISPLAY_YEAR/g" activate_msvc.sh
source ./activate_msvc.sh source ./activate_msvc.sh
@ -997,8 +966,8 @@ if [ -z $VERBOSE ]; then
else else
echo "- cmake .. $CMAKE_OPTS" echo "- cmake .. $CMAKE_OPTS"
fi fi
run_cmd cmake .. $CMAKE_OPTS RET=0
RET=$? run_cmd cmake .. $CMAKE_OPTS || RET=$?
if [ -z $VERBOSE ]; then if [ -z $VERBOSE ]; then
if [ $RET -eq 0 ]; then if [ $RET -eq 0 ]; then
echo Done. echo Done.
@ -1006,8 +975,14 @@ if [ -z $VERBOSE ]; then
echo Failed. echo Failed.
fi fi
fi fi
if [ $RET -ne 0 ]; then
wrappedExit $RET
fi
if [ -n $ACTIVATE_MSVC ]; then echo "Script completed successfully."
echo "You now have an OpenMW build system at $(unixPathAsWindows "$(pwd)")"
if [ -n "$ACTIVATE_MSVC" ]; then
echo echo
echo "Note: you must manually activate MSVC for the shell in which you want to do the build." echo "Note: you must manually activate MSVC for the shell in which you want to do the build."
echo echo

View file

@ -17,7 +17,6 @@ cmake \
-D CMAKE_OSX_SYSROOT="macosx10.14" \ -D CMAKE_OSX_SYSROOT="macosx10.14" \
-D CMAKE_BUILD_TYPE=Release \ -D CMAKE_BUILD_TYPE=Release \
-D OPENMW_OSX_DEPLOYMENT=TRUE \ -D OPENMW_OSX_DEPLOYMENT=TRUE \
-D DESIRED_QT_VERSION=5 \
-D BUILD_ESMTOOL=FALSE \ -D BUILD_ESMTOOL=FALSE \
-G"Unix Makefiles" \ -G"Unix Makefiles" \
.. ..

View file

@ -22,11 +22,6 @@ else()
set(USE_QT TRUE) set(USE_QT TRUE)
endif() endif()
if (USE_QT)
set(DESIRED_QT_VERSION 4 CACHE STRING "The QT version OpenMW should use (4 or 5)")
set_property(CACHE DESIRED_QT_VERSION PROPERTY STRINGS 4 5)
endif()
# set the minimum required version across the board # set the minimum required version across the board
cmake_minimum_required(VERSION 3.1.0) cmake_minimum_required(VERSION 3.1.0)
@ -159,18 +154,12 @@ include_directories(${RakNet_INCLUDES})
find_package(OpenGL REQUIRED) find_package(OpenGL REQUIRED)
if (USE_QT) if (USE_QT)
message(STATUS "Using Qt${DESIRED_QT_VERSION}") find_package(Qt5Core 5.12 REQUIRED)
find_package(Qt5Widgets REQUIRED)
if (DESIRED_QT_VERSION MATCHES 4) find_package(Qt5Network REQUIRED)
find_package(Qt4 REQUIRED COMPONENTS QtCore QtGui QtNetwork QtOpenGL) find_package(Qt5OpenGL REQUIRED)
else()
find_package(Qt5Widgets REQUIRED)
find_package(Qt5Core REQUIRED)
find_package(Qt5Network REQUIRED)
find_package(Qt5OpenGL REQUIRED)
# Instruct CMake to run moc automatically when needed. # Instruct CMake to run moc automatically when needed.
#set(CMAKE_AUTOMOC ON) #set(CMAKE_AUTOMOC ON)
endif()
endif() endif()
IF(BUILD_OPENMW OR BUILD_OPENCS) IF(BUILD_OPENMW OR BUILD_OPENCS)
@ -303,16 +292,6 @@ IF(BUILD_OPENMW OR BUILD_OPENCS)
list(APPEND OPENSCENEGRAPH_LIBRARIES ${OSGPlugins_LIBRARIES}) list(APPEND OPENSCENEGRAPH_LIBRARIES ${OSGPlugins_LIBRARIES})
endif() endif()
if(QT_STATIC)
if(WIN32)
if(DESIRED_QT_VERSION MATCHES 4)
# QtCore needs WSAAsyncSelect from Ws2_32.lib
set(QT_QTCORE_LIBRARY ${QT_QTCORE_LIBRARY} Ws2_32.lib)
message("QT_QTCORE_LIBRARY: ${QT_QTCORE_LIBRARY}")
endif()
endif()
endif()
set(REQUIRED_BULLET_VERSION 286) # Bullet 286 required due to runtime bugfixes for btCapsuleShape set(REQUIRED_BULLET_VERSION 286) # Bullet 286 required due to runtime bugfixes for btCapsuleShape
if (DEFINED ENV{TRAVIS_BRANCH} OR DEFINED ENV{APPVEYOR}) if (DEFINED ENV{TRAVIS_BRANCH} OR DEFINED ENV{APPVEYOR})
set(REQUIRED_BULLET_VERSION 283) # but for build testing, 283 is fine set(REQUIRED_BULLET_VERSION 283) # but for build testing, 283 is fine
@ -325,10 +304,12 @@ ELSE()
include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS}) # HACK: DO NOT MOVE THIS. Used for server only build, kept here to avoid merge conflicts above. include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS}) # HACK: DO NOT MOVE THIS. Used for server only build, kept here to avoid merge conflicts above.
ENDIF(BUILD_OPENMW OR BUILD_OPENCS) ENDIF(BUILD_OPENMW OR BUILD_OPENCS)
set(BOOST_COMPONENTS system filesystem program_options iostreams) set(BOOST_COMPONENTS system filesystem program_options iostreams)
if(WIN32) if(WIN32)
set(BOOST_COMPONENTS ${BOOST_COMPONENTS} locale zlib) set(BOOST_COMPONENTS ${BOOST_COMPONENTS} locale)
if(MSVC)
set(BOOST_COMPONENTS ${BOOST_COMPONENTS} zlib)
endif(MSVC)
endif(WIN32) endif(WIN32)
IF(BOOST_STATIC) IF(BOOST_STATIC)
@ -554,11 +535,6 @@ if(WIN32)
INSTALL(FILES "${OpenMW_BINARY_DIR}/Debug/gamecontrollerdb.txt" DESTINATION "." CONFIGURATIONS Debug) INSTALL(FILES "${OpenMW_BINARY_DIR}/Debug/gamecontrollerdb.txt" DESTINATION "." CONFIGURATIONS Debug)
INSTALL(FILES "${OpenMW_BINARY_DIR}/Release/gamecontrollerdb.txt" DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel) INSTALL(FILES "${OpenMW_BINARY_DIR}/Release/gamecontrollerdb.txt" DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel)
IF(DESIRED_QT_VERSION MATCHES 5)
INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/Debug/platforms" DESTINATION "." CONFIGURATIONS Debug)
INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/Release/platforms" DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel)
ENDIF()
INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/Debug/resources" DESTINATION "." CONFIGURATIONS Debug) INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/Debug/resources" DESTINATION "." CONFIGURATIONS Debug)
INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/Release/resources" DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel) INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/Release/resources" DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel)
@ -845,7 +821,7 @@ if (WIN32)
endif() endif()
# Apple bundling # Apple bundling
if (OPENMW_OSX_DEPLOYMENT AND APPLE AND DESIRED_QT_VERSION MATCHES 5) if (OPENMW_OSX_DEPLOYMENT AND APPLE)
if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.13 AND CMAKE_VERSION VERSION_LESS 3.13.4) if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.13 AND CMAKE_VERSION VERSION_LESS 3.13.4)
message(FATAL_ERROR "macOS packaging is broken in early CMake 3.13 releases, see https://gitlab.com/OpenMW/openmw/issues/4767. Please use at least 3.13.4 or an older version like 3.12.4") message(FATAL_ERROR "macOS packaging is broken in early CMake 3.13 releases, see https://gitlab.com/OpenMW/openmw/issues/4767. Please use at least 3.13.4 or an older version like 3.12.4")
endif () endif ()

View file

@ -70,16 +70,9 @@ if(WIN32)
set(QT_USE_QTMAIN TRUE) set(QT_USE_QTMAIN TRUE)
endif(WIN32) endif(WIN32)
if (DESIRED_QT_VERSION MATCHES 4) QT5_ADD_RESOURCES(RCC_SRCS ${CMAKE_SOURCE_DIR}/files/launcher/launcher.qrc)
include(${QT_USE_FILE}) QT5_WRAP_CPP(MOC_SRCS ${LAUNCHER_HEADER_MOC})
QT4_ADD_RESOURCES(RCC_SRCS ${CMAKE_SOURCE_DIR}/files/launcher/launcher.qrc) QT5_WRAP_UI(UI_HDRS ${LAUNCHER_UI})
QT4_WRAP_CPP(MOC_SRCS ${LAUNCHER_HEADER_MOC})
QT4_WRAP_UI(UI_HDRS ${LAUNCHER_UI})
else()
QT5_ADD_RESOURCES(RCC_SRCS ${CMAKE_SOURCE_DIR}/files/launcher/launcher.qrc)
QT5_WRAP_CPP(MOC_SRCS ${LAUNCHER_HEADER_MOC})
QT5_WRAP_UI(UI_HDRS ${LAUNCHER_UI})
endif()
include_directories(${CMAKE_CURRENT_BINARY_DIR}) include_directories(${CMAKE_CURRENT_BINARY_DIR})
if(NOT WIN32) if(NOT WIN32)
@ -105,14 +98,7 @@ target_link_libraries(openmw-launcher
components components
) )
if (DESIRED_QT_VERSION MATCHES 4) target_link_libraries(openmw-launcher Qt5::Widgets Qt5::Core)
target_link_libraries(openmw-launcher ${QT_QTGUI_LIBRARY} ${QT_QTCORE_LIBRARY})
if(WIN32)
target_link_libraries(openmw-launcher ${QT_QTMAIN_LIBRARY})
endif(WIN32)
else()
target_link_libraries(openmw-launcher Qt5::Widgets Qt5::Core)
endif()
if (BUILD_WITH_CODE_COVERAGE) if (BUILD_WITH_CODE_COVERAGE)
add_definitions (--coverage) add_definitions (--coverage)

View file

@ -1,13 +1,9 @@
#include "graphicspage.hpp" #include "graphicspage.hpp"
#include <csignal>
#include <QDesktopWidget> #include <QDesktopWidget>
#include <QMessageBox> #include <QMessageBox>
#include <QDir> #include <QDir>
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
#include <QScreen> #include <QScreen>
#endif
#ifdef MAC_OS_X_VERSION_MIN_REQUIRED #ifdef MAC_OS_X_VERSION_MIN_REQUIRED
#undef MAC_OS_X_VERSION_MIN_REQUIRED #undef MAC_OS_X_VERSION_MIN_REQUIRED
@ -55,13 +51,11 @@ Launcher::GraphicsPage::GraphicsPage(Files::ConfigurationManager &cfg, Settings:
bool Launcher::GraphicsPage::setupSDL() bool Launcher::GraphicsPage::setupSDL()
{ {
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
bool sdlConnectSuccessful = initSDL(); bool sdlConnectSuccessful = initSDL();
if (!sdlConnectSuccessful) if (!sdlConnectSuccessful)
{ {
return false; return false;
} }
#endif
int displays = SDL_GetNumVideoDisplays(); int displays = SDL_GetNumVideoDisplays();
@ -82,10 +76,8 @@ bool Launcher::GraphicsPage::setupSDL()
screenComboBox->addItem(QString(tr("Screen ")) + QString::number(i + 1)); screenComboBox->addItem(QString(tr("Screen ")) + QString::number(i + 1));
} }
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
// Disconnect from SDL processes // Disconnect from SDL processes
quitSDL(); quitSDL();
#endif
return true; return true;
} }
@ -145,6 +137,10 @@ bool Launcher::GraphicsPage::loadSettings()
if (mEngineSettings.getBool("enable indoor shadows", "Shadows")) if (mEngineSettings.getBool("enable indoor shadows", "Shadows"))
indoorShadowsCheckBox->setCheckState(Qt::Checked); indoorShadowsCheckBox->setCheckState(Qt::Checked);
shadowComputeSceneBoundsComboBox->setCurrentIndex(
shadowComputeSceneBoundsComboBox->findText(
QString(tr(mEngineSettings.getString("compute scene bounds", "Shadows").c_str()))));
int shadowDistLimit = mEngineSettings.getInt("maximum shadow map distance", "Shadows"); int shadowDistLimit = mEngineSettings.getInt("maximum shadow map distance", "Shadows");
if (shadowDistLimit > 0) if (shadowDistLimit > 0)
{ {
@ -231,7 +227,7 @@ void Launcher::GraphicsPage::saveSettings()
bool cPlayerShadows = playerShadowsCheckBox->checkState(); bool cPlayerShadows = playerShadowsCheckBox->checkState();
if (cActorShadows || cObjectShadows || cTerrainShadows || cPlayerShadows) if (cActorShadows || cObjectShadows || cTerrainShadows || cPlayerShadows)
{ {
if (mEngineSettings.getBool("enable shadows", "Shadows") != true) if (!mEngineSettings.getBool("enable shadows", "Shadows"))
mEngineSettings.setBool("enable shadows", "Shadows", true); mEngineSettings.setBool("enable shadows", "Shadows", true);
if (mEngineSettings.getBool("actor shadows", "Shadows") != cActorShadows) if (mEngineSettings.getBool("actor shadows", "Shadows") != cActorShadows)
mEngineSettings.setBool("actor shadows", "Shadows", cActorShadows); mEngineSettings.setBool("actor shadows", "Shadows", cActorShadows);
@ -263,6 +259,10 @@ void Launcher::GraphicsPage::saveSettings()
int cShadowRes = shadowResolutionComboBox->currentText().toInt(); int cShadowRes = shadowResolutionComboBox->currentText().toInt();
if (cShadowRes != mEngineSettings.getInt("shadow map resolution", "Shadows")) if (cShadowRes != mEngineSettings.getInt("shadow map resolution", "Shadows"))
mEngineSettings.setInt("shadow map resolution", "Shadows", cShadowRes); mEngineSettings.setInt("shadow map resolution", "Shadows", cShadowRes);
auto cComputeSceneBounds = shadowComputeSceneBoundsComboBox->currentText().toStdString();
if (cComputeSceneBounds != mEngineSettings.getString("compute scene bounds", "Shadows"))
mEngineSettings.setString("compute scene bounds", "Shadows", cComputeSceneBounds);
} }
QStringList Launcher::GraphicsPage::getAvailableResolutions(int screen) QStringList Launcher::GraphicsPage::getAvailableResolutions(int screen)
@ -316,7 +316,6 @@ QRect Launcher::GraphicsPage::getMaximumResolution()
{ {
QRect max; QRect max;
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
for (QScreen* screen : QGuiApplication::screens()) for (QScreen* screen : QGuiApplication::screens())
{ {
QRect res = screen->geometry(); QRect res = screen->geometry();
@ -325,17 +324,6 @@ QRect Launcher::GraphicsPage::getMaximumResolution()
if (res.height() > max.height()) if (res.height() > max.height())
max.setHeight(res.height()); max.setHeight(res.height());
} }
#else
int screens = QApplication::desktop()->screenCount();
for (int i = 0; i < screens; ++i)
{
QRect res = QApplication::desktop()->screenGeometry(i);
if (res.width() > max.width())
max.setWidth(res.width());
if (res.height() > max.height())
max.setHeight(res.height());
}
#endif
return max; return max;
} }

View file

@ -38,8 +38,8 @@ namespace Launcher
Files::ConfigurationManager &mCfgMgr; Files::ConfigurationManager &mCfgMgr;
Settings::Manager &mEngineSettings; Settings::Manager &mEngineSettings;
QStringList getAvailableResolutions(int screen); static QStringList getAvailableResolutions(int screen);
QRect getMaximumResolution(); static QRect getMaximumResolution();
bool setupSDL(); bool setupSDL();
}; };

View file

@ -13,18 +13,11 @@
#endif // MAC_OS_X_VERSION_MIN_REQUIRED #endif // MAC_OS_X_VERSION_MIN_REQUIRED
#include "maindialog.hpp" #include "maindialog.hpp"
#include "sdlinit.hpp"
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
try try
{ {
// Note: we should init SDL2 before Qt4 to avoid crashes on Linux,
// but we should init SDL2 after Qt5 to avoid input issues on MacOS X.
#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
initSDL();
#endif
QApplication app(argc, argv); QApplication app(argc, argv);
// Internationalization // Internationalization
@ -50,11 +43,6 @@ int main(int argc, char *argv[])
int exitCode = app.exec(); int exitCode = app.exec();
#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
// Disconnect from SDL processes
quitSDL();
#endif
return exitCode; return exitCode;
} }
catch (std::exception& e) catch (std::exception& e)

View file

@ -149,16 +149,9 @@ if(WIN32)
set(QT_USE_QTMAIN TRUE) set(QT_USE_QTMAIN TRUE)
endif(WIN32) endif(WIN32)
if (DESIRED_QT_VERSION MATCHES 4) qt5_wrap_ui(OPENCS_UI_HDR ${OPENCS_UI})
include(${QT_USE_FILE}) qt5_wrap_cpp(OPENCS_MOC_SRC ${OPENCS_HDR_QT})
qt4_wrap_ui(OPENCS_UI_HDR ${OPENCS_UI}) qt5_add_resources(OPENCS_RES_SRC ${OPENCS_RES})
qt4_wrap_cpp(OPENCS_MOC_SRC ${OPENCS_HDR_QT})
qt4_add_resources(OPENCS_RES_SRC ${OPENCS_RES})
else()
qt5_wrap_ui(OPENCS_UI_HDR ${OPENCS_UI})
qt5_wrap_cpp(OPENCS_MOC_SRC ${OPENCS_HDR_QT})
qt5_add_resources(OPENCS_RES_SRC ${OPENCS_RES})
endif()
# for compiled .ui files # for compiled .ui files
include_directories(${CMAKE_CURRENT_BINARY_DIR}) include_directories(${CMAKE_CURRENT_BINARY_DIR})
@ -236,19 +229,7 @@ target_link_libraries(openmw-cs
components components
) )
if (DESIRED_QT_VERSION MATCHES 4) target_link_libraries(openmw-cs Qt5::Widgets Qt5::Core Qt5::Network Qt5::OpenGL)
target_link_libraries(openmw-cs
${QT_QTGUI_LIBRARY}
${QT_QTCORE_LIBRARY}
${QT_QTNETWORK_LIBRARY}
${QT_QTOPENGL_LIBRARY})
if (WIN32)
target_link_libraries(openmw-cs ${QT_QTMAIN_LIBRARY})
endif()
else()
target_link_libraries(openmw-cs Qt5::Widgets Qt5::Core Qt5::Network Qt5::OpenGL)
endif()
if (WIN32) if (WIN32)
target_link_libraries(openmw-cs ${Boost_LOCALE_LIBRARY}) target_link_libraries(openmw-cs ${Boost_LOCALE_LIBRARY})

View file

@ -685,7 +685,6 @@ namespace CSMPrefs
std::make_pair((int)Qt::Key_ContrastAdjust , "ContrastAdjust"), std::make_pair((int)Qt::Key_ContrastAdjust , "ContrastAdjust"),
std::make_pair((int)Qt::Key_LaunchG , "LaunchG"), std::make_pair((int)Qt::Key_LaunchG , "LaunchG"),
std::make_pair((int)Qt::Key_LaunchH , "LaunchH"), std::make_pair((int)Qt::Key_LaunchH , "LaunchH"),
#if QT_VERSION >= QT_VERSION_CHECK(5,7,0)
std::make_pair((int)Qt::Key_TouchpadToggle , "TouchpadToggle"), std::make_pair((int)Qt::Key_TouchpadToggle , "TouchpadToggle"),
std::make_pair((int)Qt::Key_TouchpadOn , "TouchpadOn"), std::make_pair((int)Qt::Key_TouchpadOn , "TouchpadOn"),
std::make_pair((int)Qt::Key_TouchpadOff , "TouchpadOff"), std::make_pair((int)Qt::Key_TouchpadOff , "TouchpadOff"),
@ -706,7 +705,6 @@ namespace CSMPrefs
std::make_pair((int)Qt::Key_Find , "Find"), std::make_pair((int)Qt::Key_Find , "Find"),
std::make_pair((int)Qt::Key_Undo , "Undo"), std::make_pair((int)Qt::Key_Undo , "Undo"),
std::make_pair((int)Qt::Key_Redo , "Redo"), std::make_pair((int)Qt::Key_Redo , "Redo"),
#endif
std::make_pair((int)Qt::Key_AltGr , "AltGr"), std::make_pair((int)Qt::Key_AltGr , "AltGr"),
std::make_pair((int)Qt::Key_Multi_key , "Multi_key"), std::make_pair((int)Qt::Key_Multi_key , "Multi_key"),
std::make_pair((int)Qt::Key_Kanji , "Kanji"), std::make_pair((int)Qt::Key_Kanji , "Kanji"),
@ -770,9 +768,7 @@ namespace CSMPrefs
std::make_pair((int)Qt::Key_Sleep , "Sleep"), std::make_pair((int)Qt::Key_Sleep , "Sleep"),
std::make_pair((int)Qt::Key_Play , "Play"), std::make_pair((int)Qt::Key_Play , "Play"),
std::make_pair((int)Qt::Key_Zoom , "Zoom"), std::make_pair((int)Qt::Key_Zoom , "Zoom"),
#if QT_VERSION >= QT_VERSION_CHECK(5,7,0)
std::make_pair((int)Qt::Key_Exit , "Exit"), std::make_pair((int)Qt::Key_Exit , "Exit"),
#endif
std::make_pair((int)Qt::Key_Context1 , "Context1"), std::make_pair((int)Qt::Key_Context1 , "Context1"),
std::make_pair((int)Qt::Key_Context2 , "Context2"), std::make_pair((int)Qt::Key_Context2 , "Context2"),
std::make_pair((int)Qt::Key_Context3 , "Context3"), std::make_pair((int)Qt::Key_Context3 , "Context3"),

View file

@ -5,10 +5,7 @@
#include <QVBoxLayout> #include <QVBoxLayout>
#include <QDialogButtonBox> #include <QDialogButtonBox>
#include <QPushButton> #include <QPushButton>
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
#include <QScreen> #include <QScreen>
#endif
#include "filewidget.hpp" #include "filewidget.hpp"
#include "adjusterwidget.hpp" #include "adjusterwidget.hpp"
@ -50,11 +47,7 @@ CSVDoc::NewGameDialogue::NewGameDialogue()
connect (mFileWidget, SIGNAL (nameChanged (const QString&, bool)), connect (mFileWidget, SIGNAL (nameChanged (const QString&, bool)),
mAdjusterWidget, SLOT (setName (const QString&, bool))); mAdjusterWidget, SLOT (setName (const QString&, bool)));
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
QRect scr = QGuiApplication::primaryScreen()->geometry(); QRect scr = QGuiApplication::primaryScreen()->geometry();
#else
QRect scr = QApplication::desktop()->screenGeometry();
#endif
QRect rect = geometry(); QRect rect = geometry();
move (scr.center().x() - rect.center().x(), scr.center().y() - rect.center().y()); move (scr.center().x() - rect.center().x(), scr.center().y() - rect.center().y());
} }

View file

@ -9,10 +9,7 @@
#include <QLabel> #include <QLabel>
#include <QIcon> #include <QIcon>
#include <QPushButton> #include <QPushButton>
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
#include <QScreen> #include <QScreen>
#endif
QPushButton *CSVDoc::StartupDialogue::addButton (const QString& label, const QIcon& icon) QPushButton *CSVDoc::StartupDialogue::addButton (const QString& label, const QIcon& icon)
{ {
@ -123,12 +120,7 @@ CSVDoc::StartupDialogue::StartupDialogue() : mWidth (0), mColumn (2)
setLayout (layout); setLayout (layout);
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
QRect scr = QGuiApplication::primaryScreen()->geometry(); QRect scr = QGuiApplication::primaryScreen()->geometry();
#else
QRect scr = QApplication::desktop()->screenGeometry();
#endif
QRect rect = geometry(); QRect rect = geometry();
move (scr.center().x() - rect.center().x(), scr.center().y() - rect.center().y()); move (scr.center().x() - rect.center().x(), scr.center().y() - rect.center().y());
} }

View file

@ -14,10 +14,7 @@
#include <QHBoxLayout> #include <QHBoxLayout>
#include <QDesktopWidget> #include <QDesktopWidget>
#include <QScrollBar> #include <QScrollBar>
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
#include <QScreen> #include <QScreen>
#endif
#include "../../model/doc/document.hpp" #include "../../model/doc/document.hpp"
#include "../../model/prefs/state.hpp" #include "../../model/prefs/state.hpp"
@ -1071,11 +1068,7 @@ void CSVDoc::View::updateWidth(bool isGrowLimit, int minSubViewWidth)
if (isGrowLimit) if (isGrowLimit)
rect = dw->screenGeometry(this); rect = dw->screenGeometry(this);
else else
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
rect = QGuiApplication::screens().at(dw->screenNumber(this))->geometry(); rect = QGuiApplication::screens().at(dw->screenNumber(this))->geometry();
#else
rect = dw->screenGeometry(dw->screen(dw->screenNumber(this)));
#endif
if (!mScrollbarOnly && mScroll && mSubViews.size() > 1) if (!mScrollbarOnly && mScroll && mSubViews.size() > 1)
{ {

View file

@ -1,4 +1,3 @@
#include "dialogue.hpp" #include "dialogue.hpp"
#include <QApplication> #include <QApplication>
@ -7,10 +6,7 @@
#include <QListWidget> #include <QListWidget>
#include <QStackedWidget> #include <QStackedWidget>
#include <QListWidgetItem> #include <QListWidgetItem>
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
#include <QScreen> #include <QScreen>
#endif
#include <components/debug/debuglog.hpp> #include <components/debug/debuglog.hpp>
@ -39,11 +35,7 @@ void CSVPrefs::Dialogue::buildCategorySelector (QSplitter *main)
{ {
QString label = QString::fromUtf8 (iter->second.getKey().c_str()); QString label = QString::fromUtf8 (iter->second.getKey().c_str());
#if QT_VERSION >= QT_VERSION_CHECK(5,11,0)
maxWidth = std::max (maxWidth, metrics.horizontalAdvance (label)); maxWidth = std::max (maxWidth, metrics.horizontalAdvance (label));
#else
maxWidth = std::max (maxWidth, metrics.width (label));
#endif
list->addItem (label); list->addItem (label);
} }
@ -116,11 +108,7 @@ void CSVPrefs::Dialogue::show()
} }
else else
{ {
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
QRect scr = QGuiApplication::primaryScreen()->geometry(); QRect scr = QGuiApplication::primaryScreen()->geometry();
#else
QRect scr = QApplication::desktop()->screenGeometry();
#endif
// otherwise place at the centre of the screen // otherwise place at the centre of the screen
QPoint screenCenter = scr.center(); QPoint screenCenter = scr.center();

View file

@ -143,14 +143,9 @@ void RenderWidget::toggleRenderStats()
CompositeViewer::CompositeViewer() CompositeViewer::CompositeViewer()
: mSimulationTime(0.0) : mSimulationTime(0.0)
{ {
#if QT_VERSION >= 0x050000 // TODO: Upgrade osgQt to support osgViewer::ViewerBase::DrawThreadPerContext
// Qt5 is currently crashing and reporting "Cannot make QOpenGLContext current in a different thread" when the viewer is run multi-threaded, this is regression from Qt4 // https://gitlab.com/OpenMW/openmw/-/issues/5481
osgViewer::ViewerBase::ThreadingModel threadingModel = osgViewer::ViewerBase::SingleThreaded; setThreadingModel(osgViewer::ViewerBase::SingleThreaded);
#else
osgViewer::ViewerBase::ThreadingModel threadingModel = osgViewer::ViewerBase::DrawThreadPerContext;
#endif
setThreadingModel(threadingModel);
#if OSG_VERSION_GREATER_OR_EQUAL(3,5,5) #if OSG_VERSION_GREATER_OR_EQUAL(3,5,5)
setUseConfigureAffinity(false); setUseConfigureAffinity(false);

View file

@ -646,13 +646,8 @@ void CSVRender::WorldspaceWidget::mouseMoveEvent (QMouseEvent *event)
if (mDragging) if (mDragging)
{ {
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
mDragX = event->localPos().x(); mDragX = event->localPos().x();
mDragY = height() - event->localPos().y(); mDragY = height() - event->localPos().y();
#else
mDragX = event->posF().x();
mDragY = height() - event->posF().y();
#endif
} }
} }
else else

View file

@ -144,11 +144,7 @@ CSVTools::ReportTable::ReportTable (CSMDoc::Document& document,
: CSVWorld::DragRecordTable (document, parent), mModel (document.getReport (id)), : CSVWorld::DragRecordTable (document, parent), mModel (document.getReport (id)),
mRefreshAction (0), mRefreshState (refreshState) mRefreshAction (0), mRefreshState (refreshState)
{ {
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
horizontalHeader()->setSectionResizeMode (QHeaderView::Interactive); horizontalHeader()->setSectionResizeMode (QHeaderView::Interactive);
#else
horizontalHeader()->setResizeMode (QHeaderView::Interactive);
#endif
horizontalHeader()->setStretchLastSection (true); horizontalHeader()->setStretchLastSection (true);
verticalHeader()->hide(); verticalHeader()->hide();
setSortingEnabled (true); setSortingEnabled (true);

View file

@ -5,10 +5,7 @@
#include <QDesktopWidget> #include <QDesktopWidget>
#include <QPainter> #include <QPainter>
#include <QShowEvent> #include <QShowEvent>
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
#include <QScreen> #include <QScreen>
#endif
#include "colorpickerpopup.hpp" #include "colorpickerpopup.hpp"
@ -99,11 +96,7 @@ QPoint CSVWidget::ColorEditor::calculatePopupPosition()
{ {
QRect editorGeometry = geometry(); QRect editorGeometry = geometry();
QRect popupGeometry = mColorPicker->geometry(); QRect popupGeometry = mColorPicker->geometry();
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
QRect screenGeometry = QGuiApplication::primaryScreen()->geometry(); QRect screenGeometry = QGuiApplication::primaryScreen()->geometry();
#else
QRect screenGeometry = QApplication::desktop()->screenGeometry();
#endif
// Center the popup horizontally relative to the editor // Center the popup horizontally relative to the editor
int localPopupX = (editorGeometry.width() - popupGeometry.width()) / 2; int localPopupX = (editorGeometry.width() - popupGeometry.width()) / 2;

View file

@ -64,13 +64,8 @@ CSVWidget::SceneToolRun::SceneToolRun (SceneToolbar *parent, const QString& tool
mTable->setShowGrid (false); mTable->setShowGrid (false);
mTable->verticalHeader()->hide(); mTable->verticalHeader()->hide();
mTable->horizontalHeader()->hide(); mTable->horizontalHeader()->hide();
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
mTable->horizontalHeader()->setSectionResizeMode (0, QHeaderView::Stretch); mTable->horizontalHeader()->setSectionResizeMode (0, QHeaderView::Stretch);
mTable->horizontalHeader()->setSectionResizeMode (1, QHeaderView::ResizeToContents); mTable->horizontalHeader()->setSectionResizeMode (1, QHeaderView::ResizeToContents);
#else
mTable->horizontalHeader()->setResizeMode (0, QHeaderView::Stretch);
mTable->horizontalHeader()->setResizeMode (1, QHeaderView::ResizeToContents);
#endif
mTable->setSelectionMode (QAbstractItemView::NoSelection); mTable->setSelectionMode (QAbstractItemView::NoSelection);
layout->addWidget (mTable); layout->addWidget (mTable);

View file

@ -180,13 +180,8 @@ CSVWidget::SceneToolShapeBrush::SceneToolShapeBrush (SceneToolbar *parent, const
mTable->setShowGrid (true); mTable->setShowGrid (true);
mTable->verticalHeader()->hide(); mTable->verticalHeader()->hide();
mTable->horizontalHeader()->hide(); mTable->horizontalHeader()->hide();
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
mTable->horizontalHeader()->setSectionResizeMode (0, QHeaderView::Stretch); mTable->horizontalHeader()->setSectionResizeMode (0, QHeaderView::Stretch);
mTable->horizontalHeader()->setSectionResizeMode (1, QHeaderView::Stretch); mTable->horizontalHeader()->setSectionResizeMode (1, QHeaderView::Stretch);
#else
mTable->horizontalHeader()->setResizeMode (0, QHeaderView::Stretch);
mTable->horizontalHeader()->setResizeMode (1, QHeaderView::Stretch);
#endif
mTable->setSelectionMode (QAbstractItemView::NoSelection); mTable->setSelectionMode (QAbstractItemView::NoSelection);
layout->addWidget (mTable); layout->addWidget (mTable);

View file

@ -243,13 +243,8 @@ CSVWidget::SceneToolTextureBrush::SceneToolTextureBrush (SceneToolbar *parent, c
mTable->setShowGrid (true); mTable->setShowGrid (true);
mTable->verticalHeader()->hide(); mTable->verticalHeader()->hide();
mTable->horizontalHeader()->hide(); mTable->horizontalHeader()->hide();
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
mTable->horizontalHeader()->setSectionResizeMode (0, QHeaderView::Stretch); mTable->horizontalHeader()->setSectionResizeMode (0, QHeaderView::Stretch);
mTable->horizontalHeader()->setSectionResizeMode (1, QHeaderView::Stretch); mTable->horizontalHeader()->setSectionResizeMode (1, QHeaderView::Stretch);
#else
mTable->horizontalHeader()->setResizeMode (0, QHeaderView::Stretch);
mTable->horizontalHeader()->setResizeMode (1, QHeaderView::Stretch);
#endif
mTable->setSelectionMode (QAbstractItemView::NoSelection); mTable->setSelectionMode (QAbstractItemView::NoSelection);
layout->addWidget (mTable); layout->addWidget (mTable);

View file

@ -110,11 +110,7 @@ void CSVWorld::EnumDelegate::paint (QPainter *painter, const QStyleOptionViewIte
int valueIndex = getValueIndex(index); int valueIndex = getValueIndex(index);
if (valueIndex != -1) if (valueIndex != -1)
{ {
#if QT_VERSION >= QT_VERSION_CHECK(5,7,0)
QStyleOptionViewItem itemOption(option); QStyleOptionViewItem itemOption(option);
#else
QStyleOptionViewItemV4 itemOption(option);
#endif
itemOption.text = mValues.at(valueIndex).second; itemOption.text = mValues.at(valueIndex).second;
QApplication::style()->drawControl(QStyle::CE_ItemViewItem, &itemOption, painter); QApplication::style()->drawControl(QStyle::CE_ItemViewItem, &itemOption, painter);
} }
@ -134,13 +130,7 @@ QSize CSVWorld::EnumDelegate::sizeHint(const QStyleOptionViewItem &option, const
itemOption.state = option.state; itemOption.state = option.state;
const QString &valueText = mValues.at(valueIndex).second; const QString &valueText = mValues.at(valueIndex).second;
#if QT_VERSION >= QT_VERSION_CHECK(5,11,0)
QSize valueSize = QSize(itemOption.fontMetrics.horizontalAdvance(valueText), itemOption.fontMetrics.height()); QSize valueSize = QSize(itemOption.fontMetrics.horizontalAdvance(valueText), itemOption.fontMetrics.height());
#else
QSize valueSize = QSize(itemOption.fontMetrics.width(valueText), itemOption.fontMetrics.height());
#endif
itemOption.currentText = valueText; itemOption.currentText = valueText;
return QApplication::style()->sizeFromContents(QStyle::CT_ComboBox, &itemOption, valueSize); return QApplication::style()->sizeFromContents(QStyle::CT_ComboBox, &itemOption, valueSize);
} }

View file

@ -33,11 +33,7 @@ CSVWorld::NestedTable::NestedTable(CSMDoc::Document& document,
setSelectionBehavior (QAbstractItemView::SelectRows); setSelectionBehavior (QAbstractItemView::SelectRows);
setSelectionMode (QAbstractItemView::ExtendedSelection); setSelectionMode (QAbstractItemView::ExtendedSelection);
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
horizontalHeader()->setSectionResizeMode (QHeaderView::Interactive); horizontalHeader()->setSectionResizeMode (QHeaderView::Interactive);
#else
horizontalHeader()->setResizeMode (QHeaderView::Interactive);
#endif
verticalHeader()->hide(); verticalHeader()->hide();
int columns = model->columnCount(QModelIndex()); int columns = model->columnCount(QModelIndex());

View file

@ -205,12 +205,7 @@ bool CSVWorld::ScriptEdit::stringNeedsQuote (const std::string& id) const
void CSVWorld::ScriptEdit::setTabWidth() void CSVWorld::ScriptEdit::setTabWidth()
{ {
// Set tab width to specified number of characters using current font. // Set tab width to specified number of characters using current font.
#if QT_VERSION >= QT_VERSION_CHECK(5,11,0)
setTabStopDistance(mTabCharCount * fontMetrics().horizontalAdvance(' ')); setTabStopDistance(mTabCharCount * fontMetrics().horizontalAdvance(' '));
#else
setTabStopWidth(mTabCharCount * fontMetrics().width(' '));
#endif
} }
void CSVWorld::ScriptEdit::wrapLines(bool wrap) void CSVWorld::ScriptEdit::wrapLines(bool wrap)
@ -290,12 +285,7 @@ int CSVWorld::ScriptEdit::lineNumberAreaWidth()
++digits; ++digits;
} }
#if QT_VERSION >= QT_VERSION_CHECK(5,11,0)
int space = 3 + fontMetrics().horizontalAdvance(QLatin1Char('9')) * digits; int space = 3 + fontMetrics().horizontalAdvance(QLatin1Char('9')) * digits;
#else
int space = 3 + fontMetrics().width(QLatin1Char('9')) * digits;
#endif
return space; return space;
} }

View file

@ -83,13 +83,8 @@ CSVWorld::ScriptErrorTable::ScriptErrorTable (const CSMDoc::Document& document,
QStringList headers; QStringList headers;
headers << "Severity" << "Line" << "Description"; headers << "Severity" << "Line" << "Description";
setHorizontalHeaderLabels (headers); setHorizontalHeaderLabels (headers);
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
horizontalHeader()->setSectionResizeMode (0, QHeaderView::ResizeToContents); horizontalHeader()->setSectionResizeMode (0, QHeaderView::ResizeToContents);
horizontalHeader()->setSectionResizeMode (1, QHeaderView::ResizeToContents); horizontalHeader()->setSectionResizeMode (1, QHeaderView::ResizeToContents);
#else
horizontalHeader()->setResizeMode (0, QHeaderView::ResizeToContents);
horizontalHeader()->setResizeMode (1, QHeaderView::ResizeToContents);
#endif
horizontalHeader()->setStretchLastSection (true); horizontalHeader()->setStretchLastSection (true);
verticalHeader()->hide(); verticalHeader()->hide();
setColumnHidden (3, true); setColumnHidden (3, true);

View file

@ -262,11 +262,7 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id,
mDispatcher = new CSMWorld::CommandDispatcher (document, id, this); mDispatcher = new CSMWorld::CommandDispatcher (document, id, this);
setModel (mProxyModel); setModel (mProxyModel);
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
horizontalHeader()->setSectionResizeMode (QHeaderView::Interactive); horizontalHeader()->setSectionResizeMode (QHeaderView::Interactive);
#else
horizontalHeader()->setResizeMode (QHeaderView::Interactive);
#endif
verticalHeader()->hide(); verticalHeader()->hide();
setSelectionBehavior (QAbstractItemView::SelectRows); setSelectionBehavior (QAbstractItemView::SelectRows);
setSelectionMode (QAbstractItemView::ExtendedSelection); setSelectionMode (QAbstractItemView::ExtendedSelection);

View file

@ -21,7 +21,7 @@ add_openmw_dir (mwrender
actors objects renderingmanager animation rotatecontroller sky npcanimation vismask actors objects renderingmanager animation rotatecontroller sky npcanimation vismask
creatureanimation effectmanager util renderinginterface pathgrid rendermode weaponanimation creatureanimation effectmanager util renderinginterface pathgrid rendermode weaponanimation
bulletdebugdraw globalmap characterpreview camera localmap water terrainstorage ripplesimulation bulletdebugdraw globalmap characterpreview camera localmap water terrainstorage ripplesimulation
renderbin actoranimation landmanager navmesh actorspaths recastmesh fogmanager renderbin actoranimation landmanager navmesh actorspaths recastmesh fogmanager objectpaging
) )
add_openmw_dir (mwinput add_openmw_dir (mwinput
@ -86,7 +86,7 @@ add_openmw_dir (mwmechanics
drawstate spells activespells npcstats aipackage aisequence aipursue alchemy aiwander aitravel aifollow aiavoiddoor aibreathe drawstate spells activespells npcstats aipackage aisequence aipursue alchemy aiwander aitravel aifollow aiavoiddoor aibreathe
aicast aiescort aiface aiactivate aicombat recharge repair enchanting pathfinding pathgrid security spellcasting spellresistance aicast aiescort aiface aiactivate aicombat recharge repair enchanting pathfinding pathgrid security spellcasting spellresistance
disease pickpocket levelledlist combat steering obstacle autocalcspell difficultyscaling aicombataction actor summoning disease pickpocket levelledlist combat steering obstacle autocalcspell difficultyscaling aicombataction actor summoning
character actors objects aistate coordinateconverter trading weaponpriority spellpriority weapontype spellutil tickableeffects character actors objects aistate trading weaponpriority spellpriority weapontype spellutil tickableeffects
spellabsorption linkedeffects spellabsorption linkedeffects
) )

View file

@ -53,6 +53,8 @@ namespace MWBase
virtual bool startDialogue (const MWWorld::Ptr& actor, ResponseCallback* callback) = 0; virtual bool startDialogue (const MWWorld::Ptr& actor, ResponseCallback* callback) = 0;
virtual bool inJournal (const std::string& topicId, const std::string& infoId) = 0;
virtual void addTopic (const std::string& topic) = 0; virtual void addTopic (const std::string& topic) = 0;
/* /*
@ -79,7 +81,14 @@ namespace MWBase
virtual void goodbyeSelected() = 0; virtual void goodbyeSelected() = 0;
virtual void questionAnswered (int answer, ResponseCallback* callback) = 0; virtual void questionAnswered (int answer, ResponseCallback* callback) = 0;
enum TopicType
{
Specific = 1,
Exhausted = 2
};
virtual std::list<std::string> getAvailableTopics() = 0; virtual std::list<std::string> getAvailableTopics() = 0;
virtual int getTopicFlag(const std::string&) = 0;
virtual bool checkServiceRefused (ResponseCallback* callback) = 0; virtual bool checkServiceRefused (ResponseCallback* callback) = 0;

View file

@ -695,23 +695,16 @@ namespace MWClass
float Creature::getSpeed(const MWWorld::Ptr &ptr) const float Creature::getSpeed(const MWWorld::Ptr &ptr) const
{ {
MWMechanics::CreatureStats& stats = getCreatureStats(ptr); const MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
if (stats.isParalyzed() || stats.getKnockedDown() || stats.isDead()) if (stats.isParalyzed() || stats.getKnockedDown() || stats.isDead())
return 0.f; return 0.f;
const GMST& gmst = getGmst(); const GMST& gmst = getGmst();
float walkSpeed = gmst.fMinWalkSpeedCreature->mValue.getFloat() + 0.01f * stats.getAttribute(ESM::Attribute::Speed).getModified()
* (gmst.fMaxWalkSpeedCreature->mValue.getFloat() - gmst.fMinWalkSpeedCreature->mValue.getFloat());
const MWBase::World *world = MWBase::Environment::get().getWorld(); const MWBase::World *world = MWBase::Environment::get().getWorld();
const MWMechanics::MagicEffects &mageffects = stats.getMagicEffects(); const MWMechanics::MagicEffects &mageffects = stats.getMagicEffects();
bool running = stats.getStance(MWMechanics::CreatureStats::Stance_Run);
// The Run speed difference for creatures comes from the animation speed difference (see runStateToWalkState in character.cpp)
float runSpeed = walkSpeed;
float moveSpeed; float moveSpeed;
if(getEncumbrance(ptr) > getCapacity(ptr)) if(getEncumbrance(ptr) > getCapacity(ptr))
@ -728,19 +721,9 @@ namespace MWClass
moveSpeed = flySpeed; moveSpeed = flySpeed;
} }
else if(world->isSwimming(ptr)) else if(world->isSwimming(ptr))
{ moveSpeed = getSwimSpeed(ptr);
float swimSpeed = walkSpeed;
if(running)
swimSpeed = runSpeed;
swimSpeed *= 1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).getMagnitude();
swimSpeed *= gmst.fSwimRunBase->mValue.getFloat() + 0.01f*getSkill(ptr, ESM::Skill::Athletics) *
gmst.fSwimRunAthleticsMult->mValue.getFloat();
moveSpeed = swimSpeed;
}
else if(running)
moveSpeed = runSpeed;
else else
moveSpeed = walkSpeed; moveSpeed = getWalkSpeed(ptr);
if(getMovementSettings(ptr).mPosition[0] != 0 && getMovementSettings(ptr).mPosition[1] == 0) if(getMovementSettings(ptr).mPosition[0] != 0 && getMovementSettings(ptr).mPosition[1] == 0)
moveSpeed *= 0.75f; moveSpeed *= 0.75f;
@ -1077,4 +1060,31 @@ namespace MWClass
{ {
MWMechanics::setBaseAISetting<ESM::Creature>(id, setting, value); MWMechanics::setBaseAISetting<ESM::Creature>(id, setting, value);
} }
float Creature::getWalkSpeed(const MWWorld::Ptr& ptr) const
{
const MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
const GMST& gmst = getGmst();
return gmst.fMinWalkSpeedCreature->mValue.getFloat()
+ 0.01f * stats.getAttribute(ESM::Attribute::Speed).getModified()
* (gmst.fMaxWalkSpeedCreature->mValue.getFloat() - gmst.fMinWalkSpeedCreature->mValue.getFloat());
}
float Creature::getRunSpeed(const MWWorld::Ptr& ptr) const
{
return getWalkSpeed(ptr);
}
float Creature::getSwimSpeed(const MWWorld::Ptr& ptr) const
{
const MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
const GMST& gmst = getGmst();
const MWMechanics::MagicEffects& mageffects = stats.getMagicEffects();
return getWalkSpeed(ptr)
* (1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).getMagnitude())
* (gmst.fSwimRunBase->mValue.getFloat()
+ 0.01f * getSkill(ptr, ESM::Skill::Athletics) * gmst.fSwimRunAthleticsMult->mValue.getFloat());
}
} }

View file

@ -141,6 +141,12 @@ namespace MWClass
/// @param rendering Indicates if the scale to adjust is for the rendering mesh, or for the collision mesh /// @param rendering Indicates if the scale to adjust is for the rendering mesh, or for the collision mesh
virtual void setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const; virtual void setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const;
float getWalkSpeed(const MWWorld::Ptr& ptr) const final;
float getRunSpeed(const MWWorld::Ptr& ptr) const final;
float getSwimSpeed(const MWWorld::Ptr& ptr) const final;
}; };
} }

View file

@ -1170,16 +1170,6 @@ namespace MWClass
bool inair = !world->isOnGround(ptr) && !swimming && !world->isFlying(ptr); bool inair = !world->isOnGround(ptr) && !swimming && !world->isFlying(ptr);
running = running && (inair || MWBase::Environment::get().getMechanicsManager()->isRunning(ptr)); running = running && (inair || MWBase::Environment::get().getMechanicsManager()->isRunning(ptr));
float walkSpeed = gmst.fMinWalkSpeed->mValue.getFloat() + 0.01f*npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified()*
(gmst.fMaxWalkSpeed->mValue.getFloat() - gmst.fMinWalkSpeed->mValue.getFloat());
walkSpeed *= 1.0f - gmst.fEncumberedMoveEffect->mValue.getFloat()*normalizedEncumbrance;
walkSpeed = std::max(0.0f, walkSpeed);
if(sneaking)
walkSpeed *= gmst.fSneakSpeedMultiplier->mValue.getFloat();
float runSpeed = walkSpeed*(0.01f * getSkill(ptr, ESM::Skill::Athletics) *
gmst.fAthleticsRunBonus->mValue.getFloat() + gmst.fBaseRunMultiplier->mValue.getFloat());
float moveSpeed; float moveSpeed;
if(getEncumbrance(ptr) > getCapacity(ptr)) if(getEncumbrance(ptr) > getCapacity(ptr))
moveSpeed = 0.0f; moveSpeed = 0.0f;
@ -1194,19 +1184,11 @@ namespace MWClass
moveSpeed = flySpeed; moveSpeed = flySpeed;
} }
else if (swimming) else if (swimming)
{ moveSpeed = getSwimSpeed(ptr);
float swimSpeed = walkSpeed;
if(running)
swimSpeed = runSpeed;
swimSpeed *= 1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).getMagnitude();
swimSpeed *= gmst.fSwimRunBase->mValue.getFloat() + 0.01f*getSkill(ptr, ESM::Skill::Athletics)*
gmst.fSwimRunAthleticsMult->mValue.getFloat();
moveSpeed = swimSpeed;
}
else if (running && !sneaking) else if (running && !sneaking)
moveSpeed = runSpeed; moveSpeed = getRunSpeed(ptr);
else else
moveSpeed = walkSpeed; moveSpeed = getWalkSpeed(ptr);
if(getMovementSettings(ptr).mPosition[0] != 0 && getMovementSettings(ptr).mPosition[1] == 0) if(getMovementSettings(ptr).mPosition[0] != 0 && getMovementSettings(ptr).mPosition[1] == 0)
moveSpeed *= 0.75f; moveSpeed *= 0.75f;
@ -1671,4 +1653,56 @@ namespace MWClass
{ {
MWMechanics::setBaseAISetting<ESM::NPC>(id, setting, value); MWMechanics::setBaseAISetting<ESM::NPC>(id, setting, value);
} }
float Npc::getWalkSpeed(const MWWorld::Ptr& ptr) const
{
const GMST& gmst = getGmst();
const NpcCustomData* npcdata = static_cast<const NpcCustomData*>(ptr.getRefData().getCustomData());
const float normalizedEncumbrance = getNormalizedEncumbrance(ptr);
const bool sneaking = MWBase::Environment::get().getMechanicsManager()->isSneaking(ptr);
float walkSpeed = gmst.fMinWalkSpeed->mValue.getFloat()
+ 0.01f * npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified()
* (gmst.fMaxWalkSpeed->mValue.getFloat() - gmst.fMinWalkSpeed->mValue.getFloat());
walkSpeed *= 1.0f - gmst.fEncumberedMoveEffect->mValue.getFloat()*normalizedEncumbrance;
walkSpeed = std::max(0.0f, walkSpeed);
if(sneaking)
walkSpeed *= gmst.fSneakSpeedMultiplier->mValue.getFloat();
return walkSpeed;
}
float Npc::getRunSpeed(const MWWorld::Ptr& ptr) const
{
const GMST& gmst = getGmst();
return getWalkSpeed(ptr)
* (0.01f * getSkill(ptr, ESM::Skill::Athletics) * gmst.fAthleticsRunBonus->mValue.getFloat()
+ gmst.fBaseRunMultiplier->mValue.getFloat());
}
float Npc::getSwimSpeed(const MWWorld::Ptr& ptr) const
{
const GMST& gmst = getGmst();
const MWBase::World* world = MWBase::Environment::get().getWorld();
const MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
const NpcCustomData* npcdata = static_cast<const NpcCustomData*>(ptr.getRefData().getCustomData());
const MWMechanics::MagicEffects& mageffects = npcdata->mNpcStats.getMagicEffects();
const bool swimming = world->isSwimming(ptr);
const bool inair = !world->isOnGround(ptr) && !swimming && !world->isFlying(ptr);
const bool running = stats.getStance(MWMechanics::CreatureStats::Stance_Run)
&& (inair || MWBase::Environment::get().getMechanicsManager()->isRunning(ptr));
float swimSpeed;
if (running)
swimSpeed = getRunSpeed(ptr);
else
swimSpeed = getWalkSpeed(ptr);
swimSpeed *= 1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).getMagnitude();
swimSpeed *= gmst.fSwimRunBase->mValue.getFloat()
+ 0.01f * getSkill(ptr, ESM::Skill::Athletics) * gmst.fSwimRunAthleticsMult->mValue.getFloat();
return swimSpeed;
}
} }

View file

@ -176,6 +176,12 @@ namespace MWClass
virtual int getPrimaryFactionRank(const MWWorld::ConstPtr &ptr) const; virtual int getPrimaryFactionRank(const MWWorld::ConstPtr &ptr) const;
virtual void setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const; virtual void setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const;
float getWalkSpeed(const MWWorld::Ptr& ptr) const final;
float getRunSpeed(const MWWorld::Ptr& ptr) const final;
float getSwimSpeed(const MWWorld::Ptr& ptr) const final;
}; };
} }

View file

@ -278,6 +278,30 @@ namespace MWDialogue
} }
} }
bool DialogueManager::inJournal (const std::string& topicId, const std::string& infoId)
{
const MWDialogue::Topic *topicHistory = nullptr;
MWBase::Journal *journal = MWBase::Environment::get().getJournal();
for (auto it = journal->topicBegin(); it != journal->topicEnd(); ++it)
{
if (it->first == topicId)
{
topicHistory = &it->second;
break;
}
}
if (!topicHistory)
return false;
for(const auto& topic : *topicHistory)
{
if (topic.mInfoId == infoId)
return true;
}
return false;
}
void DialogueManager::executeTopic (const std::string& topic, ResponseCallback* callback) void DialogueManager::executeTopic (const std::string& topic, ResponseCallback* callback)
{ {
Filter filter (mActor, mChoice, mTalkedTo); Filter filter (mActor, mChoice, mTalkedTo);
@ -350,22 +374,34 @@ namespace MWDialogue
mActorKnownTopics.clear(); mActorKnownTopics.clear();
const MWWorld::Store<ESM::Dialogue> &dialogs = const auto& dialogs = MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>();
MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>();
Filter filter (mActor, -1, mTalkedTo); Filter filter (mActor, -1, mTalkedTo);
for (MWWorld::Store<ESM::Dialogue>::iterator iter = dialogs.begin(); iter != dialogs.end(); ++iter) for (const auto& dialog : dialogs)
{ {
if (iter->mType == ESM::Dialogue::Topic) if (dialog.mType == ESM::Dialogue::Topic)
{ {
if (filter.responseAvailable (*iter)) const auto* answer = filter.search(dialog, true);
auto topicId = Misc::StringUtils::lowerCase(dialog.mId);
if (answer != nullptr)
{ {
mActorKnownTopics.insert (iter->mId); int flag = 0;
if(!inJournal(topicId, answer->mId))
{
// Does this dialogue contains some actor-specific answer?
if (answer->mActor == mActor.getCellRef().getRefId())
flag |= MWBase::DialogueManager::TopicType::Specific;
}
else
flag |= MWBase::DialogueManager::TopicType::Exhausted;
mActorKnownTopics.insert (dialog.mId);
mActorKnownTopicsFlag[dialog.mId] = flag;
} }
} }
} }
} }
std::list<std::string> DialogueManager::getAvailableTopics() std::list<std::string> DialogueManager::getAvailableTopics()
@ -386,6 +422,11 @@ namespace MWDialogue
return keywordList; return keywordList;
} }
int DialogueManager::getTopicFlag(const std::string& topicId)
{
return mActorKnownTopicsFlag[topicId];
}
void DialogueManager::keywordSelected (const std::string& keyword, ResponseCallback* callback) void DialogueManager::keywordSelected (const std::string& keyword, ResponseCallback* callback)
{ {
if(!mIsInChoice) if(!mIsInChoice)

View file

@ -5,6 +5,7 @@
#include <map> #include <map>
#include <set> #include <set>
#include <unordered_map>
#include <components/compiler/streamerrorhandler.hpp> #include <components/compiler/streamerrorhandler.hpp>
#include <components/translation/translation.hpp> #include <components/translation/translation.hpp>
@ -30,6 +31,7 @@ namespace MWDialogue
ModFactionReactionMap mChangedFactionReaction; ModFactionReactionMap mChangedFactionReaction;
std::set<std::string, Misc::StringUtils::CiComp> mActorKnownTopics; std::set<std::string, Misc::StringUtils::CiComp> mActorKnownTopics;
std::unordered_map<std::string, int> mActorKnownTopicsFlag;
Translation::Storage& mTranslationDataStorage; Translation::Storage& mTranslationDataStorage;
MWScript::CompilerContext mCompilerContext; MWScript::CompilerContext mCompilerContext;
@ -71,6 +73,9 @@ namespace MWDialogue
virtual bool startDialogue (const MWWorld::Ptr& actor, ResponseCallback* callback); virtual bool startDialogue (const MWWorld::Ptr& actor, ResponseCallback* callback);
std::list<std::string> getAvailableTopics(); std::list<std::string> getAvailableTopics();
int getTopicFlag(const std::string& topicId) final;
bool inJournal (const std::string& topicId, const std::string& infoId) final;
virtual void addTopic (const std::string& topic); virtual void addTopic (const std::string& topic);

View file

@ -681,15 +681,3 @@ std::vector<const ESM::DialInfo *> MWDialogue::Filter::list (const ESM::Dialogue
return infos; return infos;
} }
bool MWDialogue::Filter::responseAvailable (const ESM::Dialogue& dialogue) const
{
for (ESM::Dialogue::InfoContainer::const_iterator iter = dialogue.mInfo.begin();
iter!=dialogue.mInfo.end(); ++iter)
{
if (testActor (*iter) && testPlayer (*iter) && testSelectStructs (*iter))
return true;
}
return false;
}

View file

@ -66,9 +66,6 @@ namespace MWDialogue
const ESM::DialInfo* search (const ESM::Dialogue& dialogue, const bool fallbackToInfoRefusal) const; const ESM::DialInfo* search (const ESM::Dialogue& dialogue, const bool fallbackToInfoRefusal) const;
///< Get a matching response for the requested dialogue. ///< Get a matching response for the requested dialogue.
/// Redirect to "Info Refusal" topic if a response fulfills all conditions but disposition. /// Redirect to "Info Refusal" topic if a response fulfills all conditions but disposition.
bool responseAvailable (const ESM::Dialogue& dialogue) const;
///< Does a matching response exist? (disposition is ignored for this check)
}; };
} }

View file

@ -351,6 +351,7 @@ namespace MWGui
mTopicsList->adjustSize(); mTopicsList->adjustSize();
updateHistory(); updateHistory();
updateTopicFormat();
mCurrentWindowSize = _sender->getSize(); mCurrentWindowSize = _sender->getSize();
} }
@ -460,7 +461,6 @@ namespace MWGui
setTitle(mPtr.getClass().getName(mPtr)); setTitle(mPtr.getClass().getName(mPtr));
updateTopics(); updateTopics();
updateTopicsPane(); // force update for new services
updateDisposition(); updateDisposition();
restock(); restock();
@ -519,8 +519,6 @@ namespace MWGui
return; return;
mIsCompanion = isCompanion(); mIsCompanion = isCompanion();
mKeywords = keyWords; mKeywords = keyWords;
updateTopicsPane();
} }
void DialogueWindow::updateTopicsPane() void DialogueWindow::updateTopicsPane()
@ -570,15 +568,16 @@ namespace MWGui
mTopicsList->addSeparator(); mTopicsList->addSeparator();
for(std::string& keyword : mKeywords) for(const auto& keyword : mKeywords)
{ {
std::string topicId = Misc::StringUtils::lowerCase(keyword);
mTopicsList->addItem(keyword); mTopicsList->addItem(keyword);
Topic* t = new Topic(keyword); Topic* t = new Topic(keyword);
t->eventTopicActivated += MyGUI::newDelegate(this, &DialogueWindow::onTopicActivated); t->eventTopicActivated += MyGUI::newDelegate(this, &DialogueWindow::onTopicActivated);
mTopicLinks[Misc::StringUtils::lowerCase(keyword)] = t; mTopicLinks[topicId] = t;
mKeywordSearch.seed(Misc::StringUtils::lowerCase(keyword), intptr_t(t)); mKeywordSearch.seed(topicId, intptr_t(t));
} }
mTopicsList->adjustSize(); mTopicsList->adjustSize();
@ -764,9 +763,28 @@ namespace MWGui
updateHistory(); updateHistory();
} }
void DialogueWindow::updateTopicFormat()
{
std::string specialColour = Settings::Manager::getString("color topic specific", "GUI");
std::string oldColour = Settings::Manager::getString("color topic exhausted", "GUI");
for (const std::string& keyword : mKeywords)
{
int flag = MWBase::Environment::get().getDialogueManager()->getTopicFlag(keyword);
MyGUI::Button* button = mTopicsList->getItemWidget(keyword);
if (!specialColour.empty() && flag & MWBase::DialogueManager::TopicType::Specific)
button->getSubWidgetText()->setTextColour(MyGUI::Colour::parse(specialColour));
else if (!oldColour.empty() && flag & MWBase::DialogueManager::TopicType::Exhausted)
button->getSubWidgetText()->setTextColour(MyGUI::Colour::parse(oldColour));
}
}
void DialogueWindow::updateTopics() void DialogueWindow::updateTopics()
{ {
setKeywords(MWBase::Environment::get().getDialogueManager()->getAvailableTopics()); setKeywords(MWBase::Environment::get().getDialogueManager()->getAvailableTopics());
updateTopicsPane();
updateTopicFormat();
} }
bool DialogueWindow::isCompanion() bool DialogueWindow::isCompanion()

View file

@ -186,6 +186,8 @@ namespace MWGui
std::unique_ptr<ResponseCallback> mCallback; std::unique_ptr<ResponseCallback> mCallback;
std::unique_ptr<ResponseCallback> mGreetingCallback; std::unique_ptr<ResponseCallback> mGreetingCallback;
void updateTopicFormat();
}; };
} }
#endif #endif

View file

@ -196,9 +196,7 @@ namespace MWGui
getWidget(mCrosshair, "Crosshair"); getWidget(mCrosshair, "Crosshair");
int mapSize = std::max(1, Settings::Manager::getInt("local map hud widget size", "Map")); LocalMapBase::init(mMinimap, mCompass);
int cellDistance = std::max(1, Settings::Manager::getInt("local map cell distance", "Map"));
LocalMapBase::init(mMinimap, mCompass, mapSize, cellDistance);
mMainWidget->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onWorldClicked); mMainWidget->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onWorldClicked);
mMainWidget->eventMouseMove += MyGUI::newDelegate(this, &HUD::onWorldMouseOver); mMainWidget->eventMouseMove += MyGUI::newDelegate(this, &HUD::onWorldMouseOver);

View file

@ -16,6 +16,7 @@
#include <components/myguiplatform/myguitexture.hpp> #include <components/myguiplatform/myguitexture.hpp>
#include <components/settings/settings.hpp> #include <components/settings/settings.hpp>
#include <components/vfs/manager.hpp> #include <components/vfs/manager.hpp>
#include <components/resource/resourcesystem.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/statemanager.hpp" #include "../mwbase/statemanager.hpp"
@ -29,9 +30,9 @@
namespace MWGui namespace MWGui
{ {
LoadingScreen::LoadingScreen(const VFS::Manager* vfs, osgViewer::Viewer* viewer) LoadingScreen::LoadingScreen(Resource::ResourceSystem* resourceSystem, osgViewer::Viewer* viewer)
: WindowBase("openmw_loading_screen.layout") : WindowBase("openmw_loading_screen.layout")
, mVFS(vfs) , mResourceSystem(resourceSystem)
, mViewer(viewer) , mViewer(viewer)
, mTargetFrameRate(120.0) , mTargetFrameRate(120.0)
, mLastWallpaperChangeTime(0.0) , mLastWallpaperChangeTime(0.0)
@ -39,6 +40,7 @@ namespace MWGui
, mLoadingOnTime(0.0) , mLoadingOnTime(0.0)
, mImportantLabel(false) , mImportantLabel(false)
, mVisible(false) , mVisible(false)
, mNestedLoadingCount(0)
, mProgress(0) , mProgress(0)
, mShowWallpaper(true) , mShowWallpaper(true)
{ {
@ -64,9 +66,9 @@ namespace MWGui
void LoadingScreen::findSplashScreens() void LoadingScreen::findSplashScreens()
{ {
const std::map<std::string, VFS::File*>& index = mVFS->getIndex(); const std::map<std::string, VFS::File*>& index = mResourceSystem->getVFS()->getIndex();
std::string pattern = "Splash/"; std::string pattern = "Splash/";
mVFS->normalizeFilename(pattern); mResourceSystem->getVFS()->normalizeFilename(pattern);
/* priority given to the left */ /* priority given to the left */
const std::array<std::string, 7> supported_extensions {{".tga", ".dds", ".ktx", ".png", ".bmp", ".jpeg", ".jpg"}}; const std::array<std::string, 7> supported_extensions {{".tga", ".dds", ".ktx", ".png", ".bmp", ".jpeg", ".jpg"}};
@ -162,15 +164,21 @@ namespace MWGui
void LoadingScreen::loadingOn(bool visible) void LoadingScreen::loadingOn(bool visible)
{ {
mLoadingOnTime = mTimer.time_m();
// Early-out if already on // Early-out if already on
if (mMainWidget->getVisible()) if (mNestedLoadingCount++ > 0 && mMainWidget->getVisible())
return; return;
mLoadingOnTime = mTimer.time_m();
// Assign dummy bounding sphere callback to avoid the bounding sphere of the entire scene being recomputed after each frame of loading // Assign dummy bounding sphere callback to avoid the bounding sphere of the entire scene being recomputed after each frame of loading
// We are already using node masks to avoid the scene from being updated/rendered, but node masks don't work for computeBound() // We are already using node masks to avoid the scene from being updated/rendered, but node masks don't work for computeBound()
mViewer->getSceneData()->setComputeBoundingSphereCallback(new DontComputeBoundCallback); mViewer->getSceneData()->setComputeBoundingSphereCallback(new DontComputeBoundCallback);
if (const osgUtil::IncrementalCompileOperation* ico = mViewer->getIncrementalCompileOperation()) {
mOldIcoMin = ico->getMinimumTimeAvailableForGLCompileAndDeletePerFrame();
mOldIcoMax = ico->getMaximumNumOfObjectsToCompilePerFrame();
}
mVisible = visible; mVisible = visible;
mLoadingBox->setVisible(mVisible); mLoadingBox->setVisible(mVisible);
setVisible(true); setVisible(true);
@ -194,6 +202,8 @@ namespace MWGui
void LoadingScreen::loadingOff() void LoadingScreen::loadingOff()
{ {
if (--mNestedLoadingCount > 0)
return;
mLoadingBox->setVisible(true); // restore mLoadingBox->setVisible(true); // restore
if (mLastRenderTime < mLoadingOnTime) if (mLastRenderTime < mLoadingOnTime)
@ -215,6 +225,12 @@ namespace MWGui
//std::cout << "loading took " << mTimer.time_m() - mLoadingOnTime << std::endl; //std::cout << "loading took " << mTimer.time_m() - mLoadingOnTime << std::endl;
setVisible(false); setVisible(false);
if (osgUtil::IncrementalCompileOperation* ico = mViewer->getIncrementalCompileOperation())
{
ico->setMinimumTimeAvailableForGLCompileAndDeletePerFrame(mOldIcoMin);
ico->setMaximumNumOfObjectsToCompilePerFrame(mOldIcoMax);
}
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Loading); MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Loading);
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_LoadingWallpaper); MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_LoadingWallpaper);
} }
@ -336,7 +352,13 @@ namespace MWGui
MWBase::Environment::get().getInputManager()->update(0, true, true); MWBase::Environment::get().getInputManager()->update(0, true, true);
//osg::Timer timer; mResourceSystem->reportStats(mViewer->getFrameStamp()->getFrameNumber(), mViewer->getViewerStats());
if (osgUtil::IncrementalCompileOperation* ico = mViewer->getIncrementalCompileOperation())
{
ico->setMinimumTimeAvailableForGLCompileAndDeletePerFrame(1.f/getTargetFrameRate());
ico->setMaximumNumOfObjectsToCompilePerFrame(1000);
}
// at the time this function is called we are in the middle of a frame, // at the time this function is called we are in the middle of a frame,
// so out of order calls are necessary to get a correct frameNumber for the next frame. // so out of order calls are necessary to get a correct frameNumber for the next frame.
// refer to the advance() and frame() order in Engine::go() // refer to the advance() and frame() order in Engine::go()
@ -344,10 +366,6 @@ namespace MWGui
mViewer->updateTraversal(); mViewer->updateTraversal();
mViewer->renderingTraversals(); mViewer->renderingTraversals();
mViewer->advance(mViewer->getFrameStamp()->getSimulationTime()); mViewer->advance(mViewer->getFrameStamp()->getSimulationTime());
//std::cout << "frame took " << timer.time_m() << std::endl;
//if (mViewer->getIncrementalCompileOperation())
//std::cout << "num to compile " << mViewer->getIncrementalCompileOperation()->getToCompile().size() << std::endl;
mLastRenderTime = mTimer.time_m(); mLastRenderTime = mTimer.time_m();
} }

View file

@ -20,9 +20,9 @@ namespace osg
class Texture2D; class Texture2D;
} }
namespace VFS namespace Resource
{ {
class Manager; class ResourceSystem;
} }
namespace MWGui namespace MWGui
@ -32,7 +32,7 @@ namespace MWGui
class LoadingScreen : public WindowBase, public Loading::Listener class LoadingScreen : public WindowBase, public Loading::Listener
{ {
public: public:
LoadingScreen(const VFS::Manager* vfs, osgViewer::Viewer* viewer); LoadingScreen(Resource::ResourceSystem* resourceSystem, osgViewer::Viewer* viewer);
virtual ~LoadingScreen(); virtual ~LoadingScreen();
/// Overridden from Loading::Listener, see the Loading::Listener documentation for usage details /// Overridden from Loading::Listener, see the Loading::Listener documentation for usage details
@ -53,7 +53,7 @@ namespace MWGui
void setupCopyFramebufferToTextureCallback(); void setupCopyFramebufferToTextureCallback();
const VFS::Manager* mVFS; Resource::ResourceSystem* mResourceSystem;
osg::ref_ptr<osgViewer::Viewer> mViewer; osg::ref_ptr<osgViewer::Viewer> mViewer;
double mTargetFrameRate; double mTargetFrameRate;
@ -66,10 +66,13 @@ namespace MWGui
bool mImportantLabel; bool mImportantLabel;
bool mVisible; bool mVisible;
int mNestedLoadingCount;
size_t mProgress; size_t mProgress;
bool mShowWallpaper; bool mShowWallpaper;
float mOldIcoMin = 0.f;
unsigned int mOldIcoMax = 0;
MyGUI::Widget* mLoadingBox; MyGUI::Widget* mLoadingBox;

View file

@ -214,13 +214,13 @@ namespace MWGui
*/ */
} }
void LocalMapBase::init(MyGUI::ScrollView* widget, MyGUI::ImageBox* compass, int mapWidgetSize, int cellDistance) void LocalMapBase::init(MyGUI::ScrollView* widget, MyGUI::ImageBox* compass)
{ {
mLocalMap = widget; mLocalMap = widget;
mCompass = compass; mCompass = compass;
mMapWidgetSize = mapWidgetSize; mMapWidgetSize = std::max(1, Settings::Manager::getInt("local map widget size", "Map"));
mCellDistance = cellDistance; mCellDistance = Constants::CellGridRadius;
mNumCells = cellDistance * 2 + 1; mNumCells = mCellDistance * 2 + 1;
mLocalMap->setCanvasSize(mMapWidgetSize*mNumCells, mMapWidgetSize*mNumCells); mLocalMap->setCanvasSize(mMapWidgetSize*mNumCells, mMapWidgetSize*mNumCells);
@ -756,9 +756,7 @@ namespace MWGui
mEventBoxLocal->eventMouseButtonPressed += MyGUI::newDelegate(this, &MapWindow::onDragStart); mEventBoxLocal->eventMouseButtonPressed += MyGUI::newDelegate(this, &MapWindow::onDragStart);
mEventBoxLocal->eventMouseButtonDoubleClick += MyGUI::newDelegate(this, &MapWindow::onMapDoubleClicked); mEventBoxLocal->eventMouseButtonDoubleClick += MyGUI::newDelegate(this, &MapWindow::onMapDoubleClicked);
int mapSize = std::max(1, Settings::Manager::getInt("local map widget size", "Map")); LocalMapBase::init(mLocalMap, mPlayerArrowLocal);
int cellDistance = std::max(1, Settings::Manager::getInt("local map cell distance", "Map"));
LocalMapBase::init(mLocalMap, mPlayerArrowLocal, mapSize, cellDistance);
mGlobalMap->setVisible(mGlobal); mGlobalMap->setVisible(mGlobal);
mLocalMap->setVisible(!mGlobal); mLocalMap->setVisible(!mGlobal);

View file

@ -94,7 +94,7 @@ namespace MWGui
public: public:
LocalMapBase(CustomMarkerCollection& markers, MWRender::LocalMap* localMapRender, bool fogOfWarEnabled = true); LocalMapBase(CustomMarkerCollection& markers, MWRender::LocalMap* localMapRender, bool fogOfWarEnabled = true);
virtual ~LocalMapBase(); virtual ~LocalMapBase();
void init(MyGUI::ScrollView* widget, MyGUI::ImageBox* compass, int mapWidgetSize, int cellDistance); void init(MyGUI::ScrollView* widget, MyGUI::ImageBox* compass);
void setCellPrefix(const std::string& prefix); void setCellPrefix(const std::string& prefix);
void setActiveCell(const int x, const int y, bool interior=false); void setActiveCell(const int x, const int y, bool interior=false);

View file

@ -132,6 +132,13 @@ namespace MWGui
MWBase::Environment::get().getWindowManager()->injectKeyRelease(MyGUI::KeyCode::None); MWBase::Environment::get().getWindowManager()->injectKeyRelease(MyGUI::KeyCode::None);
} }
void SaveGameDialog::onClose()
{
mSaveList->setIndexSelected(MyGUI::ITEM_NONE);
WindowModal::onClose();
}
void SaveGameDialog::onOpen() void SaveGameDialog::onOpen()
{ {
WindowModal::onOpen(); WindowModal::onOpen();

View file

@ -20,6 +20,7 @@ namespace MWGui
SaveGameDialog(); SaveGameDialog();
virtual void onOpen(); virtual void onOpen();
virtual void onClose();
void setLoadOrSave(bool load); void setLoadOrSave(bool load);

View file

@ -242,7 +242,7 @@ namespace MWGui
mKeyboardNavigation->setEnabled(keyboardNav); mKeyboardNavigation->setEnabled(keyboardNav);
Gui::ImageButton::setDefaultNeedKeyFocus(keyboardNav); Gui::ImageButton::setDefaultNeedKeyFocus(keyboardNav);
mLoadingScreen = new LoadingScreen(mResourceSystem->getVFS(), mViewer); mLoadingScreen = new LoadingScreen(mResourceSystem, mViewer);
mWindows.push_back(mLoadingScreen); mWindows.push_back(mLoadingScreen);
//set up the hardware cursor manager //set up the hardware cursor manager

View file

@ -36,6 +36,7 @@ namespace MWInput
, mSneakToggleShortcutTimer(0.f) , mSneakToggleShortcutTimer(0.f)
, mGamepadZoom(0) , mGamepadZoom(0)
, mGamepadGuiCursorEnabled(true) , mGamepadGuiCursorEnabled(true)
, mGuiCursorEnabled(true)
, mJoystickLastUsed(false) , mJoystickLastUsed(false)
, mSneakGamepadShortcut(false) , mSneakGamepadShortcut(false)
, mGamepadPreviewMode(false) , mGamepadPreviewMode(false)

View file

@ -56,8 +56,8 @@ namespace MWInput
float mSneakToggleShortcutTimer; float mSneakToggleShortcutTimer;
float mGamepadZoom; float mGamepadZoom;
bool mGamepadGuiCursorEnabled; bool mGamepadGuiCursorEnabled;
bool mJoystickLastUsed;
bool mGuiCursorEnabled; bool mGuiCursorEnabled;
bool mJoystickLastUsed;
bool mSneakGamepadShortcut; bool mSneakGamepadShortcut;
bool mGamepadPreviewMode; bool mGamepadPreviewMode;
}; };

View file

@ -134,7 +134,7 @@ void adjustCommandedActor (const MWWorld::Ptr& actor)
auto it = stats.getAiSequence().begin(); auto it = stats.getAiSequence().begin();
for (; it != stats.getAiSequence().end(); ++it) for (; it != stats.getAiSequence().end(); ++it)
{ {
if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdFollow && if ((*it)->getTypeId() == MWMechanics::AiPackageTypeId::Follow &&
static_cast<const MWMechanics::AiFollow*>(it->get())->isCommanded()) static_cast<const MWMechanics::AiFollow*>(it->get())->isCommanded())
{ {
hasCommandPackage = true; hasCommandPackage = true;
@ -486,7 +486,7 @@ namespace MWMechanics
return; return;
const MWMechanics::AiSequence& seq = stats.getAiSequence(); const MWMechanics::AiSequence& seq = stats.getAiSequence();
if (seq.isInCombat() || seq.hasPackage(AiPackage::TypeIdFollow) || seq.hasPackage(AiPackage::TypeIdEscort)) if (seq.isInCombat() || seq.hasPackage(AiPackageTypeId::Follow) || seq.hasPackage(AiPackageTypeId::Escort))
return; return;
const osg::Vec3f playerPos(getPlayer().getRefData().getPosition().asVec3()); const osg::Vec3f playerPos(getPlayer().getRefData().getPosition().asVec3());
@ -530,11 +530,11 @@ namespace MWMechanics
CreatureStats &stats = actor.getClass().getCreatureStats(actor); CreatureStats &stats = actor.getClass().getCreatureStats(actor);
const MWMechanics::AiSequence& seq = stats.getAiSequence(); const MWMechanics::AiSequence& seq = stats.getAiSequence();
int packageId = seq.getTypeId(); const auto packageId = seq.getTypeId();
if (seq.isInCombat() || if (seq.isInCombat() ||
MWBase::Environment::get().getWorld()->isSwimming(actor) || MWBase::Environment::get().getWorld()->isSwimming(actor) ||
(packageId != AiPackage::TypeIdWander && packageId != AiPackage::TypeIdTravel && packageId != -1)) (packageId != AiPackageTypeId::Wander && packageId != AiPackageTypeId::Travel && packageId != AiPackageTypeId::None))
{ {
actorState.setTurningToPlayer(false); actorState.setTurningToPlayer(false);
actorState.setGreetingTimer(0); actorState.setGreetingTimer(0);
@ -769,7 +769,7 @@ namespace MWMechanics
followerOrEscorter = true; followerOrEscorter = true;
break; break;
} }
else if (package->getTypeId() != MWMechanics::AiPackage::TypeIdCombat) else if (package->getTypeId() != MWMechanics::AiPackageTypeId::Combat)
break; break;
} }
if (!followerOrEscorter) if (!followerOrEscorter)
@ -1323,7 +1323,7 @@ namespace MWMechanics
if (!isPlayer && stats.getTimeToStartDrowning() < fHoldBreathTime / 2) if (!isPlayer && stats.getTimeToStartDrowning() < fHoldBreathTime / 2)
{ {
AiSequence& seq = ptr.getClass().getCreatureStats(ptr).getAiSequence(); AiSequence& seq = ptr.getClass().getCreatureStats(ptr).getAiSequence();
if (seq.getTypeId() != AiPackage::TypeIdBreathe) //Only add it once if (seq.getTypeId() != AiPackageTypeId::Breathe) //Only add it once
seq.stack(AiBreathe(), ptr); seq.stack(AiBreathe(), ptr);
} }
@ -1485,7 +1485,7 @@ namespace MWMechanics
if (player.getClass().getNpcStats(player).isWerewolf()) if (player.getClass().getNpcStats(player).isWerewolf())
return; return;
if (ptr.getClass().isClass(ptr, "Guard") && creatureStats.getAiSequence().getTypeId() != AiPackage::TypeIdPursue && !creatureStats.getAiSequence().isInCombat() if (ptr.getClass().isClass(ptr, "Guard") && creatureStats.getAiSequence().getTypeId() != AiPackageTypeId::Pursue && !creatureStats.getAiSequence().isInCombat()
&& creatureStats.getMagicEffects().get(ESM::MagicEffect::CalmHumanoid).getMagnitude() == 0) && creatureStats.getMagicEffects().get(ESM::MagicEffect::CalmHumanoid).getMagnitude() == 0)
{ {
const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore();
@ -1945,7 +1945,7 @@ namespace MWMechanics
// 3. Player character does not use headtracking in the 1st-person view // 3. Player character does not use headtracking in the 1st-person view
if (!stats.getKnockedDown() && if (!stats.getKnockedDown() &&
!stats.getAiSequence().isInCombat() && !stats.getAiSequence().isInCombat() &&
!stats.getAiSequence().hasPackage(AiPackage::TypeIdPursue) && !stats.getAiSequence().hasPackage(AiPackageTypeId::Pursue) &&
!firstPersonPlayer) !firstPersonPlayer)
{ {
for(PtrActorMap::iterator it(mActors.begin()); it != mActors.end(); ++it) for(PtrActorMap::iterator it(mActors.begin()); it != mActors.end(); ++it)
@ -2563,7 +2563,7 @@ namespace MWMechanics
} }
break; break;
} }
else if (package->getTypeId() != AiPackage::TypeIdCombat && package->getTypeId() != AiPackage::TypeIdWander) else if (package->getTypeId() != AiPackageTypeId::Combat && package->getTypeId() != AiPackageTypeId::Wander)
break; break;
} }
} }
@ -2589,7 +2589,7 @@ namespace MWMechanics
{ {
if (package->followTargetThroughDoors() && package->getTarget() == actor) if (package->followTargetThroughDoors() && package->getTarget() == actor)
list.push_back(iteratedActor); list.push_back(iteratedActor);
else if (package->getTypeId() != AiPackage::TypeIdCombat && package->getTypeId() != AiPackage::TypeIdWander) else if (package->getTypeId() != AiPackageTypeId::Combat && package->getTypeId() != AiPackageTypeId::Wander)
break; break;
} }
} }
@ -2655,7 +2655,7 @@ namespace MWMechanics
list.push_back(static_cast<const AiFollow*>(package.get())->getFollowIndex()); list.push_back(static_cast<const AiFollow*>(package.get())->getFollowIndex());
break; break;
} }
else if (package->getTypeId() != AiPackage::TypeIdCombat && package->getTypeId() != AiPackage::TypeIdWander) else if (package->getTypeId() != AiPackageTypeId::Combat && package->getTypeId() != AiPackageTypeId::Wander)
break; break;
} }
} }

View file

@ -51,7 +51,7 @@ namespace MWMechanics
bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final;
static constexpr TypeId getTypeId() { return TypeIdActivate; } static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::Activate; }
void writeState(ESM::AiSequence::AiSequence& sequence) const final; void writeState(ESM::AiSequence::AiSequence& sequence) const final;

View file

@ -63,7 +63,7 @@ bool MWMechanics::AiAvoidDoor::execute (const MWWorld::Ptr& actor, CharacterCont
for(std::vector<MWWorld::Ptr>::iterator it = actors.begin(); it != actors.end(); ++it) { for(std::vector<MWWorld::Ptr>::iterator it = actors.begin(); it != actors.end(); ++it) {
if(*it != getPlayer()) { //Not the player if(*it != getPlayer()) { //Not the player
MWMechanics::AiSequence& seq = it->getClass().getCreatureStats(*it).getAiSequence(); MWMechanics::AiSequence& seq = it->getClass().getCreatureStats(*it).getAiSequence();
if(seq.getTypeId() != MWMechanics::AiPackage::TypeIdAvoidDoor) { //Only add it once if(seq.getTypeId() != MWMechanics::AiPackageTypeId::AvoidDoor) { //Only add it once
seq.stack(MWMechanics::AiAvoidDoor(mDoorPtr),*it); seq.stack(MWMechanics::AiAvoidDoor(mDoorPtr),*it);
} }
} }

View file

@ -24,7 +24,7 @@ namespace MWMechanics
bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final;
static constexpr TypeId getTypeId() { return TypeIdAvoidDoor; } static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::AvoidDoor; }
static constexpr Options makeDefaultOptions() static constexpr Options makeDefaultOptions()
{ {

View file

@ -12,7 +12,7 @@ namespace MWMechanics
public: public:
bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final;
static constexpr TypeId getTypeId() { return TypeIdBreathe; } static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::Breathe; }
static constexpr Options makeDefaultOptions() static constexpr Options makeDefaultOptions()
{ {

View file

@ -17,7 +17,7 @@ namespace MWMechanics
bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final;
static constexpr TypeId getTypeId() { return TypeIdCast; } static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::Cast; }
MWWorld::Ptr getTarget() const final; MWWorld::Ptr getTarget() const final;

View file

@ -1,6 +1,7 @@
#include "aicombat.hpp" #include "aicombat.hpp"
#include <components/misc/rng.hpp> #include <components/misc/rng.hpp>
#include <components/misc/coordinateconverter.hpp>
#include <components/esm/aisequence.hpp> #include <components/esm/aisequence.hpp>
@ -36,7 +37,6 @@
#include "movement.hpp" #include "movement.hpp"
#include "character.hpp" #include "character.hpp"
#include "aicombataction.hpp" #include "aicombataction.hpp"
#include "coordinateconverter.hpp"
#include "actorutil.hpp" #include "actorutil.hpp"
namespace namespace
@ -370,7 +370,7 @@ namespace MWMechanics
if (pathgrid && !actor.getClass().isPureWaterCreature(actor)) if (pathgrid && !actor.getClass().isPureWaterCreature(actor))
{ {
ESM::Pathgrid::PointList points; ESM::Pathgrid::PointList points;
CoordinateConverter coords(storage.mCell->getCell()); Misc::CoordinateConverter coords(storage.mCell->getCell());
osg::Vec3f localPos = actor.getRefData().getPosition().asVec3(); osg::Vec3f localPos = actor.getRefData().getPosition().asVec3();
coords.toLocal(localPos); coords.toLocal(localPos);

View file

@ -104,7 +104,7 @@ namespace MWMechanics
bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final;
static constexpr TypeId getTypeId() { return TypeIdCombat; } static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::Combat; }
static constexpr Options makeDefaultOptions() static constexpr Options makeDefaultOptions()
{ {

View file

@ -32,7 +32,7 @@ namespace MWMechanics
bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final;
static constexpr TypeId getTypeId() { return TypeIdEscort; } static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::Escort; }
static constexpr Options makeDefaultOptions() static constexpr Options makeDefaultOptions()
{ {

View file

@ -12,7 +12,7 @@ namespace MWMechanics
bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final;
static constexpr TypeId getTypeId() { return TypeIdFace; } static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::Face; }
static constexpr Options makeDefaultOptions() static constexpr Options makeDefaultOptions()
{ {

View file

@ -55,7 +55,7 @@ namespace MWMechanics
bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final;
static constexpr TypeId getTypeId() { return TypeIdFollow; } static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::Follow; }
static constexpr Options makeDefaultOptions() static constexpr Options makeDefaultOptions()
{ {

View file

@ -4,6 +4,7 @@
#include <components/esm/loadland.hpp> #include <components/esm/loadland.hpp>
#include <components/esm/loadmgef.hpp> #include <components/esm/loadmgef.hpp>
#include <components/detournavigator/navigator.hpp> #include <components/detournavigator/navigator.hpp>
#include <components/misc/coordinateconverter.hpp>
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
@ -20,11 +21,10 @@
#include "movement.hpp" #include "movement.hpp"
#include "steering.hpp" #include "steering.hpp"
#include "actorutil.hpp" #include "actorutil.hpp"
#include "coordinateconverter.hpp"
#include <osg/Quat> #include <osg/Quat>
MWMechanics::AiPackage::AiPackage(TypeId typeId, const Options& options) : MWMechanics::AiPackage::AiPackage(AiPackageTypeId typeId, const Options& options) :
mTypeId(typeId), mTypeId(typeId),
mOptions(options), mOptions(options),
mTimer(AI_REACTION_TIME + 1.0f), // to force initial pathbuild mTimer(AI_REACTION_TIME + 1.0f), // to force initial pathbuild
@ -114,7 +114,7 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f&
{ {
const auto pathfindingHalfExtents = world->getPathfindingHalfExtents(actor); const auto pathfindingHalfExtents = world->getPathfindingHalfExtents(actor);
mPathFinder.buildPath(actor, position, dest, actor.getCell(), getPathGridGraph(actor.getCell()), mPathFinder.buildPath(actor, position, dest, actor.getCell(), getPathGridGraph(actor.getCell()),
pathfindingHalfExtents, getNavigatorFlags(actor)); pathfindingHalfExtents, getNavigatorFlags(actor), getAreaCosts(actor));
mRotateOnTheRunChecks = 3; mRotateOnTheRunChecks = 3;
// give priority to go directly on target if there is minimal opportunity // give priority to go directly on target if there is minimal opportunity
@ -216,7 +216,7 @@ namespace
void MWMechanics::AiPackage::openDoors(const MWWorld::Ptr& actor) void MWMechanics::AiPackage::openDoors(const MWWorld::Ptr& actor)
{ {
// note: AiWander currently does not open doors // note: AiWander currently does not open doors
if (getTypeId() == TypeIdWander) if (getTypeId() == AiPackageTypeId::Wander)
return; return;
if (mPathFinder.getPathSize() == 0) if (mPathFinder.getPathSize() == 0)
@ -341,10 +341,9 @@ bool MWMechanics::AiPackage::isNearInactiveCell(osg::Vec3f position)
if (playerCell->isExterior()) if (playerCell->isExterior())
{ {
// get actor's distance from origin of center cell // get actor's distance from origin of center cell
CoordinateConverter(playerCell).toLocal(position); Misc::CoordinateConverter(playerCell).toLocal(position);
// currently assumes 3 x 3 grid for exterior cells, with player at center cell. // currently assumes 3 x 3 grid for exterior cells, with player at center cell.
// ToDo: (Maybe) use "exterior cell load distance" setting to get count of actual active cells
// AI shuts down actors before they reach edges of 3 x 3 grid. // AI shuts down actors before they reach edges of 3 x 3 grid.
const float distanceFromEdge = 200.0; const float distanceFromEdge = 200.0;
float minThreshold = (-1.0f * ESM::Land::REAL_SIZE) + distanceFromEdge; float minThreshold = (-1.0f * ESM::Land::REAL_SIZE) + distanceFromEdge;
@ -391,14 +390,38 @@ DetourNavigator::Flags MWMechanics::AiPackage::getNavigatorFlags(const MWWorld::
const MWWorld::Class& actorClass = actor.getClass(); const MWWorld::Class& actorClass = actor.getClass();
DetourNavigator::Flags result = DetourNavigator::Flag_none; DetourNavigator::Flags result = DetourNavigator::Flag_none;
if (actorClass.isPureWaterCreature(actor) || (getTypeId() != TypeIdWander && actorClass.canSwim(actor))) if (actorClass.isPureWaterCreature(actor) || (getTypeId() != AiPackageTypeId::Wander && actorClass.canSwim(actor)))
result |= DetourNavigator::Flag_swim; result |= DetourNavigator::Flag_swim;
if (actorClass.canWalk(actor)) if (actorClass.canWalk(actor))
result |= DetourNavigator::Flag_walk; result |= DetourNavigator::Flag_walk;
if (actorClass.isBipedal(actor) && getTypeId() != TypeIdWander) if (actorClass.isBipedal(actor) && getTypeId() != AiPackageTypeId::Wander)
result |= DetourNavigator::Flag_openDoor; result |= DetourNavigator::Flag_openDoor;
return result; return result;
} }
DetourNavigator::AreaCosts MWMechanics::AiPackage::getAreaCosts(const MWWorld::Ptr& actor) const
{
DetourNavigator::AreaCosts costs;
const DetourNavigator::Flags flags = getNavigatorFlags(actor);
const MWWorld::Class& actorClass = actor.getClass();
if (flags & DetourNavigator::Flag_swim)
costs.mWater = costs.mWater / actorClass.getSwimSpeed(actor);
if (flags & DetourNavigator::Flag_walk)
{
float walkCost;
if (getTypeId() == AiPackageTypeId::Wander)
walkCost = 1.0 / actorClass.getWalkSpeed(actor);
else
walkCost = 1.0 / actorClass.getRunSpeed(actor);
costs.mDoor = costs.mDoor * walkCost;
costs.mPathgrid = costs.mPathgrid * walkCost;
costs.mGround = costs.mGround * walkCost;
}
return costs;
}

View file

@ -4,10 +4,12 @@
#include <memory> #include <memory>
#include <components/esm/defs.hpp> #include <components/esm/defs.hpp>
#include <components/detournavigator/areatype.hpp>
#include "pathfinding.hpp" #include "pathfinding.hpp"
#include "obstacle.hpp" #include "obstacle.hpp"
#include "aistate.hpp" #include "aistate.hpp"
#include "aipackagetypeid.hpp"
namespace MWWorld namespace MWWorld
{ {
@ -35,26 +37,6 @@ namespace MWMechanics
class AiPackage class AiPackage
{ {
public: public:
///Enumerates the various AITypes available
enum TypeId {
TypeIdNone = -1,
TypeIdWander = 0,
TypeIdTravel = 1,
TypeIdEscort = 2,
TypeIdFollow = 3,
TypeIdActivate = 4,
// These 5 are not really handled as Ai Packages in the MW engine
// For compatibility do *not* return these in the getCurrentAiPackage script function..
TypeIdCombat = 5,
TypeIdPursue = 6,
TypeIdAvoidDoor = 7,
TypeIdFace = 8,
TypeIdBreathe = 9,
TypeIdInternalTravel = 10,
TypeIdCast = 11
};
struct Options struct Options
{ {
unsigned int mPriority = 0; unsigned int mPriority = 0;
@ -79,7 +61,7 @@ namespace MWMechanics
} }
}; };
AiPackage(TypeId typeId, const Options& options); AiPackage(AiPackageTypeId typeId, const Options& options);
virtual ~AiPackage() = default; virtual ~AiPackage() = default;
@ -97,7 +79,7 @@ namespace MWMechanics
/// Returns the TypeID of the AiPackage /// Returns the TypeID of the AiPackage
/// \see enum TypeId /// \see enum TypeId
TypeId getTypeId() const { return mTypeId; } AiPackageTypeId getTypeId() const { return mTypeId; }
/// Higher number is higher priority (0 being the lowest) /// Higher number is higher priority (0 being the lowest)
unsigned int getPriority() const { return mOptions.mPriority; } unsigned int getPriority() const { return mOptions.mPriority; }
@ -167,7 +149,9 @@ namespace MWMechanics
DetourNavigator::Flags getNavigatorFlags(const MWWorld::Ptr& actor) const; DetourNavigator::Flags getNavigatorFlags(const MWWorld::Ptr& actor) const;
const TypeId mTypeId; DetourNavigator::AreaCosts getAreaCosts(const MWWorld::Ptr& actor) const;
const AiPackageTypeId mTypeId;
const Options mOptions; const Options mOptions;
// TODO: all this does not belong here, move into temporary storage // TODO: all this does not belong here, move into temporary storage

View file

@ -0,0 +1,28 @@
#ifndef GAME_MWMECHANICS_AIPACKAGETYPEID_H
#define GAME_MWMECHANICS_AIPACKAGETYPEID_H
namespace MWMechanics
{
///Enumerates the various AITypes available
enum class AiPackageTypeId
{
None = -1,
Wander = 0,
Travel = 1,
Escort = 2,
Follow = 3,
Activate = 4,
// These 5 are not really handled as Ai Packages in the MW engine
// For compatibility do *not* return these in the getCurrentAiPackage script function..
Combat = 5,
Pursue = 6,
AvoidDoor = 7,
Face = 8,
Breathe = 9,
InternalTravel = 10,
Cast = 11
};
}
#endif

View file

@ -28,7 +28,7 @@ namespace MWMechanics
bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final;
static constexpr TypeId getTypeId() { return TypeIdPursue; } static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::Pursue; }
static constexpr Options makeDefaultOptions() static constexpr Options makeDefaultOptions()
{ {

View file

@ -33,7 +33,7 @@ void AiSequence::copy (const AiSequence& sequence)
sequence.mAiState.copy<AiWanderStorage>(mAiState); sequence.mAiState.copy<AiWanderStorage>(mAiState);
} }
AiSequence::AiSequence() : mDone (false), mRepeat(false), mLastAiPackage(-1) {} AiSequence::AiSequence() : mDone (false), mRepeat(false), mLastAiPackage(AiPackageTypeId::None) {}
AiSequence::AiSequence (const AiSequence& sequence) AiSequence::AiSequence (const AiSequence& sequence)
{ {
@ -61,17 +61,17 @@ AiSequence::~AiSequence()
clear(); clear();
} }
int AiSequence::getTypeId() const AiPackageTypeId AiSequence::getTypeId() const
{ {
if (mPackages.empty()) if (mPackages.empty())
return -1; return AiPackageTypeId::None;
return mPackages.front()->getTypeId(); return mPackages.front()->getTypeId();
} }
bool AiSequence::getCombatTarget(MWWorld::Ptr &targetActor) const bool AiSequence::getCombatTarget(MWWorld::Ptr &targetActor) const
{ {
if (getTypeId() != AiPackage::TypeIdCombat) if (getTypeId() != AiPackageTypeId::Combat)
return false; return false;
targetActor = mPackages.front()->getTarget(); targetActor = mPackages.front()->getTarget();
@ -83,7 +83,7 @@ bool AiSequence::getCombatTargets(std::vector<MWWorld::Ptr> &targetActors) const
{ {
for (auto it = mPackages.begin(); it != mPackages.end(); ++it) for (auto it = mPackages.begin(); it != mPackages.end(); ++it)
{ {
if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdCombat) if ((*it)->getTypeId() == MWMechanics::AiPackageTypeId::Combat)
targetActors.push_back((*it)->getTarget()); targetActors.push_back((*it)->getTarget());
} }
@ -118,7 +118,7 @@ bool AiSequence::isInCombat() const
{ {
for (auto it = mPackages.begin(); it != mPackages.end(); ++it) for (auto it = mPackages.begin(); it != mPackages.end(); ++it)
{ {
if ((*it)->getTypeId() == AiPackage::TypeIdCombat) if ((*it)->getTypeId() == AiPackageTypeId::Combat)
return true; return true;
} }
return false; return false;
@ -128,7 +128,7 @@ bool AiSequence::isEngagedWithActor() const
{ {
for (auto it = mPackages.begin(); it != mPackages.end(); ++it) for (auto it = mPackages.begin(); it != mPackages.end(); ++it)
{ {
if ((*it)->getTypeId() == AiPackage::TypeIdCombat) if ((*it)->getTypeId() == AiPackageTypeId::Combat)
{ {
MWWorld::Ptr target2 = (*it)->getTarget(); MWWorld::Ptr target2 = (*it)->getTarget();
if (!target2.isEmpty() && target2.getClass().isNpc()) if (!target2.isEmpty() && target2.getClass().isNpc())
@ -138,7 +138,7 @@ bool AiSequence::isEngagedWithActor() const
return false; return false;
} }
bool AiSequence::hasPackage(int typeId) const bool AiSequence::hasPackage(AiPackageTypeId typeId) const
{ {
for (auto it = mPackages.begin(); it != mPackages.end(); ++it) for (auto it = mPackages.begin(); it != mPackages.end(); ++it)
{ {
@ -152,7 +152,7 @@ bool AiSequence::isInCombat(const MWWorld::Ptr &actor) const
{ {
for (auto it = mPackages.begin(); it != mPackages.end(); ++it) for (auto it = mPackages.begin(); it != mPackages.end(); ++it)
{ {
if ((*it)->getTypeId() == AiPackage::TypeIdCombat) if ((*it)->getTypeId() == AiPackageTypeId::Combat)
{ {
if ((*it)->getTarget() == actor) if ((*it)->getTarget() == actor)
return true; return true;
@ -165,7 +165,7 @@ void AiSequence::stopCombat()
{ {
for(auto it = mPackages.begin(); it != mPackages.end(); ) for(auto it = mPackages.begin(); it != mPackages.end(); )
{ {
if ((*it)->getTypeId() == AiPackage::TypeIdCombat) if ((*it)->getTypeId() == AiPackageTypeId::Combat)
{ {
it = mPackages.erase(it); it = mPackages.erase(it);
} }
@ -178,7 +178,7 @@ void AiSequence::stopPursuit()
{ {
for(auto it = mPackages.begin(); it != mPackages.end(); ) for(auto it = mPackages.begin(); it != mPackages.end(); )
{ {
if ((*it)->getTypeId() == AiPackage::TypeIdPursue) if ((*it)->getTypeId() == AiPackageTypeId::Pursue)
{ {
it = mPackages.erase(it); it = mPackages.erase(it);
} }
@ -192,10 +192,13 @@ bool AiSequence::isPackageDone() const
return mDone; return mDone;
} }
bool isActualAiPackage(int packageTypeId) namespace
{ {
return (packageTypeId >= AiPackage::TypeIdWander && bool isActualAiPackage(AiPackageTypeId packageTypeId)
packageTypeId <= AiPackage::TypeIdActivate); {
return (packageTypeId >= AiPackageTypeId::Wander &&
packageTypeId <= AiPackageTypeId::Activate);
}
} }
void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& characterController, float duration, bool outOfRange) void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& characterController, float duration, bool outOfRange)
@ -204,7 +207,7 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac
{ {
if (mPackages.empty()) if (mPackages.empty())
{ {
mLastAiPackage = -1; mLastAiPackage = AiPackageTypeId::None;
return; return;
} }
@ -213,12 +216,12 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac
if (!package->alwaysActive() && outOfRange) if (!package->alwaysActive() && outOfRange)
return; return;
int packageTypeId = package->getTypeId(); auto packageTypeId = package->getTypeId();
// workaround ai packages not being handled as in the vanilla engine // workaround ai packages not being handled as in the vanilla engine
if (isActualAiPackage(packageTypeId)) if (isActualAiPackage(packageTypeId))
mLastAiPackage = packageTypeId; mLastAiPackage = packageTypeId;
// if active package is combat one, choose nearest target // if active package is combat one, choose nearest target
if (packageTypeId == AiPackage::TypeIdCombat) if (packageTypeId == AiPackageTypeId::Combat)
{ {
auto itActualCombat = mPackages.end(); auto itActualCombat = mPackages.end();
@ -229,7 +232,7 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac
for (auto it = mPackages.begin(); it != mPackages.end();) for (auto it = mPackages.begin(); it != mPackages.end();)
{ {
if ((*it)->getTypeId() != AiPackage::TypeIdCombat) break; if ((*it)->getTypeId() != AiPackageTypeId::Combat) break;
MWWorld::Ptr target = (*it)->getTarget(); MWWorld::Ptr target = (*it)->getTarget();
@ -320,16 +323,16 @@ void AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor, boo
// We should return a wandering actor back after combat, casting or pursuit. // We should return a wandering actor back after combat, casting or pursuit.
// The same thing for actors without AI packages. // The same thing for actors without AI packages.
// Also there is no point to stack return packages. // Also there is no point to stack return packages.
int currentTypeId = getTypeId(); const auto currentTypeId = getTypeId();
int newTypeId = package.getTypeId(); const auto newTypeId = package.getTypeId();
if (currentTypeId <= MWMechanics::AiPackage::TypeIdWander if (currentTypeId <= MWMechanics::AiPackageTypeId::Wander
&& !hasPackage(MWMechanics::AiPackage::TypeIdInternalTravel) && !hasPackage(MWMechanics::AiPackageTypeId::InternalTravel)
&& (newTypeId <= MWMechanics::AiPackage::TypeIdCombat && (newTypeId <= MWMechanics::AiPackageTypeId::Combat
|| newTypeId == MWMechanics::AiPackage::TypeIdPursue || newTypeId == MWMechanics::AiPackageTypeId::Pursue
|| newTypeId == MWMechanics::AiPackage::TypeIdCast)) || newTypeId == MWMechanics::AiPackageTypeId::Cast))
{ {
osg::Vec3f dest; osg::Vec3f dest;
if (currentTypeId == MWMechanics::AiPackage::TypeIdWander) if (currentTypeId == MWMechanics::AiPackageTypeId::Wander)
{ {
dest = getActivePackage().getDestination(actor); dest = getActivePackage().getDestination(actor);
} }
@ -361,8 +364,8 @@ void AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor, boo
for (auto it = mPackages.begin(); it != mPackages.end(); ++it) for (auto it = mPackages.begin(); it != mPackages.end(); ++it)
{ {
// We should keep current AiCast package, if we try to add a new one. // We should keep current AiCast package, if we try to add a new one.
if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdCast && if ((*it)->getTypeId() == MWMechanics::AiPackageTypeId::Cast &&
package.getTypeId() == MWMechanics::AiPackage::TypeIdCast) package.getTypeId() == MWMechanics::AiPackageTypeId::Cast)
{ {
continue; continue;
} }
@ -444,7 +447,7 @@ void AiSequence::writeState(ESM::AiSequence::AiSequence &sequence) const
for (const auto& package : mPackages) for (const auto& package : mPackages)
package->writeState(sequence); package->writeState(sequence);
sequence.mLastAiPackage = mLastAiPackage; sequence.mLastAiPackage = static_cast<int>(mLastAiPackage);
} }
void AiSequence::readState(const ESM::AiSequence::AiSequence &sequence) void AiSequence::readState(const ESM::AiSequence::AiSequence &sequence)
@ -457,7 +460,7 @@ void AiSequence::readState(const ESM::AiSequence::AiSequence &sequence)
for (std::vector<ESM::AiSequence::AiPackageContainer>::const_iterator it = sequence.mPackages.begin(); for (std::vector<ESM::AiSequence::AiPackageContainer>::const_iterator it = sequence.mPackages.begin();
it != sequence.mPackages.end(); ++it) it != sequence.mPackages.end(); ++it)
{ {
if (isActualAiPackage(it->mType)) if (isActualAiPackage(static_cast<AiPackageTypeId>(it->mType)))
count++; count++;
} }
@ -520,7 +523,7 @@ void AiSequence::readState(const ESM::AiSequence::AiSequence &sequence)
mPackages.push_back(std::move(package)); mPackages.push_back(std::move(package));
} }
mLastAiPackage = sequence.mLastAiPackage; mLastAiPackage = static_cast<AiPackageTypeId>(sequence.mLastAiPackage);
} }
void AiSequence::fastForward(const MWWorld::Ptr& actor) void AiSequence::fastForward(const MWWorld::Ptr& actor)

View file

@ -5,6 +5,7 @@
#include <memory> #include <memory>
#include "aistate.hpp" #include "aistate.hpp"
#include "aipackagetypeid.hpp"
#include <components/esm/loadnpc.hpp> #include <components/esm/loadnpc.hpp>
@ -49,7 +50,7 @@ namespace MWMechanics
void copy (const AiSequence& sequence); void copy (const AiSequence& sequence);
/// The type of AI package that ran last /// The type of AI package that ran last
int mLastAiPackage; AiPackageTypeId mLastAiPackage;
AiState mAiState; AiState mAiState;
public: public:
@ -71,14 +72,14 @@ namespace MWMechanics
void erase(std::list<std::unique_ptr<AiPackage>>::const_iterator package); void erase(std::list<std::unique_ptr<AiPackage>>::const_iterator package);
/// Returns currently executing AiPackage type /// Returns currently executing AiPackage type
/** \see enum AiPackage::TypeId **/ /** \see enum class AiPackageTypeId **/
int getTypeId() const; AiPackageTypeId getTypeId() const;
/// Get the typeid of the Ai package that ran last /// Get the typeid of the Ai package that ran last
/** NOT the currently "active" Ai package that will be run in the next frame. /** NOT the currently "active" Ai package that will be run in the next frame.
This difference is important when an Ai package has just finished and been removed. This difference is important when an Ai package has just finished and been removed.
\see enum AiPackage::TypeId **/ \see enum class AiPackageTypeId **/
int getLastRunTypeId() const { return mLastAiPackage; } AiPackageTypeId getLastRunTypeId() const { return mLastAiPackage; }
/// Return true and assign target if combat package is currently active, return false otherwise /// Return true and assign target if combat package is currently active, return false otherwise
bool getCombatTarget (MWWorld::Ptr &targetActor) const; bool getCombatTarget (MWWorld::Ptr &targetActor) const;
@ -93,7 +94,7 @@ namespace MWMechanics
bool isEngagedWithActor () const; bool isEngagedWithActor () const;
/// Does this AI sequence have the given package type? /// Does this AI sequence have the given package type?
bool hasPackage(int typeId) const; bool hasPackage(AiPackageTypeId typeId) const;
/// Are we in combat with this particular actor? /// Are we in combat with this particular actor?
bool isInCombat (const MWWorld::Ptr& actor) const; bool isInCombat (const MWWorld::Ptr& actor) const;

View file

@ -34,7 +34,7 @@ namespace MWMechanics
bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final;
static constexpr TypeId getTypeId() { return TypeIdTravel; } static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::Travel; }
static constexpr Options makeDefaultOptions() static constexpr Options makeDefaultOptions()
{ {
@ -60,7 +60,7 @@ namespace MWMechanics
explicit AiInternalTravel(const ESM::AiSequence::AiTravel* travel); explicit AiInternalTravel(const ESM::AiSequence::AiTravel* travel);
static constexpr TypeId getTypeId() { return TypeIdInternalTravel; } static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::InternalTravel; }
std::unique_ptr<AiPackage> clone() const final; std::unique_ptr<AiPackage> clone() const final;
}; };

View file

@ -6,6 +6,7 @@
#include <components/misc/rng.hpp> #include <components/misc/rng.hpp>
#include <components/esm/aisequence.hpp> #include <components/esm/aisequence.hpp>
#include <components/detournavigator/navigator.hpp> #include <components/detournavigator/navigator.hpp>
#include <components/misc/coordinateconverter.hpp>
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
@ -21,7 +22,6 @@
#include "pathgrid.hpp" #include "pathgrid.hpp"
#include "creaturestats.hpp" #include "creaturestats.hpp"
#include "movement.hpp" #include "movement.hpp"
#include "coordinateconverter.hpp"
#include "actorutil.hpp" #include "actorutil.hpp"
namespace MWMechanics namespace MWMechanics
@ -202,7 +202,7 @@ namespace MWMechanics
{ {
const osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getPathfindingHalfExtents(actor); const osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getPathfindingHalfExtents(actor);
mPathFinder.buildPath(actor, pos.asVec3(), mDestination, actor.getCell(), mPathFinder.buildPath(actor, pos.asVec3(), mDestination, actor.getCell(),
getPathGridGraph(actor.getCell()), halfExtents, getNavigatorFlags(actor)); getPathGridGraph(actor.getCell()), halfExtents, getNavigatorFlags(actor), getAreaCosts(actor));
} }
if (mPathFinder.isPathConstructed()) if (mPathFinder.isPathConstructed())
@ -337,6 +337,7 @@ namespace MWMechanics
const auto halfExtents = world->getPathfindingHalfExtents(actor); const auto halfExtents = world->getPathfindingHalfExtents(actor);
const auto navigator = world->getNavigator(); const auto navigator = world->getNavigator();
const auto navigatorFlags = getNavigatorFlags(actor); const auto navigatorFlags = getNavigatorFlags(actor);
const auto areaCosts = getAreaCosts(actor);
do { do {
// Determine a random location within radius of original position // Determine a random location within radius of original position
@ -365,7 +366,8 @@ namespace MWMechanics
if (isWaterCreature || isFlyingCreature) if (isWaterCreature || isFlyingCreature)
mPathFinder.buildStraightPath(mDestination); mPathFinder.buildStraightPath(mDestination);
else else
mPathFinder.buildPathByNavMesh(actor, currentPosition, mDestination, halfExtents, navigatorFlags); mPathFinder.buildPathByNavMesh(actor, currentPosition, mDestination, halfExtents, navigatorFlags,
areaCosts);
if (mPathFinder.isPathConstructed()) if (mPathFinder.isPathConstructed())
{ {
@ -496,7 +498,8 @@ namespace MWMechanics
if (mUsePathgrid) if (mUsePathgrid)
{ {
const auto halfExtents = MWBase::Environment::get().getWorld()->getHalfExtents(actor); const auto halfExtents = MWBase::Environment::get().getWorld()->getHalfExtents(actor);
mPathFinder.buildPathByNavMeshToNextPoint(actor, halfExtents, getNavigatorFlags(actor)); mPathFinder.buildPathByNavMeshToNextPoint(actor, halfExtents, getNavigatorFlags(actor),
getAreaCosts(actor));
} }
if (mObstacleCheck.isEvading()) if (mObstacleCheck.isEvading())
@ -566,7 +569,7 @@ namespace MWMechanics
void AiWander::ToWorldCoordinates(ESM::Pathgrid::Point& point, const ESM::Cell * cell) void AiWander::ToWorldCoordinates(ESM::Pathgrid::Point& point, const ESM::Cell * cell)
{ {
CoordinateConverter(cell).toWorld(point); Misc::CoordinateConverter(cell).toWorld(point);
} }
void AiWander::trimAllowedNodes(std::vector<ESM::Pathgrid::Point>& nodes, void AiWander::trimAllowedNodes(std::vector<ESM::Pathgrid::Point>& nodes,
@ -767,7 +770,7 @@ namespace MWMechanics
{ {
// get NPC's position in local (i.e. cell) coordinates // get NPC's position in local (i.e. cell) coordinates
osg::Vec3f npcPos(mInitialActorPosition); osg::Vec3f npcPos(mInitialActorPosition);
CoordinateConverter(cell).toLocal(npcPos); Misc::CoordinateConverter(cell).toLocal(npcPos);
// Find closest pathgrid point // Find closest pathgrid point
int closestPointIndex = PathFinder::getClosestPoint(pathgrid, npcPos); int closestPointIndex = PathFinder::getClosestPoint(pathgrid, npcPos);

View file

@ -93,7 +93,7 @@ namespace MWMechanics
bool execute(const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; bool execute(const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final;
static constexpr TypeId getTypeId() { return TypeIdWander; } static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::Wander; }
static constexpr Options makeDefaultOptions() static constexpr Options makeDefaultOptions()
{ {

View file

@ -1,44 +0,0 @@
#include "coordinateconverter.hpp"
#include <components/esm/loadcell.hpp>
#include <components/esm/loadland.hpp>
namespace MWMechanics
{
CoordinateConverter::CoordinateConverter(const ESM::Cell* cell)
: mCellX(0), mCellY(0)
{
if (cell->isExterior())
{
mCellX = cell->mData.mX * ESM::Land::REAL_SIZE;
mCellY = cell->mData.mY * ESM::Land::REAL_SIZE;
}
}
void CoordinateConverter::toWorld(ESM::Pathgrid::Point& point)
{
point.mX += mCellX;
point.mY += mCellY;
}
void CoordinateConverter::toWorld(osg::Vec3f& point)
{
point.x() += static_cast<float>(mCellX);
point.y() += static_cast<float>(mCellY);
}
void CoordinateConverter::toLocal(osg::Vec3f& point)
{
point.x() -= static_cast<float>(mCellX);
point.y() -= static_cast<float>(mCellY);
}
osg::Vec3f CoordinateConverter::toLocalVec3(const osg::Vec3f& point)
{
return osg::Vec3f(
point.x() - static_cast<float>(mCellX),
point.y() - static_cast<float>(mCellY),
point.z()
);
}
}

View file

@ -1,37 +0,0 @@
#ifndef GAME_MWMECHANICS_COORDINATECONVERTER_H
#define GAME_MWMECHANICS_COORDINATECONVERTER_H
#include <components/esm/defs.hpp>
#include <components/esm/loadpgrd.hpp>
namespace ESM
{
struct Cell;
}
namespace MWMechanics
{
/// \brief convert coordinates between world and local cell
class CoordinateConverter
{
public:
CoordinateConverter(const ESM::Cell* cell);
/// in-place conversion from local to world
void toWorld(ESM::Pathgrid::Point& point);
/// in-place conversion from local to world
void toWorld(osg::Vec3f& point);
/// in-place conversion from world to local
void toLocal(osg::Vec3f& point);
osg::Vec3f toLocalVec3(const osg::Vec3f& point);
private:
int mCellX;
int mCellY;
};
}
#endif

View file

@ -7,6 +7,8 @@
#include <components/esm/esmwriter.hpp> #include <components/esm/esmwriter.hpp>
#include <components/esm/stolenitems.hpp> #include <components/esm/stolenitems.hpp>
#include <components/detournavigator/navigator.hpp>
#include <components/sceneutil/positionattitudetransform.hpp> #include <components/sceneutil/positionattitudetransform.hpp>
/* /*
@ -927,6 +929,12 @@ namespace MWMechanics
bool MechanicsManager::toggleAI() bool MechanicsManager::toggleAI()
{ {
mAI = !mAI; mAI = !mAI;
MWBase::World* world = MWBase::Environment::get().getWorld();
world->getNavigator()->setUpdatesEnabled(mAI);
if (mAI)
world->getNavigator()->update(world->getPlayerPtr().getRefData().getPosition().asVec3());
return mAI; return mAI;
} }
@ -1351,7 +1359,7 @@ namespace MWMechanics
{ {
bool reported = false; bool reported = false;
if (victim.getClass().isClass(victim, "guard") if (victim.getClass().isClass(victim, "guard")
&& !victim.getClass().getCreatureStats(victim).getAiSequence().hasPackage(AiPackage::TypeIdPursue)) && !victim.getClass().getCreatureStats(victim).getAiSequence().hasPackage(AiPackageTypeId::Pursue))
reported = reportCrime(player, victim, type, std::string(), arg); reported = reportCrime(player, victim, type, std::string(), arg);
if (!reported) if (!reported)
@ -1374,7 +1382,7 @@ namespace MWMechanics
return false; return false;
// Player's followers should not attack player, or try to arrest him // Player's followers should not attack player, or try to arrest him
if (actor.getClass().getCreatureStats(actor).getAiSequence().hasPackage(AiPackage::TypeIdFollow)) if (actor.getClass().getCreatureStats(actor).getAiSequence().hasPackage(AiPackageTypeId::Follow))
{ {
if (playerFollowers.find(actor) != playerFollowers.end()) if (playerFollowers.find(actor) != playerFollowers.end())
return false; return false;
@ -1491,7 +1499,7 @@ namespace MWMechanics
// once the bounty has been paid. // once the bounty has been paid.
actor.getClass().getNpcStats(actor).setCrimeId(id); actor.getClass().getNpcStats(actor).setCrimeId(id);
if (!actor.getClass().getCreatureStats(actor).getAiSequence().hasPackage(AiPackage::TypeIdPursue)) if (!actor.getClass().getCreatureStats(actor).getAiSequence().hasPackage(AiPackageTypeId::Pursue))
{ {
actor.getClass().getCreatureStats(actor).getAiSequence().stack(AiPursue(player), actor); actor.getClass().getCreatureStats(actor).getAiSequence().stack(AiPursue(player), actor);
} }
@ -1579,7 +1587,7 @@ namespace MWMechanics
{ {
// Attacker is in combat with us, but we are not in combat with the attacker yet. Time to fight back. // Attacker is in combat with us, but we are not in combat with the attacker yet. Time to fight back.
// Note: accidental or collateral damage attacks are ignored. // Note: accidental or collateral damage attacks are ignored.
if (!victim.getClass().getCreatureStats(victim).getAiSequence().hasPackage(AiPackage::TypeIdPursue)) if (!victim.getClass().getCreatureStats(victim).getAiSequence().hasPackage(AiPackageTypeId::Pursue))
startCombat(victim, player); startCombat(victim, player);
// Set the crime ID, which we will use to calm down participants // Set the crime ID, which we will use to calm down participants
@ -1681,7 +1689,7 @@ namespace MWMechanics
{ {
// Attacker is in combat with us, but we are not in combat with the attacker yet. Time to fight back. // Attacker is in combat with us, but we are not in combat with the attacker yet. Time to fight back.
// Note: accidental or collateral damage attacks are ignored. // Note: accidental or collateral damage attacks are ignored.
if (!target.getClass().getCreatureStats(target).getAiSequence().hasPackage(AiPackage::TypeIdPursue)) if (!target.getClass().getCreatureStats(target).getAiSequence().hasPackage(AiPackageTypeId::Pursue))
{ {
// If an actor has OnPCHitMe declared in his script, his Fight = 0 and the attacker is player, // If an actor has OnPCHitMe declared in his script, his Fight = 0 and the attacker is player,
// he will attack the player only if we will force him (e.g. via StartCombat console command) // he will attack the player only if we will force him (e.g. via StartCombat console command)
@ -1706,7 +1714,7 @@ namespace MWMechanics
const MWMechanics::AiSequence& seq = target.getClass().getCreatureStats(target).getAiSequence(); const MWMechanics::AiSequence& seq = target.getClass().getCreatureStats(target).getAiSequence();
return target.getClass().isNpc() && !attacker.isEmpty() && !seq.isInCombat(attacker) return target.getClass().isNpc() && !attacker.isEmpty() && !seq.isInCombat(attacker)
&& !isAggressive(target, attacker) && !seq.isEngagedWithActor() && !isAggressive(target, attacker) && !seq.isEngagedWithActor()
&& !target.getClass().getCreatureStats(target).getAiSequence().hasPackage(AiPackage::TypeIdPursue); && !target.getClass().getCreatureStats(target).getAiSequence().hasPackage(AiPackageTypeId::Pursue);
} }
void MechanicsManager::actorKilled(const MWWorld::Ptr &victim, const MWWorld::Ptr &attacker) void MechanicsManager::actorKilled(const MWWorld::Ptr &victim, const MWWorld::Ptr &attacker)
@ -1842,7 +1850,7 @@ namespace MWMechanics
if (iter->first.getClass().isClass(iter->first, "Guard")) if (iter->first.getClass().isClass(iter->first, "Guard"))
{ {
MWMechanics::AiSequence& aiSeq = iter->first.getClass().getCreatureStats(iter->first).getAiSequence(); MWMechanics::AiSequence& aiSeq = iter->first.getClass().getCreatureStats(iter->first).getAiSequence();
if (aiSeq.getTypeId() == MWMechanics::AiPackage::TypeIdPursue) if (aiSeq.getTypeId() == MWMechanics::AiPackageTypeId::Pursue)
{ {
aiSeq.stopPursuit(); aiSeq.stopPursuit();
aiSeq.stack(MWMechanics::AiCombat(target), ptr); aiSeq.stack(MWMechanics::AiCombat(target), ptr);

View file

@ -7,6 +7,7 @@
#include <components/detournavigator/debug.hpp> #include <components/detournavigator/debug.hpp>
#include <components/detournavigator/navigator.hpp> #include <components/detournavigator/navigator.hpp>
#include <components/debug/debuglog.hpp> #include <components/debug/debuglog.hpp>
#include <components/misc/coordinateconverter.hpp>
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
@ -17,7 +18,6 @@
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include "pathgrid.hpp" #include "pathgrid.hpp"
#include "coordinateconverter.hpp"
#include "actorutil.hpp" #include "actorutil.hpp"
namespace namespace
@ -158,13 +158,10 @@ namespace MWMechanics
// Maybe there is no pathgrid for this cell. Just go to destination and let // Maybe there is no pathgrid for this cell. Just go to destination and let
// physics take care of any blockages. // physics take care of any blockages.
if(!pathgrid || pathgrid->mPoints.empty()) if(!pathgrid || pathgrid->mPoints.empty())
{
*out++ = endPoint;
return; return;
}
// NOTE: getClosestPoint expects local coordinates // NOTE: getClosestPoint expects local coordinates
CoordinateConverter converter(mCell->getCell()); Misc::CoordinateConverter converter(mCell->getCell());
// NOTE: It is possible that getClosestPoint returns a pathgrind point index // NOTE: It is possible that getClosestPoint returns a pathgrind point index
// that is unreachable in some situations. e.g. actor is standing // that is unreachable in some situations. e.g. actor is standing
@ -179,6 +176,9 @@ namespace MWMechanics
endPointInLocalCoords, endPointInLocalCoords,
startNode); startNode);
if (!endNode.second)
return;
// if it's shorter for actor to travel from start to end, than to travel from either // if it's shorter for actor to travel from start to end, than to travel from either
// start or end to nearest pathgrid point, just travel from start to end. // start or end to nearest pathgrid point, just travel from start to end.
float startToEndLength2 = (endPointInLocalCoords - startPointInLocalCoords).length2(); float startToEndLength2 = (endPointInLocalCoords - startPointInLocalCoords).length2();
@ -249,8 +249,7 @@ namespace MWMechanics
// unreachable pathgrid point. // unreachable pathgrid point.
// //
// The AI routines will have to deal with such situations. // The AI routines will have to deal with such situations.
if(endNode.second) *out++ = endPoint;
*out++ = endPoint;
} }
float PathFinder::getZAngleToNext(float x, float y) const float PathFinder::getZAngleToNext(float x, float y) const
@ -310,12 +309,13 @@ namespace MWMechanics
} }
void PathFinder::buildPathByNavMesh(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, void PathFinder::buildPathByNavMesh(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint,
const osg::Vec3f& endPoint, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags) const osg::Vec3f& endPoint, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags,
const DetourNavigator::AreaCosts& areaCosts)
{ {
mPath.clear(); mPath.clear();
// If it's not possible to build path over navmesh due to disabled navmesh generation fallback to straight path // If it's not possible to build path over navmesh due to disabled navmesh generation fallback to straight path
if (!buildPathByNavigatorImpl(actor, startPoint, endPoint, halfExtents, flags, std::back_inserter(mPath))) if (!buildPathByNavigatorImpl(actor, startPoint, endPoint, halfExtents, flags, areaCosts, std::back_inserter(mPath)))
mPath.push_back(endPoint); mPath.push_back(endPoint);
mConstructed = true; mConstructed = true;
@ -323,28 +323,37 @@ namespace MWMechanics
void PathFinder::buildPath(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, const osg::Vec3f& endPoint, void PathFinder::buildPath(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, const osg::Vec3f& endPoint,
const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph, const osg::Vec3f& halfExtents, const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph, const osg::Vec3f& halfExtents,
const DetourNavigator::Flags flags) const DetourNavigator::Flags flags, const DetourNavigator::AreaCosts& areaCosts)
{ {
mPath.clear(); mPath.clear();
mCell = cell; mCell = cell;
bool hasNavMesh = false;
if (!actor.getClass().isPureWaterCreature(actor) && !actor.getClass().isPureFlyingCreature(actor)) if (!actor.getClass().isPureWaterCreature(actor) && !actor.getClass().isPureFlyingCreature(actor))
buildPathByNavigatorImpl(actor, startPoint, endPoint, halfExtents, flags, std::back_inserter(mPath)); hasNavMesh = buildPathByNavigatorImpl(actor, startPoint, endPoint, halfExtents, flags, areaCosts, std::back_inserter(mPath));
if (hasNavMesh && mPath.empty())
buildPathByNavigatorImpl(actor, startPoint, endPoint, halfExtents,
flags | DetourNavigator::Flag_usePathgrid, areaCosts, std::back_inserter(mPath));
if (mPath.empty()) if (mPath.empty())
buildPathByPathgridImpl(startPoint, endPoint, pathgridGraph, std::back_inserter(mPath)); buildPathByPathgridImpl(startPoint, endPoint, pathgridGraph, std::back_inserter(mPath));
if (!hasNavMesh && mPath.empty())
mPath.push_back(endPoint);
mConstructed = true; mConstructed = true;
} }
bool PathFinder::buildPathByNavigatorImpl(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, bool PathFinder::buildPathByNavigatorImpl(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint,
const osg::Vec3f& endPoint, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags, const osg::Vec3f& endPoint, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags,
std::back_insert_iterator<std::deque<osg::Vec3f>> out) const DetourNavigator::AreaCosts& areaCosts, std::back_insert_iterator<std::deque<osg::Vec3f>> out)
{ {
const auto world = MWBase::Environment::get().getWorld(); const auto world = MWBase::Environment::get().getWorld();
const auto stepSize = getPathStepSize(actor); const auto stepSize = getPathStepSize(actor);
const auto navigator = world->getNavigator(); const auto navigator = world->getNavigator();
const auto status = navigator->findPath(halfExtents, stepSize, startPoint, endPoint, flags, out); const auto status = navigator->findPath(halfExtents, stepSize, startPoint, endPoint, flags, areaCosts, out);
if (status == DetourNavigator::Status::NavMeshNotFound) if (status == DetourNavigator::Status::NavMeshNotFound)
return false; return false;
@ -361,7 +370,7 @@ namespace MWMechanics
} }
void PathFinder::buildPathByNavMeshToNextPoint(const MWWorld::ConstPtr& actor, const osg::Vec3f& halfExtents, void PathFinder::buildPathByNavMeshToNextPoint(const MWWorld::ConstPtr& actor, const osg::Vec3f& halfExtents,
const DetourNavigator::Flags flags) const DetourNavigator::Flags flags, const DetourNavigator::AreaCosts& areaCosts)
{ {
if (mPath.empty()) if (mPath.empty())
return; return;
@ -375,7 +384,7 @@ namespace MWMechanics
const auto navigator = MWBase::Environment::get().getWorld()->getNavigator(); const auto navigator = MWBase::Environment::get().getWorld()->getNavigator();
std::deque<osg::Vec3f> prePath; std::deque<osg::Vec3f> prePath;
auto prePathInserter = std::back_inserter(prePath); auto prePathInserter = std::back_inserter(prePath);
const auto status = navigator->findPath(halfExtents, stepSize, startPoint, mPath.front(), flags, const auto status = navigator->findPath(halfExtents, stepSize, startPoint, mPath.front(), flags, areaCosts,
prePathInserter); prePathInserter);
if (status == DetourNavigator::Status::NavMeshNotFound) if (status == DetourNavigator::Status::NavMeshNotFound)

View file

@ -6,6 +6,7 @@
#include <iterator> #include <iterator>
#include <components/detournavigator/flags.hpp> #include <components/detournavigator/flags.hpp>
#include <components/detournavigator/areatype.hpp>
#include <components/esm/defs.hpp> #include <components/esm/defs.hpp>
#include <components/esm/loadpgrd.hpp> #include <components/esm/loadpgrd.hpp>
@ -90,14 +91,15 @@ namespace MWMechanics
const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph); const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph);
void buildPathByNavMesh(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, void buildPathByNavMesh(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint,
const osg::Vec3f& endPoint, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags); const osg::Vec3f& endPoint, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags,
const DetourNavigator::AreaCosts& areaCosts);
void buildPath(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, const osg::Vec3f& endPoint, void buildPath(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, const osg::Vec3f& endPoint,
const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph, const osg::Vec3f& halfExtents, const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph, const osg::Vec3f& halfExtents,
const DetourNavigator::Flags flags); const DetourNavigator::Flags flags, const DetourNavigator::AreaCosts& areaCosts);
void buildPathByNavMeshToNextPoint(const MWWorld::ConstPtr& actor, const osg::Vec3f& halfExtents, void buildPathByNavMeshToNextPoint(const MWWorld::ConstPtr& actor, const osg::Vec3f& halfExtents,
const DetourNavigator::Flags flags); const DetourNavigator::Flags flags, const DetourNavigator::AreaCosts& areaCosts);
/// Remove front point if exist and within tolerance /// Remove front point if exist and within tolerance
void update(const osg::Vec3f& position, const float pointTolerance, const float destinationTolerance); void update(const osg::Vec3f& position, const float pointTolerance, const float destinationTolerance);
@ -203,7 +205,7 @@ namespace MWMechanics
bool buildPathByNavigatorImpl(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, bool buildPathByNavigatorImpl(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint,
const osg::Vec3f& endPoint, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags, const osg::Vec3f& endPoint, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags,
std::back_insert_iterator<std::deque<osg::Vec3f>> out); const DetourNavigator::AreaCosts& areaCosts, std::back_insert_iterator<std::deque<osg::Vec3f>> out);
}; };
} }

View file

@ -52,7 +52,6 @@ namespace MWMechanics
PathgridGraph::PathgridGraph(const MWWorld::CellStore *cell) PathgridGraph::PathgridGraph(const MWWorld::CellStore *cell)
: mCell(nullptr) : mCell(nullptr)
, mPathgrid(nullptr) , mPathgrid(nullptr)
, mIsExterior(0)
, mGraph(0) , mGraph(0)
, mIsGraphConstructed(false) , mIsGraphConstructed(false)
, mSCCId(0) , mSCCId(0)
@ -106,7 +105,6 @@ namespace MWMechanics
return true; return true;
mCell = cell->getCell(); mCell = cell->getCell();
mIsExterior = cell->getCell()->isExterior();
mPathgrid = MWBase::Environment::get().getWorld()->getStore().get<ESM::Pathgrid>().search(*cell->getCell()); mPathgrid = MWBase::Environment::get().getWorld()->getStore().get<ESM::Pathgrid>().search(*cell->getCell());
if(!mPathgrid) if(!mPathgrid)
return false; return false;

View file

@ -44,7 +44,6 @@ namespace MWMechanics
const ESM::Cell *mCell; const ESM::Cell *mCell;
const ESM::Pathgrid *mPathgrid; const ESM::Pathgrid *mPathgrid;
bool mIsExterior;
struct ConnectedPoint // edge struct ConnectedPoint // edge
{ {

View file

@ -419,6 +419,15 @@ namespace MWPhysics
return osg::Vec3f(); return osg::Vec3f();
} }
osg::BoundingBox PhysicsSystem::getBoundingBox(const MWWorld::ConstPtr &object) const
{
const Object * physobject = getObject(object);
if (!physobject) return osg::BoundingBox();
btVector3 min, max;
physobject->getCollisionObject()->getCollisionShape()->getAabb(physobject->getCollisionObject()->getWorldTransform(), min, max);
return osg::BoundingBox(Misc::Convert::toOsg(min), Misc::Convert::toOsg(max));
}
osg::Vec3f PhysicsSystem::getCollisionObjectPosition(const MWWorld::ConstPtr &actor) const osg::Vec3f PhysicsSystem::getCollisionObjectPosition(const MWWorld::ConstPtr &actor) const
{ {
const Actor* physactor = getActor(actor); const Actor* physactor = getActor(actor);

View file

@ -7,6 +7,7 @@
#include <algorithm> #include <algorithm>
#include <osg/Quat> #include <osg/Quat>
#include <osg/BoundingBox>
#include <osg/ref_ptr> #include <osg/ref_ptr>
#include "../mwworld/ptr.hpp" #include "../mwworld/ptr.hpp"
@ -144,6 +145,9 @@ namespace MWPhysics
/// @note The collision shape's origin is in its center, so the position returned can be described as center of the actor collision box in world space. /// @note The collision shape's origin is in its center, so the position returned can be described as center of the actor collision box in world space.
osg::Vec3f getCollisionObjectPosition(const MWWorld::ConstPtr& actor) const; osg::Vec3f getCollisionObjectPosition(const MWWorld::ConstPtr& actor) const;
/// Get bounding box in world space of the given object.
osg::BoundingBox getBoundingBox(const MWWorld::ConstPtr &object) const;
/// Queues velocity movement for a Ptr. If a Ptr is already queued, its velocity will /// Queues velocity movement for a Ptr. If a Ptr is already queued, its velocity will
/// be overwritten. Valid until the next call to applyQueuedMovement. /// be overwritten. Valid until the next call to applyQueuedMovement.
void queueObjectMovement(const MWWorld::Ptr &ptr, const osg::Vec3f &velocity); void queueObjectMovement(const MWWorld::Ptr &ptr, const osg::Vec3f &velocity);

View file

@ -513,6 +513,9 @@ namespace MWRender
if (mShadowUniform) if (mShadowUniform)
stateset->addUniform(mShadowUniform); stateset->addUniform(mShadowUniform);
stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
stateset->setRenderBinMode(osg::StateSet::OVERRIDE_RENDERBIN_DETAILS);
// FIXME: overriding diffuse/ambient/emissive colors // FIXME: overriding diffuse/ambient/emissive colors
osg::Material* material = new osg::Material; osg::Material* material = new osg::Material;
material->setColorMode(osg::Material::OFF); material->setColorMode(osg::Material::OFF);
@ -1369,7 +1372,7 @@ namespace MWRender
osg::Group* sheathParent = findVisitor.mFoundNode; osg::Group* sheathParent = findVisitor.mFoundNode;
if (sheathParent) if (sheathParent)
{ {
osg::Node* copy = osg::clone(nodePair.first, osg::CopyOp::DEEP_COPY_NODES); osg::Node* copy = static_cast<osg::Node*>(nodePair.first->clone(osg::CopyOp::DEEP_COPY_NODES));
sheathParent->addChild(copy); sheathParent->addChild(copy);
} }
} }
@ -1741,31 +1744,16 @@ namespace MWRender
if (mTransparencyUpdater == nullptr) if (mTransparencyUpdater == nullptr)
{ {
mTransparencyUpdater = new TransparencyUpdater(alpha, mResourceSystem->getSceneManager()->getShaderManager().getShadowMapAlphaTestEnableUniform()); mTransparencyUpdater = new TransparencyUpdater(alpha, mResourceSystem->getSceneManager()->getShaderManager().getShadowMapAlphaTestEnableUniform());
mObjectRoot->addUpdateCallback(mTransparencyUpdater); mObjectRoot->addCullCallback(mTransparencyUpdater);
} }
else else
mTransparencyUpdater->setAlpha(alpha); mTransparencyUpdater->setAlpha(alpha);
} }
else else
{ {
mObjectRoot->removeUpdateCallback(mTransparencyUpdater); mObjectRoot->removeCullCallback(mTransparencyUpdater);
mTransparencyUpdater = nullptr; mTransparencyUpdater = nullptr;
mObjectRoot->setStateSet(nullptr);
} }
setRenderBin();
}
void Animation::setRenderBin()
{
if (mAlpha != 1.f)
{
osg::StateSet* stateset = mObjectRoot->getOrCreateStateSet();
stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
stateset->setRenderBinMode(osg::StateSet::OVERRIDE_RENDERBIN_DETAILS);
}
else if (osg::StateSet* stateset = mObjectRoot->getStateSet())
stateset->setRenderBinToInherit();
} }
void Animation::setLightEffect(float effect) void Animation::setLightEffect(float effect)

View file

@ -336,9 +336,6 @@ protected:
*/ */
virtual void addControllers(); virtual void addControllers();
/// Set the render bin for this animation's object root. May be customized by subclasses.
virtual void setRenderBin();
public: public:
Animation(const MWWorld::Ptr &ptr, osg::ref_ptr<osg::Group> parentNode, Resource::ResourceSystem* resourceSystem); Animation(const MWWorld::Ptr &ptr, osg::ref_ptr<osg::Group> parentNode, Resource::ResourceSystem* resourceSystem);

View file

@ -74,7 +74,7 @@ LocalMap::LocalMap(osg::Group* root)
: mRoot(root) : mRoot(root)
, mMapResolution(Settings::Manager::getInt("local map resolution", "Map")) , mMapResolution(Settings::Manager::getInt("local map resolution", "Map"))
, mMapWorldSize(Constants::CellSizeInUnits) , mMapWorldSize(Constants::CellSizeInUnits)
, mCellDistance(Settings::Manager::getInt("local map cell distance", "Map")) , mCellDistance(Constants::CellGridRadius)
, mAngle(0.f) , mAngle(0.f)
, mInterior(false) , mInterior(false)
{ {
@ -168,11 +168,10 @@ void LocalMap::saveFogOfWar(MWWorld::CellStore* cell)
osg::ref_ptr<osg::Camera> LocalMap::createOrthographicCamera(float x, float y, float width, float height, const osg::Vec3d& upVector, float zmin, float zmax) osg::ref_ptr<osg::Camera> LocalMap::createOrthographicCamera(float x, float y, float width, float height, const osg::Vec3d& upVector, float zmin, float zmax)
{ {
osg::ref_ptr<osg::Camera> camera (new osg::Camera); osg::ref_ptr<osg::Camera> camera (new osg::Camera);
camera->setProjectionMatrixAsOrtho(-width/2, width/2, -height/2, height/2, 5, (zmax-zmin) + 10); camera->setProjectionMatrixAsOrtho(-width/2, width/2, -height/2, height/2, 5, (zmax-zmin) + 10);
camera->setComputeNearFarMode(osg::Camera::DO_NOT_COMPUTE_NEAR_FAR); camera->setComputeNearFarMode(osg::Camera::DO_NOT_COMPUTE_NEAR_FAR);
camera->setViewMatrixAsLookAt(osg::Vec3d(x, y, zmax + 5), osg::Vec3d(x, y, zmin), upVector); camera->setViewMatrixAsLookAt(osg::Vec3d(x, y, zmax + 5), osg::Vec3d(x, y, zmin), upVector);
camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF); camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF_INHERIT_VIEWPOINT);
camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT, osg::Camera::PIXEL_BUFFER_RTT); camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT, osg::Camera::PIXEL_BUFFER_RTT);
camera->setClearColor(osg::Vec4(0.f, 0.f, 0.f, 1.f)); camera->setClearColor(osg::Vec4(0.f, 0.f, 0.f, 1.f));
camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
@ -360,11 +359,6 @@ void LocalMap::requestExteriorMap(const MWWorld::CellStore* cell)
osg::ref_ptr<osg::Camera> camera = createOrthographicCamera(x*mMapWorldSize + mMapWorldSize/2.f, y*mMapWorldSize + mMapWorldSize/2.f, mMapWorldSize, mMapWorldSize, osg::ref_ptr<osg::Camera> camera = createOrthographicCamera(x*mMapWorldSize + mMapWorldSize/2.f, y*mMapWorldSize + mMapWorldSize/2.f, mMapWorldSize, mMapWorldSize,
osg::Vec3d(0,1,0), zmin, zmax); osg::Vec3d(0,1,0), zmin, zmax);
camera->getOrCreateUserDataContainer()->addDescription("NoTerrainLod");
std::ostringstream stream;
stream << x << " " << y;
camera->getOrCreateUserDataContainer()->addDescription(stream.str());
setupRenderToTexture(camera, cell->getCell()->getGridX(), cell->getCell()->getGridY()); setupRenderToTexture(camera, cell->getCell()->getGridX(), cell->getCell()->getGridY());
MapSegment& segment = mSegments[std::make_pair(cell->getCell()->getGridX(), cell->getCell()->getGridY())]; MapSegment& segment = mSegments[std::make_pair(cell->getCell()->getGridX(), cell->getCell()->getGridY())];

View file

@ -435,12 +435,10 @@ void NpcAnimation::setRenderBin()
osgUtil::RenderBin::addRenderBinPrototype("DepthClear", depthClearBin); osgUtil::RenderBin::addRenderBinPrototype("DepthClear", depthClearBin);
prototypeAdded = true; prototypeAdded = true;
} }
mObjectRoot->getOrCreateStateSet()->setRenderBinDetails(RenderBin_FirstPerson, "DepthClear", osg::StateSet::OVERRIDE_RENDERBIN_DETAILS);
osg::StateSet* stateset = mObjectRoot->getOrCreateStateSet();
stateset->setRenderBinDetails(RenderBin_FirstPerson, "DepthClear", osg::StateSet::OVERRIDE_RENDERBIN_DETAILS);
} }
else else if (osg::StateSet* stateset = mObjectRoot->getStateSet())
Animation::setRenderBin(); stateset->setRenderBinToInherit();
} }
void NpcAnimation::rebuild() void NpcAnimation::rebuild()

View file

@ -88,7 +88,7 @@ private:
void addPartGroup(int group, int priority, const std::vector<ESM::PartReference> &parts, void addPartGroup(int group, int priority, const std::vector<ESM::PartReference> &parts,
bool enchantedGlow=false, osg::Vec4f* glowColor=nullptr); bool enchantedGlow=false, osg::Vec4f* glowColor=nullptr);
virtual void setRenderBin(); void setRenderBin();
osg::ref_ptr<NeckController> mFirstPersonNeckController; osg::ref_ptr<NeckController> mFirstPersonNeckController;

View file

@ -0,0 +1,782 @@
#include "objectpaging.hpp"
#include <unordered_map>
#include <osg/Version>
#include <osg/LOD>
#include <osg/Switch>
#include <osg/MatrixTransform>
#include <osg/Material>
#include <osgUtil/IncrementalCompileOperation>
#include <components/esm/esmreader.hpp>
#include <components/misc/resourcehelpers.hpp>
#include <components/resource/scenemanager.hpp>
#include <components/sceneutil/optimizer.hpp>
#include <components/sceneutil/clone.hpp>
#include <components/sceneutil/util.hpp>
#include <components/vfs/manager.hpp>
#include <osgParticle/ParticleProcessor>
#include <osgParticle/ParticleSystemUpdater>
#include <components/sceneutil/lightmanager.hpp>
#include <components/sceneutil/morphgeometry.hpp>
#include <components/sceneutil/riggeometry.hpp>
#include <components/settings/settings.hpp>
#include <components/misc/rng.hpp>
#include "apps/openmw/mwworld/esmstore.hpp"
#include "apps/openmw/mwbase/environment.hpp"
#include "apps/openmw/mwbase/world.hpp"
#include "vismask.hpp"
namespace MWRender
{
bool typeFilter(int type, bool far)
{
switch (type)
{
case ESM::REC_STAT:
case ESM::REC_ACTI:
case ESM::REC_DOOR:
return true;
case ESM::REC_CONT:
return !far;
default:
return false;
}
}
std::string getModel(int type, const std::string& id, const MWWorld::ESMStore& store)
{
switch (type)
{
case ESM::REC_STAT:
return store.get<ESM::Static>().searchStatic(id)->mModel;
case ESM::REC_ACTI:
return store.get<ESM::Activator>().searchStatic(id)->mModel;
case ESM::REC_DOOR:
return store.get<ESM::Door>().searchStatic(id)->mModel;
case ESM::REC_CONT:
return store.get<ESM::Container>().searchStatic(id)->mModel;
default:
return std::string();
}
}
osg::ref_ptr<osg::Node> ObjectPaging::getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool activeGrid, const osg::Vec3f& viewPoint, bool compile)
{
if (activeGrid && !mActiveGrid)
return nullptr;
ChunkId id = std::make_tuple(center, size, activeGrid);
osg::ref_ptr<osg::Object> obj = mCache->getRefFromObjectCache(id);
if (obj)
return obj->asNode();
else
{
osg::ref_ptr<osg::Node> node = createChunk(size, center, activeGrid, viewPoint, compile);
mCache->addEntryToObjectCache(id, node.get());
return node;
}
}
class CanOptimizeCallback : public SceneUtil::Optimizer::IsOperationPermissibleForObjectCallback
{
public:
virtual bool isOperationPermissibleForObjectImplementation(const SceneUtil::Optimizer* optimizer, const osg::Drawable* node,unsigned int option) const
{
return true;
}
virtual bool isOperationPermissibleForObjectImplementation(const SceneUtil::Optimizer* optimizer, const osg::Node* node,unsigned int option) const
{
return (node->getDataVariance() != osg::Object::DYNAMIC);
}
};
class CopyOp : public osg::CopyOp
{
public:
bool mOptimizeBillboards = true;
float mSqrDistance = 0.f;
osg::Vec3f mViewVector;
mutable std::vector<const osg::Node*> mNodePath;
void copy(const osg::Node* toCopy, osg::Group* attachTo)
{
const osg::Group* groupToCopy = toCopy->asGroup();
if (toCopy->getStateSet() || toCopy->asTransform() || !groupToCopy)
attachTo->addChild(operator()(toCopy));
else
{
for (unsigned int i=0; i<groupToCopy->getNumChildren(); ++i)
attachTo->addChild(operator()(groupToCopy->getChild(i)));
}
}
virtual osg::Node* operator() (const osg::Node* node) const
{
if (const osg::Drawable* d = node->asDrawable())
return operator()(d);
if (dynamic_cast<const osgParticle::ParticleProcessor*>(node))
return nullptr;
if (dynamic_cast<const osgParticle::ParticleSystemUpdater*>(node))
return nullptr;
if (const osg::Switch* sw = node->asSwitch())
{
osg::Group* n = new osg::Group;
for (unsigned int i=0; i<sw->getNumChildren(); ++i)
if (sw->getValue(i))
n->addChild(operator()(sw->getChild(i)));
n->setDataVariance(osg::Object::STATIC);
return n;
}
if (const osg::LOD* lod = dynamic_cast<const osg::LOD*>(node))
{
osg::Group* n = new osg::Group;
for (unsigned int i=0; i<lod->getNumChildren(); ++i)
if (lod->getMinRange(i) * lod->getMinRange(i) <= mSqrDistance && mSqrDistance < lod->getMaxRange(i) * lod->getMaxRange(i))
n->addChild(operator()(lod->getChild(i)));
n->setDataVariance(osg::Object::STATIC);
return n;
}
mNodePath.push_back(node);
osg::Node* cloned = static_cast<osg::Node*>(node->clone(*this));
cloned->setDataVariance(osg::Object::STATIC);
cloned->setUserDataContainer(nullptr);
cloned->setName("");
mNodePath.pop_back();
handleCallbacks(node, cloned);
return cloned;
}
void handleCallbacks(const osg::Node* node, osg::Node *cloned) const
{
for (const osg::Callback* callback = node->getCullCallback(); callback != nullptr; callback = callback->getNestedCallback())
{
if (callback->className() == std::string("BillboardCallback"))
{
if (mOptimizeBillboards)
{
handleBillboard(cloned);
continue;
}
else
cloned->setDataVariance(osg::Object::DYNAMIC);
}
if (node->getCullCallback()->getNestedCallback())
{
osg::Callback *clonedCallback = osg::clone(callback, osg::CopyOp::SHALLOW_COPY);
clonedCallback->setNestedCallback(nullptr);
cloned->addCullCallback(clonedCallback);
}
else
cloned->addCullCallback(const_cast<osg::Callback*>(callback));
}
}
void handleBillboard(osg::Node* node) const
{
osg::Transform* transform = node->asTransform();
if (!transform) return;
osg::MatrixTransform* matrixTransform = transform->asMatrixTransform();
if (!matrixTransform) return;
osg::Matrix worldToLocal = osg::Matrix::identity();
for (auto node : mNodePath)
if (const osg::Transform* t = node->asTransform())
t->computeWorldToLocalMatrix(worldToLocal, nullptr);
worldToLocal = osg::Matrix::orthoNormal(worldToLocal);
osg::Matrix billboardMatrix;
osg::Vec3f viewVector = -(mViewVector + worldToLocal.getTrans());
viewVector.normalize();
osg::Vec3f right = viewVector ^ osg::Vec3f(0,0,1);
right.normalize();
osg::Vec3f up = right ^ viewVector;
up.normalize();
billboardMatrix.makeLookAt(osg::Vec3f(0,0,0), viewVector, up);
billboardMatrix.invert(billboardMatrix);
const osg::Matrix& oldMatrix = matrixTransform->getMatrix();
float mag[3]; // attempt to preserve scale
for (int i=0;i<3;++i)
mag[i] = std::sqrt(oldMatrix(0,i) * oldMatrix(0,i) + oldMatrix(1,i) * oldMatrix(1,i) + oldMatrix(2,i) * oldMatrix(2,i));
osg::Matrix newMatrix;
worldToLocal.setTrans(0,0,0);
newMatrix *= worldToLocal;
newMatrix.preMult(billboardMatrix);
newMatrix.preMultScale(osg::Vec3f(mag[0], mag[1], mag[2]));
newMatrix.setTrans(oldMatrix.getTrans());
matrixTransform->setMatrix(newMatrix);
}
virtual osg::Drawable* operator() (const osg::Drawable* drawable) const
{
if (dynamic_cast<const osgParticle::ParticleSystem*>(drawable))
return nullptr;
if (const SceneUtil::RigGeometry* rig = dynamic_cast<const SceneUtil::RigGeometry*>(drawable))
return operator()(rig->getSourceGeometry());
if (const SceneUtil::MorphGeometry* morph = dynamic_cast<const SceneUtil::MorphGeometry*>(drawable))
return operator()(morph->getSourceGeometry());
if (getCopyFlags() & DEEP_COPY_DRAWABLES)
{
osg::Drawable* d = static_cast<osg::Drawable*>(drawable->clone(*this));
d->setDataVariance(osg::Object::STATIC);
d->setUserDataContainer(nullptr);
d->setName("");
return d;
}
else
return const_cast<osg::Drawable*>(drawable);
}
virtual osg::Callback* operator() (const osg::Callback* callback) const
{
return nullptr;
}
};
class TemplateRef : public osg::Object
{
public:
TemplateRef() {}
TemplateRef(const TemplateRef& copy, const osg::CopyOp&) : mObjects(copy.mObjects) {}
META_Object(MWRender, TemplateRef)
std::vector<osg::ref_ptr<const Object>> mObjects;
};
class RefnumSet : public osg::Object
{
public:
RefnumSet(){}
RefnumSet(const RefnumSet& copy, const osg::CopyOp&) : mRefnums(copy.mRefnums) {}
META_Object(MWRender, RefnumSet)
std::set<ESM::RefNum> mRefnums;
};
class AnalyzeVisitor : public osg::NodeVisitor
{
public:
AnalyzeVisitor()
: osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)
, mCurrentStateSet(nullptr) {}
typedef std::unordered_map<osg::StateSet*, unsigned int> StateSetCounter;
struct Result
{
StateSetCounter mStateSetCounter;
unsigned int mNumVerts = 0;
};
virtual void apply(osg::Node& node)
{
if (node.getStateSet())
mCurrentStateSet = node.getStateSet();
traverse(node);
}
virtual void apply(osg::Geometry& geom)
{
mResult.mNumVerts += geom.getVertexArray()->getNumElements();
++mResult.mStateSetCounter[mCurrentStateSet];
++mGlobalStateSetCounter[mCurrentStateSet];
}
Result retrieveResult()
{
Result result = mResult;
mResult = Result();
mCurrentStateSet = nullptr;
return result;
}
void addInstance(const Result& result)
{
for (auto pair : result.mStateSetCounter)
mGlobalStateSetCounter[pair.first] += pair.second;
}
float getMergeBenefit(const Result& result)
{
if (result.mStateSetCounter.empty()) return 1;
float mergeBenefit = 0;
for (auto pair : result.mStateSetCounter)
{
mergeBenefit += mGlobalStateSetCounter[pair.first];
}
mergeBenefit /= result.mStateSetCounter.size();
return mergeBenefit;
}
Result mResult;
osg::StateSet* mCurrentStateSet;
StateSetCounter mGlobalStateSetCounter;
};
class DebugVisitor : public osg::NodeVisitor
{
public:
DebugVisitor() : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) {}
virtual void apply(osg::Drawable& node)
{
osg::ref_ptr<osg::Material> m (new osg::Material);
osg::Vec4f color(Misc::Rng::rollProbability(), Misc::Rng::rollProbability(), Misc::Rng::rollProbability(), 0.f);
color.normalize();
m->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.1f,0.1f,0.1f,1.f));
m->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.1f,0.1f,0.1f,1.f));
m->setColorMode(osg::Material::OFF);
m->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(color));
osg::ref_ptr<osg::StateSet> stateset = node.getStateSet() ? osg::clone(node.getStateSet(), osg::CopyOp::SHALLOW_COPY) : new osg::StateSet;
stateset->setAttribute(m);
stateset->addUniform(new osg::Uniform("colorMode", 0));
node.setStateSet(stateset);
}
};
class AddRefnumMarkerVisitor : public osg::NodeVisitor
{
public:
AddRefnumMarkerVisitor(const ESM::RefNum &refnum) : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN), mRefnum(refnum) {}
ESM::RefNum mRefnum;
virtual void apply(osg::Geometry &node)
{
osg::ref_ptr<RefnumMarker> marker (new RefnumMarker);
marker->mRefnum = mRefnum;
if (osg::Array* array = node.getVertexArray())
marker->mNumVertices = array->getNumElements();
node.getOrCreateUserDataContainer()->addUserObject(marker);
}
};
ObjectPaging::ObjectPaging(Resource::SceneManager* sceneManager)
: GenericResourceManager<ChunkId>(nullptr)
, mSceneManager(sceneManager)
, mRefTrackerLocked(false)
{
mActiveGrid = Settings::Manager::getBool("object paging active grid", "Terrain");
mDebugBatches = Settings::Manager::getBool("object paging debug batches", "Terrain");
mMergeFactor = Settings::Manager::getFloat("object paging merge factor", "Terrain");
mMinSize = Settings::Manager::getFloat("object paging min size", "Terrain");
mMinSizeMergeFactor = Settings::Manager::getFloat("object paging min size merge factor", "Terrain");
mMinSizeCostMultiplier = Settings::Manager::getFloat("object paging min size cost multiplier", "Terrain");
}
osg::ref_ptr<osg::Node> ObjectPaging::createChunk(float size, const osg::Vec2f& center, bool activeGrid, const osg::Vec3f& viewPoint, bool compile)
{
osg::Vec2i startCell = osg::Vec2i(std::floor(center.x() - size/2.f), std::floor(center.y() - size/2.f));
osg::Vec3f worldCenter = osg::Vec3f(center.x(), center.y(), 0)*ESM::Land::REAL_SIZE;
osg::Vec3f relativeViewPoint = viewPoint - worldCenter;
std::map<ESM::RefNum, ESM::CellRef> refs;
std::vector<ESM::ESMReader> esm;
const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();
for (int cellX = startCell.x(); cellX < startCell.x() + size; ++cellX)
{
for (int cellY = startCell.y(); cellY < startCell.y() + size; ++cellY)
{
const ESM::Cell* cell = store.get<ESM::Cell>().searchStatic(cellX, cellY);
if (!cell) continue;
for (size_t i=0; i<cell->mContextList.size(); ++i)
{
try
{
unsigned int index = cell->mContextList.at(i).index;
if (esm.size()<=index)
esm.resize(index+1);
cell->restore(esm[index], i);
ESM::CellRef ref;
ref.mRefNum.mContentFile = ESM::RefNum::RefNum_NoContentFile;
bool deleted = false;
while(cell->getNextRef(esm[index], ref, deleted))
{
Misc::StringUtils::lowerCaseInPlace(ref.mRefID);
if (std::find(cell->mMovedRefs.begin(), cell->mMovedRefs.end(), ref.mRefNum) != cell->mMovedRefs.end()) continue;
int type = store.findStatic(ref.mRefID);
if (!typeFilter(type,size>=2)) continue;
if (deleted) { refs.erase(ref.mRefNum); continue; }
refs[ref.mRefNum] = ref;
}
}
catch (std::exception& e)
{
continue;
}
}
for (ESM::CellRefTracker::const_iterator it = cell->mLeasedRefs.begin(); it != cell->mLeasedRefs.end(); ++it)
{
ESM::CellRef ref = it->first;
Misc::StringUtils::lowerCaseInPlace(ref.mRefID);
bool deleted = it->second;
if (deleted) { refs.erase(ref.mRefNum); continue; }
int type = store.findStatic(ref.mRefID);
if (!typeFilter(type,size>=2)) continue;
refs[ref.mRefNum] = ref;
}
}
}
if (activeGrid)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mRefTrackerMutex);
for (auto ref : getRefTracker().mBlacklist)
refs.erase(ref);
}
osg::Vec2f minBound = (center - osg::Vec2f(size/2.f, size/2.f));
osg::Vec2f maxBound = (center + osg::Vec2f(size/2.f, size/2.f));
struct InstanceList
{
std::vector<const ESM::CellRef*> mInstances;
AnalyzeVisitor::Result mAnalyzeResult;
bool mNeedCompile = false;
};
typedef std::map<osg::ref_ptr<const osg::Node>, InstanceList> NodeMap;
NodeMap nodes;
osg::ref_ptr<RefnumSet> refnumSet = activeGrid ? new RefnumSet : nullptr;
AnalyzeVisitor analyzeVisitor;
float minSize = mMinSize;
if (mMinSizeMergeFactor)
minSize *= mMinSizeMergeFactor;
for (const auto& pair : refs)
{
const ESM::CellRef& ref = pair.second;
osg::Vec3f pos = ref.mPos.asVec3();
if (size < 1.f)
{
osg::Vec3f cellPos = pos / ESM::Land::REAL_SIZE;
if ((minBound.x() > std::floor(minBound.x()) && cellPos.x() < minBound.x()) || (minBound.y() > std::floor(minBound.y()) && cellPos.y() < minBound.y())
|| (maxBound.x() < std::ceil(maxBound.x()) && cellPos.x() >= maxBound.x()) || (minBound.y() < std::ceil(maxBound.y()) && cellPos.y() >= maxBound.y()))
continue;
}
float dSqr = (viewPoint - pos).length2();
if (!activeGrid)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mSizeCacheMutex);
SizeCache::iterator found = mSizeCache.find(pair.first);
if (found != mSizeCache.end() && found->second < dSqr*minSize*minSize)
continue;
}
if (ref.mRefID == "prisonmarker" || ref.mRefID == "divinemarker" || ref.mRefID == "templemarker" || ref.mRefID == "northmarker")
continue; // marker objects that have a hardcoded function in the game logic, should be hidden from the player
int type = store.findStatic(ref.mRefID);
std::string model = getModel(type, ref.mRefID, store);
if (model.empty()) continue;
model = "meshes/" + model;
if (activeGrid && type != ESM::REC_STAT)
{
model = Misc::ResourceHelpers::correctActorModelPath(model, mSceneManager->getVFS());
std::string kfname = Misc::StringUtils::lowerCase(model);
if(kfname.size() > 4 && kfname.compare(kfname.size()-4, 4, ".nif") == 0)
{
kfname.replace(kfname.size()-4, 4, ".kf");
if (mSceneManager->getVFS()->exists(kfname))
continue;
}
}
osg::ref_ptr<const osg::Node> cnode = mSceneManager->getTemplate(model, false);
if (activeGrid)
{
if (cnode->getNumChildrenRequiringUpdateTraversal() > 0 || SceneUtil::hasUserDescription(cnode, Constants::NightDayLabel) || SceneUtil::hasUserDescription(cnode, Constants::HerbalismLabel))
continue;
else
refnumSet->mRefnums.insert(pair.first);
}
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mRefTrackerMutex);
if (getRefTracker().mDisabled.count(pair.first))
continue;
}
float radius2 = cnode->getBound().radius2() * ref.mScale*ref.mScale;
if (radius2 < dSqr*minSize*minSize && !activeGrid)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mSizeCacheMutex);
mSizeCache[pair.first] = radius2;
continue;
}
auto emplaced = nodes.emplace(cnode, InstanceList());
if (emplaced.second)
{
const_cast<osg::Node*>(cnode.get())->accept(analyzeVisitor); // const-trickery required because there is no const version of NodeVisitor
emplaced.first->second.mAnalyzeResult = analyzeVisitor.retrieveResult();
emplaced.first->second.mNeedCompile = compile && cnode->referenceCount() <= 3;
}
else
analyzeVisitor.addInstance(emplaced.first->second.mAnalyzeResult);
emplaced.first->second.mInstances.push_back(&ref);
}
osg::ref_ptr<osg::Group> group = new osg::Group;
osg::ref_ptr<osg::Group> mergeGroup = new osg::Group;
osg::ref_ptr<TemplateRef> templateRefs = new TemplateRef;
osgUtil::StateToCompile stateToCompile(0, nullptr);
CopyOp copyop;
for (const auto& pair : nodes)
{
const osg::Node* cnode = pair.first;
const AnalyzeVisitor::Result& analyzeResult = pair.second.mAnalyzeResult;
float mergeCost = analyzeResult.mNumVerts * size;
float mergeBenefit = analyzeVisitor.getMergeBenefit(analyzeResult) * mMergeFactor;
bool merge = mergeBenefit > mergeCost;
float minSizeMerged = mMinSize;
float factor2 = mergeBenefit > 0 ? std::min(1.f, mergeCost * mMinSizeCostMultiplier / mergeBenefit) : 1;
float minSizeMergeFactor2 = (1-factor2) * mMinSizeMergeFactor + factor2;
if (minSizeMergeFactor2 > 0)
minSizeMerged *= minSizeMergeFactor2;
unsigned int numinstances = 0;
for (auto cref : pair.second.mInstances)
{
const ESM::CellRef& ref = *cref;
osg::Vec3f pos = ref.mPos.asVec3();
if (!activeGrid && minSizeMerged != minSize && cnode->getBound().radius2() * cref->mScale*cref->mScale < (viewPoint-pos).length2()*minSizeMerged*minSizeMerged)
continue;
osg::Matrixf matrix;
matrix.preMultTranslate(pos - worldCenter);
matrix.preMultRotate( osg::Quat(ref.mPos.rot[2], osg::Vec3f(0,0,-1)) *
osg::Quat(ref.mPos.rot[1], osg::Vec3f(0,-1,0)) *
osg::Quat(ref.mPos.rot[0], osg::Vec3f(-1,0,0)) );
matrix.preMultScale(osg::Vec3f(ref.mScale, ref.mScale, ref.mScale));
osg::ref_ptr<osg::MatrixTransform> trans = new osg::MatrixTransform(matrix);
trans->setDataVariance(osg::Object::STATIC);
copyop.setCopyFlags(merge ? osg::CopyOp::DEEP_COPY_NODES|osg::CopyOp::DEEP_COPY_DRAWABLES : osg::CopyOp::DEEP_COPY_NODES);
copyop.mOptimizeBillboards = (size > 1/4.f);
copyop.mNodePath.push_back(trans);
copyop.mSqrDistance = (viewPoint - pos).length2();
copyop.mViewVector = (viewPoint - worldCenter);
copyop.copy(cnode, trans);
copyop.mNodePath.pop_back();
if (activeGrid)
{
if (merge)
{
AddRefnumMarkerVisitor visitor(ref.mRefNum);
trans->accept(visitor);
}
else
{
osg::ref_ptr<RefnumMarker> marker = new RefnumMarker; marker->mRefnum = ref.mRefNum;
trans->getOrCreateUserDataContainer()->addUserObject(marker);
}
}
osg::Group* attachTo = merge ? mergeGroup : group;
attachTo->addChild(trans);
++numinstances;
}
if (numinstances > 0)
{
// add a ref to the original template, to hint to the cache that it's still being used and should be kept in cache
templateRefs->mObjects.push_back(cnode);
if (pair.second.mNeedCompile)
{
int mode = osgUtil::GLObjectsVisitor::COMPILE_STATE_ATTRIBUTES;
if (!merge)
mode |= osgUtil::GLObjectsVisitor::COMPILE_DISPLAY_LISTS;
stateToCompile._mode = mode;
const_cast<osg::Node*>(cnode)->accept(stateToCompile);
}
}
}
if (mergeGroup->getNumChildren())
{
SceneUtil::Optimizer optimizer;
if (size > 1/8.f)
{
optimizer.setViewPoint(relativeViewPoint);
optimizer.setMergeAlphaBlending(true);
}
optimizer.setIsOperationPermissibleForObjectCallback(new CanOptimizeCallback);
unsigned int options = SceneUtil::Optimizer::FLATTEN_STATIC_TRANSFORMS|SceneUtil::Optimizer::REMOVE_REDUNDANT_NODES|SceneUtil::Optimizer::MERGE_GEOMETRY;
optimizer.optimize(mergeGroup, options);
group->addChild(mergeGroup);
if (mDebugBatches)
{
DebugVisitor dv;
mergeGroup->accept(dv);
}
if (compile)
{
stateToCompile._mode = osgUtil::GLObjectsVisitor::COMPILE_DISPLAY_LISTS;
mergeGroup->accept(stateToCompile);
}
}
auto ico = mSceneManager->getIncrementalCompileOperation();
if (!stateToCompile.empty() && ico)
{
auto compileSet = new osgUtil::IncrementalCompileOperation::CompileSet(group);
compileSet->buildCompileMap(ico->getContextSet(), stateToCompile);
ico->add(compileSet, false);
}
group->getBound();
group->setNodeMask(Mask_Static);
osg::UserDataContainer* udc = group->getOrCreateUserDataContainer();
if (activeGrid)
{
udc->addUserObject(refnumSet);
group->addCullCallback(new SceneUtil::LightListCallback);
}
udc->addUserObject(templateRefs);
return group;
}
unsigned int ObjectPaging::getNodeMask()
{
return Mask_Static;
}
struct ClearCacheFunctor
{
void operator()(MWRender::ChunkId id, osg::Object* obj)
{
if (intersects(id, mPosition))
mToClear.insert(id);
}
bool intersects(ChunkId id, osg::Vec3f pos)
{
if (mActiveGridOnly && !std::get<2>(id)) return false;
pos /= ESM::Land::REAL_SIZE;
osg::Vec2f center = std::get<0>(id);
float halfSize = std::get<1>(id)/2;
return pos.x() >= center.x()-halfSize && pos.y() >= center.y()-halfSize && pos.x() <= center.x()+halfSize && pos.y() <= center.y()+halfSize;
}
osg::Vec3f mPosition;
std::set<MWRender::ChunkId> mToClear;
bool mActiveGridOnly = false;
};
bool ObjectPaging::enableObject(int type, const ESM::RefNum & refnum, const osg::Vec3f& pos, bool enabled)
{
if (!typeFilter(type, false))
return false;
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mRefTrackerMutex);
if (enabled && !getWritableRefTracker().mDisabled.erase(refnum)) return false;
if (!enabled && !getWritableRefTracker().mDisabled.insert(refnum).second) return false;
if (mRefTrackerLocked) return false;
}
ClearCacheFunctor ccf;
ccf.mPosition = pos;
mCache->call(ccf);
if (ccf.mToClear.empty()) return false;
for (auto chunk : ccf.mToClear)
mCache->removeFromObjectCache(chunk);
return true;
}
bool ObjectPaging::blacklistObject(int type, const ESM::RefNum & refnum, const osg::Vec3f& pos)
{
if (!typeFilter(type, false))
return false;
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mRefTrackerMutex);
if (!getWritableRefTracker().mBlacklist.insert(refnum).second) return false;
if (mRefTrackerLocked) return false;
}
ClearCacheFunctor ccf;
ccf.mPosition = pos;
ccf.mActiveGridOnly = true;
mCache->call(ccf);
if (ccf.mToClear.empty()) return false;
for (auto chunk : ccf.mToClear)
mCache->removeFromObjectCache(chunk);
return true;
}
void ObjectPaging::clear()
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mRefTrackerMutex);
mRefTrackerNew.mDisabled.clear();
mRefTrackerNew.mBlacklist.clear();
mRefTrackerLocked = true;
}
bool ObjectPaging::unlockCache()
{
if (!mRefTrackerLocked) return false;
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mRefTrackerMutex);
mRefTrackerLocked = false;
if (mRefTracker == mRefTrackerNew)
return false;
else
mRefTracker = mRefTrackerNew;
}
mCache->clear();
return true;
}
struct GetRefnumsFunctor
{
GetRefnumsFunctor(std::set<ESM::RefNum>& output) : mOutput(output) {}
void operator()(MWRender::ChunkId chunkId, osg::Object* obj)
{
if (!std::get<2>(chunkId)) return;
const osg::Vec2f& center = std::get<0>(chunkId);
bool activeGrid = (center.x() > mActiveGrid.x() || center.y() > mActiveGrid.y() || center.x() < mActiveGrid.z() || center.y() < mActiveGrid.w());
if (!activeGrid) return;
osg::UserDataContainer* udc = obj->getUserDataContainer();
if (udc && udc->getNumUserObjects())
{
RefnumSet* refnums = dynamic_cast<RefnumSet*>(udc->getUserObject(0));
if (!refnums) return;
mOutput.insert(refnums->mRefnums.begin(), refnums->mRefnums.end());
}
}
osg::Vec4i mActiveGrid;
std::set<ESM::RefNum>& mOutput;
};
void ObjectPaging::getPagedRefnums(const osg::Vec4i &activeGrid, std::set<ESM::RefNum> &out)
{
GetRefnumsFunctor grf(out);
grf.mActiveGrid = activeGrid;
mCache->call(grf);
}
void ObjectPaging::reportStats(unsigned int frameNumber, osg::Stats *stats) const
{
stats->setAttribute(frameNumber, "Object Chunk", mCache->getCacheSize());
}
}

View file

@ -0,0 +1,92 @@
#ifndef OPENMW_COMPONENTS_ESMPAGING_CHUNKMANAGER_H
#define OPENMW_COMPONENTS_ESMPAGING_CHUNKMANAGER_H
#include <components/terrain/quadtreeworld.hpp>
#include <components/resource/resourcemanager.hpp>
#include <components/esm/loadcell.hpp>
#include <OpenThreads/Mutex>
namespace Resource
{
class SceneManager;
}
namespace MWWorld
{
class ESMStore;
}
namespace MWRender
{
typedef std::tuple<osg::Vec2f, float, bool> ChunkId; // Center, Size, ActiveGrid
class ObjectPaging : public Resource::GenericResourceManager<ChunkId>, public Terrain::QuadTreeWorld::ChunkManager
{
public:
ObjectPaging(Resource::SceneManager* sceneManager);
~ObjectPaging() = default;
osg::ref_ptr<osg::Node> getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool activeGrid, const osg::Vec3f& viewPoint, bool compile) override;
osg::ref_ptr<osg::Node> createChunk(float size, const osg::Vec2f& center, bool activeGrid, const osg::Vec3f& viewPoint, bool compile);
virtual unsigned int getNodeMask() override;
/// @return true if view needs rebuild
bool enableObject(int type, const ESM::RefNum & refnum, const osg::Vec3f& pos, bool enabled);
/// @return true if view needs rebuild
bool blacklistObject(int type, const ESM::RefNum & refnum, const osg::Vec3f& pos);
void clear();
/// Must be called after clear() before rendering starts.
/// @return true if view needs rebuild
bool unlockCache();
void reportStats(unsigned int frameNumber, osg::Stats* stats) const override;
void getPagedRefnums(const osg::Vec4i &activeGrid, std::set<ESM::RefNum> &out);
private:
Resource::SceneManager* mSceneManager;
bool mActiveGrid;
bool mDebugBatches;
float mMergeFactor;
float mMinSize;
float mMinSizeMergeFactor;
float mMinSizeCostMultiplier;
OpenThreads::Mutex mRefTrackerMutex;
struct RefTracker
{
std::set<ESM::RefNum> mDisabled;
std::set<ESM::RefNum> mBlacklist;
bool operator==(const RefTracker&other) { return mDisabled == other.mDisabled && mBlacklist == other.mBlacklist; }
};
RefTracker mRefTracker;
RefTracker mRefTrackerNew;
bool mRefTrackerLocked;
const RefTracker& getRefTracker() const { return mRefTracker; }
RefTracker& getWritableRefTracker() { return mRefTrackerLocked ? mRefTrackerNew : mRefTracker; }
OpenThreads::Mutex mSizeCacheMutex;
typedef std::map<ESM::RefNum, float> SizeCache;
SizeCache mSizeCache;
};
class RefnumMarker : public osg::Object
{
public:
RefnumMarker() : mNumVertices(0) { mRefnum.unset(); }
RefnumMarker(const RefnumMarker &copy, osg::CopyOp co) : mRefnum(copy.mRefnum), mNumVertices(copy.mNumVertices) {}
META_Object(MWRender, RefnumMarker)
ESM::RefNum mRefnum;
unsigned int mNumVertices;
};
}
#endif

View file

@ -8,6 +8,7 @@
#include <components/esm/loadpgrd.hpp> #include <components/esm/loadpgrd.hpp>
#include <components/sceneutil/pathgridutil.hpp> #include <components/sceneutil/pathgridutil.hpp>
#include <components/misc/coordinateconverter.hpp>
#include "../mwbase/world.hpp" // these includes can be removed once the static-hack is gone #include "../mwbase/world.hpp" // these includes can be removed once the static-hack is gone
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
@ -15,7 +16,6 @@
#include "../mwworld/cellstore.hpp" #include "../mwworld/cellstore.hpp"
#include "../mwworld/esmstore.hpp" #include "../mwworld/esmstore.hpp"
#include "../mwmechanics/pathfinding.hpp" #include "../mwmechanics/pathfinding.hpp"
#include "../mwmechanics/coordinateconverter.hpp"
#include "vismask.hpp" #include "vismask.hpp"
@ -105,7 +105,7 @@ void Pathgrid::enableCellPathgrid(const MWWorld::CellStore *store)
if (!pathgrid) return; if (!pathgrid) return;
osg::Vec3f cellPathGridPos(0, 0, 0); osg::Vec3f cellPathGridPos(0, 0, 0);
MWMechanics::CoordinateConverter(store->getCell()).toWorld(cellPathGridPos); Misc::CoordinateConverter(store->getCell()).toWorld(cellPathGridPos);
osg::ref_ptr<osg::PositionAttitudeTransform> cellPathGrid = new osg::PositionAttitudeTransform; osg::ref_ptr<osg::PositionAttitudeTransform> cellPathGrid = new osg::PositionAttitudeTransform;
cellPathGrid->setPosition(cellPathGridPos); cellPathGrid->setPosition(cellPathGridPos);

View file

@ -70,6 +70,8 @@
#include "actorspaths.hpp" #include "actorspaths.hpp"
#include "recastmesh.hpp" #include "recastmesh.hpp"
#include "fogmanager.hpp" #include "fogmanager.hpp"
#include "objectpaging.hpp"
namespace MWRender namespace MWRender
{ {
@ -258,7 +260,6 @@ namespace MWRender
{ {
mViewer->setIncrementalCompileOperation(new osgUtil::IncrementalCompileOperation); mViewer->setIncrementalCompileOperation(new osgUtil::IncrementalCompileOperation);
mViewer->getIncrementalCompileOperation()->setTargetFrameRate(Settings::Manager::getFloat("target framerate", "Cells")); mViewer->getIncrementalCompileOperation()->setTargetFrameRate(Settings::Manager::getFloat("target framerate", "Cells"));
mViewer->getIncrementalCompileOperation()->setMaximumNumOfObjectsToCompilePerFrame(100);
} }
mResourceSystem->getSceneManager()->setIncrementalCompileOperation(mViewer->getIncrementalCompileOperation()); mResourceSystem->getSceneManager()->setIncrementalCompileOperation(mViewer->getIncrementalCompileOperation());
@ -286,6 +287,12 @@ namespace MWRender
mTerrain.reset(new Terrain::QuadTreeWorld( mTerrain.reset(new Terrain::QuadTreeWorld(
sceneRoot, mRootNode, mResourceSystem, mTerrainStorage, Mask_Terrain, Mask_PreCompile, Mask_Debug, sceneRoot, mRootNode, mResourceSystem, mTerrainStorage, Mask_Terrain, Mask_PreCompile, Mask_Debug,
compMapResolution, compMapLevel, lodFactor, vertexLodMod, maxCompGeometrySize)); compMapResolution, compMapLevel, lodFactor, vertexLodMod, maxCompGeometrySize));
if (Settings::Manager::getBool("object paging", "Terrain"))
{
mObjectPaging.reset(new ObjectPaging(mResourceSystem->getSceneManager()));
static_cast<Terrain::QuadTreeWorld*>(mTerrain.get())->addChunkManager(mObjectPaging.get());
mResourceSystem->addResourceManager(mObjectPaging.get());
}
} }
else else
mTerrain.reset(new Terrain::TerrainGrid(sceneRoot, mRootNode, mResourceSystem, mTerrainStorage, Mask_Terrain, Mask_PreCompile, Mask_Debug)); mTerrain.reset(new Terrain::TerrainGrid(sceneRoot, mRootNode, mResourceSystem, mTerrainStorage, Mask_Terrain, Mask_PreCompile, Mask_Debug));
@ -970,20 +977,14 @@ namespace MWRender
renderCameraToImage(rttCamera.get(),image,w,h); renderCameraToImage(rttCamera.get(),image,w,h);
} }
osg::Vec4f RenderingManager::getScreenBounds(const MWWorld::Ptr& ptr) osg::Vec4f RenderingManager::getScreenBounds(const osg::BoundingBox &worldbb)
{ {
if (!ptr.getRefData().getBaseNode()) if (!worldbb.valid()) return osg::Vec4f();
return osg::Vec4f();
osg::ComputeBoundsVisitor computeBoundsVisitor;
computeBoundsVisitor.setTraversalMask(~(Mask_ParticleSystem|Mask_Effect));
ptr.getRefData().getBaseNode()->accept(computeBoundsVisitor);
osg::Matrix viewProj = mViewer->getCamera()->getViewMatrix() * mViewer->getCamera()->getProjectionMatrix(); osg::Matrix viewProj = mViewer->getCamera()->getViewMatrix() * mViewer->getCamera()->getProjectionMatrix();
float min_x = 1.0f, max_x = 0.0f, min_y = 1.0f, max_y = 0.0f; float min_x = 1.0f, max_x = 0.0f, min_y = 1.0f, max_y = 0.0f;
for (int i=0; i<8; ++i) for (int i=0; i<8; ++i)
{ {
osg::Vec3f corner = computeBoundsVisitor.getBoundingBox().corner(i); osg::Vec3f corner = worldbb.corner(i);
corner = corner * viewProj; corner = corner * viewProj;
float x = (corner.x() + 1.f) * 0.5f; float x = (corner.x() + 1.f) * 0.5f;
@ -1009,6 +1010,7 @@ namespace MWRender
{ {
RenderingManager::RayResult result; RenderingManager::RayResult result;
result.mHit = false; result.mHit = false;
result.mHitRefnum.mContentFile = -1;
result.mRatio = 0; result.mRatio = 0;
if (intersector->containsIntersections()) if (intersector->containsIntersections())
{ {
@ -1020,6 +1022,7 @@ namespace MWRender
result.mRatio = intersection.ratio; result.mRatio = intersection.ratio;
PtrHolder* ptrHolder = nullptr; PtrHolder* ptrHolder = nullptr;
std::vector<RefnumMarker*> refnumMarkers;
for (osg::NodePath::const_iterator it = intersection.nodePath.begin(); it != intersection.nodePath.end(); ++it) for (osg::NodePath::const_iterator it = intersection.nodePath.begin(); it != intersection.nodePath.end(); ++it)
{ {
osg::UserDataContainer* userDataContainer = (*it)->getUserDataContainer(); osg::UserDataContainer* userDataContainer = (*it)->getUserDataContainer();
@ -1029,11 +1032,25 @@ namespace MWRender
{ {
if (PtrHolder* p = dynamic_cast<PtrHolder*>(userDataContainer->getUserObject(i))) if (PtrHolder* p = dynamic_cast<PtrHolder*>(userDataContainer->getUserObject(i)))
ptrHolder = p; ptrHolder = p;
if (RefnumMarker* r = dynamic_cast<RefnumMarker*>(userDataContainer->getUserObject(i)))
refnumMarkers.push_back(r);
} }
} }
if (ptrHolder) if (ptrHolder)
result.mHitObject = ptrHolder->mPtr; result.mHitObject = ptrHolder->mPtr;
unsigned int vertexCounter = 0;
for (unsigned int i=0; i<refnumMarkers.size(); ++i)
{
unsigned int intersectionIndex = intersection.indexList.empty() ? 0 : intersection.indexList[0];
if (!refnumMarkers[i]->mNumVertices || (intersectionIndex >= vertexCounter && intersectionIndex < vertexCounter + refnumMarkers[i]->mNumVertices))
{
result.mHitRefnum = refnumMarkers[i]->mRefnum;
break;
}
vertexCounter += refnumMarkers[i]->mNumVertices;
}
} }
return result; return result;
@ -1046,6 +1063,7 @@ namespace MWRender
mIntersectionVisitor = new osgUtil::IntersectionVisitor; mIntersectionVisitor = new osgUtil::IntersectionVisitor;
mIntersectionVisitor->setTraversalNumber(mViewer->getFrameStamp()->getFrameNumber()); mIntersectionVisitor->setTraversalNumber(mViewer->getFrameStamp()->getFrameNumber());
mIntersectionVisitor->setFrameStamp(mViewer->getFrameStamp());
mIntersectionVisitor->setIntersector(intersector); mIntersectionVisitor->setIntersector(intersector);
int mask = ~0; int mask = ~0;
@ -1111,6 +1129,8 @@ namespace MWRender
mSky->setMoonColour(false); mSky->setMoonColour(false);
notifyWorldSpaceChanged(); notifyWorldSpaceChanged();
if (mObjectPaging)
mObjectPaging->clear();
} }
MWRender::Animation* RenderingManager::getAnimation(const MWWorld::Ptr &ptr) MWRender::Animation* RenderingManager::getAnimation(const MWWorld::Ptr &ptr)
@ -1467,4 +1487,43 @@ namespace MWRender
mRecastMesh->update(mNavigator.getRecastMeshTiles(), mNavigator.getSettings()); mRecastMesh->update(mNavigator.getRecastMeshTiles(), mNavigator.getSettings());
} }
void RenderingManager::setActiveGrid(const osg::Vec4i &grid)
{
mTerrain->setActiveGrid(grid);
}
bool RenderingManager::pagingEnableObject(int type, const MWWorld::ConstPtr& ptr, bool enabled)
{
if (!ptr.isInCell() || !ptr.getCell()->isExterior() || !mObjectPaging)
return false;
if (mObjectPaging->enableObject(type, ptr.getCellRef().getRefNum(), ptr.getCellRef().getPosition().asVec3(), enabled))
{
mTerrain->rebuildViews();
return true;
}
return false;
}
void RenderingManager::pagingBlacklistObject(int type, const MWWorld::ConstPtr &ptr)
{
if (!ptr.isInCell() || !ptr.getCell()->isExterior() || !mObjectPaging)
return;
const ESM::RefNum & refnum = ptr.getCellRef().getRefNum();
if (!refnum.hasContentFile()) return;
if (mObjectPaging->blacklistObject(type, refnum, ptr.getCellRef().getPosition().asVec3()))
mTerrain->rebuildViews();
}
bool RenderingManager::pagingUnlockCache()
{
if (mObjectPaging && mObjectPaging->unlockCache())
{
mTerrain->rebuildViews();
return true;
}
return false;
}
void RenderingManager::getPagedRefnums(const osg::Vec4i &activeGrid, std::set<ESM::RefNum> &out)
{
if (mObjectPaging)
mObjectPaging->getPagedRefnums(activeGrid, out);
}
} }

View file

@ -42,6 +42,7 @@ namespace osgViewer
namespace ESM namespace ESM
{ {
struct Cell; struct Cell;
struct RefNum;
} }
namespace Terrain namespace Terrain
@ -84,6 +85,7 @@ namespace MWRender
class NavMesh; class NavMesh;
class ActorsPaths; class ActorsPaths;
class RecastMesh; class RecastMesh;
class ObjectPaging;
class RenderingManager : public MWRender::RenderingInterface class RenderingManager : public MWRender::RenderingInterface
{ {
@ -155,6 +157,7 @@ namespace MWRender
osg::Vec3f mHitNormalWorld; osg::Vec3f mHitNormalWorld;
osg::Vec3f mHitPointWorld; osg::Vec3f mHitPointWorld;
MWWorld::Ptr mHitObject; MWWorld::Ptr mHitObject;
ESM::RefNum mHitRefnum;
float mRatio; float mRatio;
}; };
@ -165,7 +168,7 @@ namespace MWRender
RayResult castCameraToViewportRay(const float nX, const float nY, float maxDistance, bool ignorePlayer, bool ignoreActors=false); RayResult castCameraToViewportRay(const float nX, const float nY, float maxDistance, bool ignorePlayer, bool ignoreActors=false);
/// Get the bounding box of the given object in screen coordinates as (minX, minY, maxX, maxY), with (0,0) being the top left corner. /// Get the bounding box of the given object in screen coordinates as (minX, minY, maxX, maxY), with (0,0) being the top left corner.
osg::Vec4f getScreenBounds(const MWWorld::Ptr& ptr); osg::Vec4f getScreenBounds(const osg::BoundingBox &worldbb);
void setSkyEnabled(bool enabled); void setSkyEnabled(bool enabled);
@ -237,6 +240,13 @@ namespace MWRender
void setNavMeshNumber(const std::size_t value); void setNavMeshNumber(const std::size_t value);
void setActiveGrid(const osg::Vec4i &grid);
bool pagingEnableObject(int type, const MWWorld::ConstPtr& ptr, bool enabled);
void pagingBlacklistObject(int type, const MWWorld::ConstPtr &ptr);
bool pagingUnlockCache();
void getPagedRefnums(const osg::Vec4i &activeGrid, std::set<ESM::RefNum> &out);
private: private:
void updateProjectionMatrix(); void updateProjectionMatrix();
void updateTextureFiltering(); void updateTextureFiltering();
@ -275,6 +285,7 @@ namespace MWRender
std::unique_ptr<Water> mWater; std::unique_ptr<Water> mWater;
std::unique_ptr<Terrain::World> mTerrain; std::unique_ptr<Terrain::World> mTerrain;
TerrainStorage* mTerrainStorage; TerrainStorage* mTerrainStorage;
std::unique_ptr<ObjectPaging> mObjectPaging;
std::unique_ptr<SkyManager> mSky; std::unique_ptr<SkyManager> mSky;
std::unique_ptr<FogManager> mFog; std::unique_ptr<FogManager> mFog;
std::unique_ptr<EffectManager> mEffectManager; std::unique_ptr<EffectManager> mEffectManager;

View file

@ -427,7 +427,7 @@ namespace MWScript
{ {
MWWorld::Ptr ptr = R()(runtime); MWWorld::Ptr ptr = R()(runtime);
Interpreter::Type_Integer value = ptr.getClass().getCreatureStats (ptr).getAiSequence().getLastRunTypeId(); const auto value = static_cast<Interpreter::Type_Integer>(ptr.getClass().getCreatureStats (ptr).getAiSequence().getLastRunTypeId());
runtime.push (value); runtime.push (value);
} }

View file

@ -24,6 +24,7 @@ namespace
{ {
ESM::GlobalScript script; ESM::GlobalScript script;
script.mTargetRef.unset(); script.mTargetRef.unset();
script.mRunning = false;
if (!ptr.isEmpty()) if (!ptr.isEmpty())
{ {
if (ptr.getCellRef().hasContentFile()) if (ptr.getCellRef().hasContentFile())
@ -42,6 +43,7 @@ namespace
ESM::GlobalScript script; ESM::GlobalScript script;
script.mTargetId = pair.second; script.mTargetId = pair.second;
script.mTargetRef = pair.first; script.mTargetRef = pair.first;
script.mRunning = false;
return script; return script;
} }
}; };

View file

@ -8,6 +8,7 @@
#include <components/resource/resourcesystem.hpp> #include <components/resource/resourcesystem.hpp>
#include <components/resource/bulletshapemanager.hpp> #include <components/resource/bulletshapemanager.hpp>
#include <components/resource/keyframemanager.hpp> #include <components/resource/keyframemanager.hpp>
#include <components/vfs/manager.hpp>
#include <components/misc/resourcehelpers.hpp> #include <components/misc/resourcehelpers.hpp>
#include <components/misc/stringops.hpp> #include <components/misc/stringops.hpp>
#include <components/terrain/world.hpp> #include <components/terrain/world.hpp>
@ -65,23 +66,7 @@ namespace MWWorld
mTerrainView = mTerrain->createView(); mTerrainView = mTerrain->createView();
ListModelsVisitor visitor (mMeshes); ListModelsVisitor visitor (mMeshes);
if (cell->getState() == MWWorld::CellStore::State_Loaded) cell->forEach(visitor);
{
cell->forEach(visitor);
}
else
{
const std::vector<std::string>& objectIds = cell->getPreloadedIds();
// could possibly build the model list in the worker thread if we manage to make the Store thread safe
for (const std::string& id : objectIds)
{
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), id);
std::string model = ref.getPtr().getClass().getModel(ref.getPtr());
if (!model.empty())
mMeshes.push_back(model);
}
}
} }
virtual void abort() virtual void abort()
@ -97,7 +82,7 @@ namespace MWWorld
try try
{ {
mTerrain->cacheCell(mTerrainView.get(), mX, mY); mTerrain->cacheCell(mTerrainView.get(), mX, mY);
mPreloadedObjects.push_back(mLandManager->getLand(mX, mY)); mPreloadedObjects.insert(mLandManager->getLand(mX, mY));
} }
catch(std::exception& e) catch(std::exception& e)
{ {
@ -113,17 +98,7 @@ namespace MWWorld
{ {
mesh = Misc::ResourceHelpers::correctActorModelPath(mesh, mSceneManager->getVFS()); mesh = Misc::ResourceHelpers::correctActorModelPath(mesh, mSceneManager->getVFS());
if (mPreloadInstances) bool animated = false;
{
mPreloadedObjects.push_back(mSceneManager->cacheInstance(mesh));
mPreloadedObjects.push_back(mBulletShapeManager->cacheInstance(mesh));
}
else
{
mPreloadedObjects.push_back(mSceneManager->getTemplate(mesh));
mPreloadedObjects.push_back(mBulletShapeManager->getShape(mesh));
}
size_t slashpos = mesh.find_last_of("/\\"); size_t slashpos = mesh.find_last_of("/\\");
if (slashpos != std::string::npos && slashpos != mesh.size()-1) if (slashpos != std::string::npos && slashpos != mesh.size()-1)
{ {
@ -134,11 +109,23 @@ namespace MWWorld
if(kfname.size() > 4 && kfname.compare(kfname.size()-4, 4, ".nif") == 0) if(kfname.size() > 4 && kfname.compare(kfname.size()-4, 4, ".nif") == 0)
{ {
kfname.replace(kfname.size()-4, 4, ".kf"); kfname.replace(kfname.size()-4, 4, ".kf");
mPreloadedObjects.push_back(mKeyframeManager->get(kfname)); if (mSceneManager->getVFS()->exists(kfname))
{
mPreloadedObjects.insert(mKeyframeManager->get(kfname));
animated = true;
}
} }
} }
} }
if (mPreloadInstances && animated)
mPreloadedObjects.insert(mSceneManager->cacheInstance(mesh));
else
mPreloadedObjects.insert(mSceneManager->getTemplate(mesh));
if (mPreloadInstances)
mPreloadedObjects.insert(mBulletShapeManager->cacheInstance(mesh));
else
mPreloadedObjects.insert(mBulletShapeManager->getShape(mesh));
} }
catch (std::exception& e) catch (std::exception& e)
{ {
@ -166,24 +153,28 @@ namespace MWWorld
osg::ref_ptr<Terrain::View> mTerrainView; osg::ref_ptr<Terrain::View> mTerrainView;
// keep a ref to the loaded objects to make sure it stays loaded as long as this cell is in the preloaded state // keep a ref to the loaded objects to make sure it stays loaded as long as this cell is in the preloaded state
std::vector<osg::ref_ptr<const osg::Object> > mPreloadedObjects; std::set<osg::ref_ptr<const osg::Object> > mPreloadedObjects;
}; };
class TerrainPreloadItem : public SceneUtil::WorkItem class TerrainPreloadItem : public SceneUtil::WorkItem
{ {
public: public:
TerrainPreloadItem(const std::vector<osg::ref_ptr<Terrain::View> >& views, Terrain::World* world, const std::vector<osg::Vec3f>& preloadPositions) TerrainPreloadItem(const std::vector<osg::ref_ptr<Terrain::View> >& views, Terrain::World* world, const std::vector<CellPreloader::PositionCellGrid>& preloadPositions)
: mAbort(false) : mAbort(false)
, mProgress(views.size())
, mProgressRange(0)
, mTerrainViews(views) , mTerrainViews(views)
, mWorld(world) , mWorld(world)
, mPreloadPositions(preloadPositions) , mPreloadPositions(preloadPositions)
{ {
} }
void storeViews(double referenceTime) bool storeViews(double referenceTime)
{ {
for (unsigned int i=0; i<mTerrainViews.size() && i<mPreloadPositions.size(); ++i) for (unsigned int i=0; i<mTerrainViews.size() && i<mPreloadPositions.size(); ++i)
mWorld->storeView(mTerrainViews[i], referenceTime); if (!mWorld->storeView(mTerrainViews[i], referenceTime))
return false;
return true;
} }
virtual void doWork() virtual void doWork()
@ -191,7 +182,7 @@ namespace MWWorld
for (unsigned int i=0; i<mTerrainViews.size() && i<mPreloadPositions.size() && !mAbort; ++i) for (unsigned int i=0; i<mTerrainViews.size() && i<mPreloadPositions.size() && !mAbort; ++i)
{ {
mTerrainViews[i]->reset(); mTerrainViews[i]->reset();
mWorld->preload(mTerrainViews[i], mPreloadPositions[i], mAbort); mWorld->preload(mTerrainViews[i], mPreloadPositions[i].first, mPreloadPositions[i].second, mAbort, mProgress[i], mProgressRange);
} }
} }
@ -200,11 +191,16 @@ namespace MWWorld
mAbort = true; mAbort = true;
} }
int getProgress() const { return !mProgress.empty() ? mProgress[0].load() : 0; }
int getProgressRange() const { return !mProgress.empty() && mProgress[0].load() ? mProgressRange : 0; }
private: private:
std::atomic<bool> mAbort; std::atomic<bool> mAbort;
std::vector<std::atomic<int>> mProgress;
int mProgressRange;
std::vector<osg::ref_ptr<Terrain::View> > mTerrainViews; std::vector<osg::ref_ptr<Terrain::View> > mTerrainViews;
Terrain::World* mWorld; Terrain::World* mWorld;
std::vector<osg::Vec3f> mPreloadPositions; std::vector<CellPreloader::PositionCellGrid> mPreloadPositions;
}; };
/// Worker thread item: update the resource system's cache, effectively deleting unused entries. /// Worker thread item: update the resource system's cache, effectively deleting unused entries.
@ -237,6 +233,7 @@ namespace MWWorld
, mMaxCacheSize(0) , mMaxCacheSize(0)
, mPreloadInstances(true) , mPreloadInstances(true)
, mLastResourceCacheUpdate(0.0) , mLastResourceCacheUpdate(0.0)
, mStoreViewsFailCount(0)
{ {
} }
@ -328,9 +325,6 @@ namespace MWWorld
} }
mPreloadCells.erase(found); mPreloadCells.erase(found);
if (cell->isExterior() && mTerrainPreloadItem && mTerrainPreloadItem->isDone())
mTerrainPreloadItem->storeViews(0.0);
} }
} }
@ -375,7 +369,17 @@ namespace MWWorld
if (mTerrainPreloadItem && mTerrainPreloadItem->isDone()) if (mTerrainPreloadItem && mTerrainPreloadItem->isDone())
{ {
mTerrainPreloadItem->storeViews(timestamp); if (!mTerrainPreloadItem->storeViews(timestamp))
{
if (++mStoreViewsFailCount > 100)
{
OSG_ALWAYS << "paging views are rebuilt every frame, please check for faulty enable/disable scripts." << std::endl;
mStoreViewsFailCount = 0;
}
setTerrainPreloadPositions(std::vector<PositionCellGrid>());
}
else
mStoreViewsFailCount = 0;
mTerrainPreloadItem = nullptr; mTerrainPreloadItem = nullptr;
} }
} }
@ -415,11 +419,71 @@ namespace MWWorld
mUnrefQueue = unrefQueue; mUnrefQueue = unrefQueue;
} }
void CellPreloader::setTerrainPreloadPositions(const std::vector<osg::Vec3f> &positions) bool CellPreloader::syncTerrainLoad(const std::vector<CellPreloader::PositionCellGrid> &positions, int& progress, int& progressRange, double timestamp)
{ {
if (!mTerrainPreloadItem)
return true;
else if (mTerrainPreloadItem->isDone())
{
if (mTerrainPreloadItem->storeViews(timestamp))
{
mTerrainPreloadItem = nullptr;
return true;
}
else
{
setTerrainPreloadPositions(std::vector<CellPreloader::PositionCellGrid>());
setTerrainPreloadPositions(positions);
return false;
}
}
else
{
progress = mTerrainPreloadItem->getProgress();
progressRange = mTerrainPreloadItem->getProgressRange();
return false;
}
}
void CellPreloader::abortTerrainPreloadExcept(const CellPreloader::PositionCellGrid *exceptPos)
{
const float resetThreshold = ESM::Land::REAL_SIZE;
for (auto pos : mTerrainPreloadPositions)
if (exceptPos && (pos.first-exceptPos->first).length2() < resetThreshold*resetThreshold && pos.second == exceptPos->second)
return;
if (mTerrainPreloadItem && !mTerrainPreloadItem->isDone()) if (mTerrainPreloadItem && !mTerrainPreloadItem->isDone())
{
mTerrainPreloadItem->abort();
mTerrainPreloadItem->waitTillDone();
}
setTerrainPreloadPositions(std::vector<CellPreloader::PositionCellGrid>());
}
bool contains(const std::vector<CellPreloader::PositionCellGrid>& container, const std::vector<CellPreloader::PositionCellGrid>& contained)
{
for (auto pos : contained)
{
bool found = false;
for (auto pos2 : container)
{
if ((pos.first-pos2.first).length2() < 1 && pos.second == pos2.second)
{
found = true;
break;
}
}
if (!found) return false;
}
return true;
}
void CellPreloader::setTerrainPreloadPositions(const std::vector<CellPreloader::PositionCellGrid> &positions)
{
if (positions.empty())
mTerrainPreloadPositions.clear();
else if (contains(mTerrainPreloadPositions, positions))
return; return;
else if (positions == mTerrainPreloadPositions) if (mTerrainPreloadItem && !mTerrainPreloadItem->isDone())
return; return;
else else
{ {
@ -436,8 +500,11 @@ namespace MWWorld
} }
mTerrainPreloadPositions = positions; mTerrainPreloadPositions = positions;
mTerrainPreloadItem = new TerrainPreloadItem(mTerrainViews, mTerrain, positions); if (!positions.empty())
mWorkQueue->addWorkItem(mTerrainPreloadItem); {
mTerrainPreloadItem = new TerrainPreloadItem(mTerrainViews, mTerrain, positions);
mWorkQueue->addWorkItem(mTerrainPreloadItem);
}
} }
} }

View file

@ -4,6 +4,7 @@
#include <map> #include <map>
#include <osg/ref_ptr> #include <osg/ref_ptr>
#include <osg/Vec3f> #include <osg/Vec3f>
#include <osg/Vec4i>
#include <components/sceneutil/workqueue.hpp> #include <components/sceneutil/workqueue.hpp>
namespace Resource namespace Resource
@ -68,7 +69,11 @@ namespace MWWorld
void setUnrefQueue(SceneUtil::UnrefQueue* unrefQueue); void setUnrefQueue(SceneUtil::UnrefQueue* unrefQueue);
void setTerrainPreloadPositions(const std::vector<osg::Vec3f>& positions); typedef std::pair<osg::Vec3f, osg::Vec4i> PositionCellGrid;
void setTerrainPreloadPositions(const std::vector<PositionCellGrid>& positions);
bool syncTerrainLoad(const std::vector<CellPreloader::PositionCellGrid> &positions, int& progress, int& progressRange, double timestamp);
void abortTerrainPreloadExcept(const PositionCellGrid *exceptPos);
private: private:
Resource::ResourceSystem* mResourceSystem; Resource::ResourceSystem* mResourceSystem;
@ -83,6 +88,7 @@ namespace MWWorld
bool mPreloadInstances; bool mPreloadInstances;
double mLastResourceCacheUpdate; double mLastResourceCacheUpdate;
int mStoreViewsFailCount;
struct PreloadEntry struct PreloadEntry
{ {
@ -105,7 +111,7 @@ namespace MWWorld
PreloadMap mPreloadCells; PreloadMap mPreloadCells;
std::vector<osg::ref_ptr<Terrain::View> > mTerrainViews; std::vector<osg::ref_ptr<Terrain::View> > mTerrainViews;
std::vector<osg::Vec3f> mTerrainPreloadPositions; std::vector<PositionCellGrid> mTerrainPreloadPositions;
osg::ref_ptr<TerrainPreloadItem> mTerrainPreloadItem; osg::ref_ptr<TerrainPreloadItem> mTerrainPreloadItem;
osg::ref_ptr<SceneUtil::WorkItem> mUpdateCacheItem; osg::ref_ptr<SceneUtil::WorkItem> mUpdateCacheItem;
}; };

View file

@ -119,7 +119,7 @@ namespace
template<typename RecordType, typename T> template<typename RecordType, typename T>
void readReferenceCollection (ESM::ESMReader& reader, void readReferenceCollection (ESM::ESMReader& reader,
MWWorld::CellRefList<T>& collection, const ESM::CellRef& cref, const std::map<int, int>& contentFileMap) MWWorld::CellRefList<T>& collection, const ESM::CellRef& cref, const std::map<int, int>& contentFileMap, MWWorld::CellStore* cellstore)
{ {
const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore();
@ -154,7 +154,18 @@ namespace
if (iter->mRef.getRefNum()==state.mRef.mRefNum && *iter->mRef.getRefIdPtr() == state.mRef.mRefID) if (iter->mRef.getRefNum()==state.mRef.mRefNum && *iter->mRef.getRefIdPtr() == state.mRef.mRefID)
{ {
// overwrite existing reference // overwrite existing reference
float oldscale = iter->mRef.getScale();
iter->load (state); iter->load (state);
const ESM::Position & oldpos = iter->mRef.getPosition();
const ESM::Position & newpos = iter->mData.getPosition();
const MWWorld::Ptr ptr(&*iter, cellstore);
if ((oldscale != iter->mRef.getScale() || oldpos.asVec3() != newpos.asVec3() || oldpos.rot[0] != newpos.rot[0] || oldpos.rot[1] != newpos.rot[1] || oldpos.rot[2] != newpos.rot[2]) && !ptr.getClass().isActor())
MWBase::Environment::get().getWorld()->moveObject(ptr, newpos.pos[0], newpos.pos[1], newpos.pos[2]);
if (!iter->mData.isEnabled())
{
iter->mData.enable();
MWBase::Environment::get().getWorld()->disable(MWWorld::Ptr(&*iter, cellstore));
}
return; return;
} }
@ -167,28 +178,6 @@ namespace
ref.load (state); ref.load (state);
collection.mList.push_back (ref); collection.mList.push_back (ref);
} }
struct SearchByRefNumVisitor
{
MWWorld::LiveCellRefBase* mFound;
ESM::RefNum mRefNumToFind;
SearchByRefNumVisitor(const ESM::RefNum& toFind)
: mFound(nullptr)
, mRefNumToFind(toFind)
{
}
bool operator()(const MWWorld::Ptr& ptr)
{
if (ptr.getCellRef().getRefNum() == mRefNumToFind)
{
mFound = ptr.getBase();
return false;
}
return true;
}
};
} }
namespace MWWorld namespace MWWorld
@ -284,9 +273,7 @@ namespace MWWorld
throw std::runtime_error("moveTo: can't move object from a non-loaded cell (how did you get this object anyway?)"); throw std::runtime_error("moveTo: can't move object from a non-loaded cell (how did you get this object anyway?)");
// Ensure that the object actually exists in the cell // Ensure that the object actually exists in the cell
SearchByRefNumVisitor searchVisitor(object.getCellRef().getRefNum()); if (searchViaRefNum(object.getCellRef().getRefNum()).isEmpty())
forEach(searchVisitor);
if (!searchVisitor.mFound)
throw std::runtime_error("moveTo: object is not in this cell"); throw std::runtime_error("moveTo: object is not in this cell");
@ -1030,107 +1017,107 @@ namespace MWWorld
{ {
case ESM::REC_ACTI: case ESM::REC_ACTI:
readReferenceCollection<ESM::ObjectState> (reader, mActivators, cref, contentFileMap); readReferenceCollection<ESM::ObjectState> (reader, mActivators, cref, contentFileMap, this);
break; break;
case ESM::REC_ALCH: case ESM::REC_ALCH:
readReferenceCollection<ESM::ObjectState> (reader, mPotions, cref, contentFileMap); readReferenceCollection<ESM::ObjectState> (reader, mPotions, cref, contentFileMap, this);
break; break;
case ESM::REC_APPA: case ESM::REC_APPA:
readReferenceCollection<ESM::ObjectState> (reader, mAppas, cref, contentFileMap); readReferenceCollection<ESM::ObjectState> (reader, mAppas, cref, contentFileMap, this);
break; break;
case ESM::REC_ARMO: case ESM::REC_ARMO:
readReferenceCollection<ESM::ObjectState> (reader, mArmors, cref, contentFileMap); readReferenceCollection<ESM::ObjectState> (reader, mArmors, cref, contentFileMap, this);
break; break;
case ESM::REC_BOOK: case ESM::REC_BOOK:
readReferenceCollection<ESM::ObjectState> (reader, mBooks, cref, contentFileMap); readReferenceCollection<ESM::ObjectState> (reader, mBooks, cref, contentFileMap, this);
break; break;
case ESM::REC_CLOT: case ESM::REC_CLOT:
readReferenceCollection<ESM::ObjectState> (reader, mClothes, cref, contentFileMap); readReferenceCollection<ESM::ObjectState> (reader, mClothes, cref, contentFileMap, this);
break; break;
case ESM::REC_CONT: case ESM::REC_CONT:
readReferenceCollection<ESM::ContainerState> (reader, mContainers, cref, contentFileMap); readReferenceCollection<ESM::ContainerState> (reader, mContainers, cref, contentFileMap, this);
break; break;
case ESM::REC_CREA: case ESM::REC_CREA:
readReferenceCollection<ESM::CreatureState> (reader, mCreatures, cref, contentFileMap); readReferenceCollection<ESM::CreatureState> (reader, mCreatures, cref, contentFileMap, this);
break; break;
case ESM::REC_DOOR: case ESM::REC_DOOR:
readReferenceCollection<ESM::DoorState> (reader, mDoors, cref, contentFileMap); readReferenceCollection<ESM::DoorState> (reader, mDoors, cref, contentFileMap, this);
break; break;
case ESM::REC_INGR: case ESM::REC_INGR:
readReferenceCollection<ESM::ObjectState> (reader, mIngreds, cref, contentFileMap); readReferenceCollection<ESM::ObjectState> (reader, mIngreds, cref, contentFileMap, this);
break; break;
case ESM::REC_LEVC: case ESM::REC_LEVC:
readReferenceCollection<ESM::CreatureLevListState> (reader, mCreatureLists, cref, contentFileMap); readReferenceCollection<ESM::CreatureLevListState> (reader, mCreatureLists, cref, contentFileMap, this);
break; break;
case ESM::REC_LEVI: case ESM::REC_LEVI:
readReferenceCollection<ESM::ObjectState> (reader, mItemLists, cref, contentFileMap); readReferenceCollection<ESM::ObjectState> (reader, mItemLists, cref, contentFileMap, this);
break; break;
case ESM::REC_LIGH: case ESM::REC_LIGH:
readReferenceCollection<ESM::ObjectState> (reader, mLights, cref, contentFileMap); readReferenceCollection<ESM::ObjectState> (reader, mLights, cref, contentFileMap, this);
break; break;
case ESM::REC_LOCK: case ESM::REC_LOCK:
readReferenceCollection<ESM::ObjectState> (reader, mLockpicks, cref, contentFileMap); readReferenceCollection<ESM::ObjectState> (reader, mLockpicks, cref, contentFileMap, this);
break; break;
case ESM::REC_MISC: case ESM::REC_MISC:
readReferenceCollection<ESM::ObjectState> (reader, mMiscItems, cref, contentFileMap); readReferenceCollection<ESM::ObjectState> (reader, mMiscItems, cref, contentFileMap, this);
break; break;
case ESM::REC_NPC_: case ESM::REC_NPC_:
readReferenceCollection<ESM::NpcState> (reader, mNpcs, cref, contentFileMap); readReferenceCollection<ESM::NpcState> (reader, mNpcs, cref, contentFileMap, this);
break; break;
case ESM::REC_PROB: case ESM::REC_PROB:
readReferenceCollection<ESM::ObjectState> (reader, mProbes, cref, contentFileMap); readReferenceCollection<ESM::ObjectState> (reader, mProbes, cref, contentFileMap, this);
break; break;
case ESM::REC_REPA: case ESM::REC_REPA:
readReferenceCollection<ESM::ObjectState> (reader, mRepairs, cref, contentFileMap); readReferenceCollection<ESM::ObjectState> (reader, mRepairs, cref, contentFileMap, this);
break; break;
case ESM::REC_STAT: case ESM::REC_STAT:
readReferenceCollection<ESM::ObjectState> (reader, mStatics, cref, contentFileMap); readReferenceCollection<ESM::ObjectState> (reader, mStatics, cref, contentFileMap, this);
break; break;
case ESM::REC_WEAP: case ESM::REC_WEAP:
readReferenceCollection<ESM::ObjectState> (reader, mWeapons, cref, contentFileMap); readReferenceCollection<ESM::ObjectState> (reader, mWeapons, cref, contentFileMap, this);
break; break;
case ESM::REC_BODY: case ESM::REC_BODY:
readReferenceCollection<ESM::ObjectState> (reader, mBodyParts, cref, contentFileMap); readReferenceCollection<ESM::ObjectState> (reader, mBodyParts, cref, contentFileMap, this);
break; break;
default: default:
@ -1152,26 +1139,22 @@ namespace MWWorld
movedTo.load(reader); movedTo.load(reader);
// Search for the reference. It might no longer exist if its content file was removed. // Search for the reference. It might no longer exist if its content file was removed.
SearchByRefNumVisitor visitor(refnum); Ptr movedRef = searchViaRefNum(refnum);
forEachInternal(visitor); if (movedRef.isEmpty())
if (!visitor.mFound)
{ {
Log(Debug::Warning) << "Warning: Dropping moved ref tag for " << refnum.mIndex << " (moved object no longer exists)"; Log(Debug::Warning) << "Warning: Dropping moved ref tag for " << refnum.mIndex << " (moved object no longer exists)";
continue; continue;
} }
MWWorld::LiveCellRefBase* movedRef = visitor.mFound;
CellStore* otherCell = callback->getCellStore(movedTo); CellStore* otherCell = callback->getCellStore(movedTo);
if (otherCell == nullptr) if (otherCell == nullptr)
{ {
Log(Debug::Warning) << "Warning: Dropping moved ref tag for " << movedRef->mRef.getRefId() Log(Debug::Warning) << "Warning: Dropping moved ref tag for " << movedRef.getCellRef().getRefId()
<< " (target cell " << movedTo.mWorldspace << " no longer exists). Reference moved back to its original location."; << " (target cell " << movedTo.mWorldspace << " no longer exists). Reference moved back to its original location.";
// Note by dropping tag the object will automatically re-appear in its original cell, though potentially at inapproriate coordinates. // Note by dropping tag the object will automatically re-appear in its original cell, though potentially at inapproriate coordinates.
// Restore original coordinates: // Restore original coordinates:
movedRef->mData.setPosition(movedRef->mRef.getPosition()); movedRef.getRefData().setPosition(movedRef.getCellRef().getPosition());
continue; continue;
} }
@ -1182,7 +1165,7 @@ namespace MWWorld
continue; continue;
} }
moveTo(MWWorld::Ptr(movedRef, this), otherCell); moveTo(movedRef, otherCell);
} }
} }

View file

@ -534,4 +534,19 @@ namespace MWWorld
{ {
throw std::runtime_error ("class does not have creature stats"); throw std::runtime_error ("class does not have creature stats");
} }
float Class::getWalkSpeed(const Ptr& /*ptr*/) const
{
return 0;
}
float Class::getRunSpeed(const Ptr& /*ptr*/) const
{
return 0;
}
float Class::getSwimSpeed(const Ptr& /*ptr*/) const
{
return 0;
}
} }

View file

@ -373,6 +373,12 @@ namespace MWWorld
virtual osg::Vec4f getEnchantmentColor(const MWWorld::ConstPtr& item) const; virtual osg::Vec4f getEnchantmentColor(const MWWorld::ConstPtr& item) const;
virtual void setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const; virtual void setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const;
virtual float getWalkSpeed(const Ptr& ptr) const;
virtual float getRunSpeed(const Ptr& ptr) const;
virtual float getSwimSpeed(const Ptr& ptr) const;
}; };
} }

View file

@ -136,6 +136,10 @@ void ESMStore::setUp(bool validateRecords)
mIds[*record] = storeIt->first; mIds[*record] = storeIt->first;
} }
} }
if (mStaticIds.empty())
mStaticIds = mIds;
mSkills.setUp(); mSkills.setUp();
mMagicEffects.setUp(); mMagicEffects.setUp();
mAttributes.setUp(); mAttributes.setUp();

View file

@ -68,6 +68,8 @@ namespace MWWorld
// Lookup of all IDs. Makes looking up references faster. Just // Lookup of all IDs. Makes looking up references faster. Just
// maps the id name to the record type. // maps the id name to the record type.
std::map<std::string, int> mIds; std::map<std::string, int> mIds;
std::map<std::string, int> mStaticIds;
std::map<int, StoreBase *> mStores; std::map<int, StoreBase *> mStores;
ESM::NPC mPlayerTemplate; ESM::NPC mPlayerTemplate;
@ -99,6 +101,14 @@ namespace MWWorld
} }
return it->second; return it->second;
} }
int findStatic(const std::string &id) const
{
std::map<std::string, int>::const_iterator it = mStaticIds.find(id);
if (it == mStaticIds.end()) {
return 0;
}
return it->second;
}
ESMStore() ESMStore()
: mDynamicCount(0) : mDynamicCount(0)

View file

@ -13,6 +13,7 @@
#include <components/resource/scenemanager.hpp> #include <components/resource/scenemanager.hpp>
#include <components/resource/bulletshape.hpp> #include <components/resource/bulletshape.hpp>
#include <components/sceneutil/unrefqueue.hpp> #include <components/sceneutil/unrefqueue.hpp>
#include <components/sceneutil/positionattitudetransform.hpp>
#include <components/detournavigator/navigator.hpp> #include <components/detournavigator/navigator.hpp>
#include <components/detournavigator/debug.hpp> #include <components/detournavigator/debug.hpp>
#include <components/misc/convert.hpp> #include <components/misc/convert.hpp>
@ -97,8 +98,21 @@ namespace
); );
} }
std::string getModel(const MWWorld::Ptr &ptr, const VFS::Manager *vfs)
{
bool useAnim = ptr.getClass().useAnim();
std::string model = ptr.getClass().getModel(ptr);
if (useAnim)
model = Misc::ResourceHelpers::correctActorModelPath(model, vfs);
const std::string &id = ptr.getCellRef().getRefId();
if (id == "prisonmarker" || id == "divinemarker" || id == "templemarker" || id == "northmarker")
model = ""; // marker objects that have a hardcoded function in the game logic, should be hidden from the player
return model;
}
void addObject(const MWWorld::Ptr& ptr, MWPhysics::PhysicsSystem& physics, void addObject(const MWWorld::Ptr& ptr, MWPhysics::PhysicsSystem& physics,
MWRender::RenderingManager& rendering) MWRender::RenderingManager& rendering, std::set<ESM::RefNum>& pagedRefs)
{ {
if (ptr.getRefData().getBaseNode() || physics.getActor(ptr)) if (ptr.getRefData().getBaseNode() || physics.getActor(ptr))
{ {
@ -107,15 +121,13 @@ namespace
} }
bool useAnim = ptr.getClass().useAnim(); bool useAnim = ptr.getClass().useAnim();
std::string model = ptr.getClass().getModel(ptr); std::string model = getModel(ptr, rendering.getResourceSystem()->getVFS());
if (useAnim)
model = Misc::ResourceHelpers::correctActorModelPath(model, rendering.getResourceSystem()->getVFS());
std::string id = ptr.getCellRef().getRefId(); const ESM::RefNum& refnum = ptr.getCellRef().getRefNum();
if (id == "prisonmarker" || id == "divinemarker" || id == "templemarker" || id == "northmarker") if (!refnum.hasContentFile() || pagedRefs.find(refnum) == pagedRefs.end())
model = ""; // marker objects that have a hardcoded function in the game logic, should be hidden from the player ptr.getClass().insertObjectRendering(ptr, model, rendering);
else
ptr.getClass().insertObjectRendering(ptr, model, rendering); ptr.getRefData().setBaseNode(new SceneUtil::PositionAttitudeTransform); // FIXME remove this when physics code is fixed not to depend on basenode
setNodeRotation(ptr, rendering, RotationOrder::direct); setNodeRotation(ptr, rendering, RotationOrder::direct);
ptr.getClass().insertObject (ptr, model, physics); ptr.getClass().insertObject (ptr, model, physics);
@ -194,27 +206,6 @@ namespace
} }
} }
void updateObjectRotation (const MWWorld::Ptr& ptr, MWPhysics::PhysicsSystem& physics,
MWRender::RenderingManager& rendering, RotationOrder order)
{
setNodeRotation(ptr, rendering, order);
physics.updateRotation(ptr);
}
void updateObjectScale(const MWWorld::Ptr& ptr, MWPhysics::PhysicsSystem& physics,
MWRender::RenderingManager& rendering)
{
if (ptr.getRefData().getBaseNode() != nullptr)
{
float scale = ptr.getCellRef().getScale();
osg::Vec3f scaleVec (scale, scale, scale);
ptr.getClass().adjustScale(ptr, scaleVec, true);
rendering.scaleObject(ptr, scaleVec);
physics.updateScale(ptr);
}
}
struct InsertVisitor struct InsertVisitor
{ {
MWWorld::CellStore& mCell; MWWorld::CellStore& mCell;
@ -287,50 +278,48 @@ namespace
namespace MWWorld namespace MWWorld
{ {
void Scene::updateObjectRotation(const Ptr& ptr, RotationOrder order) void Scene::removeFromPagedRefs(const Ptr &ptr)
{ {
::updateObjectRotation(ptr, *mPhysics, mRendering, order); const ESM::RefNum& refnum = ptr.getCellRef().getRefNum();
if (refnum.hasContentFile() && mPagedRefs.erase(refnum))
{
if (!ptr.getRefData().getBaseNode()) return;
ptr.getClass().insertObjectRendering(ptr, getModel(ptr, mRendering.getResourceSystem()->getVFS()), mRendering);
setNodeRotation(ptr, mRendering, RotationOrder::direct);
reloadTerrain();
}
}
void Scene::updateObjectPosition(const Ptr &ptr, const osg::Vec3f &pos, bool movePhysics)
{
mRendering.moveObject(ptr, pos);
if (movePhysics)
{
mPhysics->updatePosition(ptr);
}
}
void Scene::updateObjectRotation(const Ptr &ptr, RotationOrder order)
{
setNodeRotation(ptr, mRendering, order);
mPhysics->updateRotation(ptr);
} }
void Scene::updateObjectScale(const Ptr &ptr) void Scene::updateObjectScale(const Ptr &ptr)
{ {
::updateObjectScale(ptr, *mPhysics, mRendering); float scale = ptr.getCellRef().getScale();
} osg::Vec3f scaleVec (scale, scale, scale);
ptr.getClass().adjustScale(ptr, scaleVec, true);
void Scene::getGridCenter(int &cellX, int &cellY) mRendering.scaleObject(ptr, scaleVec);
{ mPhysics->updateScale(ptr);
int maxX = std::numeric_limits<int>::min();
int maxY = std::numeric_limits<int>::min();
int minX = std::numeric_limits<int>::max();
int minY = std::numeric_limits<int>::max();
CellStoreCollection::iterator iter = mActiveCells.begin();
while (iter!=mActiveCells.end())
{
assert ((*iter)->getCell()->isExterior());
int x = (*iter)->getCell()->getGridX();
int y = (*iter)->getCell()->getGridY();
maxX = std::max(x, maxX);
maxY = std::max(y, maxY);
minX = std::min(x, minX);
minY = std::min(y, minY);
++iter;
}
cellX = (minX + maxX) / 2;
cellY = (minY + maxY) / 2;
} }
void Scene::update (float duration, bool paused) void Scene::update (float duration, bool paused)
{ {
mPreloadTimer += duration; mPreloader->updateCache(mRendering.getReferenceTime());
if (mPreloadTimer > 0.1f) preloadCells(duration);
{
preloadCells(0.1f);
mPreloadTimer = 0.f;
}
mRendering.update (duration, paused); mRendering.update (duration, paused);
mPreloader->updateCache(mRendering.getReferenceTime());
} }
void Scene::unloadCell (CellStoreCollection::iterator iter, bool test) void Scene::unloadCell (CellStoreCollection::iterator iter, bool test)
@ -387,6 +376,9 @@ namespace MWWorld
if ((*iter)->getCell()->hasWater()) if ((*iter)->getCell()->hasWater())
navigator->removeWater(osg::Vec2i(cellX, cellY)); navigator->removeWater(osg::Vec2i(cellX, cellY));
if (const auto pathgrid = world->getStore().get<ESM::Pathgrid>().search(*(*iter)->getCell()))
navigator->removePathgrid(*pathgrid);
const auto player = world->getPlayerPtr(); const auto player = world->getPlayerPtr();
navigator->update(player.getRefData().getPosition().asVec3()); navigator->update(player.getRefData().getPosition().asVec3());
@ -425,7 +417,8 @@ namespace MWWorld
float verts = ESM::Land::LAND_SIZE; float verts = ESM::Land::LAND_SIZE;
float worldsize = ESM::Land::REAL_SIZE; float worldsize = ESM::Land::REAL_SIZE;
const auto navigator = MWBase::Environment::get().getWorld()->getNavigator(); const auto world = MWBase::Environment::get().getWorld();
const auto navigator = world->getNavigator();
const int cellX = cell->getCell()->getGridX(); const int cellX = cell->getCell()->getGridX();
const int cellY = cell->getCell()->getGridY(); const int cellY = cell->getCell()->getGridY();
@ -451,6 +444,9 @@ namespace MWWorld
heightField->getCollisionObject()->getWorldTransform()); heightField->getCollisionObject()->getWorldTransform());
} }
if (const auto pathgrid = world->getStore().get<ESM::Pathgrid>().search(*cell->getCell()))
navigator->addPathgrid(*cell->getCell(), *pathgrid);
// register local scripts // register local scripts
// do this before insertCell, to make sure we don't add scripts from levelled creature spawning twice // do this before insertCell, to make sure we don't add scripts from levelled creature spawning twice
MWBase::Environment::get().getWorld()->getLocalScripts().addCell (cell); MWBase::Environment::get().getWorld()->getLocalScripts().addCell (cell);
@ -523,6 +519,27 @@ namespace MWWorld
mPreloader->clear(); mPreloader->clear();
} }
osg::Vec4i Scene::gridCenterToBounds(const osg::Vec2i& centerCell) const
{
return osg::Vec4i(centerCell.x()-mHalfGridSize,centerCell.y()-mHalfGridSize,centerCell.x()+mHalfGridSize+1,centerCell.y()+mHalfGridSize+1);
}
osg::Vec2i Scene::getNewGridCenter(const osg::Vec3f &pos, const osg::Vec2i* currentGridCenter) const
{
if (currentGridCenter)
{
float centerX, centerY;
MWBase::Environment::get().getWorld()->indexToPosition(currentGridCenter->x(), currentGridCenter->y(), centerX, centerY, true);
float distance = std::max(std::abs(centerX-pos.x()), std::abs(centerY-pos.y()));
const float maxDistance = Constants::CellSizeInUnits / 2 + mCellLoadingThreshold; // 1/2 cell size + threshold
if (distance <= maxDistance)
return *currentGridCenter;
}
osg::Vec2i newCenter;
MWBase::Environment::get().getWorld()->positionToIndex(pos.x(), pos.y(), newCenter.x(), newCenter.y());
return newCenter;
}
void Scene::playerMoved(const osg::Vec3f &pos) void Scene::playerMoved(const osg::Vec3f &pos)
{ {
const auto navigator = MWBase::Environment::get().getWorld()->getNavigator(); const auto navigator = MWBase::Environment::get().getWorld()->getNavigator();
@ -532,30 +549,13 @@ namespace MWWorld
if (!mCurrentCell || !mCurrentCell->isExterior()) if (!mCurrentCell || !mCurrentCell->isExterior())
return; return;
// figure out the center of the current cell grid (*not* necessarily mCurrentCell, which is the cell the player is in) osg::Vec2i newCell = getNewGridCenter(pos, &mCurrentGridCenter);
int cellX, cellY; if (newCell != mCurrentGridCenter)
getGridCenter(cellX, cellY); changeCellGrid(pos, newCell.x(), newCell.y());
float centerX, centerY;
MWBase::Environment::get().getWorld()->indexToPosition(cellX, cellY, centerX, centerY, true);
const float maxDistance = Constants::CellSizeInUnits / 2 + mCellLoadingThreshold; // 1/2 cell size + threshold
float distance = std::max(std::abs(centerX-pos.x()), std::abs(centerY-pos.y()));
if (distance > maxDistance)
{
int newX, newY;
MWBase::Environment::get().getWorld()->positionToIndex(pos.x(), pos.y(), newX, newY);
changeCellGrid(newX, newY);
}
} }
void Scene::changeCellGrid (int playerCellX, int playerCellY, bool changeEvent) void Scene::changeCellGrid (const osg::Vec3f &pos, int playerCellX, int playerCellY, bool changeEvent)
{ {
Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen();
Loading::ScopedLoad load(loadingListener);
int messagesCount = MWBase::Environment::get().getWindowManager()->getMessagesCount();
std::string loadingExteriorText = "#{sLoadingMessage3}";
loadingListener->setLabel(loadingExteriorText, false, messagesCount > 0);
CellStoreCollection::iterator active = mActiveCells.begin(); CellStoreCollection::iterator active = mActiveCells.begin();
while (active!=mActiveCells.end()) while (active!=mActiveCells.end())
{ {
@ -572,6 +572,14 @@ namespace MWWorld
unloadCell (active++); unloadCell (active++);
} }
mCurrentGridCenter = osg::Vec2i(playerCellX, playerCellY);
osg::Vec4i newGrid = gridCenterToBounds(mCurrentGridCenter);
mRendering.setActiveGrid(newGrid);
preloadTerrain(pos, true);
mPagedRefs.clear();
mRendering.getPagedRefnums(newGrid, mPagedRefs);
std::size_t refsToLoad = 0; std::size_t refsToLoad = 0;
std::vector<std::pair<int, int>> cellsPositionsToLoad; std::vector<std::pair<int, int>> cellsPositionsToLoad;
// get the number of refs to load // get the number of refs to load
@ -600,6 +608,11 @@ namespace MWWorld
} }
} }
Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen();
Loading::ScopedLoad load(loadingListener);
int messagesCount = MWBase::Environment::get().getWindowManager()->getMessagesCount();
std::string loadingExteriorText = "#{sLoadingMessage3}";
loadingListener->setLabel(loadingExteriorText, false, messagesCount > 0);
loadingListener->setProgressRange(refsToLoad); loadingListener->setProgressRange(refsToLoad);
const auto getDistanceToPlayerCell = [&] (const std::pair<int, int>& cellPosition) const auto getDistanceToPlayerCell = [&] (const std::pair<int, int>& cellPosition)
@ -795,14 +808,12 @@ namespace MWWorld
MWBase::Environment::get().getWorld()->adjustSky(); MWBase::Environment::get().getWorld()->adjustSky();
mLastPlayerPos = pos.asVec3(); mLastPlayerPos = player.getRefData().getPosition().asVec3();
} }
Scene::Scene (MWRender::RenderingManager& rendering, MWPhysics::PhysicsSystem *physics, Scene::Scene (MWRender::RenderingManager& rendering, MWPhysics::PhysicsSystem *physics,
DetourNavigator::Navigator& navigator) DetourNavigator::Navigator& navigator)
: mCurrentCell (0), mCellChanged (false), mPhysics(physics), mRendering(rendering), mNavigator(navigator) : mCurrentCell (0), mCellChanged (false), mPhysics(physics), mRendering(rendering), mNavigator(navigator)
, mPreloadTimer(0.f)
, mHalfGridSize(Settings::Manager::getInt("exterior cell load distance", "Cells"))
, mCellLoadingThreshold(1024.f) , mCellLoadingThreshold(1024.f)
, mPreloadDistance(Settings::Manager::getInt("preload distance", "Cells")) , mPreloadDistance(Settings::Manager::getInt("preload distance", "Cells"))
, mPreloadEnabled(Settings::Manager::getBool("preload enabled", "Cells")) , mPreloadEnabled(Settings::Manager::getBool("preload enabled", "Cells"))
@ -878,6 +889,7 @@ namespace MWWorld
loadingListener->setProgressRange(cell->count()); loadingListener->setProgressRange(cell->count());
// Load cell. // Load cell.
mPagedRefs.clear();
loadCell (cell, loadingListener, changeEvent); loadCell (cell, loadingListener, changeEvent);
/* /*
@ -922,7 +934,7 @@ namespace MWWorld
if (changeEvent) if (changeEvent)
MWBase::Environment::get().getWindowManager()->fadeScreenOut(0.5); MWBase::Environment::get().getWindowManager()->fadeScreenOut(0.5);
changeCellGrid(x, y, changeEvent); changeCellGrid(position.asVec3(), x, y, changeEvent);
CellStore* current = MWBase::Environment::get().getWorld()->getExterior(x, y); CellStore* current = MWBase::Environment::get().getWorld()->getExterior(x, y);
changePlayerCell(current, position, adjustPlayerPos); changePlayerCell(current, position, adjustPlayerPos);
@ -945,7 +957,7 @@ namespace MWWorld
{ {
InsertVisitor insertVisitor (cell, *loadingListener, test); InsertVisitor insertVisitor (cell, *loadingListener, test);
cell.forEach (insertVisitor); cell.forEach (insertVisitor);
insertVisitor.insert([&] (const MWWorld::Ptr& ptr) { addObject(ptr, *mPhysics, mRendering); }); insertVisitor.insert([&] (const MWWorld::Ptr& ptr) { addObject(ptr, *mPhysics, mRendering, mPagedRefs); });
insertVisitor.insert([&] (const MWWorld::Ptr& ptr) { addObject(ptr, *mPhysics, mNavigator); }); insertVisitor.insert([&] (const MWWorld::Ptr& ptr) { addObject(ptr, *mPhysics, mNavigator); });
// do adjustPosition (snapping actors to ground) after objects are loaded, so we don't depend on the loading order // do adjustPosition (snapping actors to ground) after objects are loaded, so we don't depend on the loading order
@ -957,7 +969,7 @@ namespace MWWorld
{ {
try try
{ {
addObject(ptr, *mPhysics, mRendering); addObject(ptr, *mPhysics, mRendering, mPagedRefs);
addObject(ptr, *mPhysics, mNavigator); addObject(ptr, *mPhysics, mNavigator);
MWBase::Environment::get().getWorld()->scaleObject(ptr, ptr.getCellRef().getScale()); MWBase::Environment::get().getWorld()->scaleObject(ptr, ptr.getCellRef().getScale());
const auto navigator = MWBase::Environment::get().getWorld()->getNavigator(); const auto navigator = MWBase::Environment::get().getWorld()->getNavigator();
@ -989,6 +1001,7 @@ namespace MWWorld
mRendering.removeObject (ptr); mRendering.removeObject (ptr);
if (ptr.getClass().isActor()) if (ptr.getClass().isActor())
mRendering.removeWaterRippleEmitter(ptr); mRendering.removeWaterRippleEmitter(ptr);
ptr.getRefData().setBaseNode(nullptr);
} }
bool Scene::isCellActive(const CellStore &cell) bool Scene::isCellActive(const CellStore &cell)
@ -1048,7 +1061,8 @@ namespace MWWorld
void Scene::preloadCells(float dt) void Scene::preloadCells(float dt)
{ {
std::vector<osg::Vec3f> exteriorPositions; if (dt<=1e-06) return;
std::vector<PositionCellGrid> exteriorPositions;
const MWWorld::ConstPtr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); const MWWorld::ConstPtr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
osg::Vec3f playerPos = player.getRefData().getPosition().asVec3(); osg::Vec3f playerPos = player.getRefData().getPosition().asVec3();
@ -1056,7 +1070,7 @@ namespace MWWorld
osg::Vec3f predictedPos = playerPos + moved / dt * mPredictionTime; osg::Vec3f predictedPos = playerPos + moved / dt * mPredictionTime;
if (mCurrentCell->isExterior()) if (mCurrentCell->isExterior())
exteriorPositions.push_back(predictedPos); exteriorPositions.emplace_back(predictedPos, gridCenterToBounds(getNewGridCenter(predictedPos, &mCurrentGridCenter)));
mLastPlayerPos = playerPos; mLastPlayerPos = playerPos;
@ -1073,7 +1087,7 @@ namespace MWWorld
mPreloader->setTerrainPreloadPositions(exteriorPositions); mPreloader->setTerrainPreloadPositions(exteriorPositions);
} }
void Scene::preloadTeleportDoorDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos, std::vector<osg::Vec3f>& exteriorPositions) void Scene::preloadTeleportDoorDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos, std::vector<PositionCellGrid>& exteriorPositions)
{ {
std::vector<MWWorld::ConstPtr> teleportDoors; std::vector<MWWorld::ConstPtr> teleportDoors;
for (const MWWorld::CellStore* cellStore : mActiveCells) for (const MWWorld::CellStore* cellStore : mActiveCells)
@ -1107,7 +1121,7 @@ namespace MWWorld
int x,y; int x,y;
MWBase::Environment::get().getWorld()->positionToIndex (pos.x(), pos.y(), x, y); MWBase::Environment::get().getWorld()->positionToIndex (pos.x(), pos.y(), x, y);
preloadCell(MWBase::Environment::get().getWorld()->getExterior(x,y), true); preloadCell(MWBase::Environment::get().getWorld()->getExterior(x,y), true);
exteriorPositions.push_back(pos); exteriorPositions.emplace_back(pos, gridCenterToBounds(getNewGridCenter(pos)));
} }
} }
catch (std::exception& e) catch (std::exception& e)
@ -1127,7 +1141,7 @@ namespace MWWorld
int cellX,cellY; int cellX,cellY;
getGridCenter(cellX,cellY); cellX = mCurrentGridCenter.x(); cellY = mCurrentGridCenter.y();
float centerX, centerY; float centerX, centerY;
MWBase::Environment::get().getWorld()->indexToPosition(cellX, cellY, centerX, centerY, true); MWBase::Environment::get().getWorld()->indexToPosition(cellX, cellY, centerX, centerY, true);
@ -1173,11 +1187,41 @@ namespace MWWorld
mPreloader->preload(cell, mRendering.getReferenceTime()); mPreloader->preload(cell, mRendering.getReferenceTime());
} }
void Scene::preloadTerrain(const osg::Vec3f &pos) void Scene::preloadTerrain(const osg::Vec3f &pos, bool sync)
{ {
std::vector<osg::Vec3f> vec; std::vector<PositionCellGrid> vec;
vec.push_back(pos); vec.emplace_back(pos, gridCenterToBounds(getNewGridCenter(pos)));
if (sync && mRendering.pagingUnlockCache())
mPreloader->abortTerrainPreloadExcept(nullptr);
else
mPreloader->abortTerrainPreloadExcept(&vec[0]);
mPreloader->setTerrainPreloadPositions(vec); mPreloader->setTerrainPreloadPositions(vec);
if (!sync) return;
Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen();
Loading::ScopedLoad load(loadingListener);
int progress = 0, initialProgress = -1, progressRange = 0;
while (!mPreloader->syncTerrainLoad(vec, progress, progressRange, mRendering.getReferenceTime()))
{
if (initialProgress == -1)
{
loadingListener->setLabel("#{sLoadingMessage4}");
initialProgress = progress;
}
if (progress)
{
loadingListener->setProgressRange(std::max(0, progressRange-initialProgress));
loadingListener->setProgress(progress-initialProgress);
}
else
loadingListener->setProgress(0);
OpenThreads::Thread::microSleep(5000);
}
}
void Scene::reloadTerrain()
{
mPreloader->setTerrainPreloadPositions(std::vector<PositionCellGrid>());
} }
struct ListFastTravelDestinationsVisitor struct ListFastTravelDestinationsVisitor
@ -1210,7 +1254,7 @@ namespace MWWorld
std::vector<ESM::Transport::Dest> mList; std::vector<ESM::Transport::Dest> mList;
}; };
void Scene::preloadFastTravelDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& /*predictedPos*/, std::vector<osg::Vec3f>& exteriorPositions) // ignore predictedPos here since opening dialogue with travel service takes extra time void Scene::preloadFastTravelDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& /*predictedPos*/, std::vector<PositionCellGrid>& exteriorPositions) // ignore predictedPos here since opening dialogue with travel service takes extra time
{ {
const MWWorld::ConstPtr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); const MWWorld::ConstPtr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
ListFastTravelDestinationsVisitor listVisitor(mPreloadDistance, player.getRefData().getPosition().asVec3()); ListFastTravelDestinationsVisitor listVisitor(mPreloadDistance, player.getRefData().getPosition().asVec3());
@ -1231,7 +1275,7 @@ namespace MWWorld
int x,y; int x,y;
MWBase::Environment::get().getWorld()->positionToIndex( pos.x(), pos.y(), x, y); MWBase::Environment::get().getWorld()->positionToIndex( pos.x(), pos.y(), x, y);
preloadCell(MWBase::Environment::get().getWorld()->getExterior(x,y), true); preloadCell(MWBase::Environment::get().getWorld()->getExterior(x,y), true);
exteriorPositions.push_back(pos); exteriorPositions.emplace_back(pos, gridCenterToBounds(getNewGridCenter(pos)));
} }
} }
} }

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