mirror of
				https://github.com/TES3MP/openmw-tes3mp.git
				synced 2025-10-31 20:56:42 +00:00 
			
		
		
		
	Add OpenMW commits up to 22 Jun 2020
# Conflicts: # .travis.yml # CMakeLists.txt
This commit is contained in:
		
						commit
						3b2eb6f62c
					
				
					 180 changed files with 3144 additions and 1629 deletions
				
			
		|  | @ -5,7 +5,7 @@ Debian: | |||
|   tags: | ||||
|     - docker | ||||
|     - linux | ||||
|   image: gcc | ||||
|   image: debian:bullseye | ||||
|   cache: | ||||
|     key: apt-cache | ||||
|     paths: | ||||
|  | @ -13,7 +13,7 @@ Debian: | |||
|   before_script: | ||||
|     - export APT_CACHE_DIR=`pwd`/apt-cache && mkdir -pv $APT_CACHE_DIR | ||||
|     - apt-get update -yq  | ||||
|     - apt-get -o dir::cache::archives="$APT_CACHE_DIR" install -y cmake libboost-filesystem-dev libboost-program-options-dev libboost-system-dev libboost-iostreams-dev libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev libsdl2-dev libqt4-dev libopenal-dev libopenscenegraph-3.4-dev libunshield-dev libtinyxml-dev libmygui-dev libbullet-dev | ||||
|     - apt-get -o dir::cache::archives="$APT_CACHE_DIR" install -y cmake build-essential libboost-filesystem-dev libboost-program-options-dev libboost-system-dev libboost-iostreams-dev libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev libsdl2-dev libqt5opengl5-dev libopenal-dev libopenscenegraph-dev libunshield-dev libtinyxml-dev libmygui-dev libbullet-dev | ||||
|   stage: build | ||||
|   script:  | ||||
|     - cores_to_use=$((`nproc`-2)); if (( $cores_to_use < 1 )); then cores_to_use=1; fi | ||||
|  |  | |||
|  | @ -1,6 +1,7 @@ | |||
| 0.47.0 | ||||
| ------ | ||||
| 
 | ||||
|     Bug #1662: Qt4 and Windows binaries crash if there's a non-ASCII character in a file path/config path | ||||
|     Bug #1952: Incorrect particle lighting | ||||
|     Bug #2311: Targeted scripts are not properly supported on non-unique RefIDs | ||||
|     Bug #3676: NiParticleColorModifier isn't applied properly | ||||
|  | @ -28,8 +29,10 @@ | |||
|     Bug #5441: Enemies can't push a player character when in critical strike stance | ||||
|     Bug #5451: Magic projectiles don't disappear with the caster | ||||
|     Bug #5452: Autowalk is being included in savegames | ||||
|     Feature #5297: Add a search function to the "Datafiles" tab of the OpenMW launcher | ||||
|     Feature #5362: Show the soul gems' trapped soul in count dialog | ||||
|     Feature #5445: Handle NiLines | ||||
|     Task #5480: Drop Qt4 support | ||||
| 
 | ||||
| 0.46.0 | ||||
| ------ | ||||
|  | @ -245,6 +248,7 @@ | |||
|     Bug #5350: An attempt to launch magic bolt causes "AL error invalid value" error | ||||
|     Bug #5352: Light source items' duration is decremented while they aren't visible | ||||
|     Feature #1724: Handle AvoidNode | ||||
|     Feature #2159: "Graying out" exhausted dialogue topics | ||||
|     Feature #2229: Improve pathfinding AI | ||||
|     Feature #3025: Analogue gamepad movement controls | ||||
|     Feature #3442: Default values for fallbacks from ini file | ||||
|  |  | |||
|  | @ -1,11 +1,13 @@ | |||
| & "${env:COMSPEC}" /c ActivateMSVC.bat "&&" set | ForEach-Object { | ||||
|     $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 { | ||||
|  |  | |||
|  | @ -38,7 +38,6 @@ ${ANALYZE} cmake .. \ | |||
|     -DBUILD_MASTER=ON \ | ||||
|     -DBUILD_UNITTESTS=1 \ | ||||
|     -DUSE_SYSTEM_TINYXML=1 \ | ||||
|     -DDESIRED_QT_VERSION=5 \ | ||||
|     -DCMAKE_INSTALL_PREFIX=/usr \ | ||||
|     -DBINDIR=/usr/games \ | ||||
|     -DCMAKE_BUILD_TYPE="None" \ | ||||
|  |  | |||
|  | @ -161,7 +161,7 @@ Options: | |||
| 		Build unit tests / Google test | ||||
| 	-u | ||||
| 		Configure for unity builds. | ||||
| 	-v <2013/2015/2017/2019> | ||||
| 	-v <2017/2019> | ||||
| 		Choose the Visual Studio version to use. | ||||
| 	-n | ||||
| 		Produce NMake makefiles instead of a Visual Studio solution. Cannout be used with -N. | ||||
|  | @ -213,8 +213,8 @@ run_cmd() { | |||
| 	shift | ||||
| 
 | ||||
| 	if [ -z $VERBOSE ]; then | ||||
| 		eval $CMD $@ > output.log 2>&1 | ||||
| 		RET=$? | ||||
| 		RET=0 | ||||
| 		eval $CMD $@ > output.log 2>&1 || RET=$? | ||||
| 
 | ||||
| 		if [ $RET -ne 0 ]; then | ||||
| 			if [ -z $APPVEYOR ]; then | ||||
|  | @ -230,8 +230,9 @@ run_cmd() { | |||
| 
 | ||||
| 		return $RET | ||||
| 	else | ||||
| 		eval $CMD $@ | ||||
| 		return $? | ||||
| 		RET=0 | ||||
| 		eval $CMD $@ || RET=$? | ||||
| 		return $RET | ||||
| 	fi | ||||
| } | ||||
| 
 | ||||
|  | @ -256,15 +257,16 @@ download() { | |||
| 			printf "  Downloading $FILE... " | ||||
| 
 | ||||
| 			if [ -z $VERBOSE ]; then | ||||
| 				curl --silent --retry 10 -kLy 5 -o $FILE $URL | ||||
| 				RET=$? | ||||
| 				RET=0 | ||||
| 				curl --silent --retry 10 -kLy 5 -o $FILE $URL || RET=$? | ||||
| 			else | ||||
| 				curl --retry 10 -kLy 5 -o $FILE $URL | ||||
| 				RET=$? | ||||
| 				RET=0 | ||||
| 				curl --retry 10 -kLy 5 -o $FILE $URL || RET=$? | ||||
| 			fi | ||||
| 
 | ||||
| 			if [ $RET -ne 0 ]; then | ||||
| 				echo "Failed!" | ||||
| 				wrappedExit $RET | ||||
| 			else | ||||
| 				echo "Done." | ||||
| 			fi | ||||
|  | @ -346,21 +348,13 @@ case $VS_VERSION in | |||
| 		;; | ||||
| 
 | ||||
| 	14|14.0|2015 ) | ||||
| 		GENERATOR="Visual Studio 14 2015" | ||||
| 		TOOLSET="vc140" | ||||
| 		MSVC_REAL_VER="14" | ||||
| 		MSVC_VER="14.0" | ||||
| 		MSVC_YEAR="2015" | ||||
| 		MSVC_REAL_YEAR="2015" | ||||
| 		MSVC_DISPLAY_YEAR="2015" | ||||
| 		BOOST_VER="1.67.0" | ||||
| 		BOOST_VER_URL="1_67_0" | ||||
| 		BOOST_VER_SDK="106700" | ||||
| 		echo "Visual Studio 2015 is no longer supported" | ||||
| 		wrappedExit 1 | ||||
| 		;; | ||||
| 
 | ||||
| 	12|12.0|2013 ) | ||||
| 		echo "Visual Studio 2013 is no longer supported" | ||||
| 		exit 1 | ||||
| 		wrappedExit 1 | ||||
| 		;; | ||||
| esac | ||||
| 
 | ||||
|  | @ -505,11 +499,6 @@ if [ -z $SKIP_DOWNLOAD ]; then | |||
| 
 | ||||
| 	# Qt | ||||
| 	if [ -z $APPVEYOR ]; then | ||||
| 		if [ "${MSVC_REAL_YEAR}" = "2015" ] && [ "${BITS}" = "32" ]; then | ||||
| 			echo "Qt no longer provides MSVC2015 Win32 packages, switch to 64-bit or a newer Visual Studio. Sorry." | ||||
| 			exit 1 | ||||
| 		fi | ||||
| 
 | ||||
| 		download "AQt installer" \ | ||||
| 			"https://files.pythonhosted.org/packages/f3/bb/aee972f08deecca31bfc46b5aedfad1ce6c7f3aaf1288d685e4a914b53ac/aqtinstall-0.8-py2.py3-none-any.whl" \ | ||||
| 			"aqtinstall-0.8-py2.py3-none-any.whl" | ||||
|  | @ -604,14 +593,8 @@ fi | |||
| 		# Appveyor has all the boost we need already | ||||
| 		BOOST_SDK="c:/Libraries/boost_${BOOST_VER_URL}" | ||||
| 
 | ||||
| 		if [ $MSVC_REAL_VER -ge 15 ]; then | ||||
| 			LIB_SUFFIX="1" | ||||
| 		else | ||||
| 			LIB_SUFFIX="0" | ||||
| 		fi | ||||
| 
 | ||||
| 		add_cmake_opts -DBOOST_ROOT="$BOOST_SDK" \ | ||||
| 			-DBOOST_LIBRARYDIR="${BOOST_SDK}/lib${BITS}-msvc-${MSVC_VER}.${LIB_SUFFIX}" | ||||
| 			-DBOOST_LIBRARYDIR="${BOOST_SDK}/lib${BITS}-msvc-${MSVC_VER}.1" | ||||
| 		add_cmake_opts -DBoost_COMPILER="-${TOOLSET}" | ||||
| 
 | ||||
| 		echo Done. | ||||
|  | @ -793,8 +776,7 @@ fi | |||
| 		fi | ||||
| 
 | ||||
| 		cd $QT_SDK | ||||
| 		add_cmake_opts -DDESIRED_QT_VERSION=5 \ | ||||
| 			-DQT_QMAKE_EXECUTABLE="${QT_SDK}/bin/qmake.exe" \ | ||||
| 		add_cmake_opts -DQT_QMAKE_EXECUTABLE="${QT_SDK}/bin/qmake.exe" \ | ||||
| 			-DCMAKE_PREFIX_PATH="$QT_SDK" | ||||
| 		if [ $CONFIGURATION == "Debug" ]; then | ||||
| 			SUFFIX="d" | ||||
|  | @ -806,8 +788,7 @@ fi | |||
| 		echo Done. | ||||
| 	else | ||||
| 		QT_SDK="C:/Qt/5.13/msvc2017${SUFFIX}" | ||||
| 		add_cmake_opts -DDESIRED_QT_VERSION=5 \ | ||||
| 			-DQT_QMAKE_EXECUTABLE="${QT_SDK}/bin/qmake.exe" \ | ||||
| 		add_cmake_opts -DQT_QMAKE_EXECUTABLE="${QT_SDK}/bin/qmake.exe" \ | ||||
| 			-DCMAKE_PREFIX_PATH="$QT_SDK" | ||||
| 		if [ $CONFIGURATION == "Debug" ]; then | ||||
| 			SUFFIX="d" | ||||
|  | @ -957,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 | ||||
|  |  | |||
|  | @ -17,7 +17,6 @@ cmake \ | |||
| -D CMAKE_OSX_SYSROOT="macosx10.14" \ | ||||
| -D CMAKE_BUILD_TYPE=Release \ | ||||
| -D OPENMW_OSX_DEPLOYMENT=TRUE \ | ||||
| -D DESIRED_QT_VERSION=5 \ | ||||
| -D BUILD_ESMTOOL=FALSE \ | ||||
| -G"Unix Makefiles" \ | ||||
| .. | ||||
|  |  | |||
|  | @ -22,11 +22,6 @@ else() | |||
|    set(USE_QT TRUE) | ||||
| endif() | ||||
| 
 | ||||
| if (USE_QT) | ||||
|     set(DESIRED_QT_VERSION 4 CACHE STRING "The QT version OpenMW should use (4 or 5)") | ||||
|     set_property(CACHE DESIRED_QT_VERSION PROPERTY STRINGS 4 5) | ||||
| endif() | ||||
| 
 | ||||
| # set the minimum required version across the board | ||||
| cmake_minimum_required(VERSION 3.1.0) | ||||
| 
 | ||||
|  | @ -159,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 () | ||||
|  |  | |||
|  | @ -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) | ||||
|  |  | |||
|  | @ -1,13 +1,9 @@ | |||
| #include "graphicspage.hpp" | ||||
| 
 | ||||
| #include <csignal> | ||||
| #include <QDesktopWidget> | ||||
| #include <QMessageBox> | ||||
| #include <QDir> | ||||
| 
 | ||||
| #if QT_VERSION >= QT_VERSION_CHECK(5,0,0) | ||||
| #include <QScreen> | ||||
| #endif | ||||
| 
 | ||||
| #ifdef MAC_OS_X_VERSION_MIN_REQUIRED | ||||
| #undef MAC_OS_X_VERSION_MIN_REQUIRED | ||||
|  | @ -55,13 +51,11 @@ Launcher::GraphicsPage::GraphicsPage(Files::ConfigurationManager &cfg, Settings: | |||
| 
 | ||||
| bool Launcher::GraphicsPage::setupSDL() | ||||
| { | ||||
| #if QT_VERSION >= QT_VERSION_CHECK(5,0,0) | ||||
|     bool sdlConnectSuccessful = initSDL(); | ||||
|     if (!sdlConnectSuccessful) | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
| #endif | ||||
| 
 | ||||
|     int displays = SDL_GetNumVideoDisplays(); | ||||
| 
 | ||||
|  | @ -82,10 +76,8 @@ bool Launcher::GraphicsPage::setupSDL() | |||
|         screenComboBox->addItem(QString(tr("Screen ")) + QString::number(i + 1)); | ||||
|     } | ||||
| 
 | ||||
| #if QT_VERSION >= QT_VERSION_CHECK(5,0,0) | ||||
|     // Disconnect from SDL processes
 | ||||
|     quitSDL(); | ||||
| #endif | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
|  | @ -145,6 +137,10 @@ bool Launcher::GraphicsPage::loadSettings() | |||
|     if (mEngineSettings.getBool("enable indoor shadows", "Shadows")) | ||||
|         indoorShadowsCheckBox->setCheckState(Qt::Checked); | ||||
| 
 | ||||
|     shadowComputeSceneBoundsComboBox->setCurrentIndex( | ||||
|         shadowComputeSceneBoundsComboBox->findText( | ||||
|             QString(tr(mEngineSettings.getString("compute scene bounds", "Shadows").c_str())))); | ||||
| 
 | ||||
|     int shadowDistLimit = mEngineSettings.getInt("maximum shadow map distance", "Shadows"); | ||||
|     if (shadowDistLimit > 0) | ||||
|     { | ||||
|  | @ -231,7 +227,7 @@ void Launcher::GraphicsPage::saveSettings() | |||
|     bool cPlayerShadows = playerShadowsCheckBox->checkState(); | ||||
|     if (cActorShadows || cObjectShadows || cTerrainShadows || cPlayerShadows) | ||||
|     { | ||||
|         if (mEngineSettings.getBool("enable shadows", "Shadows") != true) | ||||
|         if (!mEngineSettings.getBool("enable shadows", "Shadows")) | ||||
|             mEngineSettings.setBool("enable shadows", "Shadows", true); | ||||
|         if (mEngineSettings.getBool("actor shadows", "Shadows") != cActorShadows) | ||||
|             mEngineSettings.setBool("actor shadows", "Shadows", cActorShadows); | ||||
|  | @ -263,6 +259,10 @@ void Launcher::GraphicsPage::saveSettings() | |||
|     int cShadowRes = shadowResolutionComboBox->currentText().toInt(); | ||||
|     if (cShadowRes != mEngineSettings.getInt("shadow map resolution", "Shadows")) | ||||
|         mEngineSettings.setInt("shadow map resolution", "Shadows", cShadowRes); | ||||
| 
 | ||||
|     auto cComputeSceneBounds = shadowComputeSceneBoundsComboBox->currentText().toStdString(); | ||||
|     if (cComputeSceneBounds != mEngineSettings.getString("compute scene bounds", "Shadows")) | ||||
|         mEngineSettings.setString("compute scene bounds", "Shadows", cComputeSceneBounds); | ||||
| } | ||||
| 
 | ||||
| QStringList Launcher::GraphicsPage::getAvailableResolutions(int screen) | ||||
|  | @ -316,7 +316,6 @@ QRect Launcher::GraphicsPage::getMaximumResolution() | |||
| { | ||||
|     QRect max; | ||||
| 
 | ||||
| #if QT_VERSION >= QT_VERSION_CHECK(5,0,0) | ||||
|     for (QScreen* screen : QGuiApplication::screens()) | ||||
|     { | ||||
|         QRect res = screen->geometry(); | ||||
|  | @ -325,17 +324,6 @@ QRect Launcher::GraphicsPage::getMaximumResolution() | |||
|         if (res.height() > max.height()) | ||||
|             max.setHeight(res.height()); | ||||
|     } | ||||
| #else | ||||
|     int screens = QApplication::desktop()->screenCount(); | ||||
|     for (int i = 0; i < screens; ++i) | ||||
|     { | ||||
|         QRect res = QApplication::desktop()->screenGeometry(i); | ||||
|         if (res.width() > max.width()) | ||||
|             max.setWidth(res.width()); | ||||
|         if (res.height() > max.height()) | ||||
|             max.setHeight(res.height()); | ||||
|     } | ||||
| #endif | ||||
|     return max; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -38,8 +38,8 @@ namespace Launcher | |||
|         Files::ConfigurationManager &mCfgMgr; | ||||
|         Settings::Manager &mEngineSettings; | ||||
| 
 | ||||
|         QStringList getAvailableResolutions(int screen); | ||||
|         QRect getMaximumResolution(); | ||||
|         static QStringList getAvailableResolutions(int screen); | ||||
|         static QRect getMaximumResolution(); | ||||
| 
 | ||||
|         bool setupSDL(); | ||||
|     }; | ||||
|  |  | |||
|  | @ -13,18 +13,11 @@ | |||
| #endif // MAC_OS_X_VERSION_MIN_REQUIRED
 | ||||
| 
 | ||||
| #include "maindialog.hpp" | ||||
| #include "sdlinit.hpp" | ||||
| 
 | ||||
| int main(int argc, char *argv[]) | ||||
| { | ||||
|     try | ||||
|     { | ||||
| // Note: we should init SDL2 before Qt4 to avoid crashes on Linux,
 | ||||
| // but we should init SDL2 after Qt5 to avoid input issues on MacOS X.
 | ||||
| #if QT_VERSION < QT_VERSION_CHECK(5,0,0) | ||||
|         initSDL(); | ||||
| #endif | ||||
| 
 | ||||
|         QApplication app(argc, argv); | ||||
| 
 | ||||
|         // Internationalization 
 | ||||
|  | @ -50,11 +43,6 @@ int main(int argc, char *argv[]) | |||
| 
 | ||||
|         int exitCode = app.exec(); | ||||
| 
 | ||||
| #if QT_VERSION < QT_VERSION_CHECK(5,0,0) | ||||
|         // Disconnect from SDL processes
 | ||||
|         quitSDL(); | ||||
| #endif | ||||
| 
 | ||||
|         return exitCode; | ||||
|     } | ||||
|     catch (std::exception& e) | ||||
|  |  | |||
|  | @ -149,16 +149,9 @@ if(WIN32) | |||
|     set(QT_USE_QTMAIN TRUE) | ||||
| endif(WIN32) | ||||
| 
 | ||||
| if (DESIRED_QT_VERSION MATCHES 4) | ||||
|     include(${QT_USE_FILE}) | ||||
|     qt4_wrap_ui(OPENCS_UI_HDR ${OPENCS_UI}) | ||||
|     qt4_wrap_cpp(OPENCS_MOC_SRC ${OPENCS_HDR_QT}) | ||||
|     qt4_add_resources(OPENCS_RES_SRC ${OPENCS_RES}) | ||||
| else() | ||||
|     qt5_wrap_ui(OPENCS_UI_HDR ${OPENCS_UI}) | ||||
|     qt5_wrap_cpp(OPENCS_MOC_SRC ${OPENCS_HDR_QT}) | ||||
|     qt5_add_resources(OPENCS_RES_SRC ${OPENCS_RES}) | ||||
| endif() | ||||
| 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}) | ||||
|  |  | |||
|  | @ -685,7 +685,6 @@ namespace CSMPrefs | |||
|         std::make_pair((int)Qt::Key_ContrastAdjust         , "ContrastAdjust"), | ||||
|         std::make_pair((int)Qt::Key_LaunchG                , "LaunchG"), | ||||
|         std::make_pair((int)Qt::Key_LaunchH                , "LaunchH"), | ||||
| #if QT_VERSION >= QT_VERSION_CHECK(5,7,0) | ||||
|         std::make_pair((int)Qt::Key_TouchpadToggle         , "TouchpadToggle"), | ||||
|         std::make_pair((int)Qt::Key_TouchpadOn             , "TouchpadOn"), | ||||
|         std::make_pair((int)Qt::Key_TouchpadOff            , "TouchpadOff"), | ||||
|  | @ -706,7 +705,6 @@ namespace CSMPrefs | |||
|         std::make_pair((int)Qt::Key_Find                   , "Find"), | ||||
|         std::make_pair((int)Qt::Key_Undo                   , "Undo"), | ||||
|         std::make_pair((int)Qt::Key_Redo                   , "Redo"), | ||||
| #endif | ||||
|         std::make_pair((int)Qt::Key_AltGr                  , "AltGr"), | ||||
|         std::make_pair((int)Qt::Key_Multi_key              , "Multi_key"), | ||||
|         std::make_pair((int)Qt::Key_Kanji                  , "Kanji"), | ||||
|  | @ -770,9 +768,7 @@ namespace CSMPrefs | |||
|         std::make_pair((int)Qt::Key_Sleep                  , "Sleep"), | ||||
|         std::make_pair((int)Qt::Key_Play                   , "Play"), | ||||
|         std::make_pair((int)Qt::Key_Zoom                   , "Zoom"), | ||||
| #if QT_VERSION >= QT_VERSION_CHECK(5,7,0) | ||||
|         std::make_pair((int)Qt::Key_Exit                   , "Exit"), | ||||
| #endif | ||||
|         std::make_pair((int)Qt::Key_Context1               , "Context1"), | ||||
|         std::make_pair((int)Qt::Key_Context2               , "Context2"), | ||||
|         std::make_pair((int)Qt::Key_Context3               , "Context3"), | ||||
|  |  | |||
|  | @ -5,10 +5,7 @@ | |||
| #include <QVBoxLayout> | ||||
| #include <QDialogButtonBox> | ||||
| #include <QPushButton> | ||||
| 
 | ||||
| #if QT_VERSION >= QT_VERSION_CHECK(5,0,0) | ||||
| #include <QScreen> | ||||
| #endif | ||||
| 
 | ||||
| #include "filewidget.hpp" | ||||
| #include "adjusterwidget.hpp" | ||||
|  | @ -50,11 +47,7 @@ CSVDoc::NewGameDialogue::NewGameDialogue() | |||
|     connect (mFileWidget, SIGNAL (nameChanged (const QString&, bool)), | ||||
|         mAdjusterWidget, SLOT (setName (const QString&, bool))); | ||||
| 
 | ||||
| #if QT_VERSION >= QT_VERSION_CHECK(5,0,0) | ||||
|     QRect scr = QGuiApplication::primaryScreen()->geometry(); | ||||
| #else | ||||
|     QRect scr = QApplication::desktop()->screenGeometry(); | ||||
| #endif | ||||
|     QRect rect = geometry(); | ||||
|     move (scr.center().x() - rect.center().x(), scr.center().y() - rect.center().y()); | ||||
| } | ||||
|  |  | |||
|  | @ -9,10 +9,7 @@ | |||
| #include <QLabel> | ||||
| #include <QIcon> | ||||
| #include <QPushButton> | ||||
| 
 | ||||
| #if QT_VERSION >= QT_VERSION_CHECK(5,0,0) | ||||
| #include <QScreen> | ||||
| #endif | ||||
| 
 | ||||
| QPushButton *CSVDoc::StartupDialogue::addButton (const QString& label, const QIcon& icon) | ||||
| { | ||||
|  | @ -123,12 +120,7 @@ CSVDoc::StartupDialogue::StartupDialogue() : mWidth (0), mColumn (2) | |||
| 
 | ||||
|     setLayout (layout); | ||||
| 
 | ||||
| #if QT_VERSION >= QT_VERSION_CHECK(5,0,0) | ||||
|     QRect scr = QGuiApplication::primaryScreen()->geometry(); | ||||
| #else | ||||
|     QRect scr = QApplication::desktop()->screenGeometry(); | ||||
| #endif | ||||
| 
 | ||||
|     QRect rect = geometry(); | ||||
|     move (scr.center().x() - rect.center().x(), scr.center().y() - rect.center().y()); | ||||
| } | ||||
|  |  | |||
|  | @ -14,10 +14,7 @@ | |||
| #include <QHBoxLayout> | ||||
| #include <QDesktopWidget> | ||||
| #include <QScrollBar> | ||||
| 
 | ||||
| #if QT_VERSION >= QT_VERSION_CHECK(5,0,0) | ||||
| #include <QScreen> | ||||
| #endif | ||||
| 
 | ||||
| #include "../../model/doc/document.hpp" | ||||
| #include "../../model/prefs/state.hpp" | ||||
|  | @ -1071,11 +1068,7 @@ void CSVDoc::View::updateWidth(bool isGrowLimit, int minSubViewWidth) | |||
|     if (isGrowLimit) | ||||
|         rect = dw->screenGeometry(this); | ||||
|     else | ||||
| #if QT_VERSION >= QT_VERSION_CHECK(5,0,0) | ||||
|         rect = QGuiApplication::screens().at(dw->screenNumber(this))->geometry(); | ||||
| #else | ||||
|         rect = dw->screenGeometry(dw->screen(dw->screenNumber(this))); | ||||
| #endif | ||||
| 
 | ||||
|     if (!mScrollbarOnly && mScroll && mSubViews.size() > 1) | ||||
|     { | ||||
|  |  | |||
|  | @ -1,4 +1,3 @@ | |||
| 
 | ||||
| #include "dialogue.hpp" | ||||
| 
 | ||||
| #include <QApplication> | ||||
|  | @ -7,10 +6,7 @@ | |||
| #include <QListWidget> | ||||
| #include <QStackedWidget> | ||||
| #include <QListWidgetItem> | ||||
| 
 | ||||
| #if QT_VERSION >= QT_VERSION_CHECK(5,0,0) | ||||
| #include <QScreen> | ||||
| #endif | ||||
| 
 | ||||
| #include <components/debug/debuglog.hpp> | ||||
| 
 | ||||
|  | @ -39,11 +35,7 @@ void CSVPrefs::Dialogue::buildCategorySelector (QSplitter *main) | |||
|     { | ||||
|         QString label = QString::fromUtf8 (iter->second.getKey().c_str()); | ||||
| 
 | ||||
| #if QT_VERSION >= QT_VERSION_CHECK(5,11,0) | ||||
|         maxWidth = std::max (maxWidth, metrics.horizontalAdvance (label)); | ||||
| #else | ||||
|         maxWidth = std::max (maxWidth, metrics.width (label)); | ||||
| #endif | ||||
| 
 | ||||
|         list->addItem (label); | ||||
|     } | ||||
|  | @ -116,11 +108,7 @@ void CSVPrefs::Dialogue::show() | |||
|     } | ||||
|     else | ||||
|     { | ||||
| #if QT_VERSION >= QT_VERSION_CHECK(5,0,0) | ||||
|         QRect scr = QGuiApplication::primaryScreen()->geometry(); | ||||
| #else | ||||
|         QRect scr = QApplication::desktop()->screenGeometry(); | ||||
| #endif | ||||
| 
 | ||||
|         // otherwise place at the centre of the screen
 | ||||
|         QPoint screenCenter = scr.center(); | ||||
|  |  | |||
|  | @ -143,14 +143,9 @@ void RenderWidget::toggleRenderStats() | |||
| CompositeViewer::CompositeViewer() | ||||
|     : mSimulationTime(0.0) | ||||
| { | ||||
| #if QT_VERSION >= 0x050000 | ||||
|     // Qt5 is currently crashing and reporting "Cannot make QOpenGLContext current in a different thread" when the viewer is run multi-threaded, this is regression from Qt4
 | ||||
|     osgViewer::ViewerBase::ThreadingModel threadingModel = osgViewer::ViewerBase::SingleThreaded; | ||||
| #else | ||||
|     osgViewer::ViewerBase::ThreadingModel threadingModel = osgViewer::ViewerBase::DrawThreadPerContext; | ||||
| #endif | ||||
| 
 | ||||
|     setThreadingModel(threadingModel); | ||||
|     // TODO: Upgrade osgQt to support osgViewer::ViewerBase::DrawThreadPerContext
 | ||||
|     // https://gitlab.com/OpenMW/openmw/-/issues/5481
 | ||||
|     setThreadingModel(osgViewer::ViewerBase::SingleThreaded); | ||||
| 
 | ||||
| #if OSG_VERSION_GREATER_OR_EQUAL(3,5,5) | ||||
|     setUseConfigureAffinity(false); | ||||
|  |  | |||
|  | @ -646,13 +646,8 @@ void CSVRender::WorldspaceWidget::mouseMoveEvent (QMouseEvent *event) | |||
| 
 | ||||
|         if (mDragging) | ||||
|         { | ||||
| #if QT_VERSION >= QT_VERSION_CHECK(5,0,0) | ||||
|             mDragX = event->localPos().x(); | ||||
|             mDragY = height() - event->localPos().y(); | ||||
| #else | ||||
|             mDragX = event->posF().x(); | ||||
|             mDragY = height() - event->posF().y(); | ||||
| #endif | ||||
|         } | ||||
|     } | ||||
|     else | ||||
|  |  | |||
|  | @ -144,11 +144,7 @@ CSVTools::ReportTable::ReportTable (CSMDoc::Document& document, | |||
| : CSVWorld::DragRecordTable (document, parent), mModel (document.getReport (id)), | ||||
|   mRefreshAction (0), mRefreshState (refreshState) | ||||
| { | ||||
| #if QT_VERSION >= QT_VERSION_CHECK(5,0,0) | ||||
|     horizontalHeader()->setSectionResizeMode (QHeaderView::Interactive); | ||||
| #else | ||||
|     horizontalHeader()->setResizeMode (QHeaderView::Interactive); | ||||
| #endif | ||||
|     horizontalHeader()->setStretchLastSection (true); | ||||
|     verticalHeader()->hide(); | ||||
|     setSortingEnabled (true); | ||||
|  |  | |||
|  | @ -5,10 +5,7 @@ | |||
| #include <QDesktopWidget> | ||||
| #include <QPainter> | ||||
| #include <QShowEvent> | ||||
| 
 | ||||
| #if QT_VERSION >= QT_VERSION_CHECK(5,0,0) | ||||
| #include <QScreen> | ||||
| #endif | ||||
| 
 | ||||
| #include "colorpickerpopup.hpp" | ||||
| 
 | ||||
|  | @ -99,11 +96,7 @@ QPoint CSVWidget::ColorEditor::calculatePopupPosition() | |||
| { | ||||
|     QRect editorGeometry = geometry(); | ||||
|     QRect popupGeometry = mColorPicker->geometry(); | ||||
| #if QT_VERSION >= QT_VERSION_CHECK(5,0,0) | ||||
|     QRect screenGeometry = QGuiApplication::primaryScreen()->geometry(); | ||||
| #else | ||||
|     QRect screenGeometry = QApplication::desktop()->screenGeometry(); | ||||
| #endif | ||||
| 
 | ||||
|     // Center the popup horizontally relative to the editor
 | ||||
|     int localPopupX = (editorGeometry.width() - popupGeometry.width()) / 2; | ||||
|  |  | |||
|  | @ -64,13 +64,8 @@ CSVWidget::SceneToolRun::SceneToolRun (SceneToolbar *parent, const QString& tool | |||
|     mTable->setShowGrid (false); | ||||
|     mTable->verticalHeader()->hide(); | ||||
|     mTable->horizontalHeader()->hide(); | ||||
| #if QT_VERSION >= QT_VERSION_CHECK(5,0,0) | ||||
|     mTable->horizontalHeader()->setSectionResizeMode (0, QHeaderView::Stretch); | ||||
|     mTable->horizontalHeader()->setSectionResizeMode (1, QHeaderView::ResizeToContents); | ||||
| #else | ||||
|     mTable->horizontalHeader()->setResizeMode (0, QHeaderView::Stretch); | ||||
|     mTable->horizontalHeader()->setResizeMode (1, QHeaderView::ResizeToContents); | ||||
| #endif | ||||
|     mTable->setSelectionMode (QAbstractItemView::NoSelection); | ||||
| 
 | ||||
|     layout->addWidget (mTable); | ||||
|  |  | |||
|  | @ -180,13 +180,8 @@ CSVWidget::SceneToolShapeBrush::SceneToolShapeBrush (SceneToolbar *parent, const | |||
|     mTable->setShowGrid (true); | ||||
|     mTable->verticalHeader()->hide(); | ||||
|     mTable->horizontalHeader()->hide(); | ||||
| #if QT_VERSION >= QT_VERSION_CHECK(5,0,0) | ||||
|     mTable->horizontalHeader()->setSectionResizeMode (0, QHeaderView::Stretch); | ||||
|     mTable->horizontalHeader()->setSectionResizeMode (1, QHeaderView::Stretch); | ||||
| #else | ||||
|     mTable->horizontalHeader()->setResizeMode (0, QHeaderView::Stretch); | ||||
|     mTable->horizontalHeader()->setResizeMode (1, QHeaderView::Stretch); | ||||
| #endif | ||||
|     mTable->setSelectionMode (QAbstractItemView::NoSelection); | ||||
| 
 | ||||
|     layout->addWidget (mTable); | ||||
|  |  | |||
|  | @ -243,13 +243,8 @@ CSVWidget::SceneToolTextureBrush::SceneToolTextureBrush (SceneToolbar *parent, c | |||
|     mTable->setShowGrid (true); | ||||
|     mTable->verticalHeader()->hide(); | ||||
|     mTable->horizontalHeader()->hide(); | ||||
| #if QT_VERSION >= QT_VERSION_CHECK(5,0,0) | ||||
|     mTable->horizontalHeader()->setSectionResizeMode (0, QHeaderView::Stretch); | ||||
|     mTable->horizontalHeader()->setSectionResizeMode (1, QHeaderView::Stretch); | ||||
| #else | ||||
|     mTable->horizontalHeader()->setResizeMode (0, QHeaderView::Stretch); | ||||
|     mTable->horizontalHeader()->setResizeMode (1, QHeaderView::Stretch); | ||||
| #endif | ||||
|     mTable->setSelectionMode (QAbstractItemView::NoSelection); | ||||
| 
 | ||||
|     layout->addWidget (mTable); | ||||
|  |  | |||
|  | @ -110,11 +110,7 @@ void CSVWorld::EnumDelegate::paint (QPainter *painter, const QStyleOptionViewIte | |||
|     int valueIndex = getValueIndex(index); | ||||
|     if (valueIndex != -1) | ||||
|     { | ||||
| #if QT_VERSION >= QT_VERSION_CHECK(5,7,0) | ||||
|         QStyleOptionViewItem itemOption(option); | ||||
| #else | ||||
|         QStyleOptionViewItemV4 itemOption(option); | ||||
| #endif | ||||
|         itemOption.text = mValues.at(valueIndex).second; | ||||
|         QApplication::style()->drawControl(QStyle::CE_ItemViewItem, &itemOption, painter); | ||||
|     } | ||||
|  | @ -134,13 +130,7 @@ QSize CSVWorld::EnumDelegate::sizeHint(const QStyleOptionViewItem &option, const | |||
|         itemOption.state = option.state; | ||||
| 
 | ||||
|         const QString &valueText = mValues.at(valueIndex).second; | ||||
| 
 | ||||
| #if QT_VERSION >= QT_VERSION_CHECK(5,11,0) | ||||
|         QSize valueSize = QSize(itemOption.fontMetrics.horizontalAdvance(valueText), itemOption.fontMetrics.height()); | ||||
| #else | ||||
|         QSize valueSize = QSize(itemOption.fontMetrics.width(valueText), itemOption.fontMetrics.height()); | ||||
| #endif | ||||
| 
 | ||||
|         itemOption.currentText = valueText; | ||||
|         return QApplication::style()->sizeFromContents(QStyle::CT_ComboBox, &itemOption, valueSize); | ||||
|     } | ||||
|  |  | |||
|  | @ -33,11 +33,7 @@ CSVWorld::NestedTable::NestedTable(CSMDoc::Document& document, | |||
|     setSelectionBehavior (QAbstractItemView::SelectRows); | ||||
|     setSelectionMode (QAbstractItemView::ExtendedSelection); | ||||
| 
 | ||||
| #if QT_VERSION >= QT_VERSION_CHECK(5,0,0) | ||||
|     horizontalHeader()->setSectionResizeMode (QHeaderView::Interactive); | ||||
| #else | ||||
|     horizontalHeader()->setResizeMode (QHeaderView::Interactive); | ||||
| #endif | ||||
|     verticalHeader()->hide(); | ||||
| 
 | ||||
|     int columns = model->columnCount(QModelIndex()); | ||||
|  |  | |||
|  | @ -205,12 +205,7 @@ bool CSVWorld::ScriptEdit::stringNeedsQuote (const std::string& id) const | |||
| void CSVWorld::ScriptEdit::setTabWidth() | ||||
| { | ||||
|     // Set tab width to specified number of characters using current font.
 | ||||
| #if QT_VERSION >= QT_VERSION_CHECK(5,11,0) | ||||
|     setTabStopDistance(mTabCharCount * fontMetrics().horizontalAdvance(' ')); | ||||
| #else | ||||
|     setTabStopWidth(mTabCharCount * fontMetrics().width(' ')); | ||||
| #endif | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| void CSVWorld::ScriptEdit::wrapLines(bool wrap) | ||||
|  | @ -290,12 +285,7 @@ int CSVWorld::ScriptEdit::lineNumberAreaWidth() | |||
|         ++digits; | ||||
|     } | ||||
| 
 | ||||
| #if QT_VERSION >= QT_VERSION_CHECK(5,11,0) | ||||
|     int space = 3 + fontMetrics().horizontalAdvance(QLatin1Char('9')) * digits; | ||||
| #else | ||||
|     int space = 3 + fontMetrics().width(QLatin1Char('9')) * digits; | ||||
| #endif | ||||
| 
 | ||||
|     return space; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -83,13 +83,8 @@ CSVWorld::ScriptErrorTable::ScriptErrorTable (const CSMDoc::Document& document, | |||
|     QStringList headers; | ||||
|     headers << "Severity" << "Line" << "Description"; | ||||
|     setHorizontalHeaderLabels (headers); | ||||
| #if QT_VERSION >= QT_VERSION_CHECK(5,0,0) | ||||
|     horizontalHeader()->setSectionResizeMode (0, QHeaderView::ResizeToContents); | ||||
|     horizontalHeader()->setSectionResizeMode (1, QHeaderView::ResizeToContents); | ||||
| #else | ||||
|     horizontalHeader()->setResizeMode (0, QHeaderView::ResizeToContents); | ||||
|     horizontalHeader()->setResizeMode (1, QHeaderView::ResizeToContents); | ||||
| #endif | ||||
|     horizontalHeader()->setStretchLastSection (true); | ||||
|     verticalHeader()->hide(); | ||||
|     setColumnHidden (3, true); | ||||
|  |  | |||
|  | @ -262,11 +262,7 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, | |||
|     mDispatcher = new CSMWorld::CommandDispatcher (document, id, this); | ||||
| 
 | ||||
|     setModel (mProxyModel); | ||||
| #if QT_VERSION >= QT_VERSION_CHECK(5,0,0) | ||||
|     horizontalHeader()->setSectionResizeMode (QHeaderView::Interactive); | ||||
| #else | ||||
|     horizontalHeader()->setResizeMode (QHeaderView::Interactive); | ||||
| #endif | ||||
|     verticalHeader()->hide(); | ||||
|     setSelectionBehavior (QAbstractItemView::SelectRows); | ||||
|     setSelectionMode (QAbstractItemView::ExtendedSelection); | ||||
|  |  | |||
|  | @ -21,7 +21,7 @@ add_openmw_dir (mwrender | |||
|     actors objects renderingmanager animation rotatecontroller sky npcanimation vismask | ||||
|     creatureanimation effectmanager util renderinginterface pathgrid rendermode weaponanimation | ||||
|     bulletdebugdraw globalmap characterpreview camera localmap water terrainstorage ripplesimulation | ||||
|     renderbin actoranimation landmanager navmesh actorspaths recastmesh fogmanager | ||||
|     renderbin actoranimation landmanager navmesh actorspaths recastmesh fogmanager objectpaging | ||||
|     ) | ||||
| 
 | ||||
| add_openmw_dir (mwinput | ||||
|  | @ -86,7 +86,7 @@ add_openmw_dir (mwmechanics | |||
|     drawstate spells activespells npcstats aipackage aisequence aipursue alchemy aiwander aitravel aifollow aiavoiddoor aibreathe | ||||
|     aicast aiescort aiface aiactivate aicombat recharge repair enchanting pathfinding pathgrid security spellcasting spellresistance | ||||
|     disease pickpocket levelledlist combat steering obstacle autocalcspell difficultyscaling aicombataction actor summoning | ||||
|     character actors objects aistate coordinateconverter trading weaponpriority spellpriority weapontype spellutil tickableeffects | ||||
|     character actors objects aistate trading weaponpriority spellpriority weapontype spellutil tickableeffects | ||||
|     spellabsorption linkedeffects | ||||
|     ) | ||||
| 
 | ||||
|  |  | |||
|  | @ -53,6 +53,8 @@ namespace MWBase | |||
| 
 | ||||
|             virtual bool startDialogue (const MWWorld::Ptr& actor, ResponseCallback* callback) = 0; | ||||
| 
 | ||||
|             virtual bool inJournal (const std::string& topicId, const std::string& infoId) = 0; | ||||
| 
 | ||||
|             virtual void addTopic (const std::string& topic) = 0; | ||||
| 
 | ||||
|             /*
 | ||||
|  | @ -79,7 +81,14 @@ namespace MWBase | |||
|             virtual void goodbyeSelected() = 0; | ||||
|             virtual void questionAnswered (int answer, ResponseCallback* callback) = 0; | ||||
| 
 | ||||
|             enum TopicType | ||||
|             { | ||||
|                 Specific = 1, | ||||
|                 Exhausted = 2 | ||||
|             }; | ||||
| 
 | ||||
|             virtual std::list<std::string> getAvailableTopics() = 0; | ||||
|             virtual int getTopicFlag(const std::string&) = 0; | ||||
| 
 | ||||
|             virtual bool checkServiceRefused (ResponseCallback* callback) = 0; | ||||
| 
 | ||||
|  |  | |||
|  | @ -695,23 +695,16 @@ namespace MWClass | |||
| 
 | ||||
|     float Creature::getSpeed(const MWWorld::Ptr &ptr) const | ||||
|     { | ||||
|         MWMechanics::CreatureStats& stats = getCreatureStats(ptr); | ||||
|         const MWMechanics::CreatureStats& stats = getCreatureStats(ptr); | ||||
| 
 | ||||
|         if (stats.isParalyzed() || stats.getKnockedDown() || stats.isDead()) | ||||
|             return 0.f; | ||||
| 
 | ||||
|         const GMST& gmst = getGmst(); | ||||
| 
 | ||||
|         float walkSpeed = gmst.fMinWalkSpeedCreature->mValue.getFloat() + 0.01f * stats.getAttribute(ESM::Attribute::Speed).getModified() | ||||
|                 * (gmst.fMaxWalkSpeedCreature->mValue.getFloat() - gmst.fMinWalkSpeedCreature->mValue.getFloat()); | ||||
| 
 | ||||
|         const MWBase::World *world = MWBase::Environment::get().getWorld(); | ||||
|         const MWMechanics::MagicEffects &mageffects = stats.getMagicEffects(); | ||||
| 
 | ||||
|         bool running = stats.getStance(MWMechanics::CreatureStats::Stance_Run); | ||||
| 
 | ||||
|         // The Run speed difference for creatures comes from the animation speed difference (see runStateToWalkState in character.cpp)
 | ||||
|         float runSpeed = walkSpeed; | ||||
| 
 | ||||
|         float moveSpeed; | ||||
| 
 | ||||
|         if(getEncumbrance(ptr) > getCapacity(ptr)) | ||||
|  | @ -728,19 +721,9 @@ namespace MWClass | |||
|             moveSpeed = flySpeed; | ||||
|         } | ||||
|         else if(world->isSwimming(ptr)) | ||||
|         { | ||||
|             float swimSpeed = walkSpeed; | ||||
|             if(running) | ||||
|                 swimSpeed = runSpeed; | ||||
|             swimSpeed *= 1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).getMagnitude(); | ||||
|             swimSpeed *= gmst.fSwimRunBase->mValue.getFloat() + 0.01f*getSkill(ptr, ESM::Skill::Athletics) * | ||||
|                                                     gmst.fSwimRunAthleticsMult->mValue.getFloat(); | ||||
|             moveSpeed = swimSpeed; | ||||
|         } | ||||
|         else if(running) | ||||
|             moveSpeed = runSpeed; | ||||
|             moveSpeed = getSwimSpeed(ptr); | ||||
|         else | ||||
|             moveSpeed = walkSpeed; | ||||
|             moveSpeed = getWalkSpeed(ptr); | ||||
|         if(getMovementSettings(ptr).mPosition[0] != 0 && getMovementSettings(ptr).mPosition[1] == 0) | ||||
|             moveSpeed *= 0.75f; | ||||
| 
 | ||||
|  | @ -1077,4 +1060,31 @@ namespace MWClass | |||
|     { | ||||
|         MWMechanics::setBaseAISetting<ESM::Creature>(id, setting, value); | ||||
|     } | ||||
| 
 | ||||
|     float Creature::getWalkSpeed(const MWWorld::Ptr& ptr) const | ||||
|     { | ||||
|         const MWMechanics::CreatureStats& stats = getCreatureStats(ptr); | ||||
|         const GMST& gmst = getGmst(); | ||||
| 
 | ||||
|         return gmst.fMinWalkSpeedCreature->mValue.getFloat() | ||||
|                 + 0.01f * stats.getAttribute(ESM::Attribute::Speed).getModified() | ||||
|                 * (gmst.fMaxWalkSpeedCreature->mValue.getFloat() - gmst.fMinWalkSpeedCreature->mValue.getFloat()); | ||||
|     } | ||||
| 
 | ||||
|     float Creature::getRunSpeed(const MWWorld::Ptr& ptr) const | ||||
|     { | ||||
|         return getWalkSpeed(ptr); | ||||
|     } | ||||
| 
 | ||||
|     float Creature::getSwimSpeed(const MWWorld::Ptr& ptr) const | ||||
|     { | ||||
|         const MWMechanics::CreatureStats& stats = getCreatureStats(ptr); | ||||
|         const GMST& gmst = getGmst(); | ||||
|         const MWMechanics::MagicEffects& mageffects = stats.getMagicEffects(); | ||||
| 
 | ||||
|         return getWalkSpeed(ptr) | ||||
|             * (1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).getMagnitude()) | ||||
|             * (gmst.fSwimRunBase->mValue.getFloat() | ||||
|                + 0.01f * getSkill(ptr, ESM::Skill::Athletics) * gmst.fSwimRunAthleticsMult->mValue.getFloat()); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -141,6 +141,12 @@ namespace MWClass | |||
|             /// @param rendering Indicates if the scale to adjust is for the rendering mesh, or for the collision mesh
 | ||||
| 
 | ||||
|             virtual void setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const; | ||||
| 
 | ||||
|             float getWalkSpeed(const MWWorld::Ptr& ptr) const final; | ||||
| 
 | ||||
|             float getRunSpeed(const MWWorld::Ptr& ptr) const final; | ||||
| 
 | ||||
|             float getSwimSpeed(const MWWorld::Ptr& ptr) const final; | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1170,16 +1170,6 @@ namespace MWClass | |||
|         bool inair = !world->isOnGround(ptr) && !swimming && !world->isFlying(ptr); | ||||
|         running = running && (inair || MWBase::Environment::get().getMechanicsManager()->isRunning(ptr)); | ||||
| 
 | ||||
|         float walkSpeed = gmst.fMinWalkSpeed->mValue.getFloat() + 0.01f*npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified()* | ||||
|                                                       (gmst.fMaxWalkSpeed->mValue.getFloat() - gmst.fMinWalkSpeed->mValue.getFloat()); | ||||
|         walkSpeed *= 1.0f - gmst.fEncumberedMoveEffect->mValue.getFloat()*normalizedEncumbrance; | ||||
|         walkSpeed = std::max(0.0f, walkSpeed); | ||||
|         if(sneaking) | ||||
|             walkSpeed *= gmst.fSneakSpeedMultiplier->mValue.getFloat(); | ||||
| 
 | ||||
|         float runSpeed = walkSpeed*(0.01f * getSkill(ptr, ESM::Skill::Athletics) * | ||||
|                                     gmst.fAthleticsRunBonus->mValue.getFloat() + gmst.fBaseRunMultiplier->mValue.getFloat()); | ||||
| 
 | ||||
|         float moveSpeed; | ||||
|         if(getEncumbrance(ptr) > getCapacity(ptr)) | ||||
|             moveSpeed = 0.0f; | ||||
|  | @ -1194,19 +1184,11 @@ namespace MWClass | |||
|             moveSpeed = flySpeed; | ||||
|         } | ||||
|         else if (swimming) | ||||
|         { | ||||
|             float swimSpeed = walkSpeed; | ||||
|             if(running) | ||||
|                 swimSpeed = runSpeed; | ||||
|             swimSpeed *= 1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).getMagnitude(); | ||||
|             swimSpeed *= gmst.fSwimRunBase->mValue.getFloat() + 0.01f*getSkill(ptr, ESM::Skill::Athletics)* | ||||
|                                                     gmst.fSwimRunAthleticsMult->mValue.getFloat(); | ||||
|             moveSpeed = swimSpeed; | ||||
|         } | ||||
|             moveSpeed = getSwimSpeed(ptr); | ||||
|         else if (running && !sneaking) | ||||
|             moveSpeed = runSpeed; | ||||
|             moveSpeed = getRunSpeed(ptr); | ||||
|         else | ||||
|             moveSpeed = walkSpeed; | ||||
|             moveSpeed = getWalkSpeed(ptr); | ||||
|         if(getMovementSettings(ptr).mPosition[0] != 0 && getMovementSettings(ptr).mPosition[1] == 0) | ||||
|             moveSpeed *= 0.75f; | ||||
| 
 | ||||
|  | @ -1671,4 +1653,56 @@ namespace MWClass | |||
|     { | ||||
|         MWMechanics::setBaseAISetting<ESM::NPC>(id, setting, value); | ||||
|     } | ||||
| 
 | ||||
|     float Npc::getWalkSpeed(const MWWorld::Ptr& ptr) const | ||||
|     { | ||||
|         const GMST& gmst = getGmst(); | ||||
|         const NpcCustomData* npcdata = static_cast<const NpcCustomData*>(ptr.getRefData().getCustomData()); | ||||
|         const float normalizedEncumbrance = getNormalizedEncumbrance(ptr); | ||||
|         const bool sneaking = MWBase::Environment::get().getMechanicsManager()->isSneaking(ptr); | ||||
| 
 | ||||
|         float walkSpeed = gmst.fMinWalkSpeed->mValue.getFloat() | ||||
|                 + 0.01f * npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified() | ||||
|                 * (gmst.fMaxWalkSpeed->mValue.getFloat() - gmst.fMinWalkSpeed->mValue.getFloat()); | ||||
|         walkSpeed *= 1.0f - gmst.fEncumberedMoveEffect->mValue.getFloat()*normalizedEncumbrance; | ||||
|         walkSpeed = std::max(0.0f, walkSpeed); | ||||
|         if(sneaking) | ||||
|             walkSpeed *= gmst.fSneakSpeedMultiplier->mValue.getFloat(); | ||||
| 
 | ||||
|         return walkSpeed; | ||||
|     } | ||||
| 
 | ||||
|     float Npc::getRunSpeed(const MWWorld::Ptr& ptr) const | ||||
|     { | ||||
|         const GMST& gmst = getGmst(); | ||||
|         return getWalkSpeed(ptr) | ||||
|                 * (0.01f * getSkill(ptr, ESM::Skill::Athletics) * gmst.fAthleticsRunBonus->mValue.getFloat() | ||||
|                    + gmst.fBaseRunMultiplier->mValue.getFloat()); | ||||
|     } | ||||
| 
 | ||||
|     float Npc::getSwimSpeed(const MWWorld::Ptr& ptr) const | ||||
|     { | ||||
|         const GMST& gmst = getGmst(); | ||||
|         const MWBase::World* world = MWBase::Environment::get().getWorld(); | ||||
|         const MWMechanics::CreatureStats& stats = getCreatureStats(ptr); | ||||
|         const NpcCustomData* npcdata = static_cast<const NpcCustomData*>(ptr.getRefData().getCustomData()); | ||||
|         const MWMechanics::MagicEffects& mageffects = npcdata->mNpcStats.getMagicEffects(); | ||||
|         const bool swimming = world->isSwimming(ptr); | ||||
|         const bool inair = !world->isOnGround(ptr) && !swimming && !world->isFlying(ptr); | ||||
|         const bool running = stats.getStance(MWMechanics::CreatureStats::Stance_Run) | ||||
|                 && (inair || MWBase::Environment::get().getMechanicsManager()->isRunning(ptr)); | ||||
| 
 | ||||
|         float swimSpeed; | ||||
| 
 | ||||
|         if (running) | ||||
|             swimSpeed = getRunSpeed(ptr); | ||||
|         else | ||||
|             swimSpeed = getWalkSpeed(ptr); | ||||
| 
 | ||||
|         swimSpeed *= 1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).getMagnitude(); | ||||
|         swimSpeed *= gmst.fSwimRunBase->mValue.getFloat() | ||||
|                 + 0.01f * getSkill(ptr, ESM::Skill::Athletics) * gmst.fSwimRunAthleticsMult->mValue.getFloat(); | ||||
| 
 | ||||
|         return swimSpeed; | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -176,6 +176,12 @@ namespace MWClass | |||
|             virtual int getPrimaryFactionRank(const MWWorld::ConstPtr &ptr) const; | ||||
| 
 | ||||
|             virtual void setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const; | ||||
| 
 | ||||
|             float getWalkSpeed(const MWWorld::Ptr& ptr) const final; | ||||
| 
 | ||||
|             float getRunSpeed(const MWWorld::Ptr& ptr) const final; | ||||
| 
 | ||||
|             float getSwimSpeed(const MWWorld::Ptr& ptr) const final; | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -278,6 +278,30 @@ namespace MWDialogue | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     bool DialogueManager::inJournal (const std::string& topicId, const std::string& infoId) | ||||
|     { | ||||
|         const MWDialogue::Topic *topicHistory = nullptr; | ||||
|         MWBase::Journal *journal = MWBase::Environment::get().getJournal(); | ||||
|         for (auto it = journal->topicBegin(); it != journal->topicEnd(); ++it) | ||||
|         { | ||||
|             if (it->first == topicId) | ||||
|             { | ||||
|                 topicHistory = &it->second; | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if (!topicHistory) | ||||
|             return false; | ||||
| 
 | ||||
|         for(const auto& topic : *topicHistory) | ||||
|         { | ||||
|             if (topic.mInfoId == infoId) | ||||
|                 return true; | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     void DialogueManager::executeTopic (const std::string& topic, ResponseCallback* callback) | ||||
|     { | ||||
|         Filter filter (mActor, mChoice, mTalkedTo); | ||||
|  | @ -350,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) | ||||
|  |  | |||
|  | @ -5,6 +5,7 @@ | |||
| 
 | ||||
| #include <map> | ||||
| #include <set> | ||||
| #include <unordered_map> | ||||
| 
 | ||||
| #include <components/compiler/streamerrorhandler.hpp> | ||||
| #include <components/translation/translation.hpp> | ||||
|  | @ -30,6 +31,7 @@ namespace MWDialogue | |||
|             ModFactionReactionMap mChangedFactionReaction; | ||||
| 
 | ||||
|             std::set<std::string, Misc::StringUtils::CiComp> mActorKnownTopics; | ||||
|             std::unordered_map<std::string, int> mActorKnownTopicsFlag; | ||||
| 
 | ||||
|             Translation::Storage& mTranslationDataStorage; | ||||
|             MWScript::CompilerContext mCompilerContext; | ||||
|  | @ -71,6 +73,9 @@ namespace MWDialogue | |||
|             virtual bool startDialogue (const MWWorld::Ptr& actor, ResponseCallback* callback); | ||||
| 
 | ||||
|             std::list<std::string> getAvailableTopics(); | ||||
|             int getTopicFlag(const std::string& topicId) final; | ||||
| 
 | ||||
|             bool inJournal (const std::string& topicId, const std::string& infoId) final; | ||||
| 
 | ||||
|             virtual void addTopic (const std::string& topic); | ||||
| 
 | ||||
|  |  | |||
|  | @ -681,15 +681,3 @@ std::vector<const ESM::DialInfo *> MWDialogue::Filter::list (const ESM::Dialogue | |||
| 
 | ||||
|     return infos; | ||||
| } | ||||
| 
 | ||||
| bool MWDialogue::Filter::responseAvailable (const ESM::Dialogue& dialogue) const | ||||
| { | ||||
|     for (ESM::Dialogue::InfoContainer::const_iterator iter = dialogue.mInfo.begin(); | ||||
|         iter!=dialogue.mInfo.end(); ++iter) | ||||
|     { | ||||
|         if (testActor (*iter) && testPlayer (*iter) && testSelectStructs (*iter)) | ||||
|             return true; | ||||
|     } | ||||
| 
 | ||||
|     return false; | ||||
| } | ||||
|  |  | |||
|  | @ -66,9 +66,6 @@ namespace MWDialogue | |||
|             const ESM::DialInfo* search (const ESM::Dialogue& dialogue, const bool fallbackToInfoRefusal) const; | ||||
|             ///< Get a matching response for the requested dialogue.
 | ||||
|             ///  Redirect to "Info Refusal" topic if a response fulfills all conditions but disposition.
 | ||||
| 
 | ||||
|             bool responseAvailable (const ESM::Dialogue& dialogue) const; | ||||
|             ///< Does a matching response exist? (disposition is ignored for this check)
 | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -351,6 +351,7 @@ namespace MWGui | |||
| 
 | ||||
|         mTopicsList->adjustSize(); | ||||
|         updateHistory(); | ||||
|         updateTopicFormat(); | ||||
|         mCurrentWindowSize = _sender->getSize(); | ||||
|     } | ||||
| 
 | ||||
|  | @ -460,7 +461,6 @@ namespace MWGui | |||
|         setTitle(mPtr.getClass().getName(mPtr)); | ||||
| 
 | ||||
|         updateTopics(); | ||||
|         updateTopicsPane(); // force update for new services
 | ||||
| 
 | ||||
|         updateDisposition(); | ||||
|         restock(); | ||||
|  | @ -519,8 +519,6 @@ namespace MWGui | |||
|             return; | ||||
|         mIsCompanion = isCompanion(); | ||||
|         mKeywords = keyWords; | ||||
| 
 | ||||
|         updateTopicsPane(); | ||||
|     } | ||||
| 
 | ||||
|     void DialogueWindow::updateTopicsPane() | ||||
|  | @ -570,15 +568,16 @@ namespace MWGui | |||
|             mTopicsList->addSeparator(); | ||||
| 
 | ||||
| 
 | ||||
|         for(std::string& keyword : mKeywords) | ||||
|         for(const auto& keyword : mKeywords) | ||||
|         { | ||||
|             std::string topicId = Misc::StringUtils::lowerCase(keyword); | ||||
|             mTopicsList->addItem(keyword); | ||||
| 
 | ||||
|             Topic* t = new Topic(keyword); | ||||
|             t->eventTopicActivated += MyGUI::newDelegate(this, &DialogueWindow::onTopicActivated); | ||||
|             mTopicLinks[Misc::StringUtils::lowerCase(keyword)] = t; | ||||
|             mTopicLinks[topicId] = t; | ||||
| 
 | ||||
|             mKeywordSearch.seed(Misc::StringUtils::lowerCase(keyword), intptr_t(t)); | ||||
|             mKeywordSearch.seed(topicId, intptr_t(t)); | ||||
|         } | ||||
|         mTopicsList->adjustSize(); | ||||
| 
 | ||||
|  | @ -764,9 +763,28 @@ namespace MWGui | |||
|             updateHistory(); | ||||
|     } | ||||
| 
 | ||||
|     void DialogueWindow::updateTopicFormat() | ||||
|     { | ||||
|         std::string specialColour = Settings::Manager::getString("color topic specific", "GUI"); | ||||
|         std::string oldColour = Settings::Manager::getString("color topic exhausted", "GUI"); | ||||
| 
 | ||||
|         for (const std::string& keyword : mKeywords) | ||||
|         { | ||||
|             int flag = MWBase::Environment::get().getDialogueManager()->getTopicFlag(keyword); | ||||
|             MyGUI::Button* button = mTopicsList->getItemWidget(keyword); | ||||
| 
 | ||||
|             if (!specialColour.empty() && flag & MWBase::DialogueManager::TopicType::Specific) | ||||
|                 button->getSubWidgetText()->setTextColour(MyGUI::Colour::parse(specialColour)); | ||||
|             else if (!oldColour.empty() && flag & MWBase::DialogueManager::TopicType::Exhausted) | ||||
|                 button->getSubWidgetText()->setTextColour(MyGUI::Colour::parse(oldColour)); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void DialogueWindow::updateTopics() | ||||
|     { | ||||
|         setKeywords(MWBase::Environment::get().getDialogueManager()->getAvailableTopics()); | ||||
|         updateTopicsPane(); | ||||
|         updateTopicFormat(); | ||||
|     } | ||||
| 
 | ||||
|     bool DialogueWindow::isCompanion() | ||||
|  |  | |||
|  | @ -186,6 +186,8 @@ namespace MWGui | |||
| 
 | ||||
|         std::unique_ptr<ResponseCallback> mCallback; | ||||
|         std::unique_ptr<ResponseCallback> mGreetingCallback; | ||||
| 
 | ||||
|         void updateTopicFormat(); | ||||
|     }; | ||||
| } | ||||
| #endif | ||||
|  |  | |||
|  | @ -196,9 +196,7 @@ namespace MWGui | |||
| 
 | ||||
|         getWidget(mCrosshair, "Crosshair"); | ||||
| 
 | ||||
|         int mapSize = std::max(1, Settings::Manager::getInt("local map hud widget size", "Map")); | ||||
|         int cellDistance = std::max(1, Settings::Manager::getInt("local map cell distance", "Map")); | ||||
|         LocalMapBase::init(mMinimap, mCompass, mapSize, cellDistance); | ||||
|         LocalMapBase::init(mMinimap, mCompass); | ||||
| 
 | ||||
|         mMainWidget->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onWorldClicked); | ||||
|         mMainWidget->eventMouseMove += MyGUI::newDelegate(this, &HUD::onWorldMouseOver); | ||||
|  |  | |||
|  | @ -16,6 +16,7 @@ | |||
| #include <components/myguiplatform/myguitexture.hpp> | ||||
| #include <components/settings/settings.hpp> | ||||
| #include <components/vfs/manager.hpp> | ||||
| #include <components/resource/resourcesystem.hpp> | ||||
| 
 | ||||
| #include "../mwbase/environment.hpp" | ||||
| #include "../mwbase/statemanager.hpp" | ||||
|  | @ -29,9 +30,9 @@ | |||
| namespace MWGui | ||||
| { | ||||
| 
 | ||||
|     LoadingScreen::LoadingScreen(const VFS::Manager* vfs, osgViewer::Viewer* viewer) | ||||
|     LoadingScreen::LoadingScreen(Resource::ResourceSystem* resourceSystem, osgViewer::Viewer* viewer) | ||||
|         : WindowBase("openmw_loading_screen.layout") | ||||
|         , mVFS(vfs) | ||||
|         , mResourceSystem(resourceSystem) | ||||
|         , mViewer(viewer) | ||||
|         , mTargetFrameRate(120.0) | ||||
|         , mLastWallpaperChangeTime(0.0) | ||||
|  | @ -39,6 +40,7 @@ namespace MWGui | |||
|         , mLoadingOnTime(0.0) | ||||
|         , mImportantLabel(false) | ||||
|         , mVisible(false) | ||||
|         , mNestedLoadingCount(0) | ||||
|         , mProgress(0) | ||||
|         , mShowWallpaper(true) | ||||
|     { | ||||
|  | @ -64,9 +66,9 @@ namespace MWGui | |||
| 
 | ||||
|     void LoadingScreen::findSplashScreens() | ||||
|     { | ||||
|         const std::map<std::string, VFS::File*>& index = mVFS->getIndex(); | ||||
|         const std::map<std::string, VFS::File*>& index = mResourceSystem->getVFS()->getIndex(); | ||||
|         std::string pattern = "Splash/"; | ||||
|         mVFS->normalizeFilename(pattern); | ||||
|         mResourceSystem->getVFS()->normalizeFilename(pattern); | ||||
| 
 | ||||
|         /* priority given to the left */ | ||||
|         const std::array<std::string, 7> supported_extensions {{".tga", ".dds", ".ktx", ".png", ".bmp", ".jpeg", ".jpg"}}; | ||||
|  | @ -162,15 +164,21 @@ namespace MWGui | |||
| 
 | ||||
|     void LoadingScreen::loadingOn(bool visible) | ||||
|     { | ||||
|         mLoadingOnTime = mTimer.time_m(); | ||||
|         // Early-out if already on
 | ||||
|         if (mMainWidget->getVisible()) | ||||
|         if (mNestedLoadingCount++ > 0 && mMainWidget->getVisible()) | ||||
|             return; | ||||
| 
 | ||||
|         mLoadingOnTime = mTimer.time_m(); | ||||
| 
 | ||||
|         // Assign dummy bounding sphere callback to avoid the bounding sphere of the entire scene being recomputed after each frame of loading
 | ||||
|         // We are already using node masks to avoid the scene from being updated/rendered, but node masks don't work for computeBound()
 | ||||
|         mViewer->getSceneData()->setComputeBoundingSphereCallback(new DontComputeBoundCallback); | ||||
| 
 | ||||
|         if (const osgUtil::IncrementalCompileOperation* ico = mViewer->getIncrementalCompileOperation()) { | ||||
|             mOldIcoMin = ico->getMinimumTimeAvailableForGLCompileAndDeletePerFrame(); | ||||
|             mOldIcoMax = ico->getMaximumNumOfObjectsToCompilePerFrame(); | ||||
|         } | ||||
| 
 | ||||
|         mVisible = visible; | ||||
|         mLoadingBox->setVisible(mVisible); | ||||
|         setVisible(true); | ||||
|  | @ -194,6 +202,8 @@ namespace MWGui | |||
| 
 | ||||
|     void LoadingScreen::loadingOff() | ||||
|     { | ||||
|         if (--mNestedLoadingCount > 0) | ||||
|             return; | ||||
|         mLoadingBox->setVisible(true);   // restore
 | ||||
| 
 | ||||
|         if (mLastRenderTime < mLoadingOnTime) | ||||
|  | @ -215,6 +225,12 @@ namespace MWGui | |||
|         //std::cout << "loading took " << mTimer.time_m() - mLoadingOnTime << std::endl;
 | ||||
|         setVisible(false); | ||||
| 
 | ||||
|         if (osgUtil::IncrementalCompileOperation* ico = mViewer->getIncrementalCompileOperation()) | ||||
|         { | ||||
|             ico->setMinimumTimeAvailableForGLCompileAndDeletePerFrame(mOldIcoMin); | ||||
|             ico->setMaximumNumOfObjectsToCompilePerFrame(mOldIcoMax); | ||||
|         } | ||||
| 
 | ||||
|         MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Loading); | ||||
|         MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_LoadingWallpaper); | ||||
|     } | ||||
|  | @ -336,7 +352,13 @@ namespace MWGui | |||
| 
 | ||||
|         MWBase::Environment::get().getInputManager()->update(0, true, true); | ||||
| 
 | ||||
|         //osg::Timer timer;
 | ||||
|         mResourceSystem->reportStats(mViewer->getFrameStamp()->getFrameNumber(), mViewer->getViewerStats()); | ||||
|         if (osgUtil::IncrementalCompileOperation* ico = mViewer->getIncrementalCompileOperation()) | ||||
|         { | ||||
|             ico->setMinimumTimeAvailableForGLCompileAndDeletePerFrame(1.f/getTargetFrameRate()); | ||||
|             ico->setMaximumNumOfObjectsToCompilePerFrame(1000); | ||||
|         } | ||||
| 
 | ||||
|         // at the time this function is called we are in the middle of a frame,
 | ||||
|         // so out of order calls are necessary to get a correct frameNumber for the next frame.
 | ||||
|         // refer to the advance() and frame() order in Engine::go()
 | ||||
|  | @ -344,10 +366,6 @@ namespace MWGui | |||
|         mViewer->updateTraversal(); | ||||
|         mViewer->renderingTraversals(); | ||||
|         mViewer->advance(mViewer->getFrameStamp()->getSimulationTime()); | ||||
|         //std::cout << "frame took " << timer.time_m() << std::endl;
 | ||||
| 
 | ||||
|         //if (mViewer->getIncrementalCompileOperation())
 | ||||
|             //std::cout << "num to compile " << mViewer->getIncrementalCompileOperation()->getToCompile().size() << std::endl;
 | ||||
| 
 | ||||
|         mLastRenderTime = mTimer.time_m(); | ||||
|     } | ||||
|  |  | |||
|  | @ -20,9 +20,9 @@ namespace osg | |||
|     class Texture2D; | ||||
| } | ||||
| 
 | ||||
| namespace VFS | ||||
| namespace Resource | ||||
| { | ||||
|     class Manager; | ||||
|     class ResourceSystem; | ||||
| } | ||||
| 
 | ||||
| namespace MWGui | ||||
|  | @ -32,7 +32,7 @@ namespace MWGui | |||
|     class LoadingScreen : public WindowBase, public Loading::Listener | ||||
|     { | ||||
|     public: | ||||
|         LoadingScreen(const VFS::Manager* vfs, osgViewer::Viewer* viewer); | ||||
|         LoadingScreen(Resource::ResourceSystem* resourceSystem, osgViewer::Viewer* viewer); | ||||
|         virtual ~LoadingScreen(); | ||||
| 
 | ||||
|         /// Overridden from Loading::Listener, see the Loading::Listener documentation for usage details
 | ||||
|  | @ -53,7 +53,7 @@ namespace MWGui | |||
| 
 | ||||
|         void setupCopyFramebufferToTextureCallback(); | ||||
| 
 | ||||
|         const VFS::Manager* mVFS; | ||||
|         Resource::ResourceSystem* mResourceSystem; | ||||
|         osg::ref_ptr<osgViewer::Viewer> mViewer; | ||||
| 
 | ||||
|         double mTargetFrameRate; | ||||
|  | @ -66,10 +66,13 @@ namespace MWGui | |||
|         bool mImportantLabel; | ||||
| 
 | ||||
|         bool mVisible; | ||||
|         int mNestedLoadingCount; | ||||
| 
 | ||||
|         size_t mProgress; | ||||
| 
 | ||||
|         bool mShowWallpaper; | ||||
|         float mOldIcoMin = 0.f; | ||||
|         unsigned int mOldIcoMax = 0; | ||||
| 
 | ||||
|         MyGUI::Widget* mLoadingBox; | ||||
| 
 | ||||
|  |  | |||
|  | @ -214,13 +214,13 @@ namespace MWGui | |||
|         */ | ||||
|     } | ||||
| 
 | ||||
|     void LocalMapBase::init(MyGUI::ScrollView* widget, MyGUI::ImageBox* compass, int mapWidgetSize, int cellDistance) | ||||
|     void LocalMapBase::init(MyGUI::ScrollView* widget, MyGUI::ImageBox* compass) | ||||
|     { | ||||
|         mLocalMap = widget; | ||||
|         mCompass = compass; | ||||
|         mMapWidgetSize = mapWidgetSize; | ||||
|         mCellDistance = cellDistance; | ||||
|         mNumCells = cellDistance * 2 + 1; | ||||
|         mMapWidgetSize = std::max(1, Settings::Manager::getInt("local map widget size", "Map")); | ||||
|         mCellDistance = Constants::CellGridRadius; | ||||
|         mNumCells = mCellDistance * 2 + 1; | ||||
| 
 | ||||
|         mLocalMap->setCanvasSize(mMapWidgetSize*mNumCells, mMapWidgetSize*mNumCells); | ||||
| 
 | ||||
|  | @ -756,9 +756,7 @@ namespace MWGui | |||
|         mEventBoxLocal->eventMouseButtonPressed += MyGUI::newDelegate(this, &MapWindow::onDragStart); | ||||
|         mEventBoxLocal->eventMouseButtonDoubleClick += MyGUI::newDelegate(this, &MapWindow::onMapDoubleClicked); | ||||
| 
 | ||||
|         int mapSize = std::max(1, Settings::Manager::getInt("local map widget size", "Map")); | ||||
|         int cellDistance = std::max(1, Settings::Manager::getInt("local map cell distance", "Map")); | ||||
|         LocalMapBase::init(mLocalMap, mPlayerArrowLocal, mapSize, cellDistance); | ||||
|         LocalMapBase::init(mLocalMap, mPlayerArrowLocal); | ||||
| 
 | ||||
|         mGlobalMap->setVisible(mGlobal); | ||||
|         mLocalMap->setVisible(!mGlobal); | ||||
|  |  | |||
|  | @ -94,7 +94,7 @@ namespace MWGui | |||
|     public: | ||||
|         LocalMapBase(CustomMarkerCollection& markers, MWRender::LocalMap* localMapRender, bool fogOfWarEnabled = true); | ||||
|         virtual ~LocalMapBase(); | ||||
|         void init(MyGUI::ScrollView* widget, MyGUI::ImageBox* compass, int mapWidgetSize, int cellDistance); | ||||
|         void init(MyGUI::ScrollView* widget, MyGUI::ImageBox* compass); | ||||
| 
 | ||||
|         void setCellPrefix(const std::string& prefix); | ||||
|         void setActiveCell(const int x, const int y, bool interior=false); | ||||
|  |  | |||
|  | @ -132,6 +132,13 @@ namespace MWGui | |||
|         MWBase::Environment::get().getWindowManager()->injectKeyRelease(MyGUI::KeyCode::None); | ||||
|     } | ||||
| 
 | ||||
|     void SaveGameDialog::onClose() | ||||
|     { | ||||
|         mSaveList->setIndexSelected(MyGUI::ITEM_NONE); | ||||
| 
 | ||||
|         WindowModal::onClose(); | ||||
|     } | ||||
| 
 | ||||
|     void SaveGameDialog::onOpen() | ||||
|     { | ||||
|         WindowModal::onOpen(); | ||||
|  |  | |||
|  | @ -20,6 +20,7 @@ namespace MWGui | |||
|         SaveGameDialog(); | ||||
| 
 | ||||
|         virtual void onOpen(); | ||||
|         virtual void onClose(); | ||||
| 
 | ||||
|         void setLoadOrSave(bool load); | ||||
| 
 | ||||
|  |  | |||
|  | @ -242,7 +242,7 @@ namespace MWGui | |||
|         mKeyboardNavigation->setEnabled(keyboardNav); | ||||
|         Gui::ImageButton::setDefaultNeedKeyFocus(keyboardNav); | ||||
| 
 | ||||
|         mLoadingScreen = new LoadingScreen(mResourceSystem->getVFS(), mViewer); | ||||
|         mLoadingScreen = new LoadingScreen(mResourceSystem, mViewer); | ||||
|         mWindows.push_back(mLoadingScreen); | ||||
| 
 | ||||
|         //set up the hardware cursor manager
 | ||||
|  |  | |||
|  | @ -36,6 +36,7 @@ namespace MWInput | |||
|         , mSneakToggleShortcutTimer(0.f) | ||||
|         , mGamepadZoom(0) | ||||
|         , mGamepadGuiCursorEnabled(true) | ||||
|         , mGuiCursorEnabled(true) | ||||
|         , mJoystickLastUsed(false) | ||||
|         , mSneakGamepadShortcut(false) | ||||
|         , mGamepadPreviewMode(false) | ||||
|  |  | |||
|  | @ -56,8 +56,8 @@ namespace MWInput | |||
|         float mSneakToggleShortcutTimer; | ||||
|         float mGamepadZoom; | ||||
|         bool mGamepadGuiCursorEnabled; | ||||
|         bool mJoystickLastUsed; | ||||
|         bool mGuiCursorEnabled; | ||||
|         bool mJoystickLastUsed; | ||||
|         bool mSneakGamepadShortcut; | ||||
|         bool mGamepadPreviewMode; | ||||
|     }; | ||||
|  |  | |||
|  | @ -134,7 +134,7 @@ void adjustCommandedActor (const MWWorld::Ptr& actor) | |||
|     auto it = stats.getAiSequence().begin(); | ||||
|     for (; it != stats.getAiSequence().end(); ++it) | ||||
|     { | ||||
|         if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdFollow && | ||||
|         if ((*it)->getTypeId() == MWMechanics::AiPackageTypeId::Follow && | ||||
|                 static_cast<const MWMechanics::AiFollow*>(it->get())->isCommanded()) | ||||
|         { | ||||
|             hasCommandPackage = true; | ||||
|  | @ -486,7 +486,7 @@ namespace MWMechanics | |||
|             return; | ||||
| 
 | ||||
|         const MWMechanics::AiSequence& seq = stats.getAiSequence(); | ||||
|         if (seq.isInCombat() || seq.hasPackage(AiPackage::TypeIdFollow) || seq.hasPackage(AiPackage::TypeIdEscort)) | ||||
|         if (seq.isInCombat() || seq.hasPackage(AiPackageTypeId::Follow) || seq.hasPackage(AiPackageTypeId::Escort)) | ||||
|             return; | ||||
| 
 | ||||
|         const osg::Vec3f playerPos(getPlayer().getRefData().getPosition().asVec3()); | ||||
|  | @ -530,11 +530,11 @@ namespace MWMechanics | |||
| 
 | ||||
|         CreatureStats &stats = actor.getClass().getCreatureStats(actor); | ||||
|         const MWMechanics::AiSequence& seq = stats.getAiSequence(); | ||||
|         int packageId = seq.getTypeId(); | ||||
|         const auto packageId = seq.getTypeId(); | ||||
| 
 | ||||
|         if (seq.isInCombat() || | ||||
|             MWBase::Environment::get().getWorld()->isSwimming(actor) || | ||||
|             (packageId != AiPackage::TypeIdWander && packageId != AiPackage::TypeIdTravel && packageId != -1)) | ||||
|             (packageId != AiPackageTypeId::Wander && packageId != AiPackageTypeId::Travel && packageId != AiPackageTypeId::None)) | ||||
|         { | ||||
|             actorState.setTurningToPlayer(false); | ||||
|             actorState.setGreetingTimer(0); | ||||
|  | @ -769,7 +769,7 @@ namespace MWMechanics | |||
|                     followerOrEscorter = true; | ||||
|                     break; | ||||
|                 } | ||||
|                 else if (package->getTypeId() != MWMechanics::AiPackage::TypeIdCombat) | ||||
|                 else if (package->getTypeId() != MWMechanics::AiPackageTypeId::Combat) | ||||
|                     break; | ||||
|             } | ||||
|             if (!followerOrEscorter) | ||||
|  | @ -1323,7 +1323,7 @@ namespace MWMechanics | |||
|         if (!isPlayer && stats.getTimeToStartDrowning() < fHoldBreathTime / 2) | ||||
|         { | ||||
|             AiSequence& seq = ptr.getClass().getCreatureStats(ptr).getAiSequence(); | ||||
|             if (seq.getTypeId() != AiPackage::TypeIdBreathe) //Only add it once
 | ||||
|             if (seq.getTypeId() != AiPackageTypeId::Breathe) //Only add it once
 | ||||
|                 seq.stack(AiBreathe(), ptr); | ||||
|         } | ||||
| 
 | ||||
|  | @ -1485,7 +1485,7 @@ namespace MWMechanics | |||
|             if (player.getClass().getNpcStats(player).isWerewolf()) | ||||
|                 return; | ||||
| 
 | ||||
|             if (ptr.getClass().isClass(ptr, "Guard") && creatureStats.getAiSequence().getTypeId() != AiPackage::TypeIdPursue && !creatureStats.getAiSequence().isInCombat() | ||||
|             if (ptr.getClass().isClass(ptr, "Guard") && creatureStats.getAiSequence().getTypeId() != AiPackageTypeId::Pursue && !creatureStats.getAiSequence().isInCombat() | ||||
|                 && creatureStats.getMagicEffects().get(ESM::MagicEffect::CalmHumanoid).getMagnitude() == 0) | ||||
|             { | ||||
|                 const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); | ||||
|  | @ -1945,7 +1945,7 @@ namespace MWMechanics | |||
|                             // 3. Player character does not use headtracking in the 1st-person view
 | ||||
|                             if (!stats.getKnockedDown() && | ||||
|                                 !stats.getAiSequence().isInCombat() && | ||||
|                                 !stats.getAiSequence().hasPackage(AiPackage::TypeIdPursue) && | ||||
|                                 !stats.getAiSequence().hasPackage(AiPackageTypeId::Pursue) && | ||||
|                                 !firstPersonPlayer) | ||||
|                             { | ||||
|                                 for(PtrActorMap::iterator it(mActors.begin()); it != mActors.end(); ++it) | ||||
|  | @ -2563,7 +2563,7 @@ namespace MWMechanics | |||
|                     } | ||||
|                     break; | ||||
|                 } | ||||
|                 else if (package->getTypeId() != AiPackage::TypeIdCombat && package->getTypeId() != AiPackage::TypeIdWander) | ||||
|                 else if (package->getTypeId() != AiPackageTypeId::Combat && package->getTypeId() != AiPackageTypeId::Wander) | ||||
|                     break; | ||||
|             } | ||||
|         } | ||||
|  | @ -2589,7 +2589,7 @@ namespace MWMechanics | |||
|             { | ||||
|                 if (package->followTargetThroughDoors() && package->getTarget() == actor) | ||||
|                     list.push_back(iteratedActor); | ||||
|                 else if (package->getTypeId() != AiPackage::TypeIdCombat && package->getTypeId() != AiPackage::TypeIdWander) | ||||
|                 else if (package->getTypeId() != AiPackageTypeId::Combat && package->getTypeId() != AiPackageTypeId::Wander) | ||||
|                     break; | ||||
|             } | ||||
|         } | ||||
|  | @ -2655,7 +2655,7 @@ namespace MWMechanics | |||
|                     list.push_back(static_cast<const AiFollow*>(package.get())->getFollowIndex()); | ||||
|                     break; | ||||
|                 } | ||||
|                 else if (package->getTypeId() != AiPackage::TypeIdCombat && package->getTypeId() != AiPackage::TypeIdWander) | ||||
|                 else if (package->getTypeId() != AiPackageTypeId::Combat && package->getTypeId() != AiPackageTypeId::Wander) | ||||
|                     break; | ||||
|             } | ||||
|         } | ||||
|  |  | |||
|  | @ -51,7 +51,7 @@ namespace MWMechanics | |||
| 
 | ||||
|             bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; | ||||
| 
 | ||||
|             static constexpr TypeId getTypeId() { return TypeIdActivate; } | ||||
|             static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::Activate; } | ||||
| 
 | ||||
|             void writeState(ESM::AiSequence::AiSequence& sequence) const final; | ||||
| 
 | ||||
|  |  | |||
|  | @ -63,7 +63,7 @@ bool MWMechanics::AiAvoidDoor::execute (const MWWorld::Ptr& actor, CharacterCont | |||
|     for(std::vector<MWWorld::Ptr>::iterator it = actors.begin(); it != actors.end(); ++it) { | ||||
|         if(*it != getPlayer()) { //Not the player
 | ||||
|             MWMechanics::AiSequence& seq = it->getClass().getCreatureStats(*it).getAiSequence(); | ||||
|             if(seq.getTypeId() != MWMechanics::AiPackage::TypeIdAvoidDoor) { //Only add it once
 | ||||
|             if(seq.getTypeId() != MWMechanics::AiPackageTypeId::AvoidDoor) { //Only add it once
 | ||||
|                 seq.stack(MWMechanics::AiAvoidDoor(mDoorPtr),*it); | ||||
|             } | ||||
|         } | ||||
|  |  | |||
|  | @ -24,7 +24,7 @@ namespace MWMechanics | |||
| 
 | ||||
|             bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; | ||||
| 
 | ||||
|             static constexpr TypeId getTypeId() { return TypeIdAvoidDoor; } | ||||
|             static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::AvoidDoor; } | ||||
| 
 | ||||
|             static constexpr Options makeDefaultOptions() | ||||
|             { | ||||
|  |  | |||
|  | @ -12,7 +12,7 @@ namespace MWMechanics | |||
|         public: | ||||
|             bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; | ||||
| 
 | ||||
|             static constexpr TypeId getTypeId() { return TypeIdBreathe; } | ||||
|             static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::Breathe; } | ||||
| 
 | ||||
|             static constexpr Options makeDefaultOptions() | ||||
|             { | ||||
|  |  | |||
|  | @ -17,7 +17,7 @@ namespace MWMechanics | |||
| 
 | ||||
|             bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; | ||||
| 
 | ||||
|             static constexpr TypeId getTypeId() { return TypeIdCast; } | ||||
|             static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::Cast; } | ||||
| 
 | ||||
|             MWWorld::Ptr getTarget() const final; | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,6 +1,7 @@ | |||
| #include "aicombat.hpp" | ||||
| 
 | ||||
| #include <components/misc/rng.hpp> | ||||
| #include <components/misc/coordinateconverter.hpp> | ||||
| 
 | ||||
| #include <components/esm/aisequence.hpp> | ||||
| 
 | ||||
|  | @ -36,7 +37,6 @@ | |||
| #include "movement.hpp" | ||||
| #include "character.hpp" | ||||
| #include "aicombataction.hpp" | ||||
| #include "coordinateconverter.hpp" | ||||
| #include "actorutil.hpp" | ||||
| 
 | ||||
| namespace | ||||
|  | @ -370,7 +370,7 @@ namespace MWMechanics | |||
|                         if (pathgrid && !actor.getClass().isPureWaterCreature(actor)) | ||||
|                         { | ||||
|                             ESM::Pathgrid::PointList points; | ||||
|                             CoordinateConverter coords(storage.mCell->getCell()); | ||||
|                             Misc::CoordinateConverter coords(storage.mCell->getCell()); | ||||
| 
 | ||||
|                             osg::Vec3f localPos = actor.getRefData().getPosition().asVec3(); | ||||
|                             coords.toLocal(localPos); | ||||
|  |  | |||
|  | @ -104,7 +104,7 @@ namespace MWMechanics | |||
| 
 | ||||
|             bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; | ||||
| 
 | ||||
|             static constexpr TypeId getTypeId() { return TypeIdCombat; } | ||||
|             static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::Combat; } | ||||
| 
 | ||||
|             static constexpr Options makeDefaultOptions() | ||||
|             { | ||||
|  |  | |||
|  | @ -32,7 +32,7 @@ namespace MWMechanics | |||
| 
 | ||||
|             bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; | ||||
| 
 | ||||
|             static constexpr TypeId getTypeId() { return TypeIdEscort; } | ||||
|             static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::Escort; } | ||||
| 
 | ||||
|             static constexpr Options makeDefaultOptions() | ||||
|             { | ||||
|  |  | |||
|  | @ -12,7 +12,7 @@ namespace MWMechanics | |||
| 
 | ||||
|             bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; | ||||
| 
 | ||||
|             static constexpr TypeId getTypeId() { return TypeIdFace; } | ||||
|             static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::Face; } | ||||
| 
 | ||||
|             static constexpr Options makeDefaultOptions() | ||||
|             { | ||||
|  |  | |||
|  | @ -55,7 +55,7 @@ namespace MWMechanics | |||
| 
 | ||||
|             bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; | ||||
| 
 | ||||
|             static constexpr TypeId getTypeId() { return TypeIdFollow; } | ||||
|             static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::Follow; } | ||||
| 
 | ||||
|             static constexpr Options makeDefaultOptions() | ||||
|             { | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ | |||
| #include <components/esm/loadland.hpp> | ||||
| #include <components/esm/loadmgef.hpp> | ||||
| #include <components/detournavigator/navigator.hpp> | ||||
| #include <components/misc/coordinateconverter.hpp> | ||||
| 
 | ||||
| #include "../mwbase/world.hpp" | ||||
| #include "../mwbase/environment.hpp" | ||||
|  | @ -20,11 +21,10 @@ | |||
| #include "movement.hpp" | ||||
| #include "steering.hpp" | ||||
| #include "actorutil.hpp" | ||||
| #include "coordinateconverter.hpp" | ||||
| 
 | ||||
| #include <osg/Quat> | ||||
| 
 | ||||
| MWMechanics::AiPackage::AiPackage(TypeId typeId, const Options& options) : | ||||
| MWMechanics::AiPackage::AiPackage(AiPackageTypeId typeId, const Options& options) : | ||||
|     mTypeId(typeId), | ||||
|     mOptions(options), | ||||
|     mTimer(AI_REACTION_TIME + 1.0f), // to force initial pathbuild
 | ||||
|  | @ -114,7 +114,7 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f& | |||
|             { | ||||
|                 const auto pathfindingHalfExtents = world->getPathfindingHalfExtents(actor); | ||||
|                 mPathFinder.buildPath(actor, position, dest, actor.getCell(), getPathGridGraph(actor.getCell()), | ||||
|                     pathfindingHalfExtents, getNavigatorFlags(actor)); | ||||
|                     pathfindingHalfExtents, getNavigatorFlags(actor), getAreaCosts(actor)); | ||||
|                 mRotateOnTheRunChecks = 3; | ||||
| 
 | ||||
|                 // give priority to go directly on target if there is minimal opportunity
 | ||||
|  | @ -216,7 +216,7 @@ namespace | |||
| void MWMechanics::AiPackage::openDoors(const MWWorld::Ptr& actor) | ||||
| { | ||||
|     // note: AiWander currently does not open doors
 | ||||
|     if (getTypeId() == TypeIdWander) | ||||
|     if (getTypeId() == AiPackageTypeId::Wander) | ||||
|         return; | ||||
| 
 | ||||
|     if (mPathFinder.getPathSize() == 0) | ||||
|  | @ -341,10 +341,9 @@ bool MWMechanics::AiPackage::isNearInactiveCell(osg::Vec3f position) | |||
|     if (playerCell->isExterior()) | ||||
|     { | ||||
|         // get actor's distance from origin of center cell
 | ||||
|         CoordinateConverter(playerCell).toLocal(position); | ||||
|         Misc::CoordinateConverter(playerCell).toLocal(position); | ||||
| 
 | ||||
|         // currently assumes 3 x 3 grid for exterior cells, with player at center cell.
 | ||||
|         // ToDo: (Maybe) use "exterior cell load distance" setting to get count of actual active cells
 | ||||
|         // AI shuts down actors before they reach edges of 3 x 3 grid.
 | ||||
|         const float distanceFromEdge = 200.0; | ||||
|         float minThreshold = (-1.0f * ESM::Land::REAL_SIZE) + distanceFromEdge; | ||||
|  | @ -391,14 +390,38 @@ DetourNavigator::Flags MWMechanics::AiPackage::getNavigatorFlags(const MWWorld:: | |||
|     const MWWorld::Class& actorClass = actor.getClass(); | ||||
|     DetourNavigator::Flags result = DetourNavigator::Flag_none; | ||||
| 
 | ||||
|     if (actorClass.isPureWaterCreature(actor) || (getTypeId() != TypeIdWander && actorClass.canSwim(actor))) | ||||
|     if (actorClass.isPureWaterCreature(actor) || (getTypeId() != AiPackageTypeId::Wander && actorClass.canSwim(actor))) | ||||
|         result |= DetourNavigator::Flag_swim; | ||||
| 
 | ||||
|     if (actorClass.canWalk(actor)) | ||||
|         result |= DetourNavigator::Flag_walk; | ||||
| 
 | ||||
|     if (actorClass.isBipedal(actor) && getTypeId() != TypeIdWander) | ||||
|     if (actorClass.isBipedal(actor) && getTypeId() != AiPackageTypeId::Wander) | ||||
|         result |= DetourNavigator::Flag_openDoor; | ||||
| 
 | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
| DetourNavigator::AreaCosts MWMechanics::AiPackage::getAreaCosts(const MWWorld::Ptr& actor) const | ||||
| { | ||||
|     DetourNavigator::AreaCosts costs; | ||||
|     const DetourNavigator::Flags flags = getNavigatorFlags(actor); | ||||
|     const MWWorld::Class& actorClass = actor.getClass(); | ||||
| 
 | ||||
|     if (flags & DetourNavigator::Flag_swim) | ||||
|         costs.mWater = costs.mWater / actorClass.getSwimSpeed(actor); | ||||
| 
 | ||||
|     if (flags & DetourNavigator::Flag_walk) | ||||
|     { | ||||
|         float walkCost; | ||||
|         if (getTypeId() == AiPackageTypeId::Wander) | ||||
|             walkCost = 1.0 / actorClass.getWalkSpeed(actor); | ||||
|         else | ||||
|             walkCost = 1.0 / actorClass.getRunSpeed(actor); | ||||
|         costs.mDoor = costs.mDoor * walkCost; | ||||
|         costs.mPathgrid = costs.mPathgrid * walkCost; | ||||
|         costs.mGround = costs.mGround * walkCost; | ||||
|     } | ||||
| 
 | ||||
|     return costs; | ||||
| } | ||||
|  |  | |||
|  | @ -4,10 +4,12 @@ | |||
| #include <memory> | ||||
| 
 | ||||
| #include <components/esm/defs.hpp> | ||||
| #include <components/detournavigator/areatype.hpp> | ||||
| 
 | ||||
| #include "pathfinding.hpp" | ||||
| #include "obstacle.hpp" | ||||
| #include "aistate.hpp" | ||||
| #include "aipackagetypeid.hpp" | ||||
| 
 | ||||
| namespace MWWorld | ||||
| { | ||||
|  | @ -35,26 +37,6 @@ namespace MWMechanics | |||
|     class AiPackage | ||||
|     { | ||||
|         public: | ||||
|             ///Enumerates the various AITypes available
 | ||||
|             enum TypeId { | ||||
|                 TypeIdNone = -1, | ||||
|                 TypeIdWander = 0, | ||||
|                 TypeIdTravel = 1, | ||||
|                 TypeIdEscort = 2, | ||||
|                 TypeIdFollow = 3, | ||||
|                 TypeIdActivate = 4, | ||||
| 
 | ||||
|                 // These 5 are not really handled as Ai Packages in the MW engine
 | ||||
|                 // For compatibility do *not* return these in the getCurrentAiPackage script function..
 | ||||
|                 TypeIdCombat = 5, | ||||
|                 TypeIdPursue = 6, | ||||
|                 TypeIdAvoidDoor = 7, | ||||
|                 TypeIdFace = 8, | ||||
|                 TypeIdBreathe = 9, | ||||
|                 TypeIdInternalTravel = 10, | ||||
|                 TypeIdCast = 11 | ||||
|             }; | ||||
| 
 | ||||
|             struct Options | ||||
|             { | ||||
|                 unsigned int mPriority = 0; | ||||
|  | @ -79,7 +61,7 @@ namespace MWMechanics | |||
|                 } | ||||
|             }; | ||||
| 
 | ||||
|             AiPackage(TypeId typeId, const Options& options); | ||||
|             AiPackage(AiPackageTypeId typeId, const Options& options); | ||||
| 
 | ||||
|             virtual ~AiPackage() = default; | ||||
| 
 | ||||
|  | @ -97,7 +79,7 @@ namespace MWMechanics | |||
| 
 | ||||
|             /// Returns the TypeID of the AiPackage
 | ||||
|             /// \see enum TypeId
 | ||||
|             TypeId getTypeId() const { return mTypeId; } | ||||
|             AiPackageTypeId getTypeId() const { return mTypeId; } | ||||
| 
 | ||||
|             /// Higher number is higher priority (0 being the lowest)
 | ||||
|             unsigned int getPriority() const { return mOptions.mPriority; } | ||||
|  | @ -167,7 +149,9 @@ namespace MWMechanics | |||
| 
 | ||||
|             DetourNavigator::Flags getNavigatorFlags(const MWWorld::Ptr& actor) const; | ||||
| 
 | ||||
|             const TypeId mTypeId; | ||||
|             DetourNavigator::AreaCosts getAreaCosts(const MWWorld::Ptr& actor) const; | ||||
| 
 | ||||
|             const AiPackageTypeId mTypeId; | ||||
|             const Options mOptions; | ||||
| 
 | ||||
|             // TODO: all this does not belong here, move into temporary storage
 | ||||
|  |  | |||
							
								
								
									
										28
									
								
								apps/openmw/mwmechanics/aipackagetypeid.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								apps/openmw/mwmechanics/aipackagetypeid.hpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,28 @@ | |||
| #ifndef GAME_MWMECHANICS_AIPACKAGETYPEID_H | ||||
| #define GAME_MWMECHANICS_AIPACKAGETYPEID_H | ||||
| 
 | ||||
| namespace MWMechanics | ||||
| { | ||||
|     ///Enumerates the various AITypes available
 | ||||
|     enum class AiPackageTypeId | ||||
|     { | ||||
|         None = -1, | ||||
|         Wander = 0, | ||||
|         Travel = 1, | ||||
|         Escort = 2, | ||||
|         Follow = 3, | ||||
|         Activate = 4, | ||||
| 
 | ||||
|         // These 5 are not really handled as Ai Packages in the MW engine
 | ||||
|         // For compatibility do *not* return these in the getCurrentAiPackage script function..
 | ||||
|         Combat = 5, | ||||
|         Pursue = 6, | ||||
|         AvoidDoor = 7, | ||||
|         Face = 8, | ||||
|         Breathe = 9, | ||||
|         InternalTravel = 10, | ||||
|         Cast = 11 | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
|  | @ -28,7 +28,7 @@ namespace MWMechanics | |||
| 
 | ||||
|             bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; | ||||
| 
 | ||||
|             static constexpr TypeId getTypeId() { return TypeIdPursue; } | ||||
|             static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::Pursue; } | ||||
| 
 | ||||
|             static constexpr Options makeDefaultOptions() | ||||
|             { | ||||
|  |  | |||
|  | @ -33,7 +33,7 @@ void AiSequence::copy (const AiSequence& sequence) | |||
|     sequence.mAiState.copy<AiWanderStorage>(mAiState); | ||||
| } | ||||
| 
 | ||||
| AiSequence::AiSequence() : mDone (false), mRepeat(false), mLastAiPackage(-1) {} | ||||
| AiSequence::AiSequence() : mDone (false), mRepeat(false), mLastAiPackage(AiPackageTypeId::None) {} | ||||
| 
 | ||||
| AiSequence::AiSequence (const AiSequence& sequence) | ||||
| { | ||||
|  | @ -61,17 +61,17 @@ AiSequence::~AiSequence() | |||
|     clear(); | ||||
| } | ||||
| 
 | ||||
| int AiSequence::getTypeId() const | ||||
| AiPackageTypeId AiSequence::getTypeId() const | ||||
| { | ||||
|     if (mPackages.empty()) | ||||
|         return -1; | ||||
|         return AiPackageTypeId::None; | ||||
| 
 | ||||
|     return mPackages.front()->getTypeId(); | ||||
| } | ||||
| 
 | ||||
| bool AiSequence::getCombatTarget(MWWorld::Ptr &targetActor) const | ||||
| { | ||||
|     if (getTypeId() != AiPackage::TypeIdCombat) | ||||
|     if (getTypeId() != AiPackageTypeId::Combat) | ||||
|         return false; | ||||
| 
 | ||||
|     targetActor = mPackages.front()->getTarget(); | ||||
|  | @ -83,7 +83,7 @@ bool AiSequence::getCombatTargets(std::vector<MWWorld::Ptr> &targetActors) const | |||
| { | ||||
|     for (auto it = mPackages.begin(); it != mPackages.end(); ++it) | ||||
|     { | ||||
|         if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdCombat) | ||||
|         if ((*it)->getTypeId() == MWMechanics::AiPackageTypeId::Combat) | ||||
|             targetActors.push_back((*it)->getTarget()); | ||||
|     } | ||||
| 
 | ||||
|  | @ -118,7 +118,7 @@ bool AiSequence::isInCombat() const | |||
| { | ||||
|     for (auto it = mPackages.begin(); it != mPackages.end(); ++it) | ||||
|     { | ||||
|         if ((*it)->getTypeId() == AiPackage::TypeIdCombat) | ||||
|         if ((*it)->getTypeId() == AiPackageTypeId::Combat) | ||||
|             return true; | ||||
|     } | ||||
|     return false; | ||||
|  | @ -128,7 +128,7 @@ bool AiSequence::isEngagedWithActor() const | |||
| { | ||||
|     for (auto it = mPackages.begin(); it != mPackages.end(); ++it) | ||||
|     { | ||||
|         if ((*it)->getTypeId() == AiPackage::TypeIdCombat) | ||||
|         if ((*it)->getTypeId() == AiPackageTypeId::Combat) | ||||
|         { | ||||
|             MWWorld::Ptr target2 = (*it)->getTarget(); | ||||
|             if (!target2.isEmpty() && target2.getClass().isNpc()) | ||||
|  | @ -138,7 +138,7 @@ bool AiSequence::isEngagedWithActor() const | |||
|     return false; | ||||
| } | ||||
| 
 | ||||
| bool AiSequence::hasPackage(int typeId) const | ||||
| bool AiSequence::hasPackage(AiPackageTypeId typeId) const | ||||
| { | ||||
|     for (auto it = mPackages.begin(); it != mPackages.end(); ++it) | ||||
|     { | ||||
|  | @ -152,7 +152,7 @@ bool AiSequence::isInCombat(const MWWorld::Ptr &actor) const | |||
| { | ||||
|     for (auto it = mPackages.begin(); it != mPackages.end(); ++it) | ||||
|     { | ||||
|         if ((*it)->getTypeId() == AiPackage::TypeIdCombat) | ||||
|         if ((*it)->getTypeId() == AiPackageTypeId::Combat) | ||||
|         { | ||||
|             if ((*it)->getTarget() == actor) | ||||
|                 return true; | ||||
|  | @ -165,7 +165,7 @@ void AiSequence::stopCombat() | |||
| { | ||||
|     for(auto it = mPackages.begin(); it != mPackages.end(); ) | ||||
|     { | ||||
|         if ((*it)->getTypeId() == AiPackage::TypeIdCombat) | ||||
|         if ((*it)->getTypeId() == AiPackageTypeId::Combat) | ||||
|         { | ||||
|             it = mPackages.erase(it); | ||||
|         } | ||||
|  | @ -178,7 +178,7 @@ void AiSequence::stopPursuit() | |||
| { | ||||
|     for(auto it = mPackages.begin(); it != mPackages.end(); ) | ||||
|     { | ||||
|         if ((*it)->getTypeId() == AiPackage::TypeIdPursue) | ||||
|         if ((*it)->getTypeId() == AiPackageTypeId::Pursue) | ||||
|         { | ||||
|             it = mPackages.erase(it); | ||||
|         } | ||||
|  | @ -192,10 +192,13 @@ bool AiSequence::isPackageDone() const | |||
|     return mDone; | ||||
| } | ||||
| 
 | ||||
| bool isActualAiPackage(int packageTypeId) | ||||
| namespace | ||||
| { | ||||
|     return (packageTypeId >= AiPackage::TypeIdWander && | ||||
|             packageTypeId <= AiPackage::TypeIdActivate); | ||||
|     bool isActualAiPackage(AiPackageTypeId packageTypeId) | ||||
|     { | ||||
|         return (packageTypeId >= AiPackageTypeId::Wander && | ||||
|                 packageTypeId <= AiPackageTypeId::Activate); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& characterController, float duration, bool outOfRange) | ||||
|  | @ -204,7 +207,7 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac | |||
|     { | ||||
|         if (mPackages.empty()) | ||||
|         { | ||||
|             mLastAiPackage = -1; | ||||
|             mLastAiPackage = AiPackageTypeId::None; | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|  | @ -213,12 +216,12 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac | |||
|         if (!package->alwaysActive() && outOfRange) | ||||
|             return; | ||||
| 
 | ||||
|         int packageTypeId = package->getTypeId(); | ||||
|         auto packageTypeId = package->getTypeId(); | ||||
|         // workaround ai packages not being handled as in the vanilla engine
 | ||||
|         if (isActualAiPackage(packageTypeId)) | ||||
|             mLastAiPackage = packageTypeId; | ||||
|         // if active package is combat one, choose nearest target
 | ||||
|         if (packageTypeId == AiPackage::TypeIdCombat) | ||||
|         if (packageTypeId == AiPackageTypeId::Combat) | ||||
|         { | ||||
|             auto itActualCombat = mPackages.end(); | ||||
| 
 | ||||
|  | @ -229,7 +232,7 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac | |||
| 
 | ||||
|             for (auto it = mPackages.begin(); it != mPackages.end();) | ||||
|             { | ||||
|                 if ((*it)->getTypeId() != AiPackage::TypeIdCombat) break; | ||||
|                 if ((*it)->getTypeId() != AiPackageTypeId::Combat) break; | ||||
| 
 | ||||
|                 MWWorld::Ptr target = (*it)->getTarget(); | ||||
| 
 | ||||
|  | @ -320,16 +323,16 @@ void AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor, boo | |||
|     // We should return a wandering actor back after combat, casting or pursuit.
 | ||||
|     // The same thing for actors without AI packages.
 | ||||
|     // Also there is no point to stack return packages.
 | ||||
|     int currentTypeId = getTypeId(); | ||||
|     int newTypeId = package.getTypeId(); | ||||
|     if (currentTypeId <= MWMechanics::AiPackage::TypeIdWander | ||||
|         && !hasPackage(MWMechanics::AiPackage::TypeIdInternalTravel) | ||||
|         && (newTypeId <= MWMechanics::AiPackage::TypeIdCombat | ||||
|         || newTypeId == MWMechanics::AiPackage::TypeIdPursue | ||||
|         || newTypeId == MWMechanics::AiPackage::TypeIdCast)) | ||||
|     const auto currentTypeId = getTypeId(); | ||||
|     const auto newTypeId = package.getTypeId(); | ||||
|     if (currentTypeId <= MWMechanics::AiPackageTypeId::Wander | ||||
|         && !hasPackage(MWMechanics::AiPackageTypeId::InternalTravel) | ||||
|         && (newTypeId <= MWMechanics::AiPackageTypeId::Combat | ||||
|         || newTypeId == MWMechanics::AiPackageTypeId::Pursue | ||||
|         || newTypeId == MWMechanics::AiPackageTypeId::Cast)) | ||||
|     { | ||||
|         osg::Vec3f dest; | ||||
|         if (currentTypeId == MWMechanics::AiPackage::TypeIdWander) | ||||
|         if (currentTypeId == MWMechanics::AiPackageTypeId::Wander) | ||||
|         { | ||||
|             dest = getActivePackage().getDestination(actor); | ||||
|         } | ||||
|  | @ -361,8 +364,8 @@ void AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor, boo | |||
|     for (auto it = mPackages.begin(); it != mPackages.end(); ++it) | ||||
|     { | ||||
|         // We should keep current AiCast package, if we try to add a new one.
 | ||||
|         if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdCast && | ||||
|             package.getTypeId() == MWMechanics::AiPackage::TypeIdCast) | ||||
|         if ((*it)->getTypeId() == MWMechanics::AiPackageTypeId::Cast && | ||||
|             package.getTypeId() == MWMechanics::AiPackageTypeId::Cast) | ||||
|         { | ||||
|             continue; | ||||
|         } | ||||
|  | @ -444,7 +447,7 @@ void AiSequence::writeState(ESM::AiSequence::AiSequence &sequence) const | |||
|     for (const auto& package : mPackages) | ||||
|         package->writeState(sequence); | ||||
| 
 | ||||
|     sequence.mLastAiPackage = mLastAiPackage; | ||||
|     sequence.mLastAiPackage = static_cast<int>(mLastAiPackage); | ||||
| } | ||||
| 
 | ||||
| void AiSequence::readState(const ESM::AiSequence::AiSequence &sequence) | ||||
|  | @ -457,7 +460,7 @@ void AiSequence::readState(const ESM::AiSequence::AiSequence &sequence) | |||
|     for (std::vector<ESM::AiSequence::AiPackageContainer>::const_iterator it = sequence.mPackages.begin(); | ||||
|          it != sequence.mPackages.end(); ++it) | ||||
|     { | ||||
|         if (isActualAiPackage(it->mType)) | ||||
|         if (isActualAiPackage(static_cast<AiPackageTypeId>(it->mType))) | ||||
|             count++; | ||||
|     } | ||||
| 
 | ||||
|  | @ -520,7 +523,7 @@ void AiSequence::readState(const ESM::AiSequence::AiSequence &sequence) | |||
|         mPackages.push_back(std::move(package)); | ||||
|     } | ||||
| 
 | ||||
|     mLastAiPackage = sequence.mLastAiPackage; | ||||
|     mLastAiPackage = static_cast<AiPackageTypeId>(sequence.mLastAiPackage); | ||||
| } | ||||
| 
 | ||||
| void AiSequence::fastForward(const MWWorld::Ptr& actor) | ||||
|  |  | |||
|  | @ -5,6 +5,7 @@ | |||
| #include <memory> | ||||
| 
 | ||||
| #include "aistate.hpp" | ||||
| #include "aipackagetypeid.hpp" | ||||
| 
 | ||||
| #include <components/esm/loadnpc.hpp> | ||||
| 
 | ||||
|  | @ -49,7 +50,7 @@ namespace MWMechanics | |||
|             void copy (const AiSequence& sequence); | ||||
| 
 | ||||
|             /// The type of AI package that ran last
 | ||||
|             int mLastAiPackage; | ||||
|             AiPackageTypeId mLastAiPackage; | ||||
|             AiState mAiState; | ||||
| 
 | ||||
|         public: | ||||
|  | @ -71,14 +72,14 @@ namespace MWMechanics | |||
|             void erase(std::list<std::unique_ptr<AiPackage>>::const_iterator package); | ||||
| 
 | ||||
|             /// Returns currently executing AiPackage type
 | ||||
|             /** \see enum AiPackage::TypeId **/ | ||||
|             int getTypeId() const; | ||||
|             /** \see enum class AiPackageTypeId **/ | ||||
|             AiPackageTypeId getTypeId() const; | ||||
| 
 | ||||
|             /// Get the typeid of the Ai package that ran last
 | ||||
|             /** NOT the currently "active" Ai package that will be run in the next frame.
 | ||||
|                 This difference is important when an Ai package has just finished and been removed. | ||||
|                 \see enum AiPackage::TypeId **/ | ||||
|             int getLastRunTypeId() const { return mLastAiPackage; } | ||||
|                 \see enum class AiPackageTypeId **/ | ||||
|             AiPackageTypeId getLastRunTypeId() const { return mLastAiPackage; } | ||||
| 
 | ||||
|             /// Return true and assign target if combat package is currently active, return false otherwise
 | ||||
|             bool getCombatTarget (MWWorld::Ptr &targetActor) const; | ||||
|  | @ -93,7 +94,7 @@ namespace MWMechanics | |||
|             bool isEngagedWithActor () const; | ||||
| 
 | ||||
|             /// Does this AI sequence have the given package type?
 | ||||
|             bool hasPackage(int typeId) const; | ||||
|             bool hasPackage(AiPackageTypeId typeId) const; | ||||
| 
 | ||||
|             /// Are we in combat with this particular actor?
 | ||||
|             bool isInCombat (const MWWorld::Ptr& actor) const; | ||||
|  |  | |||
|  | @ -34,7 +34,7 @@ namespace MWMechanics | |||
| 
 | ||||
|             bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; | ||||
| 
 | ||||
|             static constexpr TypeId getTypeId() { return TypeIdTravel; } | ||||
|             static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::Travel; } | ||||
| 
 | ||||
|             static constexpr Options makeDefaultOptions() | ||||
|             { | ||||
|  | @ -60,7 +60,7 @@ namespace MWMechanics | |||
| 
 | ||||
|         explicit AiInternalTravel(const ESM::AiSequence::AiTravel* travel); | ||||
| 
 | ||||
|         static constexpr TypeId getTypeId() { return TypeIdInternalTravel; } | ||||
|         static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::InternalTravel; } | ||||
| 
 | ||||
|         std::unique_ptr<AiPackage> clone() const final; | ||||
|     }; | ||||
|  |  | |||
|  | @ -6,6 +6,7 @@ | |||
| #include <components/misc/rng.hpp> | ||||
| #include <components/esm/aisequence.hpp> | ||||
| #include <components/detournavigator/navigator.hpp> | ||||
| #include <components/misc/coordinateconverter.hpp> | ||||
| 
 | ||||
| #include "../mwbase/world.hpp" | ||||
| #include "../mwbase/environment.hpp" | ||||
|  | @ -21,7 +22,6 @@ | |||
| #include "pathgrid.hpp" | ||||
| #include "creaturestats.hpp" | ||||
| #include "movement.hpp" | ||||
| #include "coordinateconverter.hpp" | ||||
| #include "actorutil.hpp" | ||||
| 
 | ||||
| namespace MWMechanics | ||||
|  | @ -202,7 +202,7 @@ namespace MWMechanics | |||
|             { | ||||
|                 const osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getPathfindingHalfExtents(actor); | ||||
|                 mPathFinder.buildPath(actor, pos.asVec3(), mDestination, actor.getCell(), | ||||
|                     getPathGridGraph(actor.getCell()), halfExtents, getNavigatorFlags(actor)); | ||||
|                     getPathGridGraph(actor.getCell()), halfExtents, getNavigatorFlags(actor), getAreaCosts(actor)); | ||||
|             } | ||||
| 
 | ||||
|             if (mPathFinder.isPathConstructed()) | ||||
|  | @ -337,6 +337,7 @@ namespace MWMechanics | |||
|         const auto halfExtents = world->getPathfindingHalfExtents(actor); | ||||
|         const auto navigator = world->getNavigator(); | ||||
|         const auto navigatorFlags = getNavigatorFlags(actor); | ||||
|         const auto areaCosts = getAreaCosts(actor); | ||||
| 
 | ||||
|         do { | ||||
|             // Determine a random location within radius of original position
 | ||||
|  | @ -365,7 +366,8 @@ namespace MWMechanics | |||
|             if (isWaterCreature || isFlyingCreature) | ||||
|                 mPathFinder.buildStraightPath(mDestination); | ||||
|             else | ||||
|                 mPathFinder.buildPathByNavMesh(actor, currentPosition, mDestination, halfExtents, navigatorFlags); | ||||
|                 mPathFinder.buildPathByNavMesh(actor, currentPosition, mDestination, halfExtents, navigatorFlags, | ||||
|                                                areaCosts); | ||||
| 
 | ||||
|             if (mPathFinder.isPathConstructed()) | ||||
|             { | ||||
|  | @ -496,7 +498,8 @@ namespace MWMechanics | |||
|         if (mUsePathgrid) | ||||
|         { | ||||
|             const auto halfExtents = MWBase::Environment::get().getWorld()->getHalfExtents(actor); | ||||
|             mPathFinder.buildPathByNavMeshToNextPoint(actor, halfExtents, getNavigatorFlags(actor)); | ||||
|             mPathFinder.buildPathByNavMeshToNextPoint(actor, halfExtents, getNavigatorFlags(actor), | ||||
|                                                       getAreaCosts(actor)); | ||||
|         } | ||||
| 
 | ||||
|         if (mObstacleCheck.isEvading()) | ||||
|  | @ -566,7 +569,7 @@ namespace MWMechanics | |||
| 
 | ||||
|     void AiWander::ToWorldCoordinates(ESM::Pathgrid::Point& point, const ESM::Cell * cell) | ||||
|     { | ||||
|         CoordinateConverter(cell).toWorld(point); | ||||
|         Misc::CoordinateConverter(cell).toWorld(point); | ||||
|     } | ||||
| 
 | ||||
|     void AiWander::trimAllowedNodes(std::vector<ESM::Pathgrid::Point>& nodes, | ||||
|  | @ -767,7 +770,7 @@ namespace MWMechanics | |||
|         { | ||||
|             // get NPC's position in local (i.e. cell) coordinates
 | ||||
|             osg::Vec3f npcPos(mInitialActorPosition); | ||||
|             CoordinateConverter(cell).toLocal(npcPos); | ||||
|             Misc::CoordinateConverter(cell).toLocal(npcPos); | ||||
| 
 | ||||
|             // Find closest pathgrid point
 | ||||
|             int closestPointIndex = PathFinder::getClosestPoint(pathgrid, npcPos); | ||||
|  |  | |||
|  | @ -93,7 +93,7 @@ namespace MWMechanics | |||
| 
 | ||||
|             bool execute(const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; | ||||
| 
 | ||||
|             static constexpr TypeId getTypeId() { return TypeIdWander; } | ||||
|             static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::Wander; } | ||||
| 
 | ||||
|             static constexpr Options makeDefaultOptions() | ||||
|             { | ||||
|  |  | |||
|  | @ -1,44 +0,0 @@ | |||
| #include "coordinateconverter.hpp" | ||||
| 
 | ||||
| #include <components/esm/loadcell.hpp> | ||||
| #include <components/esm/loadland.hpp> | ||||
| 
 | ||||
| namespace MWMechanics | ||||
| { | ||||
|     CoordinateConverter::CoordinateConverter(const ESM::Cell* cell) | ||||
|         : mCellX(0), mCellY(0) | ||||
|     { | ||||
|         if (cell->isExterior()) | ||||
|         { | ||||
|             mCellX = cell->mData.mX * ESM::Land::REAL_SIZE; | ||||
|             mCellY = cell->mData.mY * ESM::Land::REAL_SIZE; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void CoordinateConverter::toWorld(ESM::Pathgrid::Point& point) | ||||
|     { | ||||
|         point.mX += mCellX; | ||||
|         point.mY += mCellY; | ||||
|     } | ||||
| 
 | ||||
|     void CoordinateConverter::toWorld(osg::Vec3f& point) | ||||
|     { | ||||
|         point.x() += static_cast<float>(mCellX); | ||||
|         point.y() += static_cast<float>(mCellY); | ||||
|     } | ||||
| 
 | ||||
|     void CoordinateConverter::toLocal(osg::Vec3f& point) | ||||
|     { | ||||
|         point.x() -= static_cast<float>(mCellX); | ||||
|         point.y() -= static_cast<float>(mCellY); | ||||
|     } | ||||
| 
 | ||||
|     osg::Vec3f CoordinateConverter::toLocalVec3(const osg::Vec3f& point) | ||||
|     { | ||||
|         return osg::Vec3f( | ||||
|             point.x() - static_cast<float>(mCellX), | ||||
|             point.y() - static_cast<float>(mCellY), | ||||
|             point.z() | ||||
|         ); | ||||
|     } | ||||
| } | ||||
|  | @ -1,37 +0,0 @@ | |||
| #ifndef GAME_MWMECHANICS_COORDINATECONVERTER_H | ||||
| #define GAME_MWMECHANICS_COORDINATECONVERTER_H | ||||
| 
 | ||||
| #include <components/esm/defs.hpp> | ||||
| #include <components/esm/loadpgrd.hpp> | ||||
| 
 | ||||
| namespace ESM | ||||
| { | ||||
|     struct Cell; | ||||
| } | ||||
| 
 | ||||
| namespace MWMechanics | ||||
| { | ||||
|     /// \brief convert coordinates between world and local cell
 | ||||
|     class CoordinateConverter | ||||
|     { | ||||
|         public: | ||||
|             CoordinateConverter(const ESM::Cell* cell); | ||||
| 
 | ||||
|             /// in-place conversion from local to world
 | ||||
|             void toWorld(ESM::Pathgrid::Point& point); | ||||
| 
 | ||||
|             /// in-place conversion from local to world
 | ||||
|             void toWorld(osg::Vec3f& point); | ||||
| 
 | ||||
|             /// in-place conversion from world to local
 | ||||
|             void toLocal(osg::Vec3f& point); | ||||
| 
 | ||||
|             osg::Vec3f toLocalVec3(const osg::Vec3f& point); | ||||
| 
 | ||||
|         private: | ||||
|             int mCellX; | ||||
|             int mCellY; | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
|  | @ -7,6 +7,8 @@ | |||
| #include <components/esm/esmwriter.hpp> | ||||
| #include <components/esm/stolenitems.hpp> | ||||
| 
 | ||||
| #include <components/detournavigator/navigator.hpp> | ||||
| 
 | ||||
| #include <components/sceneutil/positionattitudetransform.hpp> | ||||
| 
 | ||||
| /*
 | ||||
|  | @ -927,6 +929,12 @@ namespace MWMechanics | |||
|     bool MechanicsManager::toggleAI() | ||||
|     { | ||||
|         mAI = !mAI; | ||||
| 
 | ||||
|         MWBase::World* world = MWBase::Environment::get().getWorld(); | ||||
|         world->getNavigator()->setUpdatesEnabled(mAI); | ||||
|         if (mAI) | ||||
|            world->getNavigator()->update(world->getPlayerPtr().getRefData().getPosition().asVec3()); | ||||
| 
 | ||||
|         return mAI; | ||||
|     } | ||||
| 
 | ||||
|  | @ -1351,7 +1359,7 @@ namespace MWMechanics | |||
|         { | ||||
|             bool reported = false; | ||||
|             if (victim.getClass().isClass(victim, "guard") | ||||
|                 && !victim.getClass().getCreatureStats(victim).getAiSequence().hasPackage(AiPackage::TypeIdPursue)) | ||||
|                 && !victim.getClass().getCreatureStats(victim).getAiSequence().hasPackage(AiPackageTypeId::Pursue)) | ||||
|                 reported = reportCrime(player, victim, type, std::string(), arg); | ||||
| 
 | ||||
|             if (!reported) | ||||
|  | @ -1374,7 +1382,7 @@ namespace MWMechanics | |||
|             return false; | ||||
| 
 | ||||
|         // Player's followers should not attack player, or try to arrest him
 | ||||
|         if (actor.getClass().getCreatureStats(actor).getAiSequence().hasPackage(AiPackage::TypeIdFollow)) | ||||
|         if (actor.getClass().getCreatureStats(actor).getAiSequence().hasPackage(AiPackageTypeId::Follow)) | ||||
|         { | ||||
|             if (playerFollowers.find(actor) != playerFollowers.end()) | ||||
|                 return false; | ||||
|  | @ -1491,7 +1499,7 @@ namespace MWMechanics | |||
|                 // once the bounty has been paid.
 | ||||
|                 actor.getClass().getNpcStats(actor).setCrimeId(id); | ||||
| 
 | ||||
|                 if (!actor.getClass().getCreatureStats(actor).getAiSequence().hasPackage(AiPackage::TypeIdPursue)) | ||||
|                 if (!actor.getClass().getCreatureStats(actor).getAiSequence().hasPackage(AiPackageTypeId::Pursue)) | ||||
|                 { | ||||
|                     actor.getClass().getCreatureStats(actor).getAiSequence().stack(AiPursue(player), actor); | ||||
|                 } | ||||
|  | @ -1579,7 +1587,7 @@ namespace MWMechanics | |||
|             { | ||||
|                 // Attacker is in combat with us, but we are not in combat with the attacker yet. Time to fight back.
 | ||||
|                 // Note: accidental or collateral damage attacks are ignored.
 | ||||
|                 if (!victim.getClass().getCreatureStats(victim).getAiSequence().hasPackage(AiPackage::TypeIdPursue)) | ||||
|                 if (!victim.getClass().getCreatureStats(victim).getAiSequence().hasPackage(AiPackageTypeId::Pursue)) | ||||
|                     startCombat(victim, player); | ||||
| 
 | ||||
|                 // Set the crime ID, which we will use to calm down participants
 | ||||
|  | @ -1681,7 +1689,7 @@ namespace MWMechanics | |||
|         { | ||||
|             // Attacker is in combat with us, but we are not in combat with the attacker yet. Time to fight back.
 | ||||
|             // Note: accidental or collateral damage attacks are ignored.
 | ||||
|             if (!target.getClass().getCreatureStats(target).getAiSequence().hasPackage(AiPackage::TypeIdPursue)) | ||||
|             if (!target.getClass().getCreatureStats(target).getAiSequence().hasPackage(AiPackageTypeId::Pursue)) | ||||
|             { | ||||
|                 // If an actor has OnPCHitMe declared in his script, his Fight = 0 and the attacker is player,
 | ||||
|                 // he will attack the player only if we will force him (e.g. via StartCombat console command)
 | ||||
|  | @ -1706,7 +1714,7 @@ namespace MWMechanics | |||
|         const MWMechanics::AiSequence& seq = target.getClass().getCreatureStats(target).getAiSequence(); | ||||
|         return target.getClass().isNpc() && !attacker.isEmpty() && !seq.isInCombat(attacker) | ||||
|                 && !isAggressive(target, attacker) && !seq.isEngagedWithActor() | ||||
|                 && !target.getClass().getCreatureStats(target).getAiSequence().hasPackage(AiPackage::TypeIdPursue); | ||||
|                 && !target.getClass().getCreatureStats(target).getAiSequence().hasPackage(AiPackageTypeId::Pursue); | ||||
|     } | ||||
| 
 | ||||
|     void MechanicsManager::actorKilled(const MWWorld::Ptr &victim, const MWWorld::Ptr &attacker) | ||||
|  | @ -1842,7 +1850,7 @@ namespace MWMechanics | |||
|                     if (iter->first.getClass().isClass(iter->first, "Guard")) | ||||
|                     { | ||||
|                         MWMechanics::AiSequence& aiSeq = iter->first.getClass().getCreatureStats(iter->first).getAiSequence(); | ||||
|                         if (aiSeq.getTypeId() == MWMechanics::AiPackage::TypeIdPursue) | ||||
|                         if (aiSeq.getTypeId() == MWMechanics::AiPackageTypeId::Pursue) | ||||
|                         { | ||||
|                             aiSeq.stopPursuit(); | ||||
|                             aiSeq.stack(MWMechanics::AiCombat(target), ptr); | ||||
|  |  | |||
|  | @ -7,6 +7,7 @@ | |||
| #include <components/detournavigator/debug.hpp> | ||||
| #include <components/detournavigator/navigator.hpp> | ||||
| #include <components/debug/debuglog.hpp> | ||||
| #include <components/misc/coordinateconverter.hpp> | ||||
| 
 | ||||
| #include "../mwbase/world.hpp" | ||||
| #include "../mwbase/environment.hpp" | ||||
|  | @ -17,7 +18,6 @@ | |||
| #include "../mwworld/class.hpp" | ||||
| 
 | ||||
| #include "pathgrid.hpp" | ||||
| #include "coordinateconverter.hpp" | ||||
| #include "actorutil.hpp" | ||||
| 
 | ||||
| namespace | ||||
|  | @ -158,13 +158,10 @@ namespace MWMechanics | |||
|         // Maybe there is no pathgrid for this cell.  Just go to destination and let
 | ||||
|         // physics take care of any blockages.
 | ||||
|         if(!pathgrid || pathgrid->mPoints.empty()) | ||||
|         { | ||||
|             *out++ = endPoint; | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         // NOTE: getClosestPoint expects local coordinates
 | ||||
|         CoordinateConverter converter(mCell->getCell()); | ||||
|         Misc::CoordinateConverter converter(mCell->getCell()); | ||||
| 
 | ||||
|         // NOTE: It is possible that getClosestPoint returns a pathgrind point index
 | ||||
|         //       that is unreachable in some situations. e.g. actor is standing
 | ||||
|  | @ -179,6 +176,9 @@ namespace MWMechanics | |||
|             endPointInLocalCoords, | ||||
|                 startNode); | ||||
| 
 | ||||
|         if (!endNode.second) | ||||
|             return; | ||||
| 
 | ||||
|         // if it's shorter for actor to travel from start to end, than to travel from either
 | ||||
|         // start or end to nearest pathgrid point, just travel from start to end.
 | ||||
|         float startToEndLength2 = (endPointInLocalCoords - startPointInLocalCoords).length2(); | ||||
|  | @ -249,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) | ||||
|  |  | |||
|  | @ -6,6 +6,7 @@ | |||
| #include <iterator> | ||||
| 
 | ||||
| #include <components/detournavigator/flags.hpp> | ||||
| #include <components/detournavigator/areatype.hpp> | ||||
| #include <components/esm/defs.hpp> | ||||
| #include <components/esm/loadpgrd.hpp> | ||||
| 
 | ||||
|  | @ -90,14 +91,15 @@ namespace MWMechanics | |||
|                 const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph); | ||||
| 
 | ||||
|             void buildPathByNavMesh(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, | ||||
|                 const osg::Vec3f& endPoint, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags); | ||||
|                 const osg::Vec3f& endPoint, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags, | ||||
|                 const DetourNavigator::AreaCosts& areaCosts); | ||||
| 
 | ||||
|             void buildPath(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, const osg::Vec3f& endPoint, | ||||
|                 const MWWorld::CellStore* cell, const PathgridGraph& pathgridGraph, const osg::Vec3f& halfExtents, | ||||
|                 const DetourNavigator::Flags flags); | ||||
|                 const DetourNavigator::Flags flags, const DetourNavigator::AreaCosts& areaCosts); | ||||
| 
 | ||||
|             void buildPathByNavMeshToNextPoint(const MWWorld::ConstPtr& actor, const osg::Vec3f& halfExtents, | ||||
|                 const DetourNavigator::Flags flags); | ||||
|                 const DetourNavigator::Flags flags, const DetourNavigator::AreaCosts& areaCosts); | ||||
| 
 | ||||
|             /// Remove front point if exist and within tolerance
 | ||||
|             void update(const osg::Vec3f& position, const float pointTolerance, const float destinationTolerance); | ||||
|  | @ -203,7 +205,7 @@ namespace MWMechanics | |||
| 
 | ||||
|             bool buildPathByNavigatorImpl(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, | ||||
|                 const osg::Vec3f& endPoint, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags, | ||||
|                 std::back_insert_iterator<std::deque<osg::Vec3f>> out); | ||||
|                 const DetourNavigator::AreaCosts& areaCosts, std::back_insert_iterator<std::deque<osg::Vec3f>> out); | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -52,7 +52,6 @@ namespace MWMechanics | |||
|     PathgridGraph::PathgridGraph(const MWWorld::CellStore *cell) | ||||
|         : mCell(nullptr) | ||||
|         , mPathgrid(nullptr) | ||||
|         , mIsExterior(0) | ||||
|         , mGraph(0) | ||||
|         , mIsGraphConstructed(false) | ||||
|         , mSCCId(0) | ||||
|  | @ -106,7 +105,6 @@ namespace MWMechanics | |||
|             return true; | ||||
| 
 | ||||
|         mCell = cell->getCell(); | ||||
|         mIsExterior = cell->getCell()->isExterior(); | ||||
|         mPathgrid = MWBase::Environment::get().getWorld()->getStore().get<ESM::Pathgrid>().search(*cell->getCell()); | ||||
|         if(!mPathgrid) | ||||
|             return false; | ||||
|  |  | |||
|  | @ -44,7 +44,6 @@ namespace MWMechanics | |||
| 
 | ||||
|             const ESM::Cell *mCell; | ||||
|             const ESM::Pathgrid *mPathgrid; | ||||
|             bool mIsExterior; | ||||
| 
 | ||||
|             struct ConnectedPoint // edge
 | ||||
|             { | ||||
|  |  | |||
|  | @ -419,6 +419,15 @@ namespace MWPhysics | |||
|             return osg::Vec3f(); | ||||
|     } | ||||
| 
 | ||||
|     osg::BoundingBox PhysicsSystem::getBoundingBox(const MWWorld::ConstPtr &object) const | ||||
|     { | ||||
|         const Object * physobject = getObject(object); | ||||
|         if (!physobject) return osg::BoundingBox(); | ||||
|         btVector3 min, max; | ||||
|         physobject->getCollisionObject()->getCollisionShape()->getAabb(physobject->getCollisionObject()->getWorldTransform(), min, max); | ||||
|         return osg::BoundingBox(Misc::Convert::toOsg(min), Misc::Convert::toOsg(max)); | ||||
|     } | ||||
| 
 | ||||
|     osg::Vec3f PhysicsSystem::getCollisionObjectPosition(const MWWorld::ConstPtr &actor) const | ||||
|     { | ||||
|         const Actor* physactor = getActor(actor); | ||||
|  |  | |||
|  | @ -7,6 +7,7 @@ | |||
| #include <algorithm> | ||||
| 
 | ||||
| #include <osg/Quat> | ||||
| #include <osg/BoundingBox> | ||||
| #include <osg/ref_ptr> | ||||
| 
 | ||||
| #include "../mwworld/ptr.hpp" | ||||
|  | @ -144,6 +145,9 @@ namespace MWPhysics | |||
|             /// @note The collision shape's origin is in its center, so the position returned can be described as center of the actor collision box in world space.
 | ||||
|             osg::Vec3f getCollisionObjectPosition(const MWWorld::ConstPtr& actor) const; | ||||
| 
 | ||||
|             /// Get bounding box in world space of the given object.
 | ||||
|             osg::BoundingBox getBoundingBox(const MWWorld::ConstPtr &object) const; | ||||
| 
 | ||||
|             /// Queues velocity movement for a Ptr. If a Ptr is already queued, its velocity will
 | ||||
|             /// be overwritten. Valid until the next call to applyQueuedMovement.
 | ||||
|             void queueObjectMovement(const MWWorld::Ptr &ptr, const osg::Vec3f &velocity); | ||||
|  |  | |||
|  | @ -513,6 +513,9 @@ namespace MWRender | |||
|             if (mShadowUniform) | ||||
|                 stateset->addUniform(mShadowUniform); | ||||
| 
 | ||||
|             stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); | ||||
|             stateset->setRenderBinMode(osg::StateSet::OVERRIDE_RENDERBIN_DETAILS); | ||||
| 
 | ||||
|             // FIXME: overriding diffuse/ambient/emissive colors
 | ||||
|             osg::Material* material = new osg::Material; | ||||
|             material->setColorMode(osg::Material::OFF); | ||||
|  | @ -1369,7 +1372,7 @@ namespace MWRender | |||
|             osg::Group* sheathParent = findVisitor.mFoundNode; | ||||
|             if (sheathParent) | ||||
|             { | ||||
|                 osg::Node* copy = osg::clone(nodePair.first, osg::CopyOp::DEEP_COPY_NODES); | ||||
|                 osg::Node* copy = static_cast<osg::Node*>(nodePair.first->clone(osg::CopyOp::DEEP_COPY_NODES)); | ||||
|                 sheathParent->addChild(copy); | ||||
|             } | ||||
|         } | ||||
|  | @ -1741,31 +1744,16 @@ namespace MWRender | |||
|             if (mTransparencyUpdater == nullptr) | ||||
|             { | ||||
|                 mTransparencyUpdater = new TransparencyUpdater(alpha, mResourceSystem->getSceneManager()->getShaderManager().getShadowMapAlphaTestEnableUniform()); | ||||
|                 mObjectRoot->addUpdateCallback(mTransparencyUpdater); | ||||
|                 mObjectRoot->addCullCallback(mTransparencyUpdater); | ||||
|             } | ||||
|             else | ||||
|                 mTransparencyUpdater->setAlpha(alpha); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             mObjectRoot->removeUpdateCallback(mTransparencyUpdater); | ||||
|             mObjectRoot->removeCullCallback(mTransparencyUpdater); | ||||
|             mTransparencyUpdater = nullptr; | ||||
|             mObjectRoot->setStateSet(nullptr); | ||||
|         } | ||||
| 
 | ||||
|         setRenderBin(); | ||||
|     } | ||||
| 
 | ||||
|     void Animation::setRenderBin() | ||||
|     { | ||||
|         if (mAlpha != 1.f) | ||||
|         { | ||||
|             osg::StateSet* stateset = mObjectRoot->getOrCreateStateSet(); | ||||
|             stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); | ||||
|             stateset->setRenderBinMode(osg::StateSet::OVERRIDE_RENDERBIN_DETAILS); | ||||
|         } | ||||
|         else if (osg::StateSet* stateset = mObjectRoot->getStateSet()) | ||||
|             stateset->setRenderBinToInherit(); | ||||
|     } | ||||
| 
 | ||||
|     void Animation::setLightEffect(float effect) | ||||
|  |  | |||
|  | @ -336,9 +336,6 @@ protected: | |||
|      */ | ||||
|     virtual void addControllers(); | ||||
| 
 | ||||
|     /// Set the render bin for this animation's object root. May be customized by subclasses.
 | ||||
|     virtual void setRenderBin(); | ||||
| 
 | ||||
| public: | ||||
| 
 | ||||
|     Animation(const MWWorld::Ptr &ptr, osg::ref_ptr<osg::Group> parentNode, Resource::ResourceSystem* resourceSystem); | ||||
|  |  | |||
|  | @ -74,7 +74,7 @@ LocalMap::LocalMap(osg::Group* root) | |||
|     : mRoot(root) | ||||
|     , mMapResolution(Settings::Manager::getInt("local map resolution", "Map")) | ||||
|     , mMapWorldSize(Constants::CellSizeInUnits) | ||||
|     , mCellDistance(Settings::Manager::getInt("local map cell distance", "Map")) | ||||
|     , mCellDistance(Constants::CellGridRadius) | ||||
|     , mAngle(0.f) | ||||
|     , mInterior(false) | ||||
| { | ||||
|  | @ -168,11 +168,10 @@ void LocalMap::saveFogOfWar(MWWorld::CellStore* cell) | |||
| osg::ref_ptr<osg::Camera> LocalMap::createOrthographicCamera(float x, float y, float width, float height, const osg::Vec3d& upVector, float zmin, float zmax) | ||||
| { | ||||
|     osg::ref_ptr<osg::Camera> camera (new osg::Camera); | ||||
| 
 | ||||
|     camera->setProjectionMatrixAsOrtho(-width/2, width/2, -height/2, height/2, 5, (zmax-zmin) + 10); | ||||
|     camera->setComputeNearFarMode(osg::Camera::DO_NOT_COMPUTE_NEAR_FAR); | ||||
|     camera->setViewMatrixAsLookAt(osg::Vec3d(x, y, zmax + 5), osg::Vec3d(x, y, zmin), upVector); | ||||
|     camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF); | ||||
|     camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF_INHERIT_VIEWPOINT); | ||||
|     camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT, osg::Camera::PIXEL_BUFFER_RTT); | ||||
|     camera->setClearColor(osg::Vec4(0.f, 0.f, 0.f, 1.f)); | ||||
|     camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | ||||
|  | @ -360,11 +359,6 @@ void LocalMap::requestExteriorMap(const MWWorld::CellStore* cell) | |||
| 
 | ||||
|     osg::ref_ptr<osg::Camera> camera = createOrthographicCamera(x*mMapWorldSize + mMapWorldSize/2.f, y*mMapWorldSize + mMapWorldSize/2.f, mMapWorldSize, mMapWorldSize, | ||||
|                                                                 osg::Vec3d(0,1,0), zmin, zmax); | ||||
|     camera->getOrCreateUserDataContainer()->addDescription("NoTerrainLod"); | ||||
|     std::ostringstream stream; | ||||
|     stream << x << " " << y; | ||||
|     camera->getOrCreateUserDataContainer()->addDescription(stream.str()); | ||||
| 
 | ||||
|     setupRenderToTexture(camera, cell->getCell()->getGridX(), cell->getCell()->getGridY()); | ||||
| 
 | ||||
|     MapSegment& segment = mSegments[std::make_pair(cell->getCell()->getGridX(), cell->getCell()->getGridY())]; | ||||
|  |  | |||
|  | @ -435,12 +435,10 @@ void NpcAnimation::setRenderBin() | |||
|             osgUtil::RenderBin::addRenderBinPrototype("DepthClear", depthClearBin); | ||||
|             prototypeAdded = true; | ||||
|         } | ||||
| 
 | ||||
|         osg::StateSet* stateset = mObjectRoot->getOrCreateStateSet(); | ||||
|         stateset->setRenderBinDetails(RenderBin_FirstPerson, "DepthClear", osg::StateSet::OVERRIDE_RENDERBIN_DETAILS); | ||||
|         mObjectRoot->getOrCreateStateSet()->setRenderBinDetails(RenderBin_FirstPerson, "DepthClear", osg::StateSet::OVERRIDE_RENDERBIN_DETAILS); | ||||
|     } | ||||
|     else | ||||
|         Animation::setRenderBin(); | ||||
|     else if (osg::StateSet* stateset = mObjectRoot->getStateSet()) | ||||
|         stateset->setRenderBinToInherit(); | ||||
| } | ||||
| 
 | ||||
| void NpcAnimation::rebuild() | ||||
|  |  | |||
|  | @ -88,7 +88,7 @@ private: | |||
|     void addPartGroup(int group, int priority, const std::vector<ESM::PartReference> &parts, | ||||
|                                     bool enchantedGlow=false, osg::Vec4f* glowColor=nullptr); | ||||
| 
 | ||||
|     virtual void setRenderBin(); | ||||
|     void setRenderBin(); | ||||
| 
 | ||||
|     osg::ref_ptr<NeckController> mFirstPersonNeckController; | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										782
									
								
								apps/openmw/mwrender/objectpaging.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										782
									
								
								apps/openmw/mwrender/objectpaging.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,782 @@ | |||
| #include "objectpaging.hpp" | ||||
| 
 | ||||
| #include <unordered_map> | ||||
| 
 | ||||
| #include <osg/Version> | ||||
| #include <osg/LOD> | ||||
| #include <osg/Switch> | ||||
| #include <osg/MatrixTransform> | ||||
| #include <osg/Material> | ||||
| #include <osgUtil/IncrementalCompileOperation> | ||||
| 
 | ||||
| #include <components/esm/esmreader.hpp> | ||||
| #include <components/misc/resourcehelpers.hpp> | ||||
| #include <components/resource/scenemanager.hpp> | ||||
| #include <components/sceneutil/optimizer.hpp> | ||||
| #include <components/sceneutil/clone.hpp> | ||||
| #include <components/sceneutil/util.hpp> | ||||
| #include <components/vfs/manager.hpp> | ||||
| 
 | ||||
| #include <osgParticle/ParticleProcessor> | ||||
| #include <osgParticle/ParticleSystemUpdater> | ||||
| 
 | ||||
| #include <components/sceneutil/lightmanager.hpp> | ||||
| #include <components/sceneutil/morphgeometry.hpp> | ||||
| #include <components/sceneutil/riggeometry.hpp> | ||||
| #include <components/settings/settings.hpp> | ||||
| #include <components/misc/rng.hpp> | ||||
| 
 | ||||
| #include "apps/openmw/mwworld/esmstore.hpp" | ||||
| #include "apps/openmw/mwbase/environment.hpp" | ||||
| #include "apps/openmw/mwbase/world.hpp" | ||||
| 
 | ||||
| #include "vismask.hpp" | ||||
| 
 | ||||
| namespace MWRender | ||||
| { | ||||
| 
 | ||||
|     bool typeFilter(int type, bool far) | ||||
|     { | ||||
|         switch (type) | ||||
|         { | ||||
|           case ESM::REC_STAT: | ||||
|           case ESM::REC_ACTI: | ||||
|           case ESM::REC_DOOR: | ||||
|             return true; | ||||
|           case ESM::REC_CONT: | ||||
|             return !far; | ||||
| 
 | ||||
|         default: | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     std::string getModel(int type, const std::string& id, const MWWorld::ESMStore& store) | ||||
|     { | ||||
|         switch (type) | ||||
|         { | ||||
|           case ESM::REC_STAT: | ||||
|             return store.get<ESM::Static>().searchStatic(id)->mModel; | ||||
|           case ESM::REC_ACTI: | ||||
|             return store.get<ESM::Activator>().searchStatic(id)->mModel; | ||||
|           case ESM::REC_DOOR: | ||||
|             return store.get<ESM::Door>().searchStatic(id)->mModel; | ||||
|           case ESM::REC_CONT: | ||||
|             return store.get<ESM::Container>().searchStatic(id)->mModel; | ||||
|           default: | ||||
|             return std::string(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     osg::ref_ptr<osg::Node> ObjectPaging::getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool activeGrid, const osg::Vec3f& viewPoint, bool compile) | ||||
|     { | ||||
|         if (activeGrid && !mActiveGrid) | ||||
|             return nullptr; | ||||
| 
 | ||||
|         ChunkId id = std::make_tuple(center, size, activeGrid); | ||||
| 
 | ||||
|         osg::ref_ptr<osg::Object> obj = mCache->getRefFromObjectCache(id); | ||||
|         if (obj) | ||||
|             return obj->asNode(); | ||||
|         else | ||||
|         { | ||||
|             osg::ref_ptr<osg::Node> node = createChunk(size, center, activeGrid, viewPoint, compile); | ||||
|             mCache->addEntryToObjectCache(id, node.get()); | ||||
|             return node; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     class CanOptimizeCallback : public SceneUtil::Optimizer::IsOperationPermissibleForObjectCallback | ||||
|     { | ||||
|     public: | ||||
|         virtual bool isOperationPermissibleForObjectImplementation(const SceneUtil::Optimizer* optimizer, const osg::Drawable* node,unsigned int option) const | ||||
|         { | ||||
|             return true; | ||||
|         } | ||||
|         virtual bool isOperationPermissibleForObjectImplementation(const SceneUtil::Optimizer* optimizer, const osg::Node* node,unsigned int option) const | ||||
|         { | ||||
|             return (node->getDataVariance() != osg::Object::DYNAMIC); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     class CopyOp : public osg::CopyOp | ||||
|     { | ||||
|     public: | ||||
|         bool mOptimizeBillboards = true; | ||||
|         float mSqrDistance = 0.f; | ||||
|         osg::Vec3f mViewVector; | ||||
|         mutable std::vector<const osg::Node*> mNodePath; | ||||
| 
 | ||||
|         void copy(const osg::Node* toCopy, osg::Group* attachTo) | ||||
|         { | ||||
|             const osg::Group* groupToCopy = toCopy->asGroup(); | ||||
|             if (toCopy->getStateSet() || toCopy->asTransform() || !groupToCopy) | ||||
|                 attachTo->addChild(operator()(toCopy)); | ||||
|             else | ||||
|             { | ||||
|                 for (unsigned int i=0; i<groupToCopy->getNumChildren(); ++i) | ||||
|                     attachTo->addChild(operator()(groupToCopy->getChild(i))); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         virtual osg::Node* operator() (const osg::Node* node) const | ||||
|         { | ||||
|             if (const osg::Drawable* d = node->asDrawable()) | ||||
|                 return operator()(d); | ||||
| 
 | ||||
|             if (dynamic_cast<const osgParticle::ParticleProcessor*>(node)) | ||||
|                 return nullptr; | ||||
|             if (dynamic_cast<const osgParticle::ParticleSystemUpdater*>(node)) | ||||
|                 return nullptr; | ||||
| 
 | ||||
|             if (const osg::Switch* sw = node->asSwitch()) | ||||
|             { | ||||
|                 osg::Group* n = new osg::Group; | ||||
|                 for (unsigned int i=0; i<sw->getNumChildren(); ++i) | ||||
|                     if (sw->getValue(i)) | ||||
|                         n->addChild(operator()(sw->getChild(i))); | ||||
|                 n->setDataVariance(osg::Object::STATIC); | ||||
|                 return n; | ||||
|             } | ||||
|             if (const osg::LOD* lod = dynamic_cast<const osg::LOD*>(node)) | ||||
|             { | ||||
|                 osg::Group* n = new osg::Group; | ||||
|                 for (unsigned int i=0; i<lod->getNumChildren(); ++i) | ||||
|                     if (lod->getMinRange(i) * lod->getMinRange(i) <= mSqrDistance && mSqrDistance < lod->getMaxRange(i) * lod->getMaxRange(i)) | ||||
|                         n->addChild(operator()(lod->getChild(i))); | ||||
|                 n->setDataVariance(osg::Object::STATIC); | ||||
|                 return n; | ||||
|             } | ||||
| 
 | ||||
|             mNodePath.push_back(node); | ||||
| 
 | ||||
|             osg::Node* cloned = static_cast<osg::Node*>(node->clone(*this)); | ||||
|             cloned->setDataVariance(osg::Object::STATIC); | ||||
|             cloned->setUserDataContainer(nullptr); | ||||
|             cloned->setName(""); | ||||
| 
 | ||||
|             mNodePath.pop_back(); | ||||
| 
 | ||||
|             handleCallbacks(node, cloned); | ||||
| 
 | ||||
|             return cloned; | ||||
|         } | ||||
|         void handleCallbacks(const osg::Node* node, osg::Node *cloned) const | ||||
|         { | ||||
|             for (const osg::Callback* callback = node->getCullCallback(); callback != nullptr; callback = callback->getNestedCallback()) | ||||
|             { | ||||
|                 if (callback->className() == std::string("BillboardCallback")) | ||||
|                 { | ||||
|                     if (mOptimizeBillboards) | ||||
|                     { | ||||
|                         handleBillboard(cloned); | ||||
|                         continue; | ||||
|                     } | ||||
|                     else | ||||
|                         cloned->setDataVariance(osg::Object::DYNAMIC); | ||||
|                 } | ||||
| 
 | ||||
|                 if (node->getCullCallback()->getNestedCallback()) | ||||
|                 { | ||||
|                     osg::Callback *clonedCallback = osg::clone(callback, osg::CopyOp::SHALLOW_COPY); | ||||
|                     clonedCallback->setNestedCallback(nullptr); | ||||
|                     cloned->addCullCallback(clonedCallback); | ||||
|                 } | ||||
|                 else | ||||
|                     cloned->addCullCallback(const_cast<osg::Callback*>(callback)); | ||||
|             } | ||||
|         } | ||||
|         void handleBillboard(osg::Node* node) const | ||||
|         { | ||||
|             osg::Transform* transform = node->asTransform(); | ||||
|             if (!transform) return; | ||||
|             osg::MatrixTransform* matrixTransform = transform->asMatrixTransform(); | ||||
|             if (!matrixTransform) return; | ||||
| 
 | ||||
|             osg::Matrix worldToLocal = osg::Matrix::identity(); | ||||
|             for (auto node : mNodePath) | ||||
|                 if (const osg::Transform* t = node->asTransform()) | ||||
|                     t->computeWorldToLocalMatrix(worldToLocal, nullptr); | ||||
|             worldToLocal = osg::Matrix::orthoNormal(worldToLocal); | ||||
| 
 | ||||
|             osg::Matrix billboardMatrix; | ||||
|             osg::Vec3f viewVector = -(mViewVector + worldToLocal.getTrans()); | ||||
|             viewVector.normalize(); | ||||
|             osg::Vec3f right = viewVector ^ osg::Vec3f(0,0,1); | ||||
|             right.normalize(); | ||||
|             osg::Vec3f up = right ^ viewVector; | ||||
|             up.normalize(); | ||||
|             billboardMatrix.makeLookAt(osg::Vec3f(0,0,0), viewVector, up); | ||||
|             billboardMatrix.invert(billboardMatrix); | ||||
| 
 | ||||
|             const osg::Matrix& oldMatrix = matrixTransform->getMatrix(); | ||||
|             float mag[3]; // attempt to preserve scale
 | ||||
|             for (int i=0;i<3;++i) | ||||
|                 mag[i] = std::sqrt(oldMatrix(0,i) * oldMatrix(0,i) + oldMatrix(1,i) * oldMatrix(1,i) + oldMatrix(2,i) * oldMatrix(2,i)); | ||||
|             osg::Matrix newMatrix; | ||||
|             worldToLocal.setTrans(0,0,0); | ||||
|             newMatrix *= worldToLocal; | ||||
|             newMatrix.preMult(billboardMatrix); | ||||
|             newMatrix.preMultScale(osg::Vec3f(mag[0], mag[1], mag[2])); | ||||
|             newMatrix.setTrans(oldMatrix.getTrans()); | ||||
| 
 | ||||
|             matrixTransform->setMatrix(newMatrix); | ||||
|         } | ||||
|         virtual osg::Drawable* operator() (const osg::Drawable* drawable) const | ||||
|         { | ||||
|             if (dynamic_cast<const osgParticle::ParticleSystem*>(drawable)) | ||||
|                 return nullptr; | ||||
| 
 | ||||
|             if (const SceneUtil::RigGeometry* rig = dynamic_cast<const SceneUtil::RigGeometry*>(drawable)) | ||||
|                 return operator()(rig->getSourceGeometry()); | ||||
|             if (const SceneUtil::MorphGeometry* morph = dynamic_cast<const SceneUtil::MorphGeometry*>(drawable)) | ||||
|                 return operator()(morph->getSourceGeometry()); | ||||
| 
 | ||||
|             if (getCopyFlags() & DEEP_COPY_DRAWABLES) | ||||
|             { | ||||
|                 osg::Drawable* d = static_cast<osg::Drawable*>(drawable->clone(*this)); | ||||
|                 d->setDataVariance(osg::Object::STATIC); | ||||
|                 d->setUserDataContainer(nullptr); | ||||
|                 d->setName(""); | ||||
|                 return d; | ||||
|             } | ||||
|             else | ||||
|                 return const_cast<osg::Drawable*>(drawable); | ||||
|         } | ||||
|         virtual osg::Callback* operator() (const osg::Callback* callback) const | ||||
|         { | ||||
|             return nullptr; | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     class TemplateRef : public osg::Object | ||||
|     { | ||||
|     public: | ||||
|         TemplateRef() {} | ||||
|         TemplateRef(const TemplateRef& copy, const osg::CopyOp&) : mObjects(copy.mObjects) {} | ||||
|         META_Object(MWRender, TemplateRef) | ||||
|         std::vector<osg::ref_ptr<const Object>> mObjects; | ||||
|     }; | ||||
| 
 | ||||
|     class RefnumSet : public osg::Object | ||||
|     { | ||||
|     public: | ||||
|         RefnumSet(){} | ||||
|         RefnumSet(const RefnumSet& copy, const osg::CopyOp&) : mRefnums(copy.mRefnums) {} | ||||
|         META_Object(MWRender, RefnumSet) | ||||
|         std::set<ESM::RefNum> mRefnums; | ||||
|     }; | ||||
| 
 | ||||
|     class AnalyzeVisitor : public osg::NodeVisitor | ||||
|     { | ||||
|     public: | ||||
|         AnalyzeVisitor() | ||||
|          : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) | ||||
|          , mCurrentStateSet(nullptr) {} | ||||
| 
 | ||||
|         typedef std::unordered_map<osg::StateSet*, unsigned int> StateSetCounter; | ||||
|         struct Result | ||||
|         { | ||||
|             StateSetCounter mStateSetCounter; | ||||
|             unsigned int mNumVerts = 0; | ||||
|         }; | ||||
| 
 | ||||
|         virtual void apply(osg::Node& node) | ||||
|         { | ||||
|             if (node.getStateSet()) | ||||
|                 mCurrentStateSet = node.getStateSet(); | ||||
|             traverse(node); | ||||
|         } | ||||
|         virtual void apply(osg::Geometry& geom) | ||||
|         { | ||||
|             mResult.mNumVerts += geom.getVertexArray()->getNumElements(); | ||||
|             ++mResult.mStateSetCounter[mCurrentStateSet]; | ||||
|             ++mGlobalStateSetCounter[mCurrentStateSet]; | ||||
|         } | ||||
|         Result retrieveResult() | ||||
|         { | ||||
|             Result result = mResult; | ||||
|             mResult = Result(); | ||||
|             mCurrentStateSet = nullptr; | ||||
|             return result; | ||||
|         } | ||||
|         void addInstance(const Result& result) | ||||
|         { | ||||
|             for (auto pair : result.mStateSetCounter) | ||||
|                 mGlobalStateSetCounter[pair.first] += pair.second; | ||||
|         } | ||||
|         float getMergeBenefit(const Result& result) | ||||
|         { | ||||
|             if (result.mStateSetCounter.empty()) return 1; | ||||
|             float mergeBenefit = 0; | ||||
|             for (auto pair : result.mStateSetCounter) | ||||
|             { | ||||
|                 mergeBenefit += mGlobalStateSetCounter[pair.first]; | ||||
|             } | ||||
|             mergeBenefit /= result.mStateSetCounter.size(); | ||||
|             return mergeBenefit; | ||||
|         } | ||||
| 
 | ||||
|         Result mResult; | ||||
|         osg::StateSet* mCurrentStateSet; | ||||
|         StateSetCounter mGlobalStateSetCounter; | ||||
|     }; | ||||
| 
 | ||||
|     class DebugVisitor : public osg::NodeVisitor | ||||
|     { | ||||
|     public: | ||||
|         DebugVisitor() : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) {} | ||||
|         virtual void apply(osg::Drawable& node) | ||||
|         { | ||||
|             osg::ref_ptr<osg::Material> m (new osg::Material); | ||||
|             osg::Vec4f color(Misc::Rng::rollProbability(), Misc::Rng::rollProbability(), Misc::Rng::rollProbability(), 0.f); | ||||
|             color.normalize(); | ||||
|             m->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.1f,0.1f,0.1f,1.f)); | ||||
|             m->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.1f,0.1f,0.1f,1.f)); | ||||
|             m->setColorMode(osg::Material::OFF); | ||||
|             m->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(color)); | ||||
|             osg::ref_ptr<osg::StateSet> stateset = node.getStateSet() ? osg::clone(node.getStateSet(), osg::CopyOp::SHALLOW_COPY) : new osg::StateSet; | ||||
|             stateset->setAttribute(m); | ||||
|             stateset->addUniform(new osg::Uniform("colorMode", 0)); | ||||
|             node.setStateSet(stateset); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     class AddRefnumMarkerVisitor : public osg::NodeVisitor | ||||
|     { | ||||
|     public: | ||||
|         AddRefnumMarkerVisitor(const ESM::RefNum &refnum) : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN), mRefnum(refnum) {} | ||||
|         ESM::RefNum mRefnum; | ||||
|         virtual void apply(osg::Geometry &node) | ||||
|         { | ||||
|             osg::ref_ptr<RefnumMarker> marker (new RefnumMarker); | ||||
|             marker->mRefnum = mRefnum; | ||||
|             if (osg::Array* array = node.getVertexArray()) | ||||
|                 marker->mNumVertices = array->getNumElements(); | ||||
|             node.getOrCreateUserDataContainer()->addUserObject(marker); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     ObjectPaging::ObjectPaging(Resource::SceneManager* sceneManager) | ||||
|             : GenericResourceManager<ChunkId>(nullptr) | ||||
|          , mSceneManager(sceneManager) | ||||
|          , mRefTrackerLocked(false) | ||||
|     { | ||||
|         mActiveGrid = Settings::Manager::getBool("object paging active grid", "Terrain"); | ||||
|         mDebugBatches = Settings::Manager::getBool("object paging debug batches", "Terrain"); | ||||
|         mMergeFactor = Settings::Manager::getFloat("object paging merge factor", "Terrain"); | ||||
|         mMinSize = Settings::Manager::getFloat("object paging min size", "Terrain"); | ||||
|         mMinSizeMergeFactor = Settings::Manager::getFloat("object paging min size merge factor", "Terrain"); | ||||
|         mMinSizeCostMultiplier = Settings::Manager::getFloat("object paging min size cost multiplier", "Terrain"); | ||||
|     } | ||||
| 
 | ||||
|     osg::ref_ptr<osg::Node> ObjectPaging::createChunk(float size, const osg::Vec2f& center, bool activeGrid, const osg::Vec3f& viewPoint, bool compile) | ||||
|     { | ||||
|         osg::Vec2i startCell = osg::Vec2i(std::floor(center.x() - size/2.f), std::floor(center.y() - size/2.f)); | ||||
| 
 | ||||
|         osg::Vec3f worldCenter = osg::Vec3f(center.x(), center.y(), 0)*ESM::Land::REAL_SIZE; | ||||
|         osg::Vec3f relativeViewPoint = viewPoint - worldCenter; | ||||
| 
 | ||||
|         std::map<ESM::RefNum, ESM::CellRef> refs; | ||||
|         std::vector<ESM::ESMReader> esm; | ||||
|         const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); | ||||
| 
 | ||||
|         for (int cellX = startCell.x(); cellX < startCell.x() + size; ++cellX) | ||||
|         { | ||||
|             for (int cellY = startCell.y(); cellY < startCell.y() + size; ++cellY) | ||||
|             { | ||||
|                 const ESM::Cell* cell = store.get<ESM::Cell>().searchStatic(cellX, cellY); | ||||
|                 if (!cell) continue; | ||||
|                 for (size_t i=0; i<cell->mContextList.size(); ++i) | ||||
|                 { | ||||
|                     try | ||||
|                     { | ||||
|                         unsigned int index = cell->mContextList.at(i).index; | ||||
|                         if (esm.size()<=index) | ||||
|                             esm.resize(index+1); | ||||
|                         cell->restore(esm[index], i); | ||||
|                         ESM::CellRef ref; | ||||
|                         ref.mRefNum.mContentFile = ESM::RefNum::RefNum_NoContentFile; | ||||
|                         bool deleted = false; | ||||
|                         while(cell->getNextRef(esm[index], ref, deleted)) | ||||
|                         { | ||||
|                             Misc::StringUtils::lowerCaseInPlace(ref.mRefID); | ||||
|                             if (std::find(cell->mMovedRefs.begin(), cell->mMovedRefs.end(), ref.mRefNum) != cell->mMovedRefs.end()) continue; | ||||
|                             int type = store.findStatic(ref.mRefID); | ||||
|                             if (!typeFilter(type,size>=2)) continue; | ||||
|                             if (deleted) { refs.erase(ref.mRefNum); continue; } | ||||
|                             refs[ref.mRefNum] = ref; | ||||
|                         } | ||||
|                     } | ||||
|                     catch (std::exception& e) | ||||
|                     { | ||||
|                         continue; | ||||
|                     } | ||||
|                 } | ||||
|                 for (ESM::CellRefTracker::const_iterator it = cell->mLeasedRefs.begin(); it != cell->mLeasedRefs.end(); ++it) | ||||
|                 { | ||||
|                     ESM::CellRef ref = it->first; | ||||
|                     Misc::StringUtils::lowerCaseInPlace(ref.mRefID); | ||||
|                     bool deleted = it->second; | ||||
|                     if (deleted) { refs.erase(ref.mRefNum); continue; } | ||||
|                     int type = store.findStatic(ref.mRefID); | ||||
|                     if (!typeFilter(type,size>=2)) continue; | ||||
|                     refs[ref.mRefNum] = ref; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if (activeGrid) | ||||
|         { | ||||
|             OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mRefTrackerMutex); | ||||
|             for (auto ref : getRefTracker().mBlacklist) | ||||
|                 refs.erase(ref); | ||||
|         } | ||||
| 
 | ||||
|         osg::Vec2f minBound = (center - osg::Vec2f(size/2.f, size/2.f)); | ||||
|         osg::Vec2f maxBound = (center + osg::Vec2f(size/2.f, size/2.f)); | ||||
|         struct InstanceList | ||||
|         { | ||||
|             std::vector<const ESM::CellRef*> mInstances; | ||||
|             AnalyzeVisitor::Result mAnalyzeResult; | ||||
|             bool mNeedCompile = false; | ||||
|         }; | ||||
|         typedef std::map<osg::ref_ptr<const osg::Node>, InstanceList> NodeMap; | ||||
|         NodeMap nodes; | ||||
|         osg::ref_ptr<RefnumSet> refnumSet = activeGrid ? new RefnumSet : nullptr; | ||||
|         AnalyzeVisitor analyzeVisitor; | ||||
|         float minSize = mMinSize; | ||||
|         if (mMinSizeMergeFactor) | ||||
|             minSize *= mMinSizeMergeFactor; | ||||
|         for (const auto& pair : refs) | ||||
|         { | ||||
|             const ESM::CellRef& ref = pair.second; | ||||
| 
 | ||||
|             osg::Vec3f pos = ref.mPos.asVec3(); | ||||
|             if (size < 1.f) | ||||
|             { | ||||
|                 osg::Vec3f cellPos = pos / ESM::Land::REAL_SIZE; | ||||
|                 if ((minBound.x() > std::floor(minBound.x()) && cellPos.x() < minBound.x()) || (minBound.y() > std::floor(minBound.y()) && cellPos.y() < minBound.y()) | ||||
|                  || (maxBound.x() < std::ceil(maxBound.x()) && cellPos.x() >= maxBound.x()) || (minBound.y() < std::ceil(maxBound.y()) && cellPos.y() >= maxBound.y())) | ||||
|                     continue; | ||||
|             } | ||||
| 
 | ||||
|             float dSqr = (viewPoint - pos).length2(); | ||||
|             if (!activeGrid) | ||||
|             { | ||||
|                 OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mSizeCacheMutex); | ||||
|                 SizeCache::iterator found = mSizeCache.find(pair.first); | ||||
|                 if (found != mSizeCache.end() && found->second < dSqr*minSize*minSize) | ||||
|                     continue; | ||||
|             } | ||||
| 
 | ||||
|             if (ref.mRefID == "prisonmarker" || ref.mRefID == "divinemarker" || ref.mRefID == "templemarker" || ref.mRefID == "northmarker") | ||||
|                 continue; // marker objects that have a hardcoded function in the game logic, should be hidden from the player
 | ||||
| 
 | ||||
|             int type = store.findStatic(ref.mRefID); | ||||
|             std::string model = getModel(type, ref.mRefID, store); | ||||
|             if (model.empty()) continue; | ||||
|             model = "meshes/" + model; | ||||
| 
 | ||||
|             if (activeGrid && type != ESM::REC_STAT) | ||||
|             { | ||||
|                 model = Misc::ResourceHelpers::correctActorModelPath(model, mSceneManager->getVFS()); | ||||
|                 std::string kfname = Misc::StringUtils::lowerCase(model); | ||||
|                 if(kfname.size() > 4 && kfname.compare(kfname.size()-4, 4, ".nif") == 0) | ||||
|                 { | ||||
|                     kfname.replace(kfname.size()-4, 4, ".kf"); | ||||
|                     if (mSceneManager->getVFS()->exists(kfname)) | ||||
|                         continue; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             osg::ref_ptr<const osg::Node> cnode = mSceneManager->getTemplate(model, false); | ||||
| 
 | ||||
|             if (activeGrid) | ||||
|             { | ||||
|                 if (cnode->getNumChildrenRequiringUpdateTraversal() > 0 || SceneUtil::hasUserDescription(cnode, Constants::NightDayLabel) || SceneUtil::hasUserDescription(cnode, Constants::HerbalismLabel)) | ||||
|                     continue; | ||||
|                 else | ||||
|                     refnumSet->mRefnums.insert(pair.first); | ||||
|             } | ||||
| 
 | ||||
|             { | ||||
|                 OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mRefTrackerMutex); | ||||
|                 if (getRefTracker().mDisabled.count(pair.first)) | ||||
|                     continue; | ||||
|             } | ||||
| 
 | ||||
|             float radius2 = cnode->getBound().radius2() * ref.mScale*ref.mScale; | ||||
|             if (radius2 < dSqr*minSize*minSize && !activeGrid) | ||||
|             { | ||||
|                 OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mSizeCacheMutex); | ||||
|                 mSizeCache[pair.first] = radius2; | ||||
|                 continue; | ||||
|             } | ||||
| 
 | ||||
|             auto emplaced = nodes.emplace(cnode, InstanceList()); | ||||
|             if (emplaced.second) | ||||
|             { | ||||
|                 const_cast<osg::Node*>(cnode.get())->accept(analyzeVisitor); // const-trickery required because there is no const version of NodeVisitor
 | ||||
|                 emplaced.first->second.mAnalyzeResult = analyzeVisitor.retrieveResult(); | ||||
|                 emplaced.first->second.mNeedCompile = compile && cnode->referenceCount() <= 3; | ||||
|             } | ||||
|             else | ||||
|                 analyzeVisitor.addInstance(emplaced.first->second.mAnalyzeResult); | ||||
|             emplaced.first->second.mInstances.push_back(&ref); | ||||
|         } | ||||
| 
 | ||||
|         osg::ref_ptr<osg::Group> group = new osg::Group; | ||||
|         osg::ref_ptr<osg::Group> mergeGroup = new osg::Group; | ||||
|         osg::ref_ptr<TemplateRef> templateRefs = new TemplateRef; | ||||
|         osgUtil::StateToCompile stateToCompile(0, nullptr); | ||||
|         CopyOp copyop; | ||||
|         for (const auto& pair : nodes) | ||||
|         { | ||||
|             const osg::Node* cnode = pair.first; | ||||
| 
 | ||||
|             const AnalyzeVisitor::Result& analyzeResult = pair.second.mAnalyzeResult; | ||||
| 
 | ||||
|             float mergeCost = analyzeResult.mNumVerts * size; | ||||
|             float mergeBenefit = analyzeVisitor.getMergeBenefit(analyzeResult) * mMergeFactor; | ||||
|             bool merge = mergeBenefit > mergeCost; | ||||
| 
 | ||||
|             float minSizeMerged = mMinSize; | ||||
|             float factor2 = mergeBenefit > 0 ? std::min(1.f, mergeCost * mMinSizeCostMultiplier / mergeBenefit) : 1; | ||||
|             float minSizeMergeFactor2 = (1-factor2) * mMinSizeMergeFactor + factor2; | ||||
|             if (minSizeMergeFactor2 > 0) | ||||
|                 minSizeMerged *= minSizeMergeFactor2; | ||||
| 
 | ||||
|             unsigned int numinstances = 0; | ||||
|             for (auto cref : pair.second.mInstances) | ||||
|             { | ||||
|                 const ESM::CellRef& ref = *cref; | ||||
|                 osg::Vec3f pos = ref.mPos.asVec3(); | ||||
| 
 | ||||
|                 if (!activeGrid && minSizeMerged != minSize && cnode->getBound().radius2() * cref->mScale*cref->mScale < (viewPoint-pos).length2()*minSizeMerged*minSizeMerged) | ||||
|                     continue; | ||||
| 
 | ||||
|                 osg::Matrixf matrix; | ||||
|                 matrix.preMultTranslate(pos - worldCenter); | ||||
|                 matrix.preMultRotate( osg::Quat(ref.mPos.rot[2], osg::Vec3f(0,0,-1)) * | ||||
|                                         osg::Quat(ref.mPos.rot[1], osg::Vec3f(0,-1,0)) * | ||||
|                                         osg::Quat(ref.mPos.rot[0], osg::Vec3f(-1,0,0)) ); | ||||
|                 matrix.preMultScale(osg::Vec3f(ref.mScale, ref.mScale, ref.mScale)); | ||||
|                 osg::ref_ptr<osg::MatrixTransform> trans = new osg::MatrixTransform(matrix); | ||||
|                 trans->setDataVariance(osg::Object::STATIC); | ||||
| 
 | ||||
|                 copyop.setCopyFlags(merge ? osg::CopyOp::DEEP_COPY_NODES|osg::CopyOp::DEEP_COPY_DRAWABLES : osg::CopyOp::DEEP_COPY_NODES); | ||||
|                 copyop.mOptimizeBillboards = (size > 1/4.f); | ||||
|                 copyop.mNodePath.push_back(trans); | ||||
|                 copyop.mSqrDistance = (viewPoint - pos).length2(); | ||||
|                 copyop.mViewVector = (viewPoint - worldCenter); | ||||
|                 copyop.copy(cnode, trans); | ||||
|                 copyop.mNodePath.pop_back(); | ||||
| 
 | ||||
|                 if (activeGrid) | ||||
|                 { | ||||
|                     if (merge) | ||||
|                     { | ||||
|                         AddRefnumMarkerVisitor visitor(ref.mRefNum); | ||||
|                         trans->accept(visitor); | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         osg::ref_ptr<RefnumMarker> marker = new RefnumMarker; marker->mRefnum = ref.mRefNum; | ||||
|                         trans->getOrCreateUserDataContainer()->addUserObject(marker); | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|                 osg::Group* attachTo = merge ? mergeGroup : group; | ||||
|                 attachTo->addChild(trans); | ||||
|                 ++numinstances; | ||||
|             } | ||||
|             if (numinstances > 0) | ||||
|             { | ||||
|                 // add a ref to the original template, to hint to the cache that it's still being used and should be kept in cache
 | ||||
|                 templateRefs->mObjects.push_back(cnode); | ||||
| 
 | ||||
|                 if (pair.second.mNeedCompile) | ||||
|                 { | ||||
|                     int mode = osgUtil::GLObjectsVisitor::COMPILE_STATE_ATTRIBUTES; | ||||
|                     if (!merge) | ||||
|                         mode |= osgUtil::GLObjectsVisitor::COMPILE_DISPLAY_LISTS; | ||||
|                     stateToCompile._mode = mode; | ||||
|                     const_cast<osg::Node*>(cnode)->accept(stateToCompile); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if (mergeGroup->getNumChildren()) | ||||
|         { | ||||
|             SceneUtil::Optimizer optimizer; | ||||
|             if (size > 1/8.f) | ||||
|             { | ||||
|                 optimizer.setViewPoint(relativeViewPoint); | ||||
|                 optimizer.setMergeAlphaBlending(true); | ||||
|             } | ||||
|             optimizer.setIsOperationPermissibleForObjectCallback(new CanOptimizeCallback); | ||||
|             unsigned int options = SceneUtil::Optimizer::FLATTEN_STATIC_TRANSFORMS|SceneUtil::Optimizer::REMOVE_REDUNDANT_NODES|SceneUtil::Optimizer::MERGE_GEOMETRY; | ||||
|             optimizer.optimize(mergeGroup, options); | ||||
| 
 | ||||
|             group->addChild(mergeGroup); | ||||
| 
 | ||||
|             if (mDebugBatches) | ||||
|             { | ||||
|                 DebugVisitor dv; | ||||
|                 mergeGroup->accept(dv); | ||||
|             } | ||||
|             if (compile) | ||||
|             { | ||||
|                 stateToCompile._mode = osgUtil::GLObjectsVisitor::COMPILE_DISPLAY_LISTS; | ||||
|                 mergeGroup->accept(stateToCompile); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         auto ico = mSceneManager->getIncrementalCompileOperation(); | ||||
|         if (!stateToCompile.empty() && ico) | ||||
|         { | ||||
|             auto compileSet = new osgUtil::IncrementalCompileOperation::CompileSet(group); | ||||
|             compileSet->buildCompileMap(ico->getContextSet(), stateToCompile); | ||||
|             ico->add(compileSet, false); | ||||
|         } | ||||
| 
 | ||||
|         group->getBound(); | ||||
|         group->setNodeMask(Mask_Static); | ||||
|         osg::UserDataContainer* udc = group->getOrCreateUserDataContainer(); | ||||
|         if (activeGrid) | ||||
|         { | ||||
|             udc->addUserObject(refnumSet); | ||||
|             group->addCullCallback(new SceneUtil::LightListCallback); | ||||
|         } | ||||
|         udc->addUserObject(templateRefs); | ||||
| 
 | ||||
|         return group; | ||||
|     } | ||||
| 
 | ||||
|     unsigned int ObjectPaging::getNodeMask() | ||||
|     { | ||||
|         return Mask_Static; | ||||
|     } | ||||
| 
 | ||||
|     struct ClearCacheFunctor | ||||
|     { | ||||
|         void operator()(MWRender::ChunkId id, osg::Object* obj) | ||||
|         { | ||||
|             if (intersects(id, mPosition)) | ||||
|                 mToClear.insert(id); | ||||
|         } | ||||
|         bool intersects(ChunkId id, osg::Vec3f pos) | ||||
|         { | ||||
|             if (mActiveGridOnly && !std::get<2>(id)) return false; | ||||
|             pos /= ESM::Land::REAL_SIZE; | ||||
|             osg::Vec2f center = std::get<0>(id); | ||||
|             float halfSize = std::get<1>(id)/2; | ||||
|             return pos.x() >= center.x()-halfSize && pos.y() >= center.y()-halfSize && pos.x() <= center.x()+halfSize && pos.y() <= center.y()+halfSize; | ||||
|         } | ||||
|         osg::Vec3f mPosition; | ||||
|         std::set<MWRender::ChunkId> mToClear; | ||||
|         bool mActiveGridOnly = false; | ||||
|     }; | ||||
| 
 | ||||
|     bool ObjectPaging::enableObject(int type, const ESM::RefNum & refnum, const osg::Vec3f& pos, bool enabled) | ||||
|     { | ||||
|         if (!typeFilter(type, false)) | ||||
|             return false; | ||||
| 
 | ||||
|         { | ||||
|             OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mRefTrackerMutex); | ||||
|             if (enabled && !getWritableRefTracker().mDisabled.erase(refnum)) return false; | ||||
|             if (!enabled && !getWritableRefTracker().mDisabled.insert(refnum).second) return false; | ||||
|             if (mRefTrackerLocked) return false; | ||||
|         } | ||||
| 
 | ||||
|         ClearCacheFunctor ccf; | ||||
|         ccf.mPosition = pos; | ||||
|         mCache->call(ccf); | ||||
|         if (ccf.mToClear.empty()) return false; | ||||
|         for (auto chunk : ccf.mToClear) | ||||
|             mCache->removeFromObjectCache(chunk); | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     bool ObjectPaging::blacklistObject(int type, const ESM::RefNum & refnum, const osg::Vec3f& pos) | ||||
|     { | ||||
|         if (!typeFilter(type, false)) | ||||
|             return false; | ||||
| 
 | ||||
|         { | ||||
|             OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mRefTrackerMutex); | ||||
|             if (!getWritableRefTracker().mBlacklist.insert(refnum).second) return false; | ||||
|             if (mRefTrackerLocked) return false; | ||||
|         } | ||||
| 
 | ||||
|         ClearCacheFunctor ccf; | ||||
|         ccf.mPosition = pos; | ||||
|         ccf.mActiveGridOnly = true; | ||||
|         mCache->call(ccf); | ||||
|         if (ccf.mToClear.empty()) return false; | ||||
|         for (auto chunk : ccf.mToClear) | ||||
|             mCache->removeFromObjectCache(chunk); | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     void ObjectPaging::clear() | ||||
|     { | ||||
|         OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mRefTrackerMutex); | ||||
|         mRefTrackerNew.mDisabled.clear(); | ||||
|         mRefTrackerNew.mBlacklist.clear(); | ||||
|         mRefTrackerLocked = true; | ||||
|     } | ||||
| 
 | ||||
|     bool ObjectPaging::unlockCache() | ||||
|     { | ||||
|         if (!mRefTrackerLocked) return false; | ||||
|         { | ||||
|             OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mRefTrackerMutex); | ||||
|             mRefTrackerLocked = false; | ||||
|             if (mRefTracker == mRefTrackerNew) | ||||
|                 return false; | ||||
|             else | ||||
|                 mRefTracker = mRefTrackerNew; | ||||
|         } | ||||
|         mCache->clear(); | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     struct GetRefnumsFunctor | ||||
|     { | ||||
|         GetRefnumsFunctor(std::set<ESM::RefNum>& output) : mOutput(output) {} | ||||
|         void operator()(MWRender::ChunkId chunkId, osg::Object* obj) | ||||
|         { | ||||
|             if (!std::get<2>(chunkId)) return; | ||||
|             const osg::Vec2f& center = std::get<0>(chunkId); | ||||
|             bool activeGrid = (center.x() > mActiveGrid.x() || center.y() > mActiveGrid.y() || center.x() < mActiveGrid.z() || center.y() < mActiveGrid.w()); | ||||
|             if (!activeGrid) return; | ||||
| 
 | ||||
|             osg::UserDataContainer* udc = obj->getUserDataContainer(); | ||||
|             if (udc && udc->getNumUserObjects()) | ||||
|             { | ||||
|                 RefnumSet* refnums = dynamic_cast<RefnumSet*>(udc->getUserObject(0)); | ||||
|                 if (!refnums) return; | ||||
|                 mOutput.insert(refnums->mRefnums.begin(), refnums->mRefnums.end()); | ||||
|             } | ||||
|         } | ||||
|         osg::Vec4i mActiveGrid; | ||||
|         std::set<ESM::RefNum>& mOutput; | ||||
|     }; | ||||
| 
 | ||||
|     void ObjectPaging::getPagedRefnums(const osg::Vec4i &activeGrid, std::set<ESM::RefNum> &out) | ||||
|     { | ||||
|         GetRefnumsFunctor grf(out); | ||||
|         grf.mActiveGrid = activeGrid; | ||||
|         mCache->call(grf); | ||||
|     } | ||||
| 
 | ||||
|     void ObjectPaging::reportStats(unsigned int frameNumber, osg::Stats *stats) const | ||||
|     { | ||||
|         stats->setAttribute(frameNumber, "Object Chunk", mCache->getCacheSize()); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										92
									
								
								apps/openmw/mwrender/objectpaging.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								apps/openmw/mwrender/objectpaging.hpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,92 @@ | |||
| #ifndef OPENMW_COMPONENTS_ESMPAGING_CHUNKMANAGER_H | ||||
| #define OPENMW_COMPONENTS_ESMPAGING_CHUNKMANAGER_H | ||||
| 
 | ||||
| #include <components/terrain/quadtreeworld.hpp> | ||||
| #include <components/resource/resourcemanager.hpp> | ||||
| #include <components/esm/loadcell.hpp> | ||||
| 
 | ||||
| #include <OpenThreads/Mutex> | ||||
| 
 | ||||
| namespace Resource | ||||
| { | ||||
|     class SceneManager; | ||||
| } | ||||
| namespace MWWorld | ||||
| { | ||||
|     class ESMStore; | ||||
| } | ||||
| 
 | ||||
| namespace MWRender | ||||
| { | ||||
| 
 | ||||
|     typedef std::tuple<osg::Vec2f, float, bool> ChunkId; // Center, Size, ActiveGrid
 | ||||
| 
 | ||||
|     class ObjectPaging : public Resource::GenericResourceManager<ChunkId>, public Terrain::QuadTreeWorld::ChunkManager | ||||
|     { | ||||
|     public: | ||||
|         ObjectPaging(Resource::SceneManager* sceneManager); | ||||
|         ~ObjectPaging() = default; | ||||
| 
 | ||||
|         osg::ref_ptr<osg::Node> getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool activeGrid, const osg::Vec3f& viewPoint, bool compile) override; | ||||
| 
 | ||||
|         osg::ref_ptr<osg::Node> createChunk(float size, const osg::Vec2f& center, bool activeGrid, const osg::Vec3f& viewPoint, bool compile); | ||||
| 
 | ||||
|         virtual unsigned int getNodeMask() override; | ||||
| 
 | ||||
|         /// @return true if view needs rebuild
 | ||||
|         bool enableObject(int type, const ESM::RefNum & refnum, const osg::Vec3f& pos, bool enabled); | ||||
| 
 | ||||
|         /// @return true if view needs rebuild
 | ||||
|         bool blacklistObject(int type, const ESM::RefNum & refnum, const osg::Vec3f& pos); | ||||
| 
 | ||||
|         void clear(); | ||||
| 
 | ||||
|         /// Must be called after clear() before rendering starts.
 | ||||
|         /// @return true if view needs rebuild
 | ||||
|         bool unlockCache(); | ||||
| 
 | ||||
|         void reportStats(unsigned int frameNumber, osg::Stats* stats) const override; | ||||
| 
 | ||||
|         void getPagedRefnums(const osg::Vec4i &activeGrid, std::set<ESM::RefNum> &out); | ||||
| 
 | ||||
|     private: | ||||
|         Resource::SceneManager* mSceneManager; | ||||
|         bool mActiveGrid; | ||||
|         bool mDebugBatches; | ||||
|         float mMergeFactor; | ||||
|         float mMinSize; | ||||
|         float mMinSizeMergeFactor; | ||||
|         float mMinSizeCostMultiplier; | ||||
| 
 | ||||
|         OpenThreads::Mutex mRefTrackerMutex; | ||||
|         struct RefTracker | ||||
|         { | ||||
|             std::set<ESM::RefNum> mDisabled; | ||||
|             std::set<ESM::RefNum> mBlacklist; | ||||
|             bool operator==(const RefTracker&other) { return mDisabled == other.mDisabled && mBlacklist == other.mBlacklist; } | ||||
|         }; | ||||
|         RefTracker mRefTracker; | ||||
|         RefTracker mRefTrackerNew; | ||||
|         bool mRefTrackerLocked; | ||||
| 
 | ||||
|         const RefTracker& getRefTracker() const { return mRefTracker; } | ||||
|         RefTracker& getWritableRefTracker() { return mRefTrackerLocked ? mRefTrackerNew : mRefTracker; } | ||||
| 
 | ||||
|         OpenThreads::Mutex mSizeCacheMutex; | ||||
|         typedef std::map<ESM::RefNum, float> SizeCache; | ||||
|         SizeCache mSizeCache; | ||||
|     }; | ||||
| 
 | ||||
|     class RefnumMarker : public osg::Object | ||||
|     { | ||||
|     public: | ||||
|         RefnumMarker() : mNumVertices(0) { mRefnum.unset(); } | ||||
|         RefnumMarker(const RefnumMarker ©, osg::CopyOp co) : mRefnum(copy.mRefnum), mNumVertices(copy.mNumVertices) {} | ||||
|         META_Object(MWRender, RefnumMarker) | ||||
| 
 | ||||
|         ESM::RefNum mRefnum; | ||||
|         unsigned int mNumVertices; | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
|  | @ -8,6 +8,7 @@ | |||
| 
 | ||||
| #include <components/esm/loadpgrd.hpp> | ||||
| #include <components/sceneutil/pathgridutil.hpp> | ||||
| #include <components/misc/coordinateconverter.hpp> | ||||
| 
 | ||||
| #include "../mwbase/world.hpp" // these includes can be removed once the static-hack is gone
 | ||||
| #include "../mwbase/environment.hpp" | ||||
|  | @ -15,7 +16,6 @@ | |||
| #include "../mwworld/cellstore.hpp" | ||||
| #include "../mwworld/esmstore.hpp" | ||||
| #include "../mwmechanics/pathfinding.hpp" | ||||
| #include "../mwmechanics/coordinateconverter.hpp" | ||||
| 
 | ||||
| #include "vismask.hpp" | ||||
| 
 | ||||
|  | @ -105,7 +105,7 @@ void Pathgrid::enableCellPathgrid(const MWWorld::CellStore *store) | |||
|     if (!pathgrid) return; | ||||
| 
 | ||||
|     osg::Vec3f cellPathGridPos(0, 0, 0); | ||||
|     MWMechanics::CoordinateConverter(store->getCell()).toWorld(cellPathGridPos); | ||||
|     Misc::CoordinateConverter(store->getCell()).toWorld(cellPathGridPos); | ||||
| 
 | ||||
|     osg::ref_ptr<osg::PositionAttitudeTransform> cellPathGrid = new osg::PositionAttitudeTransform; | ||||
|     cellPathGrid->setPosition(cellPathGridPos); | ||||
|  |  | |||
|  | @ -70,6 +70,8 @@ | |||
| #include "actorspaths.hpp" | ||||
| #include "recastmesh.hpp" | ||||
| #include "fogmanager.hpp" | ||||
| #include "objectpaging.hpp" | ||||
| 
 | ||||
| 
 | ||||
| namespace MWRender | ||||
| { | ||||
|  | @ -258,7 +260,6 @@ namespace MWRender | |||
|         { | ||||
|             mViewer->setIncrementalCompileOperation(new osgUtil::IncrementalCompileOperation); | ||||
|             mViewer->getIncrementalCompileOperation()->setTargetFrameRate(Settings::Manager::getFloat("target framerate", "Cells")); | ||||
|             mViewer->getIncrementalCompileOperation()->setMaximumNumOfObjectsToCompilePerFrame(100); | ||||
|         } | ||||
| 
 | ||||
|         mResourceSystem->getSceneManager()->setIncrementalCompileOperation(mViewer->getIncrementalCompileOperation()); | ||||
|  | @ -286,6 +287,12 @@ namespace MWRender | |||
|             mTerrain.reset(new Terrain::QuadTreeWorld( | ||||
|                 sceneRoot, mRootNode, mResourceSystem, mTerrainStorage, Mask_Terrain, Mask_PreCompile, Mask_Debug, | ||||
|                 compMapResolution, compMapLevel, lodFactor, vertexLodMod, maxCompGeometrySize)); | ||||
|             if (Settings::Manager::getBool("object paging", "Terrain")) | ||||
|             { | ||||
|                 mObjectPaging.reset(new ObjectPaging(mResourceSystem->getSceneManager())); | ||||
|                 static_cast<Terrain::QuadTreeWorld*>(mTerrain.get())->addChunkManager(mObjectPaging.get()); | ||||
|                 mResourceSystem->addResourceManager(mObjectPaging.get()); | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|             mTerrain.reset(new Terrain::TerrainGrid(sceneRoot, mRootNode, mResourceSystem, mTerrainStorage, Mask_Terrain, Mask_PreCompile, Mask_Debug)); | ||||
|  | @ -970,20 +977,14 @@ namespace MWRender | |||
|         renderCameraToImage(rttCamera.get(),image,w,h); | ||||
|     } | ||||
| 
 | ||||
|     osg::Vec4f RenderingManager::getScreenBounds(const MWWorld::Ptr& ptr) | ||||
|     osg::Vec4f RenderingManager::getScreenBounds(const osg::BoundingBox &worldbb) | ||||
|     { | ||||
|         if (!ptr.getRefData().getBaseNode()) | ||||
|             return osg::Vec4f(); | ||||
| 
 | ||||
|         osg::ComputeBoundsVisitor computeBoundsVisitor; | ||||
|         computeBoundsVisitor.setTraversalMask(~(Mask_ParticleSystem|Mask_Effect)); | ||||
|         ptr.getRefData().getBaseNode()->accept(computeBoundsVisitor); | ||||
| 
 | ||||
|         if (!worldbb.valid()) return osg::Vec4f(); | ||||
|         osg::Matrix viewProj = mViewer->getCamera()->getViewMatrix() * mViewer->getCamera()->getProjectionMatrix(); | ||||
|         float min_x = 1.0f, max_x = 0.0f, min_y = 1.0f, max_y = 0.0f; | ||||
|         for (int i=0; i<8; ++i) | ||||
|         { | ||||
|             osg::Vec3f corner = computeBoundsVisitor.getBoundingBox().corner(i); | ||||
|             osg::Vec3f corner = worldbb.corner(i); | ||||
|             corner = corner * viewProj; | ||||
| 
 | ||||
|             float x = (corner.x() + 1.f) * 0.5f; | ||||
|  | @ -1009,6 +1010,7 @@ namespace MWRender | |||
|     { | ||||
|         RenderingManager::RayResult result; | ||||
|         result.mHit = false; | ||||
|         result.mHitRefnum.mContentFile = -1; | ||||
|         result.mRatio = 0; | ||||
|         if (intersector->containsIntersections()) | ||||
|         { | ||||
|  | @ -1020,6 +1022,7 @@ namespace MWRender | |||
|             result.mRatio = intersection.ratio; | ||||
| 
 | ||||
|             PtrHolder* ptrHolder = nullptr; | ||||
|             std::vector<RefnumMarker*> refnumMarkers; | ||||
|             for (osg::NodePath::const_iterator it = intersection.nodePath.begin(); it != intersection.nodePath.end(); ++it) | ||||
|             { | ||||
|                 osg::UserDataContainer* userDataContainer = (*it)->getUserDataContainer(); | ||||
|  | @ -1029,11 +1032,25 @@ namespace MWRender | |||
|                 { | ||||
|                     if (PtrHolder* p = dynamic_cast<PtrHolder*>(userDataContainer->getUserObject(i))) | ||||
|                         ptrHolder = p; | ||||
|                     if (RefnumMarker* r = dynamic_cast<RefnumMarker*>(userDataContainer->getUserObject(i))) | ||||
|                         refnumMarkers.push_back(r); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             if (ptrHolder) | ||||
|                 result.mHitObject = ptrHolder->mPtr; | ||||
| 
 | ||||
|             unsigned int vertexCounter = 0; | ||||
|             for (unsigned int i=0; i<refnumMarkers.size(); ++i) | ||||
|             { | ||||
|                 unsigned int intersectionIndex = intersection.indexList.empty() ? 0 : intersection.indexList[0]; | ||||
|                 if (!refnumMarkers[i]->mNumVertices || (intersectionIndex >= vertexCounter && intersectionIndex < vertexCounter + refnumMarkers[i]->mNumVertices)) | ||||
|                 { | ||||
|                     result.mHitRefnum = refnumMarkers[i]->mRefnum; | ||||
|                     break; | ||||
|                 } | ||||
|                 vertexCounter += refnumMarkers[i]->mNumVertices; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return result; | ||||
|  | @ -1046,6 +1063,7 @@ namespace MWRender | |||
|             mIntersectionVisitor = new osgUtil::IntersectionVisitor; | ||||
| 
 | ||||
|         mIntersectionVisitor->setTraversalNumber(mViewer->getFrameStamp()->getFrameNumber()); | ||||
|         mIntersectionVisitor->setFrameStamp(mViewer->getFrameStamp()); | ||||
|         mIntersectionVisitor->setIntersector(intersector); | ||||
| 
 | ||||
|         int mask = ~0; | ||||
|  | @ -1111,6 +1129,8 @@ namespace MWRender | |||
|         mSky->setMoonColour(false); | ||||
| 
 | ||||
|         notifyWorldSpaceChanged(); | ||||
|         if (mObjectPaging) | ||||
|             mObjectPaging->clear(); | ||||
|     } | ||||
| 
 | ||||
|     MWRender::Animation* RenderingManager::getAnimation(const MWWorld::Ptr &ptr) | ||||
|  | @ -1467,4 +1487,43 @@ namespace MWRender | |||
| 
 | ||||
|         mRecastMesh->update(mNavigator.getRecastMeshTiles(), mNavigator.getSettings()); | ||||
|     } | ||||
| 
 | ||||
|     void RenderingManager::setActiveGrid(const osg::Vec4i &grid) | ||||
|     { | ||||
|         mTerrain->setActiveGrid(grid); | ||||
|     } | ||||
|     bool RenderingManager::pagingEnableObject(int type, const MWWorld::ConstPtr& ptr, bool enabled) | ||||
|     { | ||||
|         if (!ptr.isInCell() || !ptr.getCell()->isExterior() || !mObjectPaging) | ||||
|             return false; | ||||
|         if (mObjectPaging->enableObject(type, ptr.getCellRef().getRefNum(), ptr.getCellRef().getPosition().asVec3(), enabled)) | ||||
|         { | ||||
|             mTerrain->rebuildViews(); | ||||
|             return true; | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|     void RenderingManager::pagingBlacklistObject(int type, const MWWorld::ConstPtr &ptr) | ||||
|     { | ||||
|         if (!ptr.isInCell() || !ptr.getCell()->isExterior() || !mObjectPaging) | ||||
|             return; | ||||
|         const ESM::RefNum & refnum = ptr.getCellRef().getRefNum(); | ||||
|         if (!refnum.hasContentFile()) return; | ||||
|         if (mObjectPaging->blacklistObject(type, refnum, ptr.getCellRef().getPosition().asVec3())) | ||||
|             mTerrain->rebuildViews(); | ||||
|     } | ||||
|     bool RenderingManager::pagingUnlockCache() | ||||
|     { | ||||
|         if (mObjectPaging && mObjectPaging->unlockCache()) | ||||
|         { | ||||
|             mTerrain->rebuildViews(); | ||||
|             return true; | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|     void RenderingManager::getPagedRefnums(const osg::Vec4i &activeGrid, std::set<ESM::RefNum> &out) | ||||
|     { | ||||
|         if (mObjectPaging) | ||||
|             mObjectPaging->getPagedRefnums(activeGrid, out); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -42,6 +42,7 @@ namespace osgViewer | |||
| namespace ESM | ||||
| { | ||||
|     struct Cell; | ||||
|     struct RefNum; | ||||
| } | ||||
| 
 | ||||
| namespace Terrain | ||||
|  | @ -84,6 +85,7 @@ namespace MWRender | |||
|     class NavMesh; | ||||
|     class ActorsPaths; | ||||
|     class RecastMesh; | ||||
|     class ObjectPaging; | ||||
| 
 | ||||
|     class RenderingManager : public MWRender::RenderingInterface | ||||
|     { | ||||
|  | @ -155,6 +157,7 @@ namespace MWRender | |||
|             osg::Vec3f mHitNormalWorld; | ||||
|             osg::Vec3f mHitPointWorld; | ||||
|             MWWorld::Ptr mHitObject; | ||||
|             ESM::RefNum mHitRefnum; | ||||
|             float mRatio; | ||||
|         }; | ||||
| 
 | ||||
|  | @ -165,7 +168,7 @@ namespace MWRender | |||
|         RayResult castCameraToViewportRay(const float nX, const float nY, float maxDistance, bool ignorePlayer, bool ignoreActors=false); | ||||
| 
 | ||||
|         /// Get the bounding box of the given object in screen coordinates as (minX, minY, maxX, maxY), with (0,0) being the top left corner.
 | ||||
|         osg::Vec4f getScreenBounds(const MWWorld::Ptr& ptr); | ||||
|         osg::Vec4f getScreenBounds(const osg::BoundingBox &worldbb); | ||||
| 
 | ||||
|         void setSkyEnabled(bool enabled); | ||||
| 
 | ||||
|  | @ -237,6 +240,13 @@ namespace MWRender | |||
| 
 | ||||
|         void setNavMeshNumber(const std::size_t value); | ||||
| 
 | ||||
|         void setActiveGrid(const osg::Vec4i &grid); | ||||
| 
 | ||||
|         bool pagingEnableObject(int type, const MWWorld::ConstPtr& ptr, bool enabled); | ||||
|         void pagingBlacklistObject(int type, const MWWorld::ConstPtr &ptr); | ||||
|         bool pagingUnlockCache(); | ||||
|         void getPagedRefnums(const osg::Vec4i &activeGrid, std::set<ESM::RefNum> &out); | ||||
| 
 | ||||
|     private: | ||||
|         void updateProjectionMatrix(); | ||||
|         void updateTextureFiltering(); | ||||
|  | @ -275,6 +285,7 @@ namespace MWRender | |||
|         std::unique_ptr<Water> mWater; | ||||
|         std::unique_ptr<Terrain::World> mTerrain; | ||||
|         TerrainStorage* mTerrainStorage; | ||||
|         std::unique_ptr<ObjectPaging> mObjectPaging; | ||||
|         std::unique_ptr<SkyManager> mSky; | ||||
|         std::unique_ptr<FogManager> mFog; | ||||
|         std::unique_ptr<EffectManager> mEffectManager; | ||||
|  |  | |||
|  | @ -427,7 +427,7 @@ namespace MWScript | |||
|                 { | ||||
|                     MWWorld::Ptr ptr = R()(runtime); | ||||
| 
 | ||||
|                     Interpreter::Type_Integer value = ptr.getClass().getCreatureStats (ptr).getAiSequence().getLastRunTypeId(); | ||||
|                     const auto value = static_cast<Interpreter::Type_Integer>(ptr.getClass().getCreatureStats (ptr).getAiSequence().getLastRunTypeId()); | ||||
| 
 | ||||
|                     runtime.push (value); | ||||
|                 } | ||||
|  |  | |||
|  | @ -24,6 +24,7 @@ namespace | |||
|         { | ||||
|             ESM::GlobalScript script; | ||||
|             script.mTargetRef.unset(); | ||||
|             script.mRunning = false; | ||||
|             if (!ptr.isEmpty()) | ||||
|             { | ||||
|                 if (ptr.getCellRef().hasContentFile()) | ||||
|  | @ -42,6 +43,7 @@ namespace | |||
|             ESM::GlobalScript script; | ||||
|             script.mTargetId = pair.second; | ||||
|             script.mTargetRef = pair.first; | ||||
|             script.mRunning = false; | ||||
|             return script; | ||||
|         } | ||||
|     }; | ||||
|  |  | |||
|  | @ -8,6 +8,7 @@ | |||
| #include <components/resource/resourcesystem.hpp> | ||||
| #include <components/resource/bulletshapemanager.hpp> | ||||
| #include <components/resource/keyframemanager.hpp> | ||||
| #include <components/vfs/manager.hpp> | ||||
| #include <components/misc/resourcehelpers.hpp> | ||||
| #include <components/misc/stringops.hpp> | ||||
| #include <components/terrain/world.hpp> | ||||
|  | @ -65,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); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ | |||
| #include <map> | ||||
| #include <osg/ref_ptr> | ||||
| #include <osg/Vec3f> | ||||
| #include <osg/Vec4i> | ||||
| #include <components/sceneutil/workqueue.hpp> | ||||
| 
 | ||||
| namespace Resource | ||||
|  | @ -68,7 +69,11 @@ namespace MWWorld | |||
| 
 | ||||
|         void setUnrefQueue(SceneUtil::UnrefQueue* unrefQueue); | ||||
| 
 | ||||
|         void setTerrainPreloadPositions(const std::vector<osg::Vec3f>& positions); | ||||
|         typedef std::pair<osg::Vec3f, osg::Vec4i> PositionCellGrid; | ||||
|         void setTerrainPreloadPositions(const std::vector<PositionCellGrid>& positions); | ||||
| 
 | ||||
|         bool syncTerrainLoad(const std::vector<CellPreloader::PositionCellGrid> &positions, int& progress, int& progressRange, double timestamp); | ||||
|         void abortTerrainPreloadExcept(const PositionCellGrid *exceptPos); | ||||
| 
 | ||||
|     private: | ||||
|         Resource::ResourceSystem* mResourceSystem; | ||||
|  | @ -83,6 +88,7 @@ namespace MWWorld | |||
|         bool mPreloadInstances; | ||||
| 
 | ||||
|         double mLastResourceCacheUpdate; | ||||
|         int mStoreViewsFailCount; | ||||
| 
 | ||||
|         struct PreloadEntry | ||||
|         { | ||||
|  | @ -105,7 +111,7 @@ namespace MWWorld | |||
|         PreloadMap mPreloadCells; | ||||
| 
 | ||||
|         std::vector<osg::ref_ptr<Terrain::View> > mTerrainViews; | ||||
|         std::vector<osg::Vec3f> mTerrainPreloadPositions; | ||||
|         std::vector<PositionCellGrid> mTerrainPreloadPositions; | ||||
|         osg::ref_ptr<TerrainPreloadItem> mTerrainPreloadItem; | ||||
|         osg::ref_ptr<SceneUtil::WorkItem> mUpdateCacheItem; | ||||
|     }; | ||||
|  |  | |||
|  | @ -119,7 +119,7 @@ namespace | |||
| 
 | ||||
|     template<typename RecordType, typename T> | ||||
|     void readReferenceCollection (ESM::ESMReader& reader, | ||||
|         MWWorld::CellRefList<T>& collection, const ESM::CellRef& cref, const std::map<int, int>& contentFileMap) | ||||
|         MWWorld::CellRefList<T>& collection, const ESM::CellRef& cref, const std::map<int, int>& contentFileMap, MWWorld::CellStore* cellstore) | ||||
|     { | ||||
|         const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); | ||||
| 
 | ||||
|  | @ -154,7 +154,18 @@ namespace | |||
|                 if (iter->mRef.getRefNum()==state.mRef.mRefNum && *iter->mRef.getRefIdPtr() == state.mRef.mRefID) | ||||
|                 { | ||||
|                     // overwrite existing reference
 | ||||
|                     float oldscale = iter->mRef.getScale(); | ||||
|                     iter->load (state); | ||||
|                     const ESM::Position & oldpos = iter->mRef.getPosition(); | ||||
|                     const ESM::Position & newpos = iter->mData.getPosition(); | ||||
|                     const MWWorld::Ptr ptr(&*iter, cellstore); | ||||
|                     if ((oldscale != iter->mRef.getScale() || oldpos.asVec3() != newpos.asVec3() || oldpos.rot[0] != newpos.rot[0] || oldpos.rot[1] != newpos.rot[1] || oldpos.rot[2] != newpos.rot[2]) && !ptr.getClass().isActor()) | ||||
|                         MWBase::Environment::get().getWorld()->moveObject(ptr, newpos.pos[0], newpos.pos[1], newpos.pos[2]); | ||||
|                     if (!iter->mData.isEnabled()) | ||||
|                     { | ||||
|                         iter->mData.enable(); | ||||
|                         MWBase::Environment::get().getWorld()->disable(MWWorld::Ptr(&*iter, cellstore)); | ||||
|                     } | ||||
|                     return; | ||||
|                 } | ||||
| 
 | ||||
|  | @ -167,28 +178,6 @@ namespace | |||
|         ref.load (state); | ||||
|         collection.mList.push_back (ref); | ||||
|     } | ||||
| 
 | ||||
|     struct SearchByRefNumVisitor | ||||
|     { | ||||
|         MWWorld::LiveCellRefBase* mFound; | ||||
|         ESM::RefNum mRefNumToFind; | ||||
| 
 | ||||
|         SearchByRefNumVisitor(const ESM::RefNum& toFind) | ||||
|             : mFound(nullptr) | ||||
|             , mRefNumToFind(toFind) | ||||
|         { | ||||
|         } | ||||
| 
 | ||||
|         bool operator()(const MWWorld::Ptr& ptr) | ||||
|         { | ||||
|             if (ptr.getCellRef().getRefNum() == mRefNumToFind) | ||||
|             { | ||||
|                 mFound = ptr.getBase(); | ||||
|                 return false; | ||||
|             } | ||||
|             return true; | ||||
|         } | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| namespace MWWorld | ||||
|  | @ -284,9 +273,7 @@ namespace MWWorld | |||
|             throw std::runtime_error("moveTo: can't move object from a non-loaded cell (how did you get this object anyway?)"); | ||||
| 
 | ||||
|         // Ensure that the object actually exists in the cell
 | ||||
|         SearchByRefNumVisitor searchVisitor(object.getCellRef().getRefNum()); | ||||
|         forEach(searchVisitor); | ||||
|         if (!searchVisitor.mFound) | ||||
|         if (searchViaRefNum(object.getCellRef().getRefNum()).isEmpty()) | ||||
|             throw std::runtime_error("moveTo: object is not in this cell"); | ||||
| 
 | ||||
| 
 | ||||
|  | @ -1030,107 +1017,107 @@ namespace MWWorld | |||
|             { | ||||
|                 case ESM::REC_ACTI: | ||||
| 
 | ||||
|                     readReferenceCollection<ESM::ObjectState> (reader, mActivators, cref, contentFileMap); | ||||
|                     readReferenceCollection<ESM::ObjectState> (reader, mActivators, cref, contentFileMap, this); | ||||
|                     break; | ||||
| 
 | ||||
|                 case ESM::REC_ALCH: | ||||
| 
 | ||||
|                     readReferenceCollection<ESM::ObjectState> (reader, mPotions, cref, contentFileMap); | ||||
|                     readReferenceCollection<ESM::ObjectState> (reader, mPotions, cref, contentFileMap, this); | ||||
|                     break; | ||||
| 
 | ||||
|                 case ESM::REC_APPA: | ||||
| 
 | ||||
|                     readReferenceCollection<ESM::ObjectState> (reader, mAppas, cref, contentFileMap); | ||||
|                     readReferenceCollection<ESM::ObjectState> (reader, mAppas, cref, contentFileMap, this); | ||||
|                     break; | ||||
| 
 | ||||
|                 case ESM::REC_ARMO: | ||||
| 
 | ||||
|                     readReferenceCollection<ESM::ObjectState> (reader, mArmors, cref, contentFileMap); | ||||
|                     readReferenceCollection<ESM::ObjectState> (reader, mArmors, cref, contentFileMap, this); | ||||
|                     break; | ||||
| 
 | ||||
|                 case ESM::REC_BOOK: | ||||
| 
 | ||||
|                     readReferenceCollection<ESM::ObjectState> (reader, mBooks, cref, contentFileMap); | ||||
|                     readReferenceCollection<ESM::ObjectState> (reader, mBooks, cref, contentFileMap, this); | ||||
|                     break; | ||||
| 
 | ||||
|                 case ESM::REC_CLOT: | ||||
| 
 | ||||
|                     readReferenceCollection<ESM::ObjectState> (reader, mClothes, cref, contentFileMap); | ||||
|                     readReferenceCollection<ESM::ObjectState> (reader, mClothes, cref, contentFileMap, this); | ||||
|                     break; | ||||
| 
 | ||||
|                 case ESM::REC_CONT: | ||||
| 
 | ||||
|                     readReferenceCollection<ESM::ContainerState> (reader, mContainers, cref, contentFileMap); | ||||
|                     readReferenceCollection<ESM::ContainerState> (reader, mContainers, cref, contentFileMap, this); | ||||
|                     break; | ||||
| 
 | ||||
|                 case ESM::REC_CREA: | ||||
| 
 | ||||
|                     readReferenceCollection<ESM::CreatureState> (reader, mCreatures, cref, contentFileMap); | ||||
|                     readReferenceCollection<ESM::CreatureState> (reader, mCreatures, cref, contentFileMap, this); | ||||
|                     break; | ||||
| 
 | ||||
|                 case ESM::REC_DOOR: | ||||
| 
 | ||||
|                     readReferenceCollection<ESM::DoorState> (reader, mDoors, cref, contentFileMap); | ||||
|                     readReferenceCollection<ESM::DoorState> (reader, mDoors, cref, contentFileMap, this); | ||||
|                     break; | ||||
| 
 | ||||
|                 case ESM::REC_INGR: | ||||
| 
 | ||||
|                     readReferenceCollection<ESM::ObjectState> (reader, mIngreds, cref, contentFileMap); | ||||
|                     readReferenceCollection<ESM::ObjectState> (reader, mIngreds, cref, contentFileMap, this); | ||||
|                     break; | ||||
| 
 | ||||
|                 case ESM::REC_LEVC: | ||||
| 
 | ||||
|                     readReferenceCollection<ESM::CreatureLevListState> (reader, mCreatureLists, cref, contentFileMap); | ||||
|                     readReferenceCollection<ESM::CreatureLevListState> (reader, mCreatureLists, cref, contentFileMap, this); | ||||
|                     break; | ||||
| 
 | ||||
|                 case ESM::REC_LEVI: | ||||
| 
 | ||||
|                     readReferenceCollection<ESM::ObjectState> (reader, mItemLists, cref, contentFileMap); | ||||
|                     readReferenceCollection<ESM::ObjectState> (reader, mItemLists, cref, contentFileMap, this); | ||||
|                     break; | ||||
| 
 | ||||
|                 case ESM::REC_LIGH: | ||||
| 
 | ||||
|                     readReferenceCollection<ESM::ObjectState> (reader, mLights, cref, contentFileMap); | ||||
|                     readReferenceCollection<ESM::ObjectState> (reader, mLights, cref, contentFileMap, this); | ||||
|                     break; | ||||
| 
 | ||||
|                 case ESM::REC_LOCK: | ||||
| 
 | ||||
|                     readReferenceCollection<ESM::ObjectState> (reader, mLockpicks, cref, contentFileMap); | ||||
|                     readReferenceCollection<ESM::ObjectState> (reader, mLockpicks, cref, contentFileMap, this); | ||||
|                     break; | ||||
| 
 | ||||
|                 case ESM::REC_MISC: | ||||
| 
 | ||||
|                     readReferenceCollection<ESM::ObjectState> (reader, mMiscItems, cref, contentFileMap); | ||||
|                     readReferenceCollection<ESM::ObjectState> (reader, mMiscItems, cref, contentFileMap, this); | ||||
|                     break; | ||||
| 
 | ||||
|                 case ESM::REC_NPC_: | ||||
| 
 | ||||
|                     readReferenceCollection<ESM::NpcState> (reader, mNpcs, cref, contentFileMap); | ||||
|                     readReferenceCollection<ESM::NpcState> (reader, mNpcs, cref, contentFileMap, this); | ||||
|                     break; | ||||
| 
 | ||||
|                 case ESM::REC_PROB: | ||||
| 
 | ||||
|                     readReferenceCollection<ESM::ObjectState> (reader, mProbes, cref, contentFileMap); | ||||
|                     readReferenceCollection<ESM::ObjectState> (reader, mProbes, cref, contentFileMap, this); | ||||
|                     break; | ||||
| 
 | ||||
|                 case ESM::REC_REPA: | ||||
| 
 | ||||
|                     readReferenceCollection<ESM::ObjectState> (reader, mRepairs, cref, contentFileMap); | ||||
|                     readReferenceCollection<ESM::ObjectState> (reader, mRepairs, cref, contentFileMap, this); | ||||
|                     break; | ||||
| 
 | ||||
|                 case ESM::REC_STAT: | ||||
| 
 | ||||
|                     readReferenceCollection<ESM::ObjectState> (reader, mStatics, cref, contentFileMap); | ||||
|                     readReferenceCollection<ESM::ObjectState> (reader, mStatics, cref, contentFileMap, this); | ||||
|                     break; | ||||
| 
 | ||||
|                 case ESM::REC_WEAP: | ||||
| 
 | ||||
|                     readReferenceCollection<ESM::ObjectState> (reader, mWeapons, cref, contentFileMap); | ||||
|                     readReferenceCollection<ESM::ObjectState> (reader, mWeapons, cref, contentFileMap, this); | ||||
|                     break; | ||||
| 
 | ||||
|                 case ESM::REC_BODY: | ||||
| 
 | ||||
|                     readReferenceCollection<ESM::ObjectState> (reader, mBodyParts, cref, contentFileMap); | ||||
|                     readReferenceCollection<ESM::ObjectState> (reader, mBodyParts, cref, contentFileMap, this); | ||||
|                     break; | ||||
| 
 | ||||
|                 default: | ||||
|  | @ -1152,26 +1139,22 @@ namespace MWWorld | |||
|             movedTo.load(reader); | ||||
| 
 | ||||
|             // Search for the reference. It might no longer exist if its content file was removed.
 | ||||
|             SearchByRefNumVisitor visitor(refnum); | ||||
|             forEachInternal(visitor); | ||||
| 
 | ||||
|             if (!visitor.mFound) | ||||
|             Ptr movedRef = searchViaRefNum(refnum); | ||||
|             if (movedRef.isEmpty()) | ||||
|             { | ||||
|                 Log(Debug::Warning) << "Warning: Dropping moved ref tag for " << refnum.mIndex << " (moved object no longer exists)"; | ||||
|                 continue; | ||||
|             } | ||||
| 
 | ||||
|             MWWorld::LiveCellRefBase* movedRef = visitor.mFound; | ||||
| 
 | ||||
|             CellStore* otherCell = callback->getCellStore(movedTo); | ||||
| 
 | ||||
|             if (otherCell == nullptr) | ||||
|             { | ||||
|                 Log(Debug::Warning) << "Warning: Dropping moved ref tag for " << movedRef->mRef.getRefId() | ||||
|                 Log(Debug::Warning) << "Warning: Dropping moved ref tag for " << movedRef.getCellRef().getRefId() | ||||
|                                     << " (target cell " << movedTo.mWorldspace << " no longer exists). Reference moved back to its original location."; | ||||
|                 // Note by dropping tag the object will automatically re-appear in its original cell, though potentially at inapproriate coordinates.
 | ||||
|                 // Restore original coordinates:
 | ||||
|                 movedRef->mData.setPosition(movedRef->mRef.getPosition()); | ||||
|                 movedRef.getRefData().setPosition(movedRef.getCellRef().getPosition()); | ||||
|                 continue; | ||||
|             } | ||||
| 
 | ||||
|  | @ -1182,7 +1165,7 @@ namespace MWWorld | |||
|                 continue; | ||||
|             } | ||||
| 
 | ||||
|             moveTo(MWWorld::Ptr(movedRef, this), otherCell); | ||||
|             moveTo(movedRef, otherCell); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -534,4 +534,19 @@ namespace MWWorld | |||
|     { | ||||
|         throw std::runtime_error ("class does not have creature stats"); | ||||
|     } | ||||
| 
 | ||||
|     float Class::getWalkSpeed(const Ptr& /*ptr*/) const | ||||
|     { | ||||
|         return 0; | ||||
|     } | ||||
| 
 | ||||
|     float Class::getRunSpeed(const Ptr& /*ptr*/) const | ||||
|     { | ||||
|         return 0; | ||||
|     } | ||||
| 
 | ||||
|     float Class::getSwimSpeed(const Ptr& /*ptr*/) const | ||||
|     { | ||||
|         return 0; | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -373,6 +373,12 @@ namespace MWWorld | |||
|             virtual osg::Vec4f getEnchantmentColor(const MWWorld::ConstPtr& item) const; | ||||
| 
 | ||||
|             virtual void setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const; | ||||
| 
 | ||||
|             virtual float getWalkSpeed(const Ptr& ptr) const; | ||||
| 
 | ||||
|             virtual float getRunSpeed(const Ptr& ptr) const; | ||||
| 
 | ||||
|             virtual float getSwimSpeed(const Ptr& ptr) const; | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -136,6 +136,10 @@ void ESMStore::setUp(bool validateRecords) | |||
|                 mIds[*record] = storeIt->first; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if (mStaticIds.empty()) | ||||
|         mStaticIds = mIds; | ||||
| 
 | ||||
|     mSkills.setUp(); | ||||
|     mMagicEffects.setUp(); | ||||
|     mAttributes.setUp(); | ||||
|  |  | |||
|  | @ -68,6 +68,8 @@ namespace MWWorld | |||
|         // Lookup of all IDs. Makes looking up references faster. Just
 | ||||
|         // maps the id name to the record type.
 | ||||
|         std::map<std::string, int> mIds; | ||||
|         std::map<std::string, int> mStaticIds; | ||||
| 
 | ||||
|         std::map<int, StoreBase *> mStores; | ||||
| 
 | ||||
|         ESM::NPC mPlayerTemplate; | ||||
|  | @ -99,6 +101,14 @@ namespace MWWorld | |||
|             } | ||||
|             return it->second; | ||||
|         } | ||||
|         int findStatic(const std::string &id) const | ||||
|         { | ||||
|             std::map<std::string, int>::const_iterator it = mStaticIds.find(id); | ||||
|             if (it == mStaticIds.end()) { | ||||
|                 return 0; | ||||
|             } | ||||
|             return it->second; | ||||
|         } | ||||
| 
 | ||||
|         ESMStore() | ||||
|           : mDynamicCount(0) | ||||
|  |  | |||
|  | @ -13,6 +13,7 @@ | |||
| #include <components/resource/scenemanager.hpp> | ||||
| #include <components/resource/bulletshape.hpp> | ||||
| #include <components/sceneutil/unrefqueue.hpp> | ||||
| #include <components/sceneutil/positionattitudetransform.hpp> | ||||
| #include <components/detournavigator/navigator.hpp> | ||||
| #include <components/detournavigator/debug.hpp> | ||||
| #include <components/misc/convert.hpp> | ||||
|  | @ -97,8 +98,21 @@ namespace | |||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     std::string getModel(const MWWorld::Ptr &ptr, const VFS::Manager *vfs) | ||||
|     { | ||||
|         bool useAnim = ptr.getClass().useAnim(); | ||||
|         std::string model = ptr.getClass().getModel(ptr); | ||||
|         if (useAnim) | ||||
|             model = Misc::ResourceHelpers::correctActorModelPath(model, vfs); | ||||
| 
 | ||||
|         const std::string &id = ptr.getCellRef().getRefId(); | ||||
|         if (id == "prisonmarker" || id == "divinemarker" || id == "templemarker" || id == "northmarker") | ||||
|             model = ""; // marker objects that have a hardcoded function in the game logic, should be hidden from the player
 | ||||
|         return model; | ||||
|     } | ||||
| 
 | ||||
|     void addObject(const MWWorld::Ptr& ptr, MWPhysics::PhysicsSystem& physics, | ||||
|                    MWRender::RenderingManager& rendering) | ||||
|                    MWRender::RenderingManager& rendering, std::set<ESM::RefNum>& pagedRefs) | ||||
|     { | ||||
|         if (ptr.getRefData().getBaseNode() || physics.getActor(ptr)) | ||||
|         { | ||||
|  | @ -107,15 +121,13 @@ namespace | |||
|         } | ||||
| 
 | ||||
|         bool useAnim = ptr.getClass().useAnim(); | ||||
|         std::string model = ptr.getClass().getModel(ptr); | ||||
|         if (useAnim) | ||||
|             model = Misc::ResourceHelpers::correctActorModelPath(model, rendering.getResourceSystem()->getVFS()); | ||||
|         std::string 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
		Loading…
	
		Reference in a new issue