1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-01-30 02:15:32 +00:00

Add OpenMW commits up to 22 Jun 2020

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

View file

@ -5,7 +5,7 @@ Debian:
tags:
- 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

View file

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

View file

@ -1,11 +1,13 @@
& "${env:COMSPEC}" /c ActivateMSVC.bat "&&" set | ForEach-Object {
$name, $value = $_ -split '=', 2
Set-Content env:\"$name" $value
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 {

View file

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

View file

@ -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,30 +938,18 @@ 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
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
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
cp "../CI/activate_msvc.sh" .
sed -i "s/\$MSVC_DISPLAY_YEAR/$MSVC_DISPLAY_YEAR/g" activate_msvc.sh
source ./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

View file

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

View file

@ -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,18 +154,12 @@ 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(Qt5Widgets REQUIRED)
find_package(Qt5Core REQUIRED)
find_package(Qt5Network REQUIRED)
find_package(Qt5OpenGL REQUIRED)
find_package(Qt5Core 5.12 REQUIRED)
find_package(Qt5Widgets 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)
@ -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 ()

View file

@ -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()
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})
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()
target_link_libraries(openmw-launcher Qt5::Widgets Qt5::Core)
if (BUILD_WITH_CODE_COVERAGE)
add_definitions (--coverage)

View file

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

View file

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

View file

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

View file

@ -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()
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})
# 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()
target_link_libraries(openmw-cs Qt5::Widgets Qt5::Core Qt5::Network Qt5::OpenGL)
if (WIN32)
target_link_libraries(openmw-cs ${Boost_LOCALE_LIBRARY})

View file

@ -685,7 +685,6 @@ namespace CSMPrefs
std::make_pair((int)Qt::Key_ContrastAdjust , "ContrastAdjust"),
std::make_pair((int)Qt::Key_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"),

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1170,16 +1170,6 @@ namespace MWClass
bool inair = !world->isOnGround(ptr) && !swimming && !world->isFlying(ptr);
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;
}
}

View file

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

View file

@ -278,6 +278,30 @@ namespace MWDialogue
}
}
bool DialogueManager::inJournal (const std::string& topicId, const std::string& infoId)
{
const MWDialogue::Topic *topicHistory = nullptr;
MWBase::Journal *journal = MWBase::Environment::get().getJournal();
for (auto it = journal->topicBegin(); it != journal->topicEnd(); ++it)
{
if (it->first == topicId)
{
topicHistory = &it->second;
break;
}
}
if (!topicHistory)
return false;
for(const auto& topic : *topicHistory)
{
if (topic.mInfoId == infoId)
return true;
}
return false;
}
void DialogueManager::executeTopic (const std::string& topic, ResponseCallback* callback)
{
Filter filter (mActor, mChoice, mTalkedTo);
@ -350,22 +374,34 @@ 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)

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -214,13 +214,13 @@ namespace MWGui
*/
}
void LocalMapBase::init(MyGUI::ScrollView* widget, MyGUI::ImageBox* compass, int mapWidgetSize, int cellDistance)
void LocalMapBase::init(MyGUI::ScrollView* widget, MyGUI::ImageBox* compass)
{
mLocalMap = widget;
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);

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -63,7 +63,7 @@ bool MWMechanics::AiAvoidDoor::execute (const MWWorld::Ptr& actor, CharacterCont
for(std::vector<MWWorld::Ptr>::iterator it = actors.begin(); it != actors.end(); ++it) {
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);
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -7,6 +7,8 @@
#include <components/esm/esmwriter.hpp>
#include <components/esm/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);

View file

@ -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,8 +249,7 @@ namespace MWMechanics
// unreachable pathgrid point.
//
// The AI routines will have to deal with such situations.
if(endNode.second)
*out++ = endPoint;
*out++ = endPoint;
}
float PathFinder::getZAngleToNext(float x, float y) const
@ -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)

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -8,6 +8,7 @@
#include <components/esm/loadpgrd.hpp>
#include <components/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);

View file

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

View file

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

View file

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

View file

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

View file

@ -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,23 +66,7 @@ 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);
}
}
cell->forEach(visitor);
}
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,11 +109,23 @@ 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)
{
@ -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)
return true;
else if (mTerrainPreloadItem->isDone())
{
if (mTerrainPreloadItem->storeViews(timestamp))
{
mTerrainPreloadItem = nullptr;
return true;
}
else
{
setTerrainPreloadPositions(std::vector<CellPreloader::PositionCellGrid>());
setTerrainPreloadPositions(positions);
return false;
}
}
else
{
progress = mTerrainPreloadItem->getProgress();
progressRange = mTerrainPreloadItem->getProgressRange();
return false;
}
}
void CellPreloader::abortTerrainPreloadExcept(const CellPreloader::PositionCellGrid *exceptPos)
{
const float resetThreshold = ESM::Land::REAL_SIZE;
for (auto pos : mTerrainPreloadPositions)
if (exceptPos && (pos.first-exceptPos->first).length2() < resetThreshold*resetThreshold && pos.second == exceptPos->second)
return;
if (mTerrainPreloadItem && !mTerrainPreloadItem->isDone())
{
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;
else if (positions == mTerrainPreloadPositions)
if (mTerrainPreloadItem && !mTerrainPreloadItem->isDone())
return;
else
{
@ -436,8 +500,11 @@ namespace MWWorld
}
mTerrainPreloadPositions = positions;
mTerrainPreloadItem = new TerrainPreloadItem(mTerrainViews, mTerrain, positions);
mWorkQueue->addWorkItem(mTerrainPreloadItem);
if (!positions.empty())
{
mTerrainPreloadItem = new TerrainPreloadItem(mTerrainViews, mTerrain, positions);
mWorkQueue->addWorkItem(mTerrainPreloadItem);
}
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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 model = getModel(ptr, 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
ptr.getClass().insertObjectRendering(ptr, model, rendering);
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::updateObjectRotation(const Ptr& ptr, RotationOrder order)
void Scene::removeFromPagedRefs(const Ptr &ptr)
{
::updateObjectRotation(ptr, *mPhysics, mRendering, order);
const ESM::RefNum& refnum = ptr.getCellRef().getRefNum();
if (refnum.hasContentFile() && mPagedRefs.erase(refnum))
{
if (!ptr.getRefData().getBaseNode()) return;
ptr.getClass().insertObjectRendering(ptr, getModel(ptr, mRendering.getResourceSystem()->getVFS()), mRendering);
setNodeRotation(ptr, mRendering, RotationOrder::direct);
reloadTerrain();
}
}
void Scene::updateObjectPosition(const Ptr &ptr, const osg::Vec3f &pos, bool movePhysics)
{
mRendering.moveObject(ptr, pos);
if (movePhysics)
{
mPhysics->updatePosition(ptr);
}
}
void Scene::updateObjectRotation(const Ptr &ptr, RotationOrder order)
{
setNodeRotation(ptr, mRendering, order);
mPhysics->updateRotation(ptr);
}
void Scene::updateObjectScale(const Ptr &ptr)
{
::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