mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-30 15:45:31 +00:00
Add OpenMW commits up to 22 Jun 2020
# Conflicts: # .travis.yml # CMakeLists.txt
This commit is contained in:
commit
3b2eb6f62c
180 changed files with 3144 additions and 1629 deletions
|
@ -5,7 +5,7 @@ Debian:
|
|||
tags:
|
||||
- docker
|
||||
- linux
|
||||
image: gcc
|
||||
image: debian:bullseye
|
||||
cache:
|
||||
key: apt-cache
|
||||
paths:
|
||||
|
@ -13,7 +13,7 @@ Debian:
|
|||
before_script:
|
||||
- export APT_CACHE_DIR=`pwd`/apt-cache && mkdir -pv $APT_CACHE_DIR
|
||||
- apt-get update -yq
|
||||
- apt-get -o dir::cache::archives="$APT_CACHE_DIR" install -y cmake 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
|
||||
script:
|
||||
- cores_to_use=$((`nproc`-2)); if (( $cores_to_use < 1 )); then cores_to_use=1; fi
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
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 #2311: Targeted scripts are not properly supported on non-unique RefIDs
|
||||
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 #5451: Magic projectiles don't disappear with the caster
|
||||
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 #5445: Handle NiLines
|
||||
Task #5480: Drop Qt4 support
|
||||
|
||||
0.46.0
|
||||
------
|
||||
|
@ -245,6 +248,7 @@
|
|||
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
|
||||
Feature #1724: Handle AvoidNode
|
||||
Feature #2159: "Graying out" exhausted dialogue topics
|
||||
Feature #2229: Improve pathfinding AI
|
||||
Feature #3025: Analogue gamepad movement controls
|
||||
Feature #3442: Default values for fallbacks from ini file
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
& "${env:COMSPEC}" /c ActivateMSVC.bat "&&" set | ForEach-Object {
|
||||
if ($_.Contains("=")) {
|
||||
$name, $value = $_ -split '=', 2
|
||||
Set-Content env:\"$name" $value
|
||||
}
|
||||
}
|
||||
|
||||
$MissingTools = $false
|
||||
$tools = "cl", "link", "rc", "mt", "awooga"
|
||||
$descriptions = "MSVC Compiler", "MSVC Linker", "MS Windows Resource Compiler", "MS Windows Manifest Tool", "A made up command"
|
||||
$tools = "cl", "link", "rc", "mt"
|
||||
$descriptions = "MSVC Compiler", "MSVC Linker", "MS Windows Resource Compiler", "MS Windows Manifest Tool"
|
||||
for ($i = 0; $i -lt $tools.Length; $i++) {
|
||||
$present = $true
|
||||
try {
|
||||
|
|
|
@ -38,7 +38,6 @@ ${ANALYZE} cmake .. \
|
|||
-DBUILD_MASTER=ON \
|
||||
-DBUILD_UNITTESTS=1 \
|
||||
-DUSE_SYSTEM_TINYXML=1 \
|
||||
-DDESIRED_QT_VERSION=5 \
|
||||
-DCMAKE_INSTALL_PREFIX=/usr \
|
||||
-DBINDIR=/usr/games \
|
||||
-DCMAKE_BUILD_TYPE="None" \
|
||||
|
|
|
@ -161,7 +161,7 @@ Options:
|
|||
Build unit tests / Google test
|
||||
-u
|
||||
Configure for unity builds.
|
||||
-v <2013/2015/2017/2019>
|
||||
-v <2017/2019>
|
||||
Choose the Visual Studio version to use.
|
||||
-n
|
||||
Produce NMake makefiles instead of a Visual Studio solution. Cannout be used with -N.
|
||||
|
@ -213,8 +213,8 @@ run_cmd() {
|
|||
shift
|
||||
|
||||
if [ -z $VERBOSE ]; then
|
||||
eval $CMD $@ > output.log 2>&1
|
||||
RET=$?
|
||||
RET=0
|
||||
eval $CMD $@ > output.log 2>&1 || RET=$?
|
||||
|
||||
if [ $RET -ne 0 ]; then
|
||||
if [ -z $APPVEYOR ]; then
|
||||
|
@ -230,8 +230,9 @@ run_cmd() {
|
|||
|
||||
return $RET
|
||||
else
|
||||
eval $CMD $@
|
||||
return $?
|
||||
RET=0
|
||||
eval $CMD $@ || RET=$?
|
||||
return $RET
|
||||
fi
|
||||
}
|
||||
|
||||
|
@ -256,15 +257,16 @@ download() {
|
|||
printf " Downloading $FILE... "
|
||||
|
||||
if [ -z $VERBOSE ]; then
|
||||
curl --silent --retry 10 -kLy 5 -o $FILE $URL
|
||||
RET=$?
|
||||
RET=0
|
||||
curl --silent --retry 10 -kLy 5 -o $FILE $URL || RET=$?
|
||||
else
|
||||
curl --retry 10 -kLy 5 -o $FILE $URL
|
||||
RET=$?
|
||||
RET=0
|
||||
curl --retry 10 -kLy 5 -o $FILE $URL || RET=$?
|
||||
fi
|
||||
|
||||
if [ $RET -ne 0 ]; then
|
||||
echo "Failed!"
|
||||
wrappedExit $RET
|
||||
else
|
||||
echo "Done."
|
||||
fi
|
||||
|
@ -346,21 +348,13 @@ case $VS_VERSION in
|
|||
;;
|
||||
|
||||
14|14.0|2015 )
|
||||
GENERATOR="Visual Studio 14 2015"
|
||||
TOOLSET="vc140"
|
||||
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"
|
||||
echo "Visual Studio 2015 is no longer supported"
|
||||
wrappedExit 1
|
||||
;;
|
||||
|
||||
12|12.0|2013 )
|
||||
echo "Visual Studio 2013 is no longer supported"
|
||||
exit 1
|
||||
wrappedExit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
|
@ -505,11 +499,6 @@ if [ -z $SKIP_DOWNLOAD ]; then
|
|||
|
||||
# Qt
|
||||
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" \
|
||||
"https://files.pythonhosted.org/packages/f3/bb/aee972f08deecca31bfc46b5aedfad1ce6c7f3aaf1288d685e4a914b53ac/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
|
||||
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" \
|
||||
-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}"
|
||||
|
||||
echo Done.
|
||||
|
@ -793,8 +776,7 @@ fi
|
|||
fi
|
||||
|
||||
cd $QT_SDK
|
||||
add_cmake_opts -DDESIRED_QT_VERSION=5 \
|
||||
-DQT_QMAKE_EXECUTABLE="${QT_SDK}/bin/qmake.exe" \
|
||||
add_cmake_opts -DQT_QMAKE_EXECUTABLE="${QT_SDK}/bin/qmake.exe" \
|
||||
-DCMAKE_PREFIX_PATH="$QT_SDK"
|
||||
if [ $CONFIGURATION == "Debug" ]; then
|
||||
SUFFIX="d"
|
||||
|
@ -806,8 +788,7 @@ fi
|
|||
echo Done.
|
||||
else
|
||||
QT_SDK="C:/Qt/5.13/msvc2017${SUFFIX}"
|
||||
add_cmake_opts -DDESIRED_QT_VERSION=5 \
|
||||
-DQT_QMAKE_EXECUTABLE="${QT_SDK}/bin/qmake.exe" \
|
||||
add_cmake_opts -DQT_QMAKE_EXECUTABLE="${QT_SDK}/bin/qmake.exe" \
|
||||
-DCMAKE_PREFIX_PATH="$QT_SDK"
|
||||
if [ $CONFIGURATION == "Debug" ]; then
|
||||
SUFFIX="d"
|
||||
|
@ -957,29 +938,17 @@ fi
|
|||
echo
|
||||
#fi
|
||||
|
||||
if ! [ -z $ACTIVATE_MSVC ]; then
|
||||
if [ -n "$ACTIVATE_MSVC" ]; then
|
||||
echo -n "- Activating MSVC in the current shell... "
|
||||
command -v vswhere >/dev/null 2>&1 || { echo "Error: vswhere is not on the path."; wrappedExit 1; }
|
||||
|
||||
MSVC_INSTALLATION_PATH=$(vswhere -legacy -version "[$MSVC_VER,$(awk "BEGIN { print $MSVC_REAL_VER + 1; exit }"))" -property installationPath)
|
||||
if [ $MSVC_REAL_VER -ge 15 ]; then
|
||||
MSVC_INSTALLATION_PATH=$(vswhere -legacy -products '*' -version "[$MSVC_VER,$(awk "BEGIN { print $MSVC_REAL_VER + 1; exit }"))" -property installationPath)
|
||||
if [ -z "$MSVC_INSTALLATION_PATH" ]; then
|
||||
echo "vswhere was unable to find MSVC $MSVC_DISPLAY_YEAR"
|
||||
wrappedExit 1
|
||||
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
|
||||
else
|
||||
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
|
||||
|
||||
cp "../CI/activate_msvc.sh" .
|
||||
sed -i "s/\$MSVC_DISPLAY_YEAR/$MSVC_DISPLAY_YEAR/g" activate_msvc.sh
|
||||
|
@ -997,8 +966,8 @@ if [ -z $VERBOSE ]; then
|
|||
else
|
||||
echo "- cmake .. $CMAKE_OPTS"
|
||||
fi
|
||||
run_cmd cmake .. $CMAKE_OPTS
|
||||
RET=$?
|
||||
RET=0
|
||||
run_cmd cmake .. $CMAKE_OPTS || RET=$?
|
||||
if [ -z $VERBOSE ]; then
|
||||
if [ $RET -eq 0 ]; then
|
||||
echo Done.
|
||||
|
@ -1006,8 +975,14 @@ if [ -z $VERBOSE ]; then
|
|||
echo Failed.
|
||||
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 "Note: you must manually activate MSVC for the shell in which you want to do the build."
|
||||
echo
|
||||
|
|
|
@ -17,7 +17,6 @@ cmake \
|
|||
-D CMAKE_OSX_SYSROOT="macosx10.14" \
|
||||
-D CMAKE_BUILD_TYPE=Release \
|
||||
-D OPENMW_OSX_DEPLOYMENT=TRUE \
|
||||
-D DESIRED_QT_VERSION=5 \
|
||||
-D BUILD_ESMTOOL=FALSE \
|
||||
-G"Unix Makefiles" \
|
||||
..
|
||||
|
|
|
@ -22,11 +22,6 @@ else()
|
|||
set(USE_QT TRUE)
|
||||
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
|
||||
cmake_minimum_required(VERSION 3.1.0)
|
||||
|
||||
|
@ -159,19 +154,13 @@ include_directories(${RakNet_INCLUDES})
|
|||
find_package(OpenGL REQUIRED)
|
||||
|
||||
if (USE_QT)
|
||||
message(STATUS "Using Qt${DESIRED_QT_VERSION}")
|
||||
|
||||
if (DESIRED_QT_VERSION MATCHES 4)
|
||||
find_package(Qt4 REQUIRED COMPONENTS QtCore QtGui QtNetwork QtOpenGL)
|
||||
else()
|
||||
find_package(Qt5Core 5.12 REQUIRED)
|
||||
find_package(Qt5Widgets REQUIRED)
|
||||
find_package(Qt5Core REQUIRED)
|
||||
find_package(Qt5Network REQUIRED)
|
||||
find_package(Qt5OpenGL REQUIRED)
|
||||
# Instruct CMake to run moc automatically when needed.
|
||||
#set(CMAKE_AUTOMOC ON)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
IF(BUILD_OPENMW OR BUILD_OPENCS)
|
||||
# Sound setup
|
||||
|
@ -303,16 +292,6 @@ IF(BUILD_OPENMW OR BUILD_OPENCS)
|
|||
list(APPEND OPENSCENEGRAPH_LIBRARIES ${OSGPlugins_LIBRARIES})
|
||||
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
|
||||
if (DEFINED ENV{TRAVIS_BRANCH} OR DEFINED ENV{APPVEYOR})
|
||||
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.
|
||||
ENDIF(BUILD_OPENMW OR BUILD_OPENCS)
|
||||
|
||||
|
||||
set(BOOST_COMPONENTS system filesystem program_options iostreams)
|
||||
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)
|
||||
|
||||
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}/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}/Release/resources" DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel)
|
||||
|
||||
|
@ -845,7 +821,7 @@ if (WIN32)
|
|||
endif()
|
||||
|
||||
# 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)
|
||||
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 ()
|
||||
|
|
|
@ -70,16 +70,9 @@ if(WIN32)
|
|||
set(QT_USE_QTMAIN TRUE)
|
||||
endif(WIN32)
|
||||
|
||||
if (DESIRED_QT_VERSION MATCHES 4)
|
||||
include(${QT_USE_FILE})
|
||||
QT4_ADD_RESOURCES(RCC_SRCS ${CMAKE_SOURCE_DIR}/files/launcher/launcher.qrc)
|
||||
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})
|
||||
if(NOT WIN32)
|
||||
|
@ -105,14 +98,7 @@ target_link_libraries(openmw-launcher
|
|||
components
|
||||
)
|
||||
|
||||
if (DESIRED_QT_VERSION MATCHES 4)
|
||||
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)
|
||||
add_definitions (--coverage)
|
||||
|
|
|
@ -1,13 +1,9 @@
|
|||
#include "graphicspage.hpp"
|
||||
|
||||
#include <csignal>
|
||||
#include <QDesktopWidget>
|
||||
#include <QMessageBox>
|
||||
#include <QDir>
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
|
||||
#include <QScreen>
|
||||
#endif
|
||||
|
||||
#ifdef 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()
|
||||
{
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
|
||||
bool sdlConnectSuccessful = initSDL();
|
||||
if (!sdlConnectSuccessful)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
int displays = SDL_GetNumVideoDisplays();
|
||||
|
||||
|
@ -82,10 +76,8 @@ bool Launcher::GraphicsPage::setupSDL()
|
|||
screenComboBox->addItem(QString(tr("Screen ")) + QString::number(i + 1));
|
||||
}
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
|
||||
// Disconnect from SDL processes
|
||||
quitSDL();
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -145,6 +137,10 @@ bool Launcher::GraphicsPage::loadSettings()
|
|||
if (mEngineSettings.getBool("enable indoor shadows", "Shadows"))
|
||||
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");
|
||||
if (shadowDistLimit > 0)
|
||||
{
|
||||
|
@ -231,7 +227,7 @@ void Launcher::GraphicsPage::saveSettings()
|
|||
bool cPlayerShadows = playerShadowsCheckBox->checkState();
|
||||
if (cActorShadows || cObjectShadows || cTerrainShadows || cPlayerShadows)
|
||||
{
|
||||
if (mEngineSettings.getBool("enable shadows", "Shadows") != true)
|
||||
if (!mEngineSettings.getBool("enable shadows", "Shadows"))
|
||||
mEngineSettings.setBool("enable shadows", "Shadows", true);
|
||||
if (mEngineSettings.getBool("actor shadows", "Shadows") != cActorShadows)
|
||||
mEngineSettings.setBool("actor shadows", "Shadows", cActorShadows);
|
||||
|
@ -263,6 +259,10 @@ void Launcher::GraphicsPage::saveSettings()
|
|||
int cShadowRes = shadowResolutionComboBox->currentText().toInt();
|
||||
if (cShadowRes != mEngineSettings.getInt("shadow map resolution", "Shadows"))
|
||||
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)
|
||||
|
@ -316,7 +316,6 @@ QRect Launcher::GraphicsPage::getMaximumResolution()
|
|||
{
|
||||
QRect max;
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
|
||||
for (QScreen* screen : QGuiApplication::screens())
|
||||
{
|
||||
QRect res = screen->geometry();
|
||||
|
@ -325,17 +324,6 @@ QRect Launcher::GraphicsPage::getMaximumResolution()
|
|||
if (res.height() > max.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;
|
||||
}
|
||||
|
||||
|
|
|
@ -38,8 +38,8 @@ namespace Launcher
|
|||
Files::ConfigurationManager &mCfgMgr;
|
||||
Settings::Manager &mEngineSettings;
|
||||
|
||||
QStringList getAvailableResolutions(int screen);
|
||||
QRect getMaximumResolution();
|
||||
static QStringList getAvailableResolutions(int screen);
|
||||
static QRect getMaximumResolution();
|
||||
|
||||
bool setupSDL();
|
||||
};
|
||||
|
|
|
@ -13,18 +13,11 @@
|
|||
#endif // MAC_OS_X_VERSION_MIN_REQUIRED
|
||||
|
||||
#include "maindialog.hpp"
|
||||
#include "sdlinit.hpp"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
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);
|
||||
|
||||
// Internationalization
|
||||
|
@ -50,11 +43,6 @@ int main(int argc, char *argv[])
|
|||
|
||||
int exitCode = app.exec();
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
|
||||
// Disconnect from SDL processes
|
||||
quitSDL();
|
||||
#endif
|
||||
|
||||
return exitCode;
|
||||
}
|
||||
catch (std::exception& e)
|
||||
|
|
|
@ -149,16 +149,9 @@ if(WIN32)
|
|||
set(QT_USE_QTMAIN TRUE)
|
||||
endif(WIN32)
|
||||
|
||||
if (DESIRED_QT_VERSION MATCHES 4)
|
||||
include(${QT_USE_FILE})
|
||||
qt4_wrap_ui(OPENCS_UI_HDR ${OPENCS_UI})
|
||||
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
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
@ -236,19 +229,7 @@ target_link_libraries(openmw-cs
|
|||
components
|
||||
)
|
||||
|
||||
if (DESIRED_QT_VERSION MATCHES 4)
|
||||
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)
|
||||
target_link_libraries(openmw-cs ${Boost_LOCALE_LIBRARY})
|
||||
|
|
|
@ -685,7 +685,6 @@ namespace CSMPrefs
|
|||
std::make_pair((int)Qt::Key_ContrastAdjust , "ContrastAdjust"),
|
||||
std::make_pair((int)Qt::Key_LaunchG , "LaunchG"),
|
||||
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_TouchpadOn , "TouchpadOn"),
|
||||
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_Undo , "Undo"),
|
||||
std::make_pair((int)Qt::Key_Redo , "Redo"),
|
||||
#endif
|
||||
std::make_pair((int)Qt::Key_AltGr , "AltGr"),
|
||||
std::make_pair((int)Qt::Key_Multi_key , "Multi_key"),
|
||||
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_Play , "Play"),
|
||||
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"),
|
||||
#endif
|
||||
std::make_pair((int)Qt::Key_Context1 , "Context1"),
|
||||
std::make_pair((int)Qt::Key_Context2 , "Context2"),
|
||||
std::make_pair((int)Qt::Key_Context3 , "Context3"),
|
||||
|
|
|
@ -5,10 +5,7 @@
|
|||
#include <QVBoxLayout>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QPushButton>
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
|
||||
#include <QScreen>
|
||||
#endif
|
||||
|
||||
#include "filewidget.hpp"
|
||||
#include "adjusterwidget.hpp"
|
||||
|
@ -50,11 +47,7 @@ CSVDoc::NewGameDialogue::NewGameDialogue()
|
|||
connect (mFileWidget, SIGNAL (nameChanged (const QString&, bool)),
|
||||
mAdjusterWidget, SLOT (setName (const QString&, bool)));
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
|
||||
QRect scr = QGuiApplication::primaryScreen()->geometry();
|
||||
#else
|
||||
QRect scr = QApplication::desktop()->screenGeometry();
|
||||
#endif
|
||||
QRect rect = geometry();
|
||||
move (scr.center().x() - rect.center().x(), scr.center().y() - rect.center().y());
|
||||
}
|
||||
|
|
|
@ -9,10 +9,7 @@
|
|||
#include <QLabel>
|
||||
#include <QIcon>
|
||||
#include <QPushButton>
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
|
||||
#include <QScreen>
|
||||
#endif
|
||||
|
||||
QPushButton *CSVDoc::StartupDialogue::addButton (const QString& label, const QIcon& icon)
|
||||
{
|
||||
|
@ -123,12 +120,7 @@ CSVDoc::StartupDialogue::StartupDialogue() : mWidth (0), mColumn (2)
|
|||
|
||||
setLayout (layout);
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
|
||||
QRect scr = QGuiApplication::primaryScreen()->geometry();
|
||||
#else
|
||||
QRect scr = QApplication::desktop()->screenGeometry();
|
||||
#endif
|
||||
|
||||
QRect rect = geometry();
|
||||
move (scr.center().x() - rect.center().x(), scr.center().y() - rect.center().y());
|
||||
}
|
||||
|
|
|
@ -14,10 +14,7 @@
|
|||
#include <QHBoxLayout>
|
||||
#include <QDesktopWidget>
|
||||
#include <QScrollBar>
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
|
||||
#include <QScreen>
|
||||
#endif
|
||||
|
||||
#include "../../model/doc/document.hpp"
|
||||
#include "../../model/prefs/state.hpp"
|
||||
|
@ -1071,11 +1068,7 @@ void CSVDoc::View::updateWidth(bool isGrowLimit, int minSubViewWidth)
|
|||
if (isGrowLimit)
|
||||
rect = dw->screenGeometry(this);
|
||||
else
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
|
||||
rect = QGuiApplication::screens().at(dw->screenNumber(this))->geometry();
|
||||
#else
|
||||
rect = dw->screenGeometry(dw->screen(dw->screenNumber(this)));
|
||||
#endif
|
||||
|
||||
if (!mScrollbarOnly && mScroll && mSubViews.size() > 1)
|
||||
{
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
#include "dialogue.hpp"
|
||||
|
||||
#include <QApplication>
|
||||
|
@ -7,10 +6,7 @@
|
|||
#include <QListWidget>
|
||||
#include <QStackedWidget>
|
||||
#include <QListWidgetItem>
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
|
||||
#include <QScreen>
|
||||
#endif
|
||||
|
||||
#include <components/debug/debuglog.hpp>
|
||||
|
||||
|
@ -39,11 +35,7 @@ void CSVPrefs::Dialogue::buildCategorySelector (QSplitter *main)
|
|||
{
|
||||
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));
|
||||
#else
|
||||
maxWidth = std::max (maxWidth, metrics.width (label));
|
||||
#endif
|
||||
|
||||
list->addItem (label);
|
||||
}
|
||||
|
@ -116,11 +108,7 @@ void CSVPrefs::Dialogue::show()
|
|||
}
|
||||
else
|
||||
{
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
|
||||
QRect scr = QGuiApplication::primaryScreen()->geometry();
|
||||
#else
|
||||
QRect scr = QApplication::desktop()->screenGeometry();
|
||||
#endif
|
||||
|
||||
// otherwise place at the centre of the screen
|
||||
QPoint screenCenter = scr.center();
|
||||
|
|
|
@ -143,14 +143,9 @@ void RenderWidget::toggleRenderStats()
|
|||
CompositeViewer::CompositeViewer()
|
||||
: mSimulationTime(0.0)
|
||||
{
|
||||
#if QT_VERSION >= 0x050000
|
||||
// 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
|
||||
osgViewer::ViewerBase::ThreadingModel threadingModel = osgViewer::ViewerBase::SingleThreaded;
|
||||
#else
|
||||
osgViewer::ViewerBase::ThreadingModel threadingModel = osgViewer::ViewerBase::DrawThreadPerContext;
|
||||
#endif
|
||||
|
||||
setThreadingModel(threadingModel);
|
||||
// TODO: Upgrade osgQt to support osgViewer::ViewerBase::DrawThreadPerContext
|
||||
// https://gitlab.com/OpenMW/openmw/-/issues/5481
|
||||
setThreadingModel(osgViewer::ViewerBase::SingleThreaded);
|
||||
|
||||
#if OSG_VERSION_GREATER_OR_EQUAL(3,5,5)
|
||||
setUseConfigureAffinity(false);
|
||||
|
|
|
@ -646,13 +646,8 @@ void CSVRender::WorldspaceWidget::mouseMoveEvent (QMouseEvent *event)
|
|||
|
||||
if (mDragging)
|
||||
{
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
|
||||
mDragX = event->localPos().x();
|
||||
mDragY = height() - event->localPos().y();
|
||||
#else
|
||||
mDragX = event->posF().x();
|
||||
mDragY = height() - event->posF().y();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
|
@ -144,11 +144,7 @@ CSVTools::ReportTable::ReportTable (CSMDoc::Document& document,
|
|||
: CSVWorld::DragRecordTable (document, parent), mModel (document.getReport (id)),
|
||||
mRefreshAction (0), mRefreshState (refreshState)
|
||||
{
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
|
||||
horizontalHeader()->setSectionResizeMode (QHeaderView::Interactive);
|
||||
#else
|
||||
horizontalHeader()->setResizeMode (QHeaderView::Interactive);
|
||||
#endif
|
||||
horizontalHeader()->setStretchLastSection (true);
|
||||
verticalHeader()->hide();
|
||||
setSortingEnabled (true);
|
||||
|
|
|
@ -5,10 +5,7 @@
|
|||
#include <QDesktopWidget>
|
||||
#include <QPainter>
|
||||
#include <QShowEvent>
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
|
||||
#include <QScreen>
|
||||
#endif
|
||||
|
||||
#include "colorpickerpopup.hpp"
|
||||
|
||||
|
@ -99,11 +96,7 @@ QPoint CSVWidget::ColorEditor::calculatePopupPosition()
|
|||
{
|
||||
QRect editorGeometry = geometry();
|
||||
QRect popupGeometry = mColorPicker->geometry();
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
|
||||
QRect screenGeometry = QGuiApplication::primaryScreen()->geometry();
|
||||
#else
|
||||
QRect screenGeometry = QApplication::desktop()->screenGeometry();
|
||||
#endif
|
||||
|
||||
// Center the popup horizontally relative to the editor
|
||||
int localPopupX = (editorGeometry.width() - popupGeometry.width()) / 2;
|
||||
|
|
|
@ -64,13 +64,8 @@ CSVWidget::SceneToolRun::SceneToolRun (SceneToolbar *parent, const QString& tool
|
|||
mTable->setShowGrid (false);
|
||||
mTable->verticalHeader()->hide();
|
||||
mTable->horizontalHeader()->hide();
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
|
||||
mTable->horizontalHeader()->setSectionResizeMode (0, QHeaderView::Stretch);
|
||||
mTable->horizontalHeader()->setSectionResizeMode (1, QHeaderView::ResizeToContents);
|
||||
#else
|
||||
mTable->horizontalHeader()->setResizeMode (0, QHeaderView::Stretch);
|
||||
mTable->horizontalHeader()->setResizeMode (1, QHeaderView::ResizeToContents);
|
||||
#endif
|
||||
mTable->setSelectionMode (QAbstractItemView::NoSelection);
|
||||
|
||||
layout->addWidget (mTable);
|
||||
|
|
|
@ -180,13 +180,8 @@ CSVWidget::SceneToolShapeBrush::SceneToolShapeBrush (SceneToolbar *parent, const
|
|||
mTable->setShowGrid (true);
|
||||
mTable->verticalHeader()->hide();
|
||||
mTable->horizontalHeader()->hide();
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
|
||||
mTable->horizontalHeader()->setSectionResizeMode (0, 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);
|
||||
|
||||
layout->addWidget (mTable);
|
||||
|
|
|
@ -243,13 +243,8 @@ CSVWidget::SceneToolTextureBrush::SceneToolTextureBrush (SceneToolbar *parent, c
|
|||
mTable->setShowGrid (true);
|
||||
mTable->verticalHeader()->hide();
|
||||
mTable->horizontalHeader()->hide();
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
|
||||
mTable->horizontalHeader()->setSectionResizeMode (0, 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);
|
||||
|
||||
layout->addWidget (mTable);
|
||||
|
|
|
@ -110,11 +110,7 @@ void CSVWorld::EnumDelegate::paint (QPainter *painter, const QStyleOptionViewIte
|
|||
int valueIndex = getValueIndex(index);
|
||||
if (valueIndex != -1)
|
||||
{
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5,7,0)
|
||||
QStyleOptionViewItem itemOption(option);
|
||||
#else
|
||||
QStyleOptionViewItemV4 itemOption(option);
|
||||
#endif
|
||||
itemOption.text = mValues.at(valueIndex).second;
|
||||
QApplication::style()->drawControl(QStyle::CE_ItemViewItem, &itemOption, painter);
|
||||
}
|
||||
|
@ -134,13 +130,7 @@ QSize CSVWorld::EnumDelegate::sizeHint(const QStyleOptionViewItem &option, const
|
|||
itemOption.state = option.state;
|
||||
|
||||
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());
|
||||
#else
|
||||
QSize valueSize = QSize(itemOption.fontMetrics.width(valueText), itemOption.fontMetrics.height());
|
||||
#endif
|
||||
|
||||
itemOption.currentText = valueText;
|
||||
return QApplication::style()->sizeFromContents(QStyle::CT_ComboBox, &itemOption, valueSize);
|
||||
}
|
||||
|
|
|
@ -33,11 +33,7 @@ CSVWorld::NestedTable::NestedTable(CSMDoc::Document& document,
|
|||
setSelectionBehavior (QAbstractItemView::SelectRows);
|
||||
setSelectionMode (QAbstractItemView::ExtendedSelection);
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
|
||||
horizontalHeader()->setSectionResizeMode (QHeaderView::Interactive);
|
||||
#else
|
||||
horizontalHeader()->setResizeMode (QHeaderView::Interactive);
|
||||
#endif
|
||||
verticalHeader()->hide();
|
||||
|
||||
int columns = model->columnCount(QModelIndex());
|
||||
|
|
|
@ -205,12 +205,7 @@ bool CSVWorld::ScriptEdit::stringNeedsQuote (const std::string& id) const
|
|||
void CSVWorld::ScriptEdit::setTabWidth()
|
||||
{
|
||||
// Set tab width to specified number of characters using current font.
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5,11,0)
|
||||
setTabStopDistance(mTabCharCount * fontMetrics().horizontalAdvance(' '));
|
||||
#else
|
||||
setTabStopWidth(mTabCharCount * fontMetrics().width(' '));
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void CSVWorld::ScriptEdit::wrapLines(bool wrap)
|
||||
|
@ -290,12 +285,7 @@ int CSVWorld::ScriptEdit::lineNumberAreaWidth()
|
|||
++digits;
|
||||
}
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5,11,0)
|
||||
int space = 3 + fontMetrics().horizontalAdvance(QLatin1Char('9')) * digits;
|
||||
#else
|
||||
int space = 3 + fontMetrics().width(QLatin1Char('9')) * digits;
|
||||
#endif
|
||||
|
||||
return space;
|
||||
}
|
||||
|
||||
|
|
|
@ -83,13 +83,8 @@ CSVWorld::ScriptErrorTable::ScriptErrorTable (const CSMDoc::Document& document,
|
|||
QStringList headers;
|
||||
headers << "Severity" << "Line" << "Description";
|
||||
setHorizontalHeaderLabels (headers);
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
|
||||
horizontalHeader()->setSectionResizeMode (0, QHeaderView::ResizeToContents);
|
||||
horizontalHeader()->setSectionResizeMode (1, QHeaderView::ResizeToContents);
|
||||
#else
|
||||
horizontalHeader()->setResizeMode (0, QHeaderView::ResizeToContents);
|
||||
horizontalHeader()->setResizeMode (1, QHeaderView::ResizeToContents);
|
||||
#endif
|
||||
horizontalHeader()->setStretchLastSection (true);
|
||||
verticalHeader()->hide();
|
||||
setColumnHidden (3, true);
|
||||
|
|
|
@ -262,11 +262,7 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id,
|
|||
mDispatcher = new CSMWorld::CommandDispatcher (document, id, this);
|
||||
|
||||
setModel (mProxyModel);
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
|
||||
horizontalHeader()->setSectionResizeMode (QHeaderView::Interactive);
|
||||
#else
|
||||
horizontalHeader()->setResizeMode (QHeaderView::Interactive);
|
||||
#endif
|
||||
verticalHeader()->hide();
|
||||
setSelectionBehavior (QAbstractItemView::SelectRows);
|
||||
setSelectionMode (QAbstractItemView::ExtendedSelection);
|
||||
|
|
|
@ -21,7 +21,7 @@ add_openmw_dir (mwrender
|
|||
actors objects renderingmanager animation rotatecontroller sky npcanimation vismask
|
||||
creatureanimation effectmanager util renderinginterface pathgrid rendermode weaponanimation
|
||||
bulletdebugdraw globalmap characterpreview camera localmap water terrainstorage ripplesimulation
|
||||
renderbin actoranimation landmanager navmesh actorspaths recastmesh fogmanager
|
||||
renderbin actoranimation landmanager navmesh actorspaths recastmesh fogmanager objectpaging
|
||||
)
|
||||
|
||||
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
|
||||
aicast aiescort aiface aiactivate aicombat recharge repair enchanting pathfinding pathgrid security spellcasting spellresistance
|
||||
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
|
||||
)
|
||||
|
||||
|
|
|
@ -53,6 +53,8 @@ namespace MWBase
|
|||
|
||||
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;
|
||||
|
||||
/*
|
||||
|
@ -79,7 +81,14 @@ namespace MWBase
|
|||
virtual void goodbyeSelected() = 0;
|
||||
virtual void questionAnswered (int answer, ResponseCallback* callback) = 0;
|
||||
|
||||
enum TopicType
|
||||
{
|
||||
Specific = 1,
|
||||
Exhausted = 2
|
||||
};
|
||||
|
||||
virtual std::list<std::string> getAvailableTopics() = 0;
|
||||
virtual int getTopicFlag(const std::string&) = 0;
|
||||
|
||||
virtual bool checkServiceRefused (ResponseCallback* callback) = 0;
|
||||
|
||||
|
|
|
@ -695,23 +695,16 @@ namespace MWClass
|
|||
|
||||
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())
|
||||
return 0.f;
|
||||
|
||||
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 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;
|
||||
|
||||
if(getEncumbrance(ptr) > getCapacity(ptr))
|
||||
|
@ -728,19 +721,9 @@ namespace MWClass
|
|||
moveSpeed = flySpeed;
|
||||
}
|
||||
else if(world->isSwimming(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;
|
||||
moveSpeed = getSwimSpeed(ptr);
|
||||
else
|
||||
moveSpeed = walkSpeed;
|
||||
moveSpeed = getWalkSpeed(ptr);
|
||||
if(getMovementSettings(ptr).mPosition[0] != 0 && getMovementSettings(ptr).mPosition[1] == 0)
|
||||
moveSpeed *= 0.75f;
|
||||
|
||||
|
@ -1077,4 +1060,31 @@ namespace MWClass
|
|||
{
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -141,6 +141,12 @@ namespace MWClass
|
|||
/// @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;
|
||||
|
||||
float getWalkSpeed(const MWWorld::Ptr& ptr) const final;
|
||||
|
||||
float getRunSpeed(const MWWorld::Ptr& ptr) const final;
|
||||
|
||||
float getSwimSpeed(const MWWorld::Ptr& ptr) const final;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1170,16 +1170,6 @@ namespace MWClass
|
|||
bool inair = !world->isOnGround(ptr) && !swimming && !world->isFlying(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;
|
||||
if(getEncumbrance(ptr) > getCapacity(ptr))
|
||||
moveSpeed = 0.0f;
|
||||
|
@ -1194,19 +1184,11 @@ namespace MWClass
|
|||
moveSpeed = flySpeed;
|
||||
}
|
||||
else if (swimming)
|
||||
{
|
||||
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;
|
||||
}
|
||||
moveSpeed = getSwimSpeed(ptr);
|
||||
else if (running && !sneaking)
|
||||
moveSpeed = runSpeed;
|
||||
moveSpeed = getRunSpeed(ptr);
|
||||
else
|
||||
moveSpeed = walkSpeed;
|
||||
moveSpeed = getWalkSpeed(ptr);
|
||||
if(getMovementSettings(ptr).mPosition[0] != 0 && getMovementSettings(ptr).mPosition[1] == 0)
|
||||
moveSpeed *= 0.75f;
|
||||
|
||||
|
@ -1671,4 +1653,56 @@ namespace MWClass
|
|||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -176,6 +176,12 @@ namespace MWClass
|
|||
virtual int getPrimaryFactionRank(const MWWorld::ConstPtr &ptr) 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;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
Filter filter (mActor, mChoice, mTalkedTo);
|
||||
|
@ -350,23 +374,35 @@ namespace MWDialogue
|
|||
|
||||
mActorKnownTopics.clear();
|
||||
|
||||
const MWWorld::Store<ESM::Dialogue> &dialogs =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>();
|
||||
const auto& dialogs = MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>();
|
||||
|
||||
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()
|
||||
{
|
||||
|
@ -386,6 +422,11 @@ namespace MWDialogue
|
|||
return keywordList;
|
||||
}
|
||||
|
||||
int DialogueManager::getTopicFlag(const std::string& topicId)
|
||||
{
|
||||
return mActorKnownTopicsFlag[topicId];
|
||||
}
|
||||
|
||||
void DialogueManager::keywordSelected (const std::string& keyword, ResponseCallback* callback)
|
||||
{
|
||||
if(!mIsInChoice)
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <components/compiler/streamerrorhandler.hpp>
|
||||
#include <components/translation/translation.hpp>
|
||||
|
@ -30,6 +31,7 @@ namespace MWDialogue
|
|||
ModFactionReactionMap mChangedFactionReaction;
|
||||
|
||||
std::set<std::string, Misc::StringUtils::CiComp> mActorKnownTopics;
|
||||
std::unordered_map<std::string, int> mActorKnownTopicsFlag;
|
||||
|
||||
Translation::Storage& mTranslationDataStorage;
|
||||
MWScript::CompilerContext mCompilerContext;
|
||||
|
@ -71,6 +73,9 @@ namespace MWDialogue
|
|||
virtual bool startDialogue (const MWWorld::Ptr& actor, ResponseCallback* callback);
|
||||
|
||||
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);
|
||||
|
||||
|
|
|
@ -681,15 +681,3 @@ std::vector<const ESM::DialInfo *> MWDialogue::Filter::list (const ESM::Dialogue
|
|||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -66,9 +66,6 @@ namespace MWDialogue
|
|||
const ESM::DialInfo* search (const ESM::Dialogue& dialogue, const bool fallbackToInfoRefusal) const;
|
||||
///< Get a matching response for the requested dialogue.
|
||||
/// 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)
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -351,6 +351,7 @@ namespace MWGui
|
|||
|
||||
mTopicsList->adjustSize();
|
||||
updateHistory();
|
||||
updateTopicFormat();
|
||||
mCurrentWindowSize = _sender->getSize();
|
||||
}
|
||||
|
||||
|
@ -460,7 +461,6 @@ namespace MWGui
|
|||
setTitle(mPtr.getClass().getName(mPtr));
|
||||
|
||||
updateTopics();
|
||||
updateTopicsPane(); // force update for new services
|
||||
|
||||
updateDisposition();
|
||||
restock();
|
||||
|
@ -519,8 +519,6 @@ namespace MWGui
|
|||
return;
|
||||
mIsCompanion = isCompanion();
|
||||
mKeywords = keyWords;
|
||||
|
||||
updateTopicsPane();
|
||||
}
|
||||
|
||||
void DialogueWindow::updateTopicsPane()
|
||||
|
@ -570,15 +568,16 @@ namespace MWGui
|
|||
mTopicsList->addSeparator();
|
||||
|
||||
|
||||
for(std::string& keyword : mKeywords)
|
||||
for(const auto& keyword : mKeywords)
|
||||
{
|
||||
std::string topicId = Misc::StringUtils::lowerCase(keyword);
|
||||
mTopicsList->addItem(keyword);
|
||||
|
||||
Topic* t = new Topic(keyword);
|
||||
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();
|
||||
|
||||
|
@ -764,9 +763,28 @@ namespace MWGui
|
|||
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()
|
||||
{
|
||||
setKeywords(MWBase::Environment::get().getDialogueManager()->getAvailableTopics());
|
||||
updateTopicsPane();
|
||||
updateTopicFormat();
|
||||
}
|
||||
|
||||
bool DialogueWindow::isCompanion()
|
||||
|
|
|
@ -186,6 +186,8 @@ namespace MWGui
|
|||
|
||||
std::unique_ptr<ResponseCallback> mCallback;
|
||||
std::unique_ptr<ResponseCallback> mGreetingCallback;
|
||||
|
||||
void updateTopicFormat();
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -196,9 +196,7 @@ namespace MWGui
|
|||
|
||||
getWidget(mCrosshair, "Crosshair");
|
||||
|
||||
int mapSize = std::max(1, Settings::Manager::getInt("local map hud widget size", "Map"));
|
||||
int cellDistance = std::max(1, Settings::Manager::getInt("local map cell distance", "Map"));
|
||||
LocalMapBase::init(mMinimap, mCompass, mapSize, cellDistance);
|
||||
LocalMapBase::init(mMinimap, mCompass);
|
||||
|
||||
mMainWidget->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onWorldClicked);
|
||||
mMainWidget->eventMouseMove += MyGUI::newDelegate(this, &HUD::onWorldMouseOver);
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <components/myguiplatform/myguitexture.hpp>
|
||||
#include <components/settings/settings.hpp>
|
||||
#include <components/vfs/manager.hpp>
|
||||
#include <components/resource/resourcesystem.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/statemanager.hpp"
|
||||
|
@ -29,9 +30,9 @@
|
|||
namespace MWGui
|
||||
{
|
||||
|
||||
LoadingScreen::LoadingScreen(const VFS::Manager* vfs, osgViewer::Viewer* viewer)
|
||||
LoadingScreen::LoadingScreen(Resource::ResourceSystem* resourceSystem, osgViewer::Viewer* viewer)
|
||||
: WindowBase("openmw_loading_screen.layout")
|
||||
, mVFS(vfs)
|
||||
, mResourceSystem(resourceSystem)
|
||||
, mViewer(viewer)
|
||||
, mTargetFrameRate(120.0)
|
||||
, mLastWallpaperChangeTime(0.0)
|
||||
|
@ -39,6 +40,7 @@ namespace MWGui
|
|||
, mLoadingOnTime(0.0)
|
||||
, mImportantLabel(false)
|
||||
, mVisible(false)
|
||||
, mNestedLoadingCount(0)
|
||||
, mProgress(0)
|
||||
, mShowWallpaper(true)
|
||||
{
|
||||
|
@ -64,9 +66,9 @@ namespace MWGui
|
|||
|
||||
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/";
|
||||
mVFS->normalizeFilename(pattern);
|
||||
mResourceSystem->getVFS()->normalizeFilename(pattern);
|
||||
|
||||
/* priority given to the left */
|
||||
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)
|
||||
{
|
||||
mLoadingOnTime = mTimer.time_m();
|
||||
// Early-out if already on
|
||||
if (mMainWidget->getVisible())
|
||||
if (mNestedLoadingCount++ > 0 && mMainWidget->getVisible())
|
||||
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
|
||||
// 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);
|
||||
|
||||
if (const osgUtil::IncrementalCompileOperation* ico = mViewer->getIncrementalCompileOperation()) {
|
||||
mOldIcoMin = ico->getMinimumTimeAvailableForGLCompileAndDeletePerFrame();
|
||||
mOldIcoMax = ico->getMaximumNumOfObjectsToCompilePerFrame();
|
||||
}
|
||||
|
||||
mVisible = visible;
|
||||
mLoadingBox->setVisible(mVisible);
|
||||
setVisible(true);
|
||||
|
@ -194,6 +202,8 @@ namespace MWGui
|
|||
|
||||
void LoadingScreen::loadingOff()
|
||||
{
|
||||
if (--mNestedLoadingCount > 0)
|
||||
return;
|
||||
mLoadingBox->setVisible(true); // restore
|
||||
|
||||
if (mLastRenderTime < mLoadingOnTime)
|
||||
|
@ -215,6 +225,12 @@ namespace MWGui
|
|||
//std::cout << "loading took " << mTimer.time_m() - mLoadingOnTime << std::endl;
|
||||
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_LoadingWallpaper);
|
||||
}
|
||||
|
@ -336,7 +352,13 @@ namespace MWGui
|
|||
|
||||
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,
|
||||
// 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()
|
||||
|
@ -344,10 +366,6 @@ namespace MWGui
|
|||
mViewer->updateTraversal();
|
||||
mViewer->renderingTraversals();
|
||||
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();
|
||||
}
|
||||
|
|
|
@ -20,9 +20,9 @@ namespace osg
|
|||
class Texture2D;
|
||||
}
|
||||
|
||||
namespace VFS
|
||||
namespace Resource
|
||||
{
|
||||
class Manager;
|
||||
class ResourceSystem;
|
||||
}
|
||||
|
||||
namespace MWGui
|
||||
|
@ -32,7 +32,7 @@ namespace MWGui
|
|||
class LoadingScreen : public WindowBase, public Loading::Listener
|
||||
{
|
||||
public:
|
||||
LoadingScreen(const VFS::Manager* vfs, osgViewer::Viewer* viewer);
|
||||
LoadingScreen(Resource::ResourceSystem* resourceSystem, osgViewer::Viewer* viewer);
|
||||
virtual ~LoadingScreen();
|
||||
|
||||
/// Overridden from Loading::Listener, see the Loading::Listener documentation for usage details
|
||||
|
@ -53,7 +53,7 @@ namespace MWGui
|
|||
|
||||
void setupCopyFramebufferToTextureCallback();
|
||||
|
||||
const VFS::Manager* mVFS;
|
||||
Resource::ResourceSystem* mResourceSystem;
|
||||
osg::ref_ptr<osgViewer::Viewer> mViewer;
|
||||
|
||||
double mTargetFrameRate;
|
||||
|
@ -66,10 +66,13 @@ namespace MWGui
|
|||
bool mImportantLabel;
|
||||
|
||||
bool mVisible;
|
||||
int mNestedLoadingCount;
|
||||
|
||||
size_t mProgress;
|
||||
|
||||
bool mShowWallpaper;
|
||||
float mOldIcoMin = 0.f;
|
||||
unsigned int mOldIcoMax = 0;
|
||||
|
||||
MyGUI::Widget* mLoadingBox;
|
||||
|
||||
|
|
|
@ -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;
|
||||
mCompass = compass;
|
||||
mMapWidgetSize = mapWidgetSize;
|
||||
mCellDistance = cellDistance;
|
||||
mNumCells = cellDistance * 2 + 1;
|
||||
mMapWidgetSize = std::max(1, Settings::Manager::getInt("local map widget size", "Map"));
|
||||
mCellDistance = Constants::CellGridRadius;
|
||||
mNumCells = mCellDistance * 2 + 1;
|
||||
|
||||
mLocalMap->setCanvasSize(mMapWidgetSize*mNumCells, mMapWidgetSize*mNumCells);
|
||||
|
||||
|
@ -756,9 +756,7 @@ namespace MWGui
|
|||
mEventBoxLocal->eventMouseButtonPressed += MyGUI::newDelegate(this, &MapWindow::onDragStart);
|
||||
mEventBoxLocal->eventMouseButtonDoubleClick += MyGUI::newDelegate(this, &MapWindow::onMapDoubleClicked);
|
||||
|
||||
int mapSize = std::max(1, Settings::Manager::getInt("local map widget size", "Map"));
|
||||
int cellDistance = std::max(1, Settings::Manager::getInt("local map cell distance", "Map"));
|
||||
LocalMapBase::init(mLocalMap, mPlayerArrowLocal, mapSize, cellDistance);
|
||||
LocalMapBase::init(mLocalMap, mPlayerArrowLocal);
|
||||
|
||||
mGlobalMap->setVisible(mGlobal);
|
||||
mLocalMap->setVisible(!mGlobal);
|
||||
|
|
|
@ -94,7 +94,7 @@ namespace MWGui
|
|||
public:
|
||||
LocalMapBase(CustomMarkerCollection& markers, MWRender::LocalMap* localMapRender, bool fogOfWarEnabled = true);
|
||||
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 setActiveCell(const int x, const int y, bool interior=false);
|
||||
|
|
|
@ -132,6 +132,13 @@ namespace MWGui
|
|||
MWBase::Environment::get().getWindowManager()->injectKeyRelease(MyGUI::KeyCode::None);
|
||||
}
|
||||
|
||||
void SaveGameDialog::onClose()
|
||||
{
|
||||
mSaveList->setIndexSelected(MyGUI::ITEM_NONE);
|
||||
|
||||
WindowModal::onClose();
|
||||
}
|
||||
|
||||
void SaveGameDialog::onOpen()
|
||||
{
|
||||
WindowModal::onOpen();
|
||||
|
|
|
@ -20,6 +20,7 @@ namespace MWGui
|
|||
SaveGameDialog();
|
||||
|
||||
virtual void onOpen();
|
||||
virtual void onClose();
|
||||
|
||||
void setLoadOrSave(bool load);
|
||||
|
||||
|
|
|
@ -242,7 +242,7 @@ namespace MWGui
|
|||
mKeyboardNavigation->setEnabled(keyboardNav);
|
||||
Gui::ImageButton::setDefaultNeedKeyFocus(keyboardNav);
|
||||
|
||||
mLoadingScreen = new LoadingScreen(mResourceSystem->getVFS(), mViewer);
|
||||
mLoadingScreen = new LoadingScreen(mResourceSystem, mViewer);
|
||||
mWindows.push_back(mLoadingScreen);
|
||||
|
||||
//set up the hardware cursor manager
|
||||
|
|
|
@ -36,6 +36,7 @@ namespace MWInput
|
|||
, mSneakToggleShortcutTimer(0.f)
|
||||
, mGamepadZoom(0)
|
||||
, mGamepadGuiCursorEnabled(true)
|
||||
, mGuiCursorEnabled(true)
|
||||
, mJoystickLastUsed(false)
|
||||
, mSneakGamepadShortcut(false)
|
||||
, mGamepadPreviewMode(false)
|
||||
|
|
|
@ -56,8 +56,8 @@ namespace MWInput
|
|||
float mSneakToggleShortcutTimer;
|
||||
float mGamepadZoom;
|
||||
bool mGamepadGuiCursorEnabled;
|
||||
bool mJoystickLastUsed;
|
||||
bool mGuiCursorEnabled;
|
||||
bool mJoystickLastUsed;
|
||||
bool mSneakGamepadShortcut;
|
||||
bool mGamepadPreviewMode;
|
||||
};
|
||||
|
|
|
@ -134,7 +134,7 @@ void adjustCommandedActor (const MWWorld::Ptr& actor)
|
|||
auto it = stats.getAiSequence().begin();
|
||||
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())
|
||||
{
|
||||
hasCommandPackage = true;
|
||||
|
@ -486,7 +486,7 @@ namespace MWMechanics
|
|||
return;
|
||||
|
||||
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;
|
||||
|
||||
const osg::Vec3f playerPos(getPlayer().getRefData().getPosition().asVec3());
|
||||
|
@ -530,11 +530,11 @@ namespace MWMechanics
|
|||
|
||||
CreatureStats &stats = actor.getClass().getCreatureStats(actor);
|
||||
const MWMechanics::AiSequence& seq = stats.getAiSequence();
|
||||
int packageId = seq.getTypeId();
|
||||
const auto packageId = seq.getTypeId();
|
||||
|
||||
if (seq.isInCombat() ||
|
||||
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.setGreetingTimer(0);
|
||||
|
@ -769,7 +769,7 @@ namespace MWMechanics
|
|||
followerOrEscorter = true;
|
||||
break;
|
||||
}
|
||||
else if (package->getTypeId() != MWMechanics::AiPackage::TypeIdCombat)
|
||||
else if (package->getTypeId() != MWMechanics::AiPackageTypeId::Combat)
|
||||
break;
|
||||
}
|
||||
if (!followerOrEscorter)
|
||||
|
@ -1323,7 +1323,7 @@ namespace MWMechanics
|
|||
if (!isPlayer && stats.getTimeToStartDrowning() < fHoldBreathTime / 2)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -1485,7 +1485,7 @@ namespace MWMechanics
|
|||
if (player.getClass().getNpcStats(player).isWerewolf())
|
||||
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)
|
||||
{
|
||||
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
|
||||
if (!stats.getKnockedDown() &&
|
||||
!stats.getAiSequence().isInCombat() &&
|
||||
!stats.getAiSequence().hasPackage(AiPackage::TypeIdPursue) &&
|
||||
!stats.getAiSequence().hasPackage(AiPackageTypeId::Pursue) &&
|
||||
!firstPersonPlayer)
|
||||
{
|
||||
for(PtrActorMap::iterator it(mActors.begin()); it != mActors.end(); ++it)
|
||||
|
@ -2563,7 +2563,7 @@ namespace MWMechanics
|
|||
}
|
||||
break;
|
||||
}
|
||||
else if (package->getTypeId() != AiPackage::TypeIdCombat && package->getTypeId() != AiPackage::TypeIdWander)
|
||||
else if (package->getTypeId() != AiPackageTypeId::Combat && package->getTypeId() != AiPackageTypeId::Wander)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -2589,7 +2589,7 @@ namespace MWMechanics
|
|||
{
|
||||
if (package->followTargetThroughDoors() && package->getTarget() == actor)
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -2655,7 +2655,7 @@ namespace MWMechanics
|
|||
list.push_back(static_cast<const AiFollow*>(package.get())->getFollowIndex());
|
||||
break;
|
||||
}
|
||||
else if (package->getTypeId() != AiPackage::TypeIdCombat && package->getTypeId() != AiPackage::TypeIdWander)
|
||||
else if (package->getTypeId() != AiPackageTypeId::Combat && package->getTypeId() != AiPackageTypeId::Wander)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ namespace MWMechanics
|
|||
|
||||
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;
|
||||
|
||||
|
|
|
@ -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) {
|
||||
if(*it != getPlayer()) { //Not the player
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ namespace MWMechanics
|
|||
|
||||
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()
|
||||
{
|
||||
|
|
|
@ -12,7 +12,7 @@ namespace MWMechanics
|
|||
public:
|
||||
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()
|
||||
{
|
||||
|
|
|
@ -17,7 +17,7 @@ namespace MWMechanics
|
|||
|
||||
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;
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "aicombat.hpp"
|
||||
|
||||
#include <components/misc/rng.hpp>
|
||||
#include <components/misc/coordinateconverter.hpp>
|
||||
|
||||
#include <components/esm/aisequence.hpp>
|
||||
|
||||
|
@ -36,7 +37,6 @@
|
|||
#include "movement.hpp"
|
||||
#include "character.hpp"
|
||||
#include "aicombataction.hpp"
|
||||
#include "coordinateconverter.hpp"
|
||||
#include "actorutil.hpp"
|
||||
|
||||
namespace
|
||||
|
@ -370,7 +370,7 @@ namespace MWMechanics
|
|||
if (pathgrid && !actor.getClass().isPureWaterCreature(actor))
|
||||
{
|
||||
ESM::Pathgrid::PointList points;
|
||||
CoordinateConverter coords(storage.mCell->getCell());
|
||||
Misc::CoordinateConverter coords(storage.mCell->getCell());
|
||||
|
||||
osg::Vec3f localPos = actor.getRefData().getPosition().asVec3();
|
||||
coords.toLocal(localPos);
|
||||
|
|
|
@ -104,7 +104,7 @@ namespace MWMechanics
|
|||
|
||||
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()
|
||||
{
|
||||
|
|
|
@ -32,7 +32,7 @@ namespace MWMechanics
|
|||
|
||||
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()
|
||||
{
|
||||
|
|
|
@ -12,7 +12,7 @@ namespace MWMechanics
|
|||
|
||||
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()
|
||||
{
|
||||
|
|
|
@ -55,7 +55,7 @@ namespace MWMechanics
|
|||
|
||||
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()
|
||||
{
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <components/esm/loadland.hpp>
|
||||
#include <components/esm/loadmgef.hpp>
|
||||
#include <components/detournavigator/navigator.hpp>
|
||||
#include <components/misc/coordinateconverter.hpp>
|
||||
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/environment.hpp"
|
||||
|
@ -20,11 +21,10 @@
|
|||
#include "movement.hpp"
|
||||
#include "steering.hpp"
|
||||
#include "actorutil.hpp"
|
||||
#include "coordinateconverter.hpp"
|
||||
|
||||
#include <osg/Quat>
|
||||
|
||||
MWMechanics::AiPackage::AiPackage(TypeId typeId, const Options& options) :
|
||||
MWMechanics::AiPackage::AiPackage(AiPackageTypeId typeId, const Options& options) :
|
||||
mTypeId(typeId),
|
||||
mOptions(options),
|
||||
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);
|
||||
mPathFinder.buildPath(actor, position, dest, actor.getCell(), getPathGridGraph(actor.getCell()),
|
||||
pathfindingHalfExtents, getNavigatorFlags(actor));
|
||||
pathfindingHalfExtents, getNavigatorFlags(actor), getAreaCosts(actor));
|
||||
mRotateOnTheRunChecks = 3;
|
||||
|
||||
// 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)
|
||||
{
|
||||
// note: AiWander currently does not open doors
|
||||
if (getTypeId() == TypeIdWander)
|
||||
if (getTypeId() == AiPackageTypeId::Wander)
|
||||
return;
|
||||
|
||||
if (mPathFinder.getPathSize() == 0)
|
||||
|
@ -341,10 +341,9 @@ bool MWMechanics::AiPackage::isNearInactiveCell(osg::Vec3f position)
|
|||
if (playerCell->isExterior())
|
||||
{
|
||||
// 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.
|
||||
// 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.
|
||||
const float distanceFromEdge = 200.0;
|
||||
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();
|
||||
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;
|
||||
|
||||
if (actorClass.canWalk(actor))
|
||||
result |= DetourNavigator::Flag_walk;
|
||||
|
||||
if (actorClass.isBipedal(actor) && getTypeId() != TypeIdWander)
|
||||
if (actorClass.isBipedal(actor) && getTypeId() != AiPackageTypeId::Wander)
|
||||
result |= DetourNavigator::Flag_openDoor;
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -4,10 +4,12 @@
|
|||
#include <memory>
|
||||
|
||||
#include <components/esm/defs.hpp>
|
||||
#include <components/detournavigator/areatype.hpp>
|
||||
|
||||
#include "pathfinding.hpp"
|
||||
#include "obstacle.hpp"
|
||||
#include "aistate.hpp"
|
||||
#include "aipackagetypeid.hpp"
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
|
@ -35,26 +37,6 @@ namespace MWMechanics
|
|||
class AiPackage
|
||||
{
|
||||
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
|
||||
{
|
||||
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;
|
||||
|
||||
|
@ -97,7 +79,7 @@ namespace MWMechanics
|
|||
|
||||
/// Returns the TypeID of the AiPackage
|
||||
/// \see enum TypeId
|
||||
TypeId getTypeId() const { return mTypeId; }
|
||||
AiPackageTypeId getTypeId() const { return mTypeId; }
|
||||
|
||||
/// Higher number is higher priority (0 being the lowest)
|
||||
unsigned int getPriority() const { return mOptions.mPriority; }
|
||||
|
@ -167,7 +149,9 @@ namespace MWMechanics
|
|||
|
||||
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;
|
||||
|
||||
// TODO: all this does not belong here, move into temporary storage
|
||||
|
|
28
apps/openmw/mwmechanics/aipackagetypeid.hpp
Normal file
28
apps/openmw/mwmechanics/aipackagetypeid.hpp
Normal 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
|
|
@ -28,7 +28,7 @@ namespace MWMechanics
|
|||
|
||||
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()
|
||||
{
|
||||
|
|
|
@ -33,7 +33,7 @@ void AiSequence::copy (const AiSequence& sequence)
|
|||
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)
|
||||
{
|
||||
|
@ -61,17 +61,17 @@ AiSequence::~AiSequence()
|
|||
clear();
|
||||
}
|
||||
|
||||
int AiSequence::getTypeId() const
|
||||
AiPackageTypeId AiSequence::getTypeId() const
|
||||
{
|
||||
if (mPackages.empty())
|
||||
return -1;
|
||||
return AiPackageTypeId::None;
|
||||
|
||||
return mPackages.front()->getTypeId();
|
||||
}
|
||||
|
||||
bool AiSequence::getCombatTarget(MWWorld::Ptr &targetActor) const
|
||||
{
|
||||
if (getTypeId() != AiPackage::TypeIdCombat)
|
||||
if (getTypeId() != AiPackageTypeId::Combat)
|
||||
return false;
|
||||
|
||||
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)
|
||||
{
|
||||
if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdCombat)
|
||||
if ((*it)->getTypeId() == MWMechanics::AiPackageTypeId::Combat)
|
||||
targetActors.push_back((*it)->getTarget());
|
||||
}
|
||||
|
||||
|
@ -118,7 +118,7 @@ bool AiSequence::isInCombat() const
|
|||
{
|
||||
for (auto it = mPackages.begin(); it != mPackages.end(); ++it)
|
||||
{
|
||||
if ((*it)->getTypeId() == AiPackage::TypeIdCombat)
|
||||
if ((*it)->getTypeId() == AiPackageTypeId::Combat)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -128,7 +128,7 @@ bool AiSequence::isEngagedWithActor() const
|
|||
{
|
||||
for (auto it = mPackages.begin(); it != mPackages.end(); ++it)
|
||||
{
|
||||
if ((*it)->getTypeId() == AiPackage::TypeIdCombat)
|
||||
if ((*it)->getTypeId() == AiPackageTypeId::Combat)
|
||||
{
|
||||
MWWorld::Ptr target2 = (*it)->getTarget();
|
||||
if (!target2.isEmpty() && target2.getClass().isNpc())
|
||||
|
@ -138,7 +138,7 @@ bool AiSequence::isEngagedWithActor() const
|
|||
return false;
|
||||
}
|
||||
|
||||
bool AiSequence::hasPackage(int typeId) const
|
||||
bool AiSequence::hasPackage(AiPackageTypeId typeId) const
|
||||
{
|
||||
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)
|
||||
{
|
||||
if ((*it)->getTypeId() == AiPackage::TypeIdCombat)
|
||||
if ((*it)->getTypeId() == AiPackageTypeId::Combat)
|
||||
{
|
||||
if ((*it)->getTarget() == actor)
|
||||
return true;
|
||||
|
@ -165,7 +165,7 @@ void AiSequence::stopCombat()
|
|||
{
|
||||
for(auto it = mPackages.begin(); it != mPackages.end(); )
|
||||
{
|
||||
if ((*it)->getTypeId() == AiPackage::TypeIdCombat)
|
||||
if ((*it)->getTypeId() == AiPackageTypeId::Combat)
|
||||
{
|
||||
it = mPackages.erase(it);
|
||||
}
|
||||
|
@ -178,7 +178,7 @@ void AiSequence::stopPursuit()
|
|||
{
|
||||
for(auto it = mPackages.begin(); it != mPackages.end(); )
|
||||
{
|
||||
if ((*it)->getTypeId() == AiPackage::TypeIdPursue)
|
||||
if ((*it)->getTypeId() == AiPackageTypeId::Pursue)
|
||||
{
|
||||
it = mPackages.erase(it);
|
||||
}
|
||||
|
@ -192,10 +192,13 @@ bool AiSequence::isPackageDone() const
|
|||
return mDone;
|
||||
}
|
||||
|
||||
bool isActualAiPackage(int packageTypeId)
|
||||
namespace
|
||||
{
|
||||
return (packageTypeId >= AiPackage::TypeIdWander &&
|
||||
packageTypeId <= AiPackage::TypeIdActivate);
|
||||
bool isActualAiPackage(AiPackageTypeId packageTypeId)
|
||||
{
|
||||
return (packageTypeId >= AiPackageTypeId::Wander &&
|
||||
packageTypeId <= AiPackageTypeId::Activate);
|
||||
}
|
||||
}
|
||||
|
||||
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())
|
||||
{
|
||||
mLastAiPackage = -1;
|
||||
mLastAiPackage = AiPackageTypeId::None;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -213,12 +216,12 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac
|
|||
if (!package->alwaysActive() && outOfRange)
|
||||
return;
|
||||
|
||||
int packageTypeId = package->getTypeId();
|
||||
auto packageTypeId = package->getTypeId();
|
||||
// workaround ai packages not being handled as in the vanilla engine
|
||||
if (isActualAiPackage(packageTypeId))
|
||||
mLastAiPackage = packageTypeId;
|
||||
// if active package is combat one, choose nearest target
|
||||
if (packageTypeId == AiPackage::TypeIdCombat)
|
||||
if (packageTypeId == AiPackageTypeId::Combat)
|
||||
{
|
||||
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();)
|
||||
{
|
||||
if ((*it)->getTypeId() != AiPackage::TypeIdCombat) break;
|
||||
if ((*it)->getTypeId() != AiPackageTypeId::Combat) break;
|
||||
|
||||
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.
|
||||
// The same thing for actors without AI packages.
|
||||
// Also there is no point to stack return packages.
|
||||
int currentTypeId = getTypeId();
|
||||
int newTypeId = package.getTypeId();
|
||||
if (currentTypeId <= MWMechanics::AiPackage::TypeIdWander
|
||||
&& !hasPackage(MWMechanics::AiPackage::TypeIdInternalTravel)
|
||||
&& (newTypeId <= MWMechanics::AiPackage::TypeIdCombat
|
||||
|| newTypeId == MWMechanics::AiPackage::TypeIdPursue
|
||||
|| newTypeId == MWMechanics::AiPackage::TypeIdCast))
|
||||
const auto currentTypeId = getTypeId();
|
||||
const auto newTypeId = package.getTypeId();
|
||||
if (currentTypeId <= MWMechanics::AiPackageTypeId::Wander
|
||||
&& !hasPackage(MWMechanics::AiPackageTypeId::InternalTravel)
|
||||
&& (newTypeId <= MWMechanics::AiPackageTypeId::Combat
|
||||
|| newTypeId == MWMechanics::AiPackageTypeId::Pursue
|
||||
|| newTypeId == MWMechanics::AiPackageTypeId::Cast))
|
||||
{
|
||||
osg::Vec3f dest;
|
||||
if (currentTypeId == MWMechanics::AiPackage::TypeIdWander)
|
||||
if (currentTypeId == MWMechanics::AiPackageTypeId::Wander)
|
||||
{
|
||||
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)
|
||||
{
|
||||
// We should keep current AiCast package, if we try to add a new one.
|
||||
if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdCast &&
|
||||
package.getTypeId() == MWMechanics::AiPackage::TypeIdCast)
|
||||
if ((*it)->getTypeId() == MWMechanics::AiPackageTypeId::Cast &&
|
||||
package.getTypeId() == MWMechanics::AiPackageTypeId::Cast)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
@ -444,7 +447,7 @@ void AiSequence::writeState(ESM::AiSequence::AiSequence &sequence) const
|
|||
for (const auto& package : mPackages)
|
||||
package->writeState(sequence);
|
||||
|
||||
sequence.mLastAiPackage = mLastAiPackage;
|
||||
sequence.mLastAiPackage = static_cast<int>(mLastAiPackage);
|
||||
}
|
||||
|
||||
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();
|
||||
it != sequence.mPackages.end(); ++it)
|
||||
{
|
||||
if (isActualAiPackage(it->mType))
|
||||
if (isActualAiPackage(static_cast<AiPackageTypeId>(it->mType)))
|
||||
count++;
|
||||
}
|
||||
|
||||
|
@ -520,7 +523,7 @@ void AiSequence::readState(const ESM::AiSequence::AiSequence &sequence)
|
|||
mPackages.push_back(std::move(package));
|
||||
}
|
||||
|
||||
mLastAiPackage = sequence.mLastAiPackage;
|
||||
mLastAiPackage = static_cast<AiPackageTypeId>(sequence.mLastAiPackage);
|
||||
}
|
||||
|
||||
void AiSequence::fastForward(const MWWorld::Ptr& actor)
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <memory>
|
||||
|
||||
#include "aistate.hpp"
|
||||
#include "aipackagetypeid.hpp"
|
||||
|
||||
#include <components/esm/loadnpc.hpp>
|
||||
|
||||
|
@ -49,7 +50,7 @@ namespace MWMechanics
|
|||
void copy (const AiSequence& sequence);
|
||||
|
||||
/// The type of AI package that ran last
|
||||
int mLastAiPackage;
|
||||
AiPackageTypeId mLastAiPackage;
|
||||
AiState mAiState;
|
||||
|
||||
public:
|
||||
|
@ -71,14 +72,14 @@ namespace MWMechanics
|
|||
void erase(std::list<std::unique_ptr<AiPackage>>::const_iterator package);
|
||||
|
||||
/// Returns currently executing AiPackage type
|
||||
/** \see enum AiPackage::TypeId **/
|
||||
int getTypeId() const;
|
||||
/** \see enum class AiPackageTypeId **/
|
||||
AiPackageTypeId getTypeId() const;
|
||||
|
||||
/// Get the typeid of the Ai package that ran last
|
||||
/** 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.
|
||||
\see enum AiPackage::TypeId **/
|
||||
int getLastRunTypeId() const { return mLastAiPackage; }
|
||||
\see enum class AiPackageTypeId **/
|
||||
AiPackageTypeId getLastRunTypeId() const { return mLastAiPackage; }
|
||||
|
||||
/// Return true and assign target if combat package is currently active, return false otherwise
|
||||
bool getCombatTarget (MWWorld::Ptr &targetActor) const;
|
||||
|
@ -93,7 +94,7 @@ namespace MWMechanics
|
|||
bool isEngagedWithActor () const;
|
||||
|
||||
/// 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?
|
||||
bool isInCombat (const MWWorld::Ptr& actor) const;
|
||||
|
|
|
@ -34,7 +34,7 @@ namespace MWMechanics
|
|||
|
||||
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()
|
||||
{
|
||||
|
@ -60,7 +60,7 @@ namespace MWMechanics
|
|||
|
||||
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;
|
||||
};
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <components/misc/rng.hpp>
|
||||
#include <components/esm/aisequence.hpp>
|
||||
#include <components/detournavigator/navigator.hpp>
|
||||
#include <components/misc/coordinateconverter.hpp>
|
||||
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/environment.hpp"
|
||||
|
@ -21,7 +22,6 @@
|
|||
#include "pathgrid.hpp"
|
||||
#include "creaturestats.hpp"
|
||||
#include "movement.hpp"
|
||||
#include "coordinateconverter.hpp"
|
||||
#include "actorutil.hpp"
|
||||
|
||||
namespace MWMechanics
|
||||
|
@ -202,7 +202,7 @@ namespace MWMechanics
|
|||
{
|
||||
const osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getPathfindingHalfExtents(actor);
|
||||
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())
|
||||
|
@ -337,6 +337,7 @@ namespace MWMechanics
|
|||
const auto halfExtents = world->getPathfindingHalfExtents(actor);
|
||||
const auto navigator = world->getNavigator();
|
||||
const auto navigatorFlags = getNavigatorFlags(actor);
|
||||
const auto areaCosts = getAreaCosts(actor);
|
||||
|
||||
do {
|
||||
// Determine a random location within radius of original position
|
||||
|
@ -365,7 +366,8 @@ namespace MWMechanics
|
|||
if (isWaterCreature || isFlyingCreature)
|
||||
mPathFinder.buildStraightPath(mDestination);
|
||||
else
|
||||
mPathFinder.buildPathByNavMesh(actor, currentPosition, mDestination, halfExtents, navigatorFlags);
|
||||
mPathFinder.buildPathByNavMesh(actor, currentPosition, mDestination, halfExtents, navigatorFlags,
|
||||
areaCosts);
|
||||
|
||||
if (mPathFinder.isPathConstructed())
|
||||
{
|
||||
|
@ -496,7 +498,8 @@ namespace MWMechanics
|
|||
if (mUsePathgrid)
|
||||
{
|
||||
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())
|
||||
|
@ -566,7 +569,7 @@ namespace MWMechanics
|
|||
|
||||
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,
|
||||
|
@ -767,7 +770,7 @@ namespace MWMechanics
|
|||
{
|
||||
// get NPC's position in local (i.e. cell) coordinates
|
||||
osg::Vec3f npcPos(mInitialActorPosition);
|
||||
CoordinateConverter(cell).toLocal(npcPos);
|
||||
Misc::CoordinateConverter(cell).toLocal(npcPos);
|
||||
|
||||
// Find closest pathgrid point
|
||||
int closestPointIndex = PathFinder::getClosestPoint(pathgrid, npcPos);
|
||||
|
|
|
@ -93,7 +93,7 @@ namespace MWMechanics
|
|||
|
||||
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()
|
||||
{
|
||||
|
|
|
@ -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()
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -7,6 +7,8 @@
|
|||
#include <components/esm/esmwriter.hpp>
|
||||
#include <components/esm/stolenitems.hpp>
|
||||
|
||||
#include <components/detournavigator/navigator.hpp>
|
||||
|
||||
#include <components/sceneutil/positionattitudetransform.hpp>
|
||||
|
||||
/*
|
||||
|
@ -927,6 +929,12 @@ namespace MWMechanics
|
|||
bool MechanicsManager::toggleAI()
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -1351,7 +1359,7 @@ namespace MWMechanics
|
|||
{
|
||||
bool reported = false;
|
||||
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);
|
||||
|
||||
if (!reported)
|
||||
|
@ -1374,7 +1382,7 @@ namespace MWMechanics
|
|||
return false;
|
||||
|
||||
// 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())
|
||||
return false;
|
||||
|
@ -1491,7 +1499,7 @@ namespace MWMechanics
|
|||
// once the bounty has been paid.
|
||||
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);
|
||||
}
|
||||
|
@ -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.
|
||||
// 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);
|
||||
|
||||
// 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.
|
||||
// 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,
|
||||
// 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();
|
||||
return target.getClass().isNpc() && !attacker.isEmpty() && !seq.isInCombat(attacker)
|
||||
&& !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)
|
||||
|
@ -1842,7 +1850,7 @@ namespace MWMechanics
|
|||
if (iter->first.getClass().isClass(iter->first, "Guard"))
|
||||
{
|
||||
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.stack(MWMechanics::AiCombat(target), ptr);
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <components/detournavigator/debug.hpp>
|
||||
#include <components/detournavigator/navigator.hpp>
|
||||
#include <components/debug/debuglog.hpp>
|
||||
#include <components/misc/coordinateconverter.hpp>
|
||||
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/environment.hpp"
|
||||
|
@ -17,7 +18,6 @@
|
|||
#include "../mwworld/class.hpp"
|
||||
|
||||
#include "pathgrid.hpp"
|
||||
#include "coordinateconverter.hpp"
|
||||
#include "actorutil.hpp"
|
||||
|
||||
namespace
|
||||
|
@ -158,13 +158,10 @@ namespace MWMechanics
|
|||
// Maybe there is no pathgrid for this cell. Just go to destination and let
|
||||
// physics take care of any blockages.
|
||||
if(!pathgrid || pathgrid->mPoints.empty())
|
||||
{
|
||||
*out++ = endPoint;
|
||||
return;
|
||||
}
|
||||
|
||||
// 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
|
||||
// that is unreachable in some situations. e.g. actor is standing
|
||||
|
@ -179,6 +176,9 @@ namespace MWMechanics
|
|||
endPointInLocalCoords,
|
||||
startNode);
|
||||
|
||||
if (!endNode.second)
|
||||
return;
|
||||
|
||||
// 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.
|
||||
float startToEndLength2 = (endPointInLocalCoords - startPointInLocalCoords).length2();
|
||||
|
@ -249,7 +249,6 @@ namespace MWMechanics
|
|||
// unreachable pathgrid point.
|
||||
//
|
||||
// The AI routines will have to deal with such situations.
|
||||
if(endNode.second)
|
||||
*out++ = endPoint;
|
||||
}
|
||||
|
||||
|
@ -310,12 +309,13 @@ namespace MWMechanics
|
|||
}
|
||||
|
||||
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();
|
||||
|
||||
// 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);
|
||||
|
||||
mConstructed = true;
|
||||
|
@ -323,28 +323,37 @@ namespace MWMechanics
|
|||
|
||||
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 DetourNavigator::Flags flags)
|
||||
const DetourNavigator::Flags flags, const DetourNavigator::AreaCosts& areaCosts)
|
||||
{
|
||||
mPath.clear();
|
||||
mCell = cell;
|
||||
|
||||
bool hasNavMesh = false;
|
||||
|
||||
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())
|
||||
buildPathByPathgridImpl(startPoint, endPoint, pathgridGraph, std::back_inserter(mPath));
|
||||
|
||||
if (!hasNavMesh && mPath.empty())
|
||||
mPath.push_back(endPoint);
|
||||
|
||||
mConstructed = true;
|
||||
}
|
||||
|
||||
bool PathFinder::buildPathByNavigatorImpl(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint,
|
||||
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 stepSize = getPathStepSize(actor);
|
||||
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)
|
||||
return false;
|
||||
|
@ -361,7 +370,7 @@ namespace MWMechanics
|
|||
}
|
||||
|
||||
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())
|
||||
return;
|
||||
|
@ -375,7 +384,7 @@ namespace MWMechanics
|
|||
const auto navigator = MWBase::Environment::get().getWorld()->getNavigator();
|
||||
std::deque<osg::Vec3f> 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);
|
||||
|
||||
if (status == DetourNavigator::Status::NavMeshNotFound)
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <iterator>
|
||||
|
||||
#include <components/detournavigator/flags.hpp>
|
||||
#include <components/detournavigator/areatype.hpp>
|
||||
#include <components/esm/defs.hpp>
|
||||
#include <components/esm/loadpgrd.hpp>
|
||||
|
||||
|
@ -90,14 +91,15 @@ namespace MWMechanics
|
|||
const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph);
|
||||
|
||||
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,
|
||||
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,
|
||||
const DetourNavigator::Flags flags);
|
||||
const DetourNavigator::Flags flags, const DetourNavigator::AreaCosts& areaCosts);
|
||||
|
||||
/// Remove front point if exist and within tolerance
|
||||
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,
|
||||
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);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -52,7 +52,6 @@ namespace MWMechanics
|
|||
PathgridGraph::PathgridGraph(const MWWorld::CellStore *cell)
|
||||
: mCell(nullptr)
|
||||
, mPathgrid(nullptr)
|
||||
, mIsExterior(0)
|
||||
, mGraph(0)
|
||||
, mIsGraphConstructed(false)
|
||||
, mSCCId(0)
|
||||
|
@ -106,7 +105,6 @@ namespace MWMechanics
|
|||
return true;
|
||||
|
||||
mCell = cell->getCell();
|
||||
mIsExterior = cell->getCell()->isExterior();
|
||||
mPathgrid = MWBase::Environment::get().getWorld()->getStore().get<ESM::Pathgrid>().search(*cell->getCell());
|
||||
if(!mPathgrid)
|
||||
return false;
|
||||
|
|
|
@ -44,7 +44,6 @@ namespace MWMechanics
|
|||
|
||||
const ESM::Cell *mCell;
|
||||
const ESM::Pathgrid *mPathgrid;
|
||||
bool mIsExterior;
|
||||
|
||||
struct ConnectedPoint // edge
|
||||
{
|
||||
|
|
|
@ -419,6 +419,15 @@ namespace MWPhysics
|
|||
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
|
||||
{
|
||||
const Actor* physactor = getActor(actor);
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <algorithm>
|
||||
|
||||
#include <osg/Quat>
|
||||
#include <osg/BoundingBox>
|
||||
#include <osg/ref_ptr>
|
||||
|
||||
#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.
|
||||
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
|
||||
/// be overwritten. Valid until the next call to applyQueuedMovement.
|
||||
void queueObjectMovement(const MWWorld::Ptr &ptr, const osg::Vec3f &velocity);
|
||||
|
|
|
@ -513,6 +513,9 @@ namespace MWRender
|
|||
if (mShadowUniform)
|
||||
stateset->addUniform(mShadowUniform);
|
||||
|
||||
stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
|
||||
stateset->setRenderBinMode(osg::StateSet::OVERRIDE_RENDERBIN_DETAILS);
|
||||
|
||||
// FIXME: overriding diffuse/ambient/emissive colors
|
||||
osg::Material* material = new osg::Material;
|
||||
material->setColorMode(osg::Material::OFF);
|
||||
|
@ -1369,7 +1372,7 @@ namespace MWRender
|
|||
osg::Group* sheathParent = findVisitor.mFoundNode;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -1741,31 +1744,16 @@ namespace MWRender
|
|||
if (mTransparencyUpdater == nullptr)
|
||||
{
|
||||
mTransparencyUpdater = new TransparencyUpdater(alpha, mResourceSystem->getSceneManager()->getShaderManager().getShadowMapAlphaTestEnableUniform());
|
||||
mObjectRoot->addUpdateCallback(mTransparencyUpdater);
|
||||
mObjectRoot->addCullCallback(mTransparencyUpdater);
|
||||
}
|
||||
else
|
||||
mTransparencyUpdater->setAlpha(alpha);
|
||||
}
|
||||
else
|
||||
{
|
||||
mObjectRoot->removeUpdateCallback(mTransparencyUpdater);
|
||||
mObjectRoot->removeCullCallback(mTransparencyUpdater);
|
||||
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)
|
||||
|
|
|
@ -336,9 +336,6 @@ protected:
|
|||
*/
|
||||
virtual void addControllers();
|
||||
|
||||
/// Set the render bin for this animation's object root. May be customized by subclasses.
|
||||
virtual void setRenderBin();
|
||||
|
||||
public:
|
||||
|
||||
Animation(const MWWorld::Ptr &ptr, osg::ref_ptr<osg::Group> parentNode, Resource::ResourceSystem* resourceSystem);
|
||||
|
|
|
@ -74,7 +74,7 @@ LocalMap::LocalMap(osg::Group* root)
|
|||
: mRoot(root)
|
||||
, mMapResolution(Settings::Manager::getInt("local map resolution", "Map"))
|
||||
, mMapWorldSize(Constants::CellSizeInUnits)
|
||||
, mCellDistance(Settings::Manager::getInt("local map cell distance", "Map"))
|
||||
, mCellDistance(Constants::CellGridRadius)
|
||||
, mAngle(0.f)
|
||||
, 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> camera (new osg::Camera);
|
||||
|
||||
camera->setProjectionMatrixAsOrtho(-width/2, width/2, -height/2, height/2, 5, (zmax-zmin) + 10);
|
||||
camera->setComputeNearFarMode(osg::Camera::DO_NOT_COMPUTE_NEAR_FAR);
|
||||
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->setClearColor(osg::Vec4(0.f, 0.f, 0.f, 1.f));
|
||||
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::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());
|
||||
|
||||
MapSegment& segment = mSegments[std::make_pair(cell->getCell()->getGridX(), cell->getCell()->getGridY())];
|
||||
|
|
|
@ -435,12 +435,10 @@ void NpcAnimation::setRenderBin()
|
|||
osgUtil::RenderBin::addRenderBinPrototype("DepthClear", depthClearBin);
|
||||
prototypeAdded = true;
|
||||
}
|
||||
|
||||
osg::StateSet* stateset = mObjectRoot->getOrCreateStateSet();
|
||||
stateset->setRenderBinDetails(RenderBin_FirstPerson, "DepthClear", osg::StateSet::OVERRIDE_RENDERBIN_DETAILS);
|
||||
mObjectRoot->getOrCreateStateSet()->setRenderBinDetails(RenderBin_FirstPerson, "DepthClear", osg::StateSet::OVERRIDE_RENDERBIN_DETAILS);
|
||||
}
|
||||
else
|
||||
Animation::setRenderBin();
|
||||
else if (osg::StateSet* stateset = mObjectRoot->getStateSet())
|
||||
stateset->setRenderBinToInherit();
|
||||
}
|
||||
|
||||
void NpcAnimation::rebuild()
|
||||
|
|
|
@ -88,7 +88,7 @@ private:
|
|||
void addPartGroup(int group, int priority, const std::vector<ESM::PartReference> &parts,
|
||||
bool enchantedGlow=false, osg::Vec4f* glowColor=nullptr);
|
||||
|
||||
virtual void setRenderBin();
|
||||
void setRenderBin();
|
||||
|
||||
osg::ref_ptr<NeckController> mFirstPersonNeckController;
|
||||
|
||||
|
|
782
apps/openmw/mwrender/objectpaging.cpp
Normal file
782
apps/openmw/mwrender/objectpaging.cpp
Normal 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());
|
||||
}
|
||||
|
||||
}
|
92
apps/openmw/mwrender/objectpaging.hpp
Normal file
92
apps/openmw/mwrender/objectpaging.hpp
Normal 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 ©, osg::CopyOp co) : mRefnum(copy.mRefnum), mNumVertices(copy.mNumVertices) {}
|
||||
META_Object(MWRender, RefnumMarker)
|
||||
|
||||
ESM::RefNum mRefnum;
|
||||
unsigned int mNumVertices;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include <components/esm/loadpgrd.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/environment.hpp"
|
||||
|
@ -15,7 +16,6 @@
|
|||
#include "../mwworld/cellstore.hpp"
|
||||
#include "../mwworld/esmstore.hpp"
|
||||
#include "../mwmechanics/pathfinding.hpp"
|
||||
#include "../mwmechanics/coordinateconverter.hpp"
|
||||
|
||||
#include "vismask.hpp"
|
||||
|
||||
|
@ -105,7 +105,7 @@ void Pathgrid::enableCellPathgrid(const MWWorld::CellStore *store)
|
|||
if (!pathgrid) return;
|
||||
|
||||
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;
|
||||
cellPathGrid->setPosition(cellPathGridPos);
|
||||
|
|
|
@ -70,6 +70,8 @@
|
|||
#include "actorspaths.hpp"
|
||||
#include "recastmesh.hpp"
|
||||
#include "fogmanager.hpp"
|
||||
#include "objectpaging.hpp"
|
||||
|
||||
|
||||
namespace MWRender
|
||||
{
|
||||
|
@ -258,7 +260,6 @@ namespace MWRender
|
|||
{
|
||||
mViewer->setIncrementalCompileOperation(new osgUtil::IncrementalCompileOperation);
|
||||
mViewer->getIncrementalCompileOperation()->setTargetFrameRate(Settings::Manager::getFloat("target framerate", "Cells"));
|
||||
mViewer->getIncrementalCompileOperation()->setMaximumNumOfObjectsToCompilePerFrame(100);
|
||||
}
|
||||
|
||||
mResourceSystem->getSceneManager()->setIncrementalCompileOperation(mViewer->getIncrementalCompileOperation());
|
||||
|
@ -286,6 +287,12 @@ namespace MWRender
|
|||
mTerrain.reset(new Terrain::QuadTreeWorld(
|
||||
sceneRoot, mRootNode, mResourceSystem, mTerrainStorage, Mask_Terrain, Mask_PreCompile, Mask_Debug,
|
||||
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
|
||||
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);
|
||||
}
|
||||
|
||||
osg::Vec4f RenderingManager::getScreenBounds(const MWWorld::Ptr& ptr)
|
||||
osg::Vec4f RenderingManager::getScreenBounds(const osg::BoundingBox &worldbb)
|
||||
{
|
||||
if (!ptr.getRefData().getBaseNode())
|
||||
return osg::Vec4f();
|
||||
|
||||
osg::ComputeBoundsVisitor computeBoundsVisitor;
|
||||
computeBoundsVisitor.setTraversalMask(~(Mask_ParticleSystem|Mask_Effect));
|
||||
ptr.getRefData().getBaseNode()->accept(computeBoundsVisitor);
|
||||
|
||||
if (!worldbb.valid()) return osg::Vec4f();
|
||||
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;
|
||||
for (int i=0; i<8; ++i)
|
||||
{
|
||||
osg::Vec3f corner = computeBoundsVisitor.getBoundingBox().corner(i);
|
||||
osg::Vec3f corner = worldbb.corner(i);
|
||||
corner = corner * viewProj;
|
||||
|
||||
float x = (corner.x() + 1.f) * 0.5f;
|
||||
|
@ -1009,6 +1010,7 @@ namespace MWRender
|
|||
{
|
||||
RenderingManager::RayResult result;
|
||||
result.mHit = false;
|
||||
result.mHitRefnum.mContentFile = -1;
|
||||
result.mRatio = 0;
|
||||
if (intersector->containsIntersections())
|
||||
{
|
||||
|
@ -1020,6 +1022,7 @@ namespace MWRender
|
|||
result.mRatio = intersection.ratio;
|
||||
|
||||
PtrHolder* ptrHolder = nullptr;
|
||||
std::vector<RefnumMarker*> refnumMarkers;
|
||||
for (osg::NodePath::const_iterator it = intersection.nodePath.begin(); it != intersection.nodePath.end(); ++it)
|
||||
{
|
||||
osg::UserDataContainer* userDataContainer = (*it)->getUserDataContainer();
|
||||
|
@ -1029,11 +1032,25 @@ namespace MWRender
|
|||
{
|
||||
if (PtrHolder* p = dynamic_cast<PtrHolder*>(userDataContainer->getUserObject(i)))
|
||||
ptrHolder = p;
|
||||
if (RefnumMarker* r = dynamic_cast<RefnumMarker*>(userDataContainer->getUserObject(i)))
|
||||
refnumMarkers.push_back(r);
|
||||
}
|
||||
}
|
||||
|
||||
if (ptrHolder)
|
||||
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;
|
||||
|
@ -1046,6 +1063,7 @@ namespace MWRender
|
|||
mIntersectionVisitor = new osgUtil::IntersectionVisitor;
|
||||
|
||||
mIntersectionVisitor->setTraversalNumber(mViewer->getFrameStamp()->getFrameNumber());
|
||||
mIntersectionVisitor->setFrameStamp(mViewer->getFrameStamp());
|
||||
mIntersectionVisitor->setIntersector(intersector);
|
||||
|
||||
int mask = ~0;
|
||||
|
@ -1111,6 +1129,8 @@ namespace MWRender
|
|||
mSky->setMoonColour(false);
|
||||
|
||||
notifyWorldSpaceChanged();
|
||||
if (mObjectPaging)
|
||||
mObjectPaging->clear();
|
||||
}
|
||||
|
||||
MWRender::Animation* RenderingManager::getAnimation(const MWWorld::Ptr &ptr)
|
||||
|
@ -1467,4 +1487,43 @@ namespace MWRender
|
|||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@ namespace osgViewer
|
|||
namespace ESM
|
||||
{
|
||||
struct Cell;
|
||||
struct RefNum;
|
||||
}
|
||||
|
||||
namespace Terrain
|
||||
|
@ -84,6 +85,7 @@ namespace MWRender
|
|||
class NavMesh;
|
||||
class ActorsPaths;
|
||||
class RecastMesh;
|
||||
class ObjectPaging;
|
||||
|
||||
class RenderingManager : public MWRender::RenderingInterface
|
||||
{
|
||||
|
@ -155,6 +157,7 @@ namespace MWRender
|
|||
osg::Vec3f mHitNormalWorld;
|
||||
osg::Vec3f mHitPointWorld;
|
||||
MWWorld::Ptr mHitObject;
|
||||
ESM::RefNum mHitRefnum;
|
||||
float mRatio;
|
||||
};
|
||||
|
||||
|
@ -165,7 +168,7 @@ namespace MWRender
|
|||
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.
|
||||
osg::Vec4f getScreenBounds(const MWWorld::Ptr& ptr);
|
||||
osg::Vec4f getScreenBounds(const osg::BoundingBox &worldbb);
|
||||
|
||||
void setSkyEnabled(bool enabled);
|
||||
|
||||
|
@ -237,6 +240,13 @@ namespace MWRender
|
|||
|
||||
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:
|
||||
void updateProjectionMatrix();
|
||||
void updateTextureFiltering();
|
||||
|
@ -275,6 +285,7 @@ namespace MWRender
|
|||
std::unique_ptr<Water> mWater;
|
||||
std::unique_ptr<Terrain::World> mTerrain;
|
||||
TerrainStorage* mTerrainStorage;
|
||||
std::unique_ptr<ObjectPaging> mObjectPaging;
|
||||
std::unique_ptr<SkyManager> mSky;
|
||||
std::unique_ptr<FogManager> mFog;
|
||||
std::unique_ptr<EffectManager> mEffectManager;
|
||||
|
|
|
@ -427,7 +427,7 @@ namespace MWScript
|
|||
{
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ namespace
|
|||
{
|
||||
ESM::GlobalScript script;
|
||||
script.mTargetRef.unset();
|
||||
script.mRunning = false;
|
||||
if (!ptr.isEmpty())
|
||||
{
|
||||
if (ptr.getCellRef().hasContentFile())
|
||||
|
@ -42,6 +43,7 @@ namespace
|
|||
ESM::GlobalScript script;
|
||||
script.mTargetId = pair.second;
|
||||
script.mTargetRef = pair.first;
|
||||
script.mRunning = false;
|
||||
return script;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <components/resource/resourcesystem.hpp>
|
||||
#include <components/resource/bulletshapemanager.hpp>
|
||||
#include <components/resource/keyframemanager.hpp>
|
||||
#include <components/vfs/manager.hpp>
|
||||
#include <components/misc/resourcehelpers.hpp>
|
||||
#include <components/misc/stringops.hpp>
|
||||
#include <components/terrain/world.hpp>
|
||||
|
@ -65,24 +66,8 @@ namespace MWWorld
|
|||
mTerrainView = mTerrain->createView();
|
||||
|
||||
ListModelsVisitor visitor (mMeshes);
|
||||
if (cell->getState() == MWWorld::CellStore::State_Loaded)
|
||||
{
|
||||
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()
|
||||
{
|
||||
|
@ -97,7 +82,7 @@ namespace MWWorld
|
|||
try
|
||||
{
|
||||
mTerrain->cacheCell(mTerrainView.get(), mX, mY);
|
||||
mPreloadedObjects.push_back(mLandManager->getLand(mX, mY));
|
||||
mPreloadedObjects.insert(mLandManager->getLand(mX, mY));
|
||||
}
|
||||
catch(std::exception& e)
|
||||
{
|
||||
|
@ -113,17 +98,7 @@ namespace MWWorld
|
|||
{
|
||||
mesh = Misc::ResourceHelpers::correctActorModelPath(mesh, mSceneManager->getVFS());
|
||||
|
||||
if (mPreloadInstances)
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
||||
bool animated = false;
|
||||
size_t slashpos = mesh.find_last_of("/\\");
|
||||
if (slashpos != std::string::npos && slashpos != mesh.size()-1)
|
||||
{
|
||||
|
@ -134,12 +109,24 @@ namespace MWWorld
|
|||
if(kfname.size() > 4 && kfname.compare(kfname.size()-4, 4, ".nif") == 0)
|
||||
{
|
||||
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)
|
||||
{
|
||||
// ignore error for now, would spam the log too much
|
||||
|
@ -166,24 +153,28 @@ namespace MWWorld
|
|||
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
|
||||
std::vector<osg::ref_ptr<const osg::Object> > mPreloadedObjects;
|
||||
std::set<osg::ref_ptr<const osg::Object> > mPreloadedObjects;
|
||||
};
|
||||
|
||||
class TerrainPreloadItem : public SceneUtil::WorkItem
|
||||
{
|
||||
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)
|
||||
, mProgress(views.size())
|
||||
, mProgressRange(0)
|
||||
, mTerrainViews(views)
|
||||
, mWorld(world)
|
||||
, mPreloadPositions(preloadPositions)
|
||||
{
|
||||
}
|
||||
|
||||
void storeViews(double referenceTime)
|
||||
bool storeViews(double referenceTime)
|
||||
{
|
||||
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()
|
||||
|
@ -191,7 +182,7 @@ namespace MWWorld
|
|||
for (unsigned int i=0; i<mTerrainViews.size() && i<mPreloadPositions.size() && !mAbort; ++i)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
int getProgress() const { return !mProgress.empty() ? mProgress[0].load() : 0; }
|
||||
int getProgressRange() const { return !mProgress.empty() && mProgress[0].load() ? mProgressRange : 0; }
|
||||
|
||||
private:
|
||||
std::atomic<bool> mAbort;
|
||||
std::vector<std::atomic<int>> mProgress;
|
||||
int mProgressRange;
|
||||
std::vector<osg::ref_ptr<Terrain::View> > mTerrainViews;
|
||||
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.
|
||||
|
@ -237,6 +233,7 @@ namespace MWWorld
|
|||
, mMaxCacheSize(0)
|
||||
, mPreloadInstances(true)
|
||||
, mLastResourceCacheUpdate(0.0)
|
||||
, mStoreViewsFailCount(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -328,9 +325,6 @@ namespace MWWorld
|
|||
}
|
||||
|
||||
mPreloadCells.erase(found);
|
||||
|
||||
if (cell->isExterior() && mTerrainPreloadItem && mTerrainPreloadItem->isDone())
|
||||
mTerrainPreloadItem->storeViews(0.0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -375,7 +369,17 @@ namespace MWWorld
|
|||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -415,11 +419,71 @@ namespace MWWorld
|
|||
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 && !mTerrainPreloadItem->isDone())
|
||||
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;
|
||||
else if (positions == mTerrainPreloadPositions)
|
||||
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;
|
||||
if (mTerrainPreloadItem && !mTerrainPreloadItem->isDone())
|
||||
return;
|
||||
else
|
||||
{
|
||||
|
@ -436,9 +500,12 @@ namespace MWWorld
|
|||
}
|
||||
|
||||
mTerrainPreloadPositions = positions;
|
||||
if (!positions.empty())
|
||||
{
|
||||
mTerrainPreloadItem = new TerrainPreloadItem(mTerrainViews, mTerrain, positions);
|
||||
mWorkQueue->addWorkItem(mTerrainPreloadItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <map>
|
||||
#include <osg/ref_ptr>
|
||||
#include <osg/Vec3f>
|
||||
#include <osg/Vec4i>
|
||||
#include <components/sceneutil/workqueue.hpp>
|
||||
|
||||
namespace Resource
|
||||
|
@ -68,7 +69,11 @@ namespace MWWorld
|
|||
|
||||
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:
|
||||
Resource::ResourceSystem* mResourceSystem;
|
||||
|
@ -83,6 +88,7 @@ namespace MWWorld
|
|||
bool mPreloadInstances;
|
||||
|
||||
double mLastResourceCacheUpdate;
|
||||
int mStoreViewsFailCount;
|
||||
|
||||
struct PreloadEntry
|
||||
{
|
||||
|
@ -105,7 +111,7 @@ namespace MWWorld
|
|||
PreloadMap mPreloadCells;
|
||||
|
||||
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<SceneUtil::WorkItem> mUpdateCacheItem;
|
||||
};
|
||||
|
|
|
@ -119,7 +119,7 @@ namespace
|
|||
|
||||
template<typename RecordType, typename T>
|
||||
void readReferenceCollection (ESM::ESMReader& reader,
|
||||
MWWorld::CellRefList<T>& collection, const ESM::CellRef& cref, const std::map<int, int>& contentFileMap)
|
||||
MWWorld::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();
|
||||
|
||||
|
@ -154,7 +154,18 @@ namespace
|
|||
if (iter->mRef.getRefNum()==state.mRef.mRefNum && *iter->mRef.getRefIdPtr() == state.mRef.mRefID)
|
||||
{
|
||||
// overwrite existing reference
|
||||
float oldscale = iter->mRef.getScale();
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -167,28 +178,6 @@ namespace
|
|||
ref.load (state);
|
||||
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
|
||||
|
@ -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?)");
|
||||
|
||||
// Ensure that the object actually exists in the cell
|
||||
SearchByRefNumVisitor searchVisitor(object.getCellRef().getRefNum());
|
||||
forEach(searchVisitor);
|
||||
if (!searchVisitor.mFound)
|
||||
if (searchViaRefNum(object.getCellRef().getRefNum()).isEmpty())
|
||||
throw std::runtime_error("moveTo: object is not in this cell");
|
||||
|
||||
|
||||
|
@ -1030,107 +1017,107 @@ namespace MWWorld
|
|||
{
|
||||
case ESM::REC_ACTI:
|
||||
|
||||
readReferenceCollection<ESM::ObjectState> (reader, mActivators, cref, contentFileMap);
|
||||
readReferenceCollection<ESM::ObjectState> (reader, mActivators, cref, contentFileMap, this);
|
||||
break;
|
||||
|
||||
case ESM::REC_ALCH:
|
||||
|
||||
readReferenceCollection<ESM::ObjectState> (reader, mPotions, cref, contentFileMap);
|
||||
readReferenceCollection<ESM::ObjectState> (reader, mPotions, cref, contentFileMap, this);
|
||||
break;
|
||||
|
||||
case ESM::REC_APPA:
|
||||
|
||||
readReferenceCollection<ESM::ObjectState> (reader, mAppas, cref, contentFileMap);
|
||||
readReferenceCollection<ESM::ObjectState> (reader, mAppas, cref, contentFileMap, this);
|
||||
break;
|
||||
|
||||
case ESM::REC_ARMO:
|
||||
|
||||
readReferenceCollection<ESM::ObjectState> (reader, mArmors, cref, contentFileMap);
|
||||
readReferenceCollection<ESM::ObjectState> (reader, mArmors, cref, contentFileMap, this);
|
||||
break;
|
||||
|
||||
case ESM::REC_BOOK:
|
||||
|
||||
readReferenceCollection<ESM::ObjectState> (reader, mBooks, cref, contentFileMap);
|
||||
readReferenceCollection<ESM::ObjectState> (reader, mBooks, cref, contentFileMap, this);
|
||||
break;
|
||||
|
||||
case ESM::REC_CLOT:
|
||||
|
||||
readReferenceCollection<ESM::ObjectState> (reader, mClothes, cref, contentFileMap);
|
||||
readReferenceCollection<ESM::ObjectState> (reader, mClothes, cref, contentFileMap, this);
|
||||
break;
|
||||
|
||||
case ESM::REC_CONT:
|
||||
|
||||
readReferenceCollection<ESM::ContainerState> (reader, mContainers, cref, contentFileMap);
|
||||
readReferenceCollection<ESM::ContainerState> (reader, mContainers, cref, contentFileMap, this);
|
||||
break;
|
||||
|
||||
case ESM::REC_CREA:
|
||||
|
||||
readReferenceCollection<ESM::CreatureState> (reader, mCreatures, cref, contentFileMap);
|
||||
readReferenceCollection<ESM::CreatureState> (reader, mCreatures, cref, contentFileMap, this);
|
||||
break;
|
||||
|
||||
case ESM::REC_DOOR:
|
||||
|
||||
readReferenceCollection<ESM::DoorState> (reader, mDoors, cref, contentFileMap);
|
||||
readReferenceCollection<ESM::DoorState> (reader, mDoors, cref, contentFileMap, this);
|
||||
break;
|
||||
|
||||
case ESM::REC_INGR:
|
||||
|
||||
readReferenceCollection<ESM::ObjectState> (reader, mIngreds, cref, contentFileMap);
|
||||
readReferenceCollection<ESM::ObjectState> (reader, mIngreds, cref, contentFileMap, this);
|
||||
break;
|
||||
|
||||
case ESM::REC_LEVC:
|
||||
|
||||
readReferenceCollection<ESM::CreatureLevListState> (reader, mCreatureLists, cref, contentFileMap);
|
||||
readReferenceCollection<ESM::CreatureLevListState> (reader, mCreatureLists, cref, contentFileMap, this);
|
||||
break;
|
||||
|
||||
case ESM::REC_LEVI:
|
||||
|
||||
readReferenceCollection<ESM::ObjectState> (reader, mItemLists, cref, contentFileMap);
|
||||
readReferenceCollection<ESM::ObjectState> (reader, mItemLists, cref, contentFileMap, this);
|
||||
break;
|
||||
|
||||
case ESM::REC_LIGH:
|
||||
|
||||
readReferenceCollection<ESM::ObjectState> (reader, mLights, cref, contentFileMap);
|
||||
readReferenceCollection<ESM::ObjectState> (reader, mLights, cref, contentFileMap, this);
|
||||
break;
|
||||
|
||||
case ESM::REC_LOCK:
|
||||
|
||||
readReferenceCollection<ESM::ObjectState> (reader, mLockpicks, cref, contentFileMap);
|
||||
readReferenceCollection<ESM::ObjectState> (reader, mLockpicks, cref, contentFileMap, this);
|
||||
break;
|
||||
|
||||
case ESM::REC_MISC:
|
||||
|
||||
readReferenceCollection<ESM::ObjectState> (reader, mMiscItems, cref, contentFileMap);
|
||||
readReferenceCollection<ESM::ObjectState> (reader, mMiscItems, cref, contentFileMap, this);
|
||||
break;
|
||||
|
||||
case ESM::REC_NPC_:
|
||||
|
||||
readReferenceCollection<ESM::NpcState> (reader, mNpcs, cref, contentFileMap);
|
||||
readReferenceCollection<ESM::NpcState> (reader, mNpcs, cref, contentFileMap, this);
|
||||
break;
|
||||
|
||||
case ESM::REC_PROB:
|
||||
|
||||
readReferenceCollection<ESM::ObjectState> (reader, mProbes, cref, contentFileMap);
|
||||
readReferenceCollection<ESM::ObjectState> (reader, mProbes, cref, contentFileMap, this);
|
||||
break;
|
||||
|
||||
case ESM::REC_REPA:
|
||||
|
||||
readReferenceCollection<ESM::ObjectState> (reader, mRepairs, cref, contentFileMap);
|
||||
readReferenceCollection<ESM::ObjectState> (reader, mRepairs, cref, contentFileMap, this);
|
||||
break;
|
||||
|
||||
case ESM::REC_STAT:
|
||||
|
||||
readReferenceCollection<ESM::ObjectState> (reader, mStatics, cref, contentFileMap);
|
||||
readReferenceCollection<ESM::ObjectState> (reader, mStatics, cref, contentFileMap, this);
|
||||
break;
|
||||
|
||||
case ESM::REC_WEAP:
|
||||
|
||||
readReferenceCollection<ESM::ObjectState> (reader, mWeapons, cref, contentFileMap);
|
||||
readReferenceCollection<ESM::ObjectState> (reader, mWeapons, cref, contentFileMap, this);
|
||||
break;
|
||||
|
||||
case ESM::REC_BODY:
|
||||
|
||||
readReferenceCollection<ESM::ObjectState> (reader, mBodyParts, cref, contentFileMap);
|
||||
readReferenceCollection<ESM::ObjectState> (reader, mBodyParts, cref, contentFileMap, this);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -1152,26 +1139,22 @@ namespace MWWorld
|
|||
movedTo.load(reader);
|
||||
|
||||
// Search for the reference. It might no longer exist if its content file was removed.
|
||||
SearchByRefNumVisitor visitor(refnum);
|
||||
forEachInternal(visitor);
|
||||
|
||||
if (!visitor.mFound)
|
||||
Ptr movedRef = searchViaRefNum(refnum);
|
||||
if (movedRef.isEmpty())
|
||||
{
|
||||
Log(Debug::Warning) << "Warning: Dropping moved ref tag for " << refnum.mIndex << " (moved object no longer exists)";
|
||||
continue;
|
||||
}
|
||||
|
||||
MWWorld::LiveCellRefBase* movedRef = visitor.mFound;
|
||||
|
||||
CellStore* otherCell = callback->getCellStore(movedTo);
|
||||
|
||||
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.";
|
||||
// Note by dropping tag the object will automatically re-appear in its original cell, though potentially at inapproriate coordinates.
|
||||
// Restore original coordinates:
|
||||
movedRef->mData.setPosition(movedRef->mRef.getPosition());
|
||||
movedRef.getRefData().setPosition(movedRef.getCellRef().getPosition());
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -1182,7 +1165,7 @@ namespace MWWorld
|
|||
continue;
|
||||
}
|
||||
|
||||
moveTo(MWWorld::Ptr(movedRef, this), otherCell);
|
||||
moveTo(movedRef, otherCell);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -534,4 +534,19 @@ namespace MWWorld
|
|||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -373,6 +373,12 @@ namespace MWWorld
|
|||
virtual osg::Vec4f getEnchantmentColor(const MWWorld::ConstPtr& item) 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;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -136,6 +136,10 @@ void ESMStore::setUp(bool validateRecords)
|
|||
mIds[*record] = storeIt->first;
|
||||
}
|
||||
}
|
||||
|
||||
if (mStaticIds.empty())
|
||||
mStaticIds = mIds;
|
||||
|
||||
mSkills.setUp();
|
||||
mMagicEffects.setUp();
|
||||
mAttributes.setUp();
|
||||
|
|
|
@ -68,6 +68,8 @@ namespace MWWorld
|
|||
// Lookup of all IDs. Makes looking up references faster. Just
|
||||
// maps the id name to the record type.
|
||||
std::map<std::string, int> mIds;
|
||||
std::map<std::string, int> mStaticIds;
|
||||
|
||||
std::map<int, StoreBase *> mStores;
|
||||
|
||||
ESM::NPC mPlayerTemplate;
|
||||
|
@ -99,6 +101,14 @@ namespace MWWorld
|
|||
}
|
||||
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()
|
||||
: mDynamicCount(0)
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <components/resource/scenemanager.hpp>
|
||||
#include <components/resource/bulletshape.hpp>
|
||||
#include <components/sceneutil/unrefqueue.hpp>
|
||||
#include <components/sceneutil/positionattitudetransform.hpp>
|
||||
#include <components/detournavigator/navigator.hpp>
|
||||
#include <components/detournavigator/debug.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,
|
||||
MWRender::RenderingManager& rendering)
|
||||
MWRender::RenderingManager& rendering, std::set<ESM::RefNum>& pagedRefs)
|
||||
{
|
||||
if (ptr.getRefData().getBaseNode() || physics.getActor(ptr))
|
||||
{
|
||||
|
@ -107,15 +121,13 @@ namespace
|
|||
}
|
||||
|
||||
bool useAnim = ptr.getClass().useAnim();
|
||||
std::string model = ptr.getClass().getModel(ptr);
|
||||
if (useAnim)
|
||||
model = Misc::ResourceHelpers::correctActorModelPath(model, rendering.getResourceSystem()->getVFS());
|
||||
|
||||
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
|
||||
std::string model = getModel(ptr, rendering.getResourceSystem()->getVFS());
|
||||
|
||||
const ESM::RefNum& refnum = ptr.getCellRef().getRefNum();
|
||||
if (!refnum.hasContentFile() || pagedRefs.find(refnum) == pagedRefs.end())
|
||||
ptr.getClass().insertObjectRendering(ptr, model, rendering);
|
||||
else
|
||||
ptr.getRefData().setBaseNode(new SceneUtil::PositionAttitudeTransform); // FIXME remove this when physics code is fixed not to depend on basenode
|
||||
setNodeRotation(ptr, rendering, RotationOrder::direct);
|
||||
|
||||
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
|
||||
{
|
||||
MWWorld::CellStore& mCell;
|
||||
|
@ -287,50 +278,48 @@ namespace
|
|||
namespace MWWorld
|
||||
{
|
||||
|
||||
void Scene::removeFromPagedRefs(const Ptr &ptr)
|
||||
{
|
||||
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)
|
||||
{
|
||||
::updateObjectRotation(ptr, *mPhysics, mRendering, order);
|
||||
setNodeRotation(ptr, mRendering, order);
|
||||
mPhysics->updateRotation(ptr);
|
||||
}
|
||||
|
||||
void Scene::updateObjectScale(const Ptr &ptr)
|
||||
{
|
||||
::updateObjectScale(ptr, *mPhysics, mRendering);
|
||||
}
|
||||
|
||||
void Scene::getGridCenter(int &cellX, int &cellY)
|
||||
{
|
||||
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;
|
||||
float scale = ptr.getCellRef().getScale();
|
||||
osg::Vec3f scaleVec (scale, scale, scale);
|
||||
ptr.getClass().adjustScale(ptr, scaleVec, true);
|
||||
mRendering.scaleObject(ptr, scaleVec);
|
||||
mPhysics->updateScale(ptr);
|
||||
}
|
||||
|
||||
void Scene::update (float duration, bool paused)
|
||||
{
|
||||
mPreloadTimer += duration;
|
||||
if (mPreloadTimer > 0.1f)
|
||||
{
|
||||
preloadCells(0.1f);
|
||||
mPreloadTimer = 0.f;
|
||||
}
|
||||
mPreloader->updateCache(mRendering.getReferenceTime());
|
||||
preloadCells(duration);
|
||||
|
||||
mRendering.update (duration, paused);
|
||||
|
||||
mPreloader->updateCache(mRendering.getReferenceTime());
|
||||
}
|
||||
|
||||
void Scene::unloadCell (CellStoreCollection::iterator iter, bool test)
|
||||
|
@ -387,6 +376,9 @@ namespace MWWorld
|
|||
if ((*iter)->getCell()->hasWater())
|
||||
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();
|
||||
navigator->update(player.getRefData().getPosition().asVec3());
|
||||
|
||||
|
@ -425,7 +417,8 @@ namespace MWWorld
|
|||
float verts = ESM::Land::LAND_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 cellY = cell->getCell()->getGridY();
|
||||
|
@ -451,6 +444,9 @@ namespace MWWorld
|
|||
heightField->getCollisionObject()->getWorldTransform());
|
||||
}
|
||||
|
||||
if (const auto pathgrid = world->getStore().get<ESM::Pathgrid>().search(*cell->getCell()))
|
||||
navigator->addPathgrid(*cell->getCell(), *pathgrid);
|
||||
|
||||
// register local scripts
|
||||
// do this before insertCell, to make sure we don't add scripts from levelled creature spawning twice
|
||||
MWBase::Environment::get().getWorld()->getLocalScripts().addCell (cell);
|
||||
|
@ -523,6 +519,27 @@ namespace MWWorld
|
|||
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)
|
||||
{
|
||||
const auto navigator = MWBase::Environment::get().getWorld()->getNavigator();
|
||||
|
@ -532,30 +549,13 @@ namespace MWWorld
|
|||
if (!mCurrentCell || !mCurrentCell->isExterior())
|
||||
return;
|
||||
|
||||
// figure out the center of the current cell grid (*not* necessarily mCurrentCell, which is the cell the player is in)
|
||||
int cellX, cellY;
|
||||
getGridCenter(cellX, cellY);
|
||||
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);
|
||||
}
|
||||
osg::Vec2i newCell = getNewGridCenter(pos, &mCurrentGridCenter);
|
||||
if (newCell != mCurrentGridCenter)
|
||||
changeCellGrid(pos, newCell.x(), newCell.y());
|
||||
}
|
||||
|
||||
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();
|
||||
while (active!=mActiveCells.end())
|
||||
{
|
||||
|
@ -572,6 +572,14 @@ namespace MWWorld
|
|||
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::vector<std::pair<int, int>> cellsPositionsToLoad;
|
||||
// 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);
|
||||
|
||||
const auto getDistanceToPlayerCell = [&] (const std::pair<int, int>& cellPosition)
|
||||
|
@ -795,14 +808,12 @@ namespace MWWorld
|
|||
|
||||
MWBase::Environment::get().getWorld()->adjustSky();
|
||||
|
||||
mLastPlayerPos = pos.asVec3();
|
||||
mLastPlayerPos = player.getRefData().getPosition().asVec3();
|
||||
}
|
||||
|
||||
Scene::Scene (MWRender::RenderingManager& rendering, MWPhysics::PhysicsSystem *physics,
|
||||
DetourNavigator::Navigator& 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)
|
||||
, mPreloadDistance(Settings::Manager::getInt("preload distance", "Cells"))
|
||||
, mPreloadEnabled(Settings::Manager::getBool("preload enabled", "Cells"))
|
||||
|
@ -878,6 +889,7 @@ namespace MWWorld
|
|||
loadingListener->setProgressRange(cell->count());
|
||||
|
||||
// Load cell.
|
||||
mPagedRefs.clear();
|
||||
loadCell (cell, loadingListener, changeEvent);
|
||||
|
||||
/*
|
||||
|
@ -922,7 +934,7 @@ namespace MWWorld
|
|||
if (changeEvent)
|
||||
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);
|
||||
changePlayerCell(current, position, adjustPlayerPos);
|
||||
|
@ -945,7 +957,7 @@ namespace MWWorld
|
|||
{
|
||||
InsertVisitor insertVisitor (cell, *loadingListener, test);
|
||||
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); });
|
||||
|
||||
// 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
|
||||
{
|
||||
addObject(ptr, *mPhysics, mRendering);
|
||||
addObject(ptr, *mPhysics, mRendering, mPagedRefs);
|
||||
addObject(ptr, *mPhysics, mNavigator);
|
||||
MWBase::Environment::get().getWorld()->scaleObject(ptr, ptr.getCellRef().getScale());
|
||||
const auto navigator = MWBase::Environment::get().getWorld()->getNavigator();
|
||||
|
@ -989,6 +1001,7 @@ namespace MWWorld
|
|||
mRendering.removeObject (ptr);
|
||||
if (ptr.getClass().isActor())
|
||||
mRendering.removeWaterRippleEmitter(ptr);
|
||||
ptr.getRefData().setBaseNode(nullptr);
|
||||
}
|
||||
|
||||
bool Scene::isCellActive(const CellStore &cell)
|
||||
|
@ -1048,7 +1061,8 @@ namespace MWWorld
|
|||
|
||||
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();
|
||||
osg::Vec3f playerPos = player.getRefData().getPosition().asVec3();
|
||||
|
@ -1056,7 +1070,7 @@ namespace MWWorld
|
|||
osg::Vec3f predictedPos = playerPos + moved / dt * mPredictionTime;
|
||||
|
||||
if (mCurrentCell->isExterior())
|
||||
exteriorPositions.push_back(predictedPos);
|
||||
exteriorPositions.emplace_back(predictedPos, gridCenterToBounds(getNewGridCenter(predictedPos, &mCurrentGridCenter)));
|
||||
|
||||
mLastPlayerPos = playerPos;
|
||||
|
||||
|
@ -1073,7 +1087,7 @@ namespace MWWorld
|
|||
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;
|
||||
for (const MWWorld::CellStore* cellStore : mActiveCells)
|
||||
|
@ -1107,7 +1121,7 @@ namespace MWWorld
|
|||
int x,y;
|
||||
MWBase::Environment::get().getWorld()->positionToIndex (pos.x(), pos.y(), x, y);
|
||||
preloadCell(MWBase::Environment::get().getWorld()->getExterior(x,y), true);
|
||||
exteriorPositions.push_back(pos);
|
||||
exteriorPositions.emplace_back(pos, gridCenterToBounds(getNewGridCenter(pos)));
|
||||
}
|
||||
}
|
||||
catch (std::exception& e)
|
||||
|
@ -1127,7 +1141,7 @@ namespace MWWorld
|
|||
|
||||
|
||||
int cellX,cellY;
|
||||
getGridCenter(cellX,cellY);
|
||||
cellX = mCurrentGridCenter.x(); cellY = mCurrentGridCenter.y();
|
||||
|
||||
float centerX, centerY;
|
||||
MWBase::Environment::get().getWorld()->indexToPosition(cellX, cellY, centerX, centerY, true);
|
||||
|
@ -1173,11 +1187,41 @@ namespace MWWorld
|
|||
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;
|
||||
vec.push_back(pos);
|
||||
std::vector<PositionCellGrid> vec;
|
||||
vec.emplace_back(pos, gridCenterToBounds(getNewGridCenter(pos)));
|
||||
if (sync && mRendering.pagingUnlockCache())
|
||||
mPreloader->abortTerrainPreloadExcept(nullptr);
|
||||
else
|
||||
mPreloader->abortTerrainPreloadExcept(&vec[0]);
|
||||
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
|
||||
|
@ -1210,7 +1254,7 @@ namespace MWWorld
|
|||
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();
|
||||
ListFastTravelDestinationsVisitor listVisitor(mPreloadDistance, player.getRefData().getPosition().asVec3());
|
||||
|
@ -1231,7 +1275,7 @@ namespace MWWorld
|
|||
int x,y;
|
||||
MWBase::Environment::get().getWorld()->positionToIndex( pos.x(), pos.y(), x, y);
|
||||
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
Loading…
Reference in a new issue