diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 20b233c66..ceba2841f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -25,6 +25,7 @@ stages: - cmake --build . -- -j $(nproc) - cmake --install . - if [[ "${BUILD_TESTS_ONLY}" ]]; then ./openmw_test_suite; fi + - if [[ "${BUILD_TESTS_ONLY}" ]]; then ./openmw_detournavigator_navmeshtilescache_benchmark; fi - ccache -s artifacts: paths: @@ -169,6 +170,10 @@ variables: &cs-targets targets: "openmw-cs,bsatool,esmtool,niftest" package: "CS" +variables: &tests-targets + targets: "openmw_test_suite,openmw_detournavigator_navmeshtilescache_benchmark" + package: "Tests" + .Windows_Ninja_Base: tags: - windows @@ -187,7 +192,7 @@ variables: &cs-targets - $time = (Get-Date -Format "HH:mm:ss") - echo ${time} - echo "started by ${GITLAB_USER_NAME}" - - sh CI/before_script.msvc.sh -c $config -p Win64 -v 2019 -k -V -N + - sh CI/before_script.msvc.sh -c $config -p Win64 -v 2019 -k -V -N -b -t - cd MSVC2019_64_Ninja - .\ActivateMSVC.ps1 - cmake --build . --config $config --target ($targets.Split(',')) @@ -199,6 +204,7 @@ variables: &cs-targets Get-ChildItem -Recurse *.pdb | Remove-Item } - 7z a -tzip ..\..\OpenMW_MSVC2019_64_${package}_${config}_${CI_COMMIT_REF_NAME}.zip '*' + - if ($executables) { foreach ($exe in $executables.Split(',')) { & .\$exe } } after_script: - Copy-Item C:\ProgramData\chocolatey\logs\chocolatey.log cache: @@ -262,6 +268,15 @@ Windows_Ninja_CS_RelWithDebInfo: <<: *cs-targets config: "RelWithDebInfo" +Windows_Ninja_Tests_RelWithDebInfo: + extends: .Windows_Ninja_Base + stage: build + variables: + <<: *tests-targets + config: "RelWithDebInfo" + # Gitlab can't successfully execute following binaries due to unknown reason + # executables: "openmw_test_suite.exe,openmw_detournavigator_navmeshtilescache_benchmark.exe" + .Windows_MSBuild_Base: tags: - windows @@ -279,7 +294,7 @@ Windows_Ninja_CS_RelWithDebInfo: - $time = (Get-Date -Format "HH:mm:ss") - echo ${time} - echo "started by ${GITLAB_USER_NAME}" - - sh CI/before_script.msvc.sh -c $config -p Win64 -v 2019 -k -V + - sh CI/before_script.msvc.sh -c $config -p Win64 -v 2019 -k -V -b -t - cd MSVC2019_64 - cmake --build . --config $config --target ($targets.Split(',')) - cd $config @@ -290,6 +305,7 @@ Windows_Ninja_CS_RelWithDebInfo: Get-ChildItem -Recurse *.pdb | Remove-Item } - 7z a -tzip ..\..\OpenMW_MSVC2019_64_${package}_${config}_${CI_COMMIT_REF_NAME}.zip '*' + - if ($executables) { foreach ($exe in $executables.Split(',')) { & .\$exe } } after_script: - Copy-Item C:\ProgramData\chocolatey\logs\chocolatey.log cache: @@ -353,6 +369,15 @@ Windows_MSBuild_CS_RelWithDebInfo: <<: *cs-targets config: "RelWithDebInfo" +Windows_MSBuild_Tests_RelWithDebInfo: + extends: .Windows_MSBuild_Base + stage: build + variables: + <<: *tests-targets + config: "RelWithDebInfo" + # Gitlab can't successfully execute following binaries due to unknown reason + # executables: "openmw_test_suite.exe,openmw_detournavigator_navmeshtilescache_benchmark.exe" + Debian_AndroidNDK_arm64-v8a: tags: - linux diff --git a/CHANGELOG.md b/CHANGELOG.md index ba8081dae..e11d05d3d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -64,6 +64,7 @@ Bug #5452: Autowalk is being included in savegames Bug #5469: Local map is reset when re-entering certain cells Bug #5472: Mistify mod causes CTD in 0.46 on Mac + Bug #5473: OpenMW-CS: Cell border lines don't update properly on terrain change Bug #5479: NPCs who should be walking around town are standing around without walking Bug #5484: Zero value items shouldn't be able to be bought or sold for 1 gold Bug #5485: Intimidate doesn't increase disposition on marginal wins @@ -80,6 +81,7 @@ Bug #5603: Setting constant effect cast style doesn't correct effects view Bug #5604: Only one valid NIF root node is loaded from a single file Bug #5611: Usable items with "0 Uses" should be used only once + Bug #5619: Input events are queued during save loading Bug #5622: Can't properly interact with the console when in pause menu Bug #5627: Bookart not shown if it isn't followed by
statement Bug #5633: Damage Spells in effect before god mode is enabled continue to hurt the player character and can kill them @@ -121,6 +123,15 @@ Bug #5975: NIF controllers from sheath meshes are used Bug #5991: Activate should always be allowed for inventory items Bug #5995: NiUVController doesn't calculate the UV offset properly + Bug #6007: Crash when ending cutscene is playing + Bug #6016: Greeting interrupts Fargoth's sneak-walk + Bug #6022: OpenMW-CS: Terrain selection is not updated when undoing/redoing terrain changes + Bug #6023: OpenMW-CS: Clicking on a reference in "Terrain land editing" mode discards corresponding select/edit action + Bug #6028: Particle system controller values are incorrectly used + Bug #6035: OpenMW-CS: Circle brush in "Terrain land editing" mode sometimes includes vertices outside its radius + Bug #6036: OpenMW-CS: Terrain selection at the border of cells omits certain corner vertices + Bug #6043: Actor can have torch missing when torch animation is played + Bug #6047: Mouse bindings can be triggered during save loading Feature #390: 3rd person look "over the shoulder" Feature #832: OpenMW-CS: Handle deleted references Feature #1536: Show more information about level on menu @@ -141,6 +152,7 @@ Feature #5456: Basic collada animation support Feature #5457: Realistic diagonal movement Feature #5486: Fixes trainers to choose their training skills based on their base skill points + Feature #5500: Prepare enough navmesh tiles before scene loading ends Feature #5511: Add in game option to toggle HRTF support in OpenMW Feature #5519: Code Patch tab in launcher Feature #5524: Resume failed script execution after reload @@ -159,6 +171,9 @@ Feature #5814: Bsatool should be able to create BSA archives, not only to extract it Feature #5828: Support more than 8 lights Feature #5910: Fall back to delta time when physics can't keep up + Feature #6024: OpenMW-CS: Selecting terrain in "Terrain land editing" should support "Add to selection" and "Remove from selection" modes + Feature #6033: Include pathgrid to navigation mesh + Feature #6034: Find path based on area cost depending on NPC stats Task #5480: Drop Qt4 support Task #5520: Improve cell name autocompleter implementation Task #5844: Update 'toggle sneak' documentation diff --git a/CHANGELOG_PR.md b/CHANGELOG_PR.md index cfe709e97..100bc376f 100644 --- a/CHANGELOG_PR.md +++ b/CHANGELOG_PR.md @@ -38,9 +38,15 @@ Editor Bug Fixes: - Disabled record sorting in Topic and Journal Info tables, implemented drag-move for records (#4357) - Topic and Journal Info records can now be cloned with a different parent Topic/Journal Id (#4363) - Verifier no longer checks for alleged 'race' entries in clothing body parts (#5400) +- Cell borders are now properly redrawn when undoing/redoing terrain changes (#5473) - Loading mods now keeps the master index (#5675) - Flicker and crashing on XFCE4 fixed (#5703) - Collada models render properly in the Editor (#5713) +- Terrain-selection grid is now properly updated when undoing/redoing terrain changes (#6022) +- Tool outline and select/edit actions in "Terrain land editing" mode now ignore references (#6023) +- Primary-select and secondary-select actions in "Terrain land editing" mode now behave like in "Instance editing" mode (#6024) +- Using the circle brush to select terrain in the "Terrain land editing" mode no longer selects vertices outside the circle (#6035) +- Vertices at the NW and SE corners of a cell can now also be selected in "Terrain land editing" mode if the adjacent cells aren't loaded yet (#6036) Miscellaneous: - Prevent save-game bloating by using an appropriate fog texture format (#5108) diff --git a/CI/before_script.linux.sh b/CI/before_script.linux.sh index f648bee51..44fce54b5 100755 --- a/CI/before_script.linux.sh +++ b/CI/before_script.linux.sh @@ -4,9 +4,14 @@ set -xeo pipefail free -m +BUILD_UNITTESTS=OFF +BUILD_BENCHMARKS=OFF + if [[ "${BUILD_TESTS_ONLY}" ]]; then export GOOGLETEST_DIR="${PWD}/googletest/build/install" env GENERATOR='Unix Makefiles' CONFIGURATION=Release CI/build_googletest.sh + BUILD_UNITTESTS=ON + BUILD_BENCHMARKS=ON fi declare -a CMAKE_CONF_OPTS=( @@ -54,7 +59,8 @@ if [[ "${BUILD_TESTS_ONLY}" ]]; then -DBUILD_ESSIMPORTER=OFF \ -DBUILD_OPENCS=OFF \ -DBUILD_WIZARD=OFF \ - -DBUILD_UNITTESTS=ON \ + -DBUILD_UNITTESTS=${BUILD_UNITTESTS} \ + -DBUILD_BENCHMARKS=${BUILD_BENCHMARKS} \ -DGTEST_ROOT="${GOOGLETEST_DIR}" \ -DGMOCK_ROOT="${GOOGLETEST_DIR}" \ .. diff --git a/CI/before_script.msvc.sh b/CI/before_script.msvc.sh index d98c28038..bb662c9de 100644 --- a/CI/before_script.msvc.sh +++ b/CI/before_script.msvc.sh @@ -73,6 +73,7 @@ CONFIGURATIONS=() TEST_FRAMEWORK="" GOOGLE_INSTALL_ROOT="" INSTALL_PREFIX="." +BUILD_BENCHMARKS="" ACTIVATE_MSVC="" SINGLE_CONFIG="" @@ -133,6 +134,9 @@ while [ $# -gt 0 ]; do INSTALL_PREFIX=$(echo "$1" | sed 's;\\;/;g' | sed -E 's;/+;/;g') shift ;; + b ) + BUILD_BENCHMARKS=true ;; + h ) cat </dev/null 2>&1 || { echo "Error: vswhere is not on the path."; wrappedExit 1; } diff --git a/CMakeLists.txt b/CMakeLists.txt index 607a5d56b..4d66eea7b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -480,8 +480,8 @@ configure_resource_file(${OpenMW_SOURCE_DIR}/files/tes3mp/tes3mp-client-default. configure_resource_file(${OpenMW_SOURCE_DIR}/files/tes3mp/tes3mp-server-default.cfg "${OpenMW_BINARY_DIR}" "tes3mp-server-default.cfg") -configure_resource_file(${OpenMW_SOURCE_DIR}/files/settings-default.cfg - "${OpenMW_BINARY_DIR}" "settings-default.cfg") +pack_resource_file(${OpenMW_SOURCE_DIR}/files/settings-default.cfg + "${OpenMW_BINARY_DIR}" "defaults.bin") configure_resource_file(${OpenMW_SOURCE_DIR}/files/openmw.appdata.xml "${OpenMW_BINARY_DIR}" "openmw.appdata.xml") @@ -564,6 +564,7 @@ IF(BUILD_OPENMW OR BUILD_OPENCS) # End of tes3mp addition add_subdirectory (extern/osg-ffmpeg-videoplayer) add_subdirectory (extern/oics) +add_subdirectory (extern/Base64) if (BUILD_OPENCS) add_subdirectory (extern/osgQt) endif() @@ -668,11 +669,8 @@ if (WIN32) set(WARNINGS "/W4") set(WARNINGS_DISABLE - # OpenMW specific warnings 4100 # Unreferenced formal parameter (-Wunused-parameter) 4127 # Conditional expression is constant - 4244 # Storing value of one type in variable of another (size_t in int, for example) - 4267 # Conversion from 'size_t' to 'int', possible loss of data 4996 # Function was declared deprecated ) @@ -741,6 +739,10 @@ if (WIN32) if (BUILD_WIZARD) set_target_properties(openmw-wizard PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}") endif() + + if (BUILD_BENCHMARKS) + set_target_properties(openmw_detournavigator_navmeshtilescache_benchmark PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}") + endif() endif(MSVC) # TODO: At some point release builds should not use the console but rather write to a log file @@ -880,7 +882,7 @@ elseif(NOT APPLE) INSTALL(FILES "${OpenMW_SOURCE_DIR}/README.md" DESTINATION "." RENAME "README.txt") INSTALL(FILES "${OpenMW_SOURCE_DIR}/LICENSE" DESTINATION "." RENAME "LICENSE.txt") INSTALL(FILES "${OpenMW_SOURCE_DIR}/files/mygui/DejaVuFontLicense.txt" DESTINATION ".") - INSTALL(FILES "${INSTALL_SOURCE}/settings-default.cfg" DESTINATION ".") + INSTALL(FILES "${INSTALL_SOURCE}/defaults.bin" DESTINATION ".") # Start of tes3mp addition INSTALL(FILES "${INSTALL_SOURCE}/tes3mp-client-default.cfg" DESTINATION ".") INSTALL(FILES "${INSTALL_SOURCE}/tes3mp-server-default.cfg" DESTINATION ".") @@ -933,13 +935,13 @@ elseif(NOT APPLE) SET(VCREDIST32 "${OpenMW_BINARY_DIR}/vcredist_x86.exe") if(EXISTS ${VCREDIST32}) INSTALL(FILES ${VCREDIST32} DESTINATION "redist") - SET(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "ExecWait '\\\"$INSTDIR\\\\redist\\\\vcredist_x86.exe\\\" /q'" ) + SET(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "ExecWait '\\\"$INSTDIR\\\\redist\\\\vcredist_x86.exe\\\" /q /norestart'" ) endif(EXISTS ${VCREDIST32}) SET(VCREDIST64 "${OpenMW_BINARY_DIR}/vcredist_x64.exe") if(EXISTS ${VCREDIST64}) INSTALL(FILES ${VCREDIST64} DESTINATION "redist") - SET(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "ExecWait '\\\"$INSTDIR\\\\redist\\\\vcredist_x64.exe\\\" /q'" ) + SET(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "ExecWait '\\\"$INSTDIR\\\\redist\\\\vcredist_x64.exe\\\" /q /norestart'" ) endif(EXISTS ${VCREDIST64}) SET(OALREDIST "${OpenMW_BINARY_DIR}/oalinst.exe") @@ -1022,7 +1024,7 @@ elseif(NOT APPLE) ENDIF(BUILD_OPENCS) # Install global configuration files - INSTALL(FILES "${INSTALL_SOURCE}/settings-default.cfg" DESTINATION "${SYSCONFDIR}" COMPONENT "openmw") + INSTALL(FILES "${INSTALL_SOURCE}/defaults.bin" DESTINATION "${SYSCONFDIR}" COMPONENT "openmw") INSTALL(FILES "${INSTALL_SOURCE}/openmw.cfg.install" DESTINATION "${SYSCONFDIR}" RENAME "openmw.cfg" COMPONENT "openmw") INSTALL(FILES "${INSTALL_SOURCE}/resources/version" DESTINATION "${SYSCONFDIR}" COMPONENT "openmw") INSTALL(FILES "${INSTALL_SOURCE}/gamecontrollerdb.txt" DESTINATION "${SYSCONFDIR}" COMPONENT "openmw") diff --git a/apps/benchmarks/CMakeLists.txt b/apps/benchmarks/CMakeLists.txt index b7170003e..f9aa9aad4 100644 --- a/apps/benchmarks/CMakeLists.txt +++ b/apps/benchmarks/CMakeLists.txt @@ -19,16 +19,9 @@ FetchContent_MakeAvailableExcludeFromAll(benchmark) set(CMAKE_CXX_FLAGS "${SAVED_CMAKE_CXX_FLAGS}") openmw_add_executable(openmw_detournavigator_navmeshtilescache_benchmark detournavigator/navmeshtilescache.cpp) -target_compile_options(openmw_detournavigator_navmeshtilescache_benchmark PRIVATE -Wall) target_compile_features(openmw_detournavigator_navmeshtilescache_benchmark PRIVATE cxx_std_17) target_link_libraries(openmw_detournavigator_navmeshtilescache_benchmark benchmark::benchmark components) if (UNIX AND NOT APPLE) target_link_libraries(openmw_detournavigator_navmeshtilescache_benchmark ${CMAKE_THREAD_LIBS_INIT}) endif() - -if (MSVC) - if (CMAKE_CL_64) - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /bigobj") - endif (CMAKE_CL_64) -endif (MSVC) diff --git a/apps/browser/main.cpp b/apps/browser/main.cpp index 951685ad0..1fbf36867 100644 --- a/apps/browser/main.cpp +++ b/apps/browser/main.cpp @@ -13,9 +13,9 @@ std::string loadSettings (Settings::Manager & settings) // prefer local if (boost::filesystem::exists(localdefault)) - settings.loadDefault(localdefault); + settings.loadDefault(localdefault, false); else if (boost::filesystem::exists(globaldefault)) - settings.loadDefault(globaldefault); + settings.loadDefault(globaldefault, false); else throw std::runtime_error ("No default settings file found! Make sure the file \"tes3mp-client-default.cfg\" was properly installed."); diff --git a/apps/esmtool/esmtool.cpp b/apps/esmtool/esmtool.cpp index 579f5f67d..60483f981 100644 --- a/apps/esmtool/esmtool.cpp +++ b/apps/esmtool/esmtool.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -322,7 +323,7 @@ int load(Arguments& info) std::string filename = info.filename; std::cout << "Loading file: " << filename << std::endl; - std::list skipped; + std::unordered_set skipped; try { @@ -364,17 +365,17 @@ int load(Arguments& info) // Loop through all records while(esm.hasMoreRecs()) { - ESM::NAME n = esm.getRecName(); + const ESM::NAME n = esm.getRecName(); uint32_t flags; esm.getRecHeader(flags); EsmTool::RecordBase *record = EsmTool::RecordBase::create(n); if (record == nullptr) { - if (std::find(skipped.begin(), skipped.end(), n.intval) == skipped.end()) + if (skipped.count(n.intval) == 0) { std::cout << "Skipping " << n.toString() << " records." << std::endl; - skipped.push_back(n.intval); + skipped.emplace(n.intval); } esm.skipRecord(); diff --git a/apps/esmtool/record.cpp b/apps/esmtool/record.cpp index 3679184a6..55170e232 100644 --- a/apps/esmtool/record.cpp +++ b/apps/esmtool/record.cpp @@ -172,7 +172,7 @@ void printTransport(const std::vector& transport) namespace EsmTool { RecordBase * -RecordBase::create(ESM::NAME type) +RecordBase::create(const ESM::NAME type) { RecordBase *record = nullptr; diff --git a/apps/launcher/advancedpage.cpp b/apps/launcher/advancedpage.cpp index 4ba732234..ed36634b6 100644 --- a/apps/launcher/advancedpage.cpp +++ b/apps/launcher/advancedpage.cpp @@ -13,11 +13,9 @@ #include "utils/openalutil.hpp" -Launcher::AdvancedPage::AdvancedPage(Config::GameSettings &gameSettings, - Settings::Manager &engineSettings, QWidget *parent) +Launcher::AdvancedPage::AdvancedPage(Config::GameSettings &gameSettings, QWidget *parent) : QWidget(parent) , mGameSettings(gameSettings) - , mEngineSettings(engineSettings) { setObjectName ("AdvancedPage"); setupUi(this); @@ -100,12 +98,12 @@ bool Launcher::AdvancedPage::loadSettings() loadSettingBool(normaliseRaceSpeedCheckBox, "normalise race speed", "Game"); loadSettingBool(swimUpwardCorrectionCheckBox, "swim upward correction", "Game"); loadSettingBool(avoidCollisionsCheckBox, "NPCs avoid collisions", "Game"); - int unarmedFactorsStrengthIndex = mEngineSettings.getInt("strength influences hand to hand", "Game"); + int unarmedFactorsStrengthIndex = Settings::Manager::getInt("strength influences hand to hand", "Game"); if (unarmedFactorsStrengthIndex >= 0 && unarmedFactorsStrengthIndex <= 2) unarmedFactorsStrengthComboBox->setCurrentIndex(unarmedFactorsStrengthIndex); loadSettingBool(stealingFromKnockedOutCheckBox, "always allow stealing from knocked out actors", "Game"); loadSettingBool(enableNavigatorCheckBox, "enable", "Navigator"); - int numPhysicsThreads = mEngineSettings.getInt("async num threads", "Physics"); + int numPhysicsThreads = Settings::Manager::getInt("async num threads", "Physics"); if (numPhysicsThreads >= 0) physicsThreadsSpinBox->setValue(numPhysicsThreads); loadSettingBool(allowNPCToFollowOverWaterSurfaceCheckBox, "allow actors to follow over water surface", "Game"); @@ -130,26 +128,19 @@ bool Launcher::AdvancedPage::loadSettings() loadSettingBool(turnToMovementDirectionCheckBox, "turn to movement direction", "Game"); loadSettingBool(smoothMovementCheckBox, "smooth movement", "Game"); - const bool distantTerrain = mEngineSettings.getBool("distant terrain", "Terrain"); - const bool objectPaging = mEngineSettings.getBool("object paging", "Terrain"); + const bool distantTerrain = Settings::Manager::getBool("distant terrain", "Terrain"); + const bool objectPaging = Settings::Manager::getBool("object paging", "Terrain"); if (distantTerrain && objectPaging) { distantLandCheckBox->setCheckState(Qt::Checked); } loadSettingBool(activeGridObjectPagingCheckBox, "object paging active grid", "Terrain"); - viewingDistanceComboBox->setValue(convertToCells(mEngineSettings.getInt("viewing distance", "Camera"))); - - int lightingMethod = 1; - if (mEngineSettings.getString("lighting method", "Shaders") == "legacy") - lightingMethod = 0; - else if (mEngineSettings.getString("lighting method", "Shaders") == "shaders") - lightingMethod = 2; - lightingMethodComboBox->setCurrentIndex(lightingMethod); + viewingDistanceComboBox->setValue(convertToCells(Settings::Manager::getInt("viewing distance", "Camera"))); } // Audio { - std::string selectedAudioDevice = mEngineSettings.getString("device", "Sound"); + std::string selectedAudioDevice = Settings::Manager::getString("device", "Sound"); if (selectedAudioDevice.empty() == false) { int audioDeviceIndex = audioDeviceSelectorComboBox->findData(QString::fromStdString(selectedAudioDevice)); @@ -158,12 +149,12 @@ bool Launcher::AdvancedPage::loadSettings() audioDeviceSelectorComboBox->setCurrentIndex(audioDeviceIndex); } } - int hrtfEnabledIndex = mEngineSettings.getInt("hrtf enable", "Sound"); + int hrtfEnabledIndex = Settings::Manager::getInt("hrtf enable", "Sound"); if (hrtfEnabledIndex >= -1 && hrtfEnabledIndex <= 1) { enableHRTFComboBox->setCurrentIndex(hrtfEnabledIndex + 1); } - std::string selectedHRTFProfile = mEngineSettings.getString("hrtf", "Sound"); + std::string selectedHRTFProfile = Settings::Manager::getString("hrtf", "Sound"); if (selectedHRTFProfile.empty() == false) { int hrtfProfileIndex = hrtfProfileSelectorComboBox->findData(QString::fromStdString(selectedHRTFProfile)); @@ -185,7 +176,7 @@ bool Launcher::AdvancedPage::loadSettings() loadSettingBool(deferredPreviewRotationCheckBox, "deferred preview rotation", "Camera"); loadSettingBool(headBobbingCheckBox, "head bobbing", "Camera"); defaultShoulderComboBox->setCurrentIndex( - mEngineSettings.getVector2("view over shoulder offset", "Camera").x() >= 0 ? 0 : 1); + Settings::Manager::getVector2("view over shoulder offset", "Camera").x() >= 0 ? 0 : 1); } // Interface Changes @@ -195,13 +186,13 @@ bool Launcher::AdvancedPage::loadSettings() loadSettingBool(showMeleeInfoCheckBox, "show melee info", "Game"); loadSettingBool(showProjectileDamageCheckBox, "show projectile damage", "Game"); loadSettingBool(changeDialogTopicsCheckBox, "color topic enable", "GUI"); - int showOwnedIndex = mEngineSettings.getInt("show owned", "Game"); + int showOwnedIndex = Settings::Manager::getInt("show owned", "Game"); // Match the index with the option (only 0, 1, 2, or 3 are valid). Will default to 0 if invalid. if (showOwnedIndex >= 0 && showOwnedIndex <= 3) showOwnedComboBox->setCurrentIndex(showOwnedIndex); loadSettingBool(stretchBackgroundCheckBox, "stretch menu background", "GUI"); loadSettingBool(graphicHerbalismCheckBox, "graphic herbalism", "Game"); - scalingSpinBox->setValue(mEngineSettings.getFloat("scaling factor", "GUI")); + scalingSpinBox->setValue(Settings::Manager::getFloat("scaling factor", "GUI")); } // Bug fixes @@ -214,10 +205,10 @@ bool Launcher::AdvancedPage::loadSettings() { // Saves loadSettingBool(timePlayedCheckbox, "timeplayed", "Saves"); - maximumQuicksavesComboBox->setValue(mEngineSettings.getInt("max quicksaves", "Saves")); + maximumQuicksavesComboBox->setValue(Settings::Manager::getInt("max quicksaves", "Saves")); // Other Settings - QString screenshotFormatString = QString::fromStdString(mEngineSettings.getString("screenshot format", "General")).toUpper(); + QString screenshotFormatString = QString::fromStdString(Settings::Manager::getString("screenshot format", "General")).toUpper(); if (screenshotFormatComboBox->findText(screenshotFormatString) == -1) screenshotFormatComboBox->addItem(screenshotFormatString); screenshotFormatComboBox->setCurrentIndex(screenshotFormatComboBox->findText(screenshotFormatString)); @@ -258,13 +249,13 @@ void Launcher::AdvancedPage::saveSettings() saveSettingBool(swimUpwardCorrectionCheckBox, "swim upward correction", "Game"); saveSettingBool(avoidCollisionsCheckBox, "NPCs avoid collisions", "Game"); int unarmedFactorsStrengthIndex = unarmedFactorsStrengthComboBox->currentIndex(); - if (unarmedFactorsStrengthIndex != mEngineSettings.getInt("strength influences hand to hand", "Game")) - mEngineSettings.setInt("strength influences hand to hand", "Game", unarmedFactorsStrengthIndex); + if (unarmedFactorsStrengthIndex != Settings::Manager::getInt("strength influences hand to hand", "Game")) + Settings::Manager::setInt("strength influences hand to hand", "Game", unarmedFactorsStrengthIndex); saveSettingBool(stealingFromKnockedOutCheckBox, "always allow stealing from knocked out actors", "Game"); saveSettingBool(enableNavigatorCheckBox, "enable", "Navigator"); int numPhysicsThreads = physicsThreadsSpinBox->value(); - if (numPhysicsThreads != mEngineSettings.getInt("async num threads", "Physics")) - mEngineSettings.setInt("async num threads", "Physics", numPhysicsThreads); + if (numPhysicsThreads != Settings::Manager::getInt("async num threads", "Physics")) + Settings::Manager::setInt("async num threads", "Physics", numPhysicsThreads); } // Visuals @@ -282,23 +273,20 @@ void Launcher::AdvancedPage::saveSettings() saveSettingBool(turnToMovementDirectionCheckBox, "turn to movement direction", "Game"); saveSettingBool(smoothMovementCheckBox, "smooth movement", "Game"); - const bool distantTerrain = mEngineSettings.getBool("distant terrain", "Terrain"); - const bool objectPaging = mEngineSettings.getBool("object paging", "Terrain"); + const bool distantTerrain = Settings::Manager::getBool("distant terrain", "Terrain"); + const bool objectPaging = Settings::Manager::getBool("object paging", "Terrain"); const bool wantDistantLand = distantLandCheckBox->checkState(); if (wantDistantLand != (distantTerrain && objectPaging)) { - mEngineSettings.setBool("distant terrain", "Terrain", wantDistantLand); - mEngineSettings.setBool("object paging", "Terrain", wantDistantLand); + Settings::Manager::setBool("distant terrain", "Terrain", wantDistantLand); + Settings::Manager::setBool("object paging", "Terrain", wantDistantLand); } saveSettingBool(activeGridObjectPagingCheckBox, "object paging active grid", "Terrain"); double viewingDistance = viewingDistanceComboBox->value(); - if (viewingDistance != convertToCells(mEngineSettings.getInt("viewing distance", "Camera"))) + if (viewingDistance != convertToCells(Settings::Manager::getInt("viewing distance", "Camera"))) { - mEngineSettings.setInt("viewing distance", "Camera", convertToUnits(viewingDistance)); + Settings::Manager::setInt("viewing distance", "Camera", convertToUnits(viewingDistance)); } - - static std::array lightingMethodMap = {"legacy", "shaders compatibility", "shaders"}; - mEngineSettings.setString("lighting method", "Shaders", lightingMethodMap[lightingMethodComboBox->currentIndex()]); } // Audio @@ -306,25 +294,25 @@ void Launcher::AdvancedPage::saveSettings() int audioDeviceIndex = audioDeviceSelectorComboBox->currentIndex(); if (audioDeviceIndex != 0) { - mEngineSettings.setString("device", "Sound", audioDeviceSelectorComboBox->currentText().toUtf8().constData()); + Settings::Manager::setString("device", "Sound", audioDeviceSelectorComboBox->currentText().toUtf8().constData()); } else { - mEngineSettings.setString("device", "Sound", ""); + Settings::Manager::setString("device", "Sound", ""); } int hrtfEnabledIndex = enableHRTFComboBox->currentIndex() - 1; - if (hrtfEnabledIndex != mEngineSettings.getInt("hrtf enable", "Sound")) + if (hrtfEnabledIndex != Settings::Manager::getInt("hrtf enable", "Sound")) { - mEngineSettings.setInt("hrtf enable", "Sound", hrtfEnabledIndex); + Settings::Manager::setInt("hrtf enable", "Sound", hrtfEnabledIndex); } int selectedHRTFProfileIndex = hrtfProfileSelectorComboBox->currentIndex(); if (selectedHRTFProfileIndex != 0) { - mEngineSettings.setString("hrtf", "Sound", hrtfProfileSelectorComboBox->currentText().toUtf8().constData()); + Settings::Manager::setString("hrtf", "Sound", hrtfProfileSelectorComboBox->currentText().toUtf8().constData()); } else { - mEngineSettings.setString("hrtf", "Sound", ""); + Settings::Manager::setString("hrtf", "Sound", ""); } } @@ -336,14 +324,14 @@ void Launcher::AdvancedPage::saveSettings() saveSettingBool(deferredPreviewRotationCheckBox, "deferred preview rotation", "Camera"); saveSettingBool(headBobbingCheckBox, "head bobbing", "Camera"); - osg::Vec2f shoulderOffset = mEngineSettings.getVector2("view over shoulder offset", "Camera"); + osg::Vec2f shoulderOffset = Settings::Manager::getVector2("view over shoulder offset", "Camera"); if (defaultShoulderComboBox->currentIndex() != (shoulderOffset.x() >= 0 ? 0 : 1)) { if (defaultShoulderComboBox->currentIndex() == 0) shoulderOffset.x() = std::abs(shoulderOffset.x()); else shoulderOffset.x() = -std::abs(shoulderOffset.x()); - mEngineSettings.setVector2("view over shoulder offset", "Camera", shoulderOffset); + Settings::Manager::setVector2("view over shoulder offset", "Camera", shoulderOffset); } } @@ -355,13 +343,13 @@ void Launcher::AdvancedPage::saveSettings() saveSettingBool(showProjectileDamageCheckBox, "show projectile damage", "Game"); saveSettingBool(changeDialogTopicsCheckBox, "color topic enable", "GUI"); int showOwnedCurrentIndex = showOwnedComboBox->currentIndex(); - if (showOwnedCurrentIndex != mEngineSettings.getInt("show owned", "Game")) - mEngineSettings.setInt("show owned", "Game", showOwnedCurrentIndex); + if (showOwnedCurrentIndex != Settings::Manager::getInt("show owned", "Game")) + Settings::Manager::setInt("show owned", "Game", showOwnedCurrentIndex); saveSettingBool(stretchBackgroundCheckBox, "stretch menu background", "GUI"); saveSettingBool(graphicHerbalismCheckBox, "graphic herbalism", "Game"); float uiScalingFactor = scalingSpinBox->value(); - if (uiScalingFactor != mEngineSettings.getFloat("scaling factor", "GUI")) - mEngineSettings.setFloat("scaling factor", "GUI", uiScalingFactor); + if (uiScalingFactor != Settings::Manager::getFloat("scaling factor", "GUI")) + Settings::Manager::setFloat("scaling factor", "GUI", uiScalingFactor); } // Bug fixes @@ -375,15 +363,15 @@ void Launcher::AdvancedPage::saveSettings() // Saves Settings saveSettingBool(timePlayedCheckbox, "timeplayed", "Saves"); int maximumQuicksaves = maximumQuicksavesComboBox->value(); - if (maximumQuicksaves != mEngineSettings.getInt("max quicksaves", "Saves")) + if (maximumQuicksaves != Settings::Manager::getInt("max quicksaves", "Saves")) { - mEngineSettings.setInt("max quicksaves", "Saves", maximumQuicksaves); + Settings::Manager::setInt("max quicksaves", "Saves", maximumQuicksaves); } // Other Settings std::string screenshotFormatString = screenshotFormatComboBox->currentText().toLower().toStdString(); - if (screenshotFormatString != mEngineSettings.getString("screenshot format", "General")) - mEngineSettings.setString("screenshot format", "General", screenshotFormatString); + if (screenshotFormatString != Settings::Manager::getString("screenshot format", "General")) + Settings::Manager::setString("screenshot format", "General", screenshotFormatString); } // Testing @@ -407,15 +395,15 @@ void Launcher::AdvancedPage::saveSettings() void Launcher::AdvancedPage::loadSettingBool(QCheckBox *checkbox, const std::string &setting, const std::string &group) { - if (mEngineSettings.getBool(setting, group)) + if (Settings::Manager::getBool(setting, group)) checkbox->setCheckState(Qt::Checked); } void Launcher::AdvancedPage::saveSettingBool(QCheckBox *checkbox, const std::string &setting, const std::string &group) { bool cValue = checkbox->checkState(); - if (cValue != mEngineSettings.getBool(setting, group)) - mEngineSettings.setBool(setting, group, cValue); + if (cValue != Settings::Manager::getBool(setting, group)) + Settings::Manager::setBool(setting, group, cValue); } void Launcher::AdvancedPage::slotLoadedCellsChanged(QStringList cellNames) diff --git a/apps/launcher/advancedpage.hpp b/apps/launcher/advancedpage.hpp index 93b818dbb..9685dcefe 100644 --- a/apps/launcher/advancedpage.hpp +++ b/apps/launcher/advancedpage.hpp @@ -17,8 +17,7 @@ namespace Launcher Q_OBJECT public: - AdvancedPage(Config::GameSettings &gameSettings, - Settings::Manager &engineSettings, QWidget *parent = nullptr); + explicit AdvancedPage(Config::GameSettings &gameSettings, QWidget *parent = nullptr); bool loadSettings(); void saveSettings(); @@ -34,7 +33,6 @@ namespace Launcher private: Config::GameSettings &mGameSettings; - Settings::Manager &mEngineSettings; QCompleter mCellNameCompleter; QStringListModel mCellNameCompleterModel; diff --git a/apps/launcher/graphicspage.cpp b/apps/launcher/graphicspage.cpp index c6e74573c..ebb031e9e 100644 --- a/apps/launcher/graphicspage.cpp +++ b/apps/launcher/graphicspage.cpp @@ -32,9 +32,8 @@ QString getAspect(int x, int y) return QString(QString::number(xaspect) + ":" + QString::number(yaspect)); } -Launcher::GraphicsPage::GraphicsPage(Settings::Manager &engineSettings, QWidget *parent) +Launcher::GraphicsPage::GraphicsPage(QWidget *parent) : QWidget(parent) - , mEngineSettings(engineSettings) { setObjectName ("GraphicsPage"); setupUi(this); @@ -93,26 +92,27 @@ bool Launcher::GraphicsPage::loadSettings() if (!setupSDL()) return false; - if (mEngineSettings.getBool("vsync", "Video")) + // Visuals + if (Settings::Manager::getBool("vsync", "Video")) vSyncCheckBox->setCheckState(Qt::Checked); - if (mEngineSettings.getBool("fullscreen", "Video")) + if (Settings::Manager::getBool("fullscreen", "Video")) fullScreenCheckBox->setCheckState(Qt::Checked); - if (mEngineSettings.getBool("window border", "Video")) + if (Settings::Manager::getBool("window border", "Video")) windowBorderCheckBox->setCheckState(Qt::Checked); // aaValue is the actual value (0, 1, 2, 4, 8, 16) - int aaValue = mEngineSettings.getInt("antialiasing", "Video"); + int aaValue = Settings::Manager::getInt("antialiasing", "Video"); // aaIndex is the index into the allowed values in the pull down. int aaIndex = antiAliasingComboBox->findText(QString::number(aaValue)); if (aaIndex != -1) antiAliasingComboBox->setCurrentIndex(aaIndex); - int width = mEngineSettings.getInt("resolution x", "Video"); - int height = mEngineSettings.getInt("resolution y", "Video"); + int width = Settings::Manager::getInt("resolution x", "Video"); + int height = Settings::Manager::getInt("resolution y", "Video"); QString resolution = QString::number(width) + QString(" x ") + QString::number(height); - screenComboBox->setCurrentIndex(mEngineSettings.getInt("screen", "Video")); + screenComboBox->setCurrentIndex(Settings::Manager::getInt("screen", "Video")); int resIndex = resolutionComboBox->findText(resolution, Qt::MatchStartsWith); @@ -125,40 +125,49 @@ bool Launcher::GraphicsPage::loadSettings() customHeightSpinBox->setValue(height); } - float fpsLimit = mEngineSettings.getFloat("framerate limit", "Video"); + float fpsLimit = Settings::Manager::getFloat("framerate limit", "Video"); if (fpsLimit != 0) { framerateLimitCheckBox->setCheckState(Qt::Checked); framerateLimitSpinBox->setValue(fpsLimit); } - if (mEngineSettings.getBool("actor shadows", "Shadows")) + // Lighting + int lightingMethod = 1; + if (Settings::Manager::getString("lighting method", "Shaders") == "legacy") + lightingMethod = 0; + else if (Settings::Manager::getString("lighting method", "Shaders") == "shaders") + lightingMethod = 2; + lightingMethodComboBox->setCurrentIndex(lightingMethod); + + // Shadows + if (Settings::Manager::getBool("actor shadows", "Shadows")) actorShadowsCheckBox->setCheckState(Qt::Checked); - if (mEngineSettings.getBool("player shadows", "Shadows")) + if (Settings::Manager::getBool("player shadows", "Shadows")) playerShadowsCheckBox->setCheckState(Qt::Checked); - if (mEngineSettings.getBool("terrain shadows", "Shadows")) + if (Settings::Manager::getBool("terrain shadows", "Shadows")) terrainShadowsCheckBox->setCheckState(Qt::Checked); - if (mEngineSettings.getBool("object shadows", "Shadows")) + if (Settings::Manager::getBool("object shadows", "Shadows")) objectShadowsCheckBox->setCheckState(Qt::Checked); - if (mEngineSettings.getBool("enable indoor shadows", "Shadows")) + if (Settings::Manager::getBool("enable indoor shadows", "Shadows")) indoorShadowsCheckBox->setCheckState(Qt::Checked); shadowComputeSceneBoundsComboBox->setCurrentIndex( shadowComputeSceneBoundsComboBox->findText( - QString(tr(mEngineSettings.getString("compute scene bounds", "Shadows").c_str())))); + QString(tr(Settings::Manager::getString("compute scene bounds", "Shadows").c_str())))); - int shadowDistLimit = mEngineSettings.getInt("maximum shadow map distance", "Shadows"); + int shadowDistLimit = Settings::Manager::getInt("maximum shadow map distance", "Shadows"); if (shadowDistLimit > 0) { shadowDistanceCheckBox->setCheckState(Qt::Checked); shadowDistanceSpinBox->setValue(shadowDistLimit); } - float shadowFadeStart = mEngineSettings.getFloat("shadow fade start", "Shadows"); + float shadowFadeStart = Settings::Manager::getFloat("shadow fade start", "Shadows"); if (shadowFadeStart != 0) fadeStartSpinBox->setValue(shadowFadeStart); - int shadowRes = mEngineSettings.getInt("shadow map resolution", "Shadows"); + int shadowRes = Settings::Manager::getInt("shadow map resolution", "Shadows"); int shadowResIndex = shadowResolutionComboBox->findText(QString::number(shadowRes)); if (shadowResIndex != -1) shadowResolutionComboBox->setCurrentIndex(shadowResIndex); @@ -168,23 +177,25 @@ bool Launcher::GraphicsPage::loadSettings() void Launcher::GraphicsPage::saveSettings() { + // Visuals + // Ensure we only set the new settings if they changed. This is to avoid cluttering the // user settings file (which by definition should only contain settings the user has touched) bool cVSync = vSyncCheckBox->checkState(); - if (cVSync != mEngineSettings.getBool("vsync", "Video")) - mEngineSettings.setBool("vsync", "Video", cVSync); + if (cVSync != Settings::Manager::getBool("vsync", "Video")) + Settings::Manager::setBool("vsync", "Video", cVSync); bool cFullScreen = fullScreenCheckBox->checkState(); - if (cFullScreen != mEngineSettings.getBool("fullscreen", "Video")) - mEngineSettings.setBool("fullscreen", "Video", cFullScreen); + if (cFullScreen != Settings::Manager::getBool("fullscreen", "Video")) + Settings::Manager::setBool("fullscreen", "Video", cFullScreen); bool cWindowBorder = windowBorderCheckBox->checkState(); - if (cWindowBorder != mEngineSettings.getBool("window border", "Video")) - mEngineSettings.setBool("window border", "Video", cWindowBorder); + if (cWindowBorder != Settings::Manager::getBool("window border", "Video")) + Settings::Manager::setBool("window border", "Video", cWindowBorder); int cAAValue = antiAliasingComboBox->currentText().toInt(); - if (cAAValue != mEngineSettings.getInt("antialiasing", "Video")) - mEngineSettings.setInt("antialiasing", "Video", cAAValue); + if (cAAValue != Settings::Manager::getInt("antialiasing", "Video")) + Settings::Manager::setInt("antialiasing", "Video", cAAValue); int cWidth = 0; int cHeight = 0; @@ -199,33 +210,38 @@ void Launcher::GraphicsPage::saveSettings() cHeight = customHeightSpinBox->value(); } - if (cWidth != mEngineSettings.getInt("resolution x", "Video")) - mEngineSettings.setInt("resolution x", "Video", cWidth); + if (cWidth != Settings::Manager::getInt("resolution x", "Video")) + Settings::Manager::setInt("resolution x", "Video", cWidth); - if (cHeight != mEngineSettings.getInt("resolution y", "Video")) - mEngineSettings.setInt("resolution y", "Video", cHeight); + if (cHeight != Settings::Manager::getInt("resolution y", "Video")) + Settings::Manager::setInt("resolution y", "Video", cHeight); int cScreen = screenComboBox->currentIndex(); - if (cScreen != mEngineSettings.getInt("screen", "Video")) - mEngineSettings.setInt("screen", "Video", cScreen); + if (cScreen != Settings::Manager::getInt("screen", "Video")) + Settings::Manager::setInt("screen", "Video", cScreen); if (framerateLimitCheckBox->checkState() != Qt::Unchecked) { float cFpsLimit = framerateLimitSpinBox->value(); - if (cFpsLimit != mEngineSettings.getFloat("framerate limit", "Video")) - mEngineSettings.setFloat("framerate limit", "Video", cFpsLimit); + if (cFpsLimit != Settings::Manager::getFloat("framerate limit", "Video")) + Settings::Manager::setFloat("framerate limit", "Video", cFpsLimit); } - else if (mEngineSettings.getFloat("framerate limit", "Video") != 0) + else if (Settings::Manager::getFloat("framerate limit", "Video") != 0) { - mEngineSettings.setFloat("framerate limit", "Video", 0); + Settings::Manager::setFloat("framerate limit", "Video", 0); } + // Lighting + static std::array lightingMethodMap = {"legacy", "shaders compatibility", "shaders"}; + Settings::Manager::setString("lighting method", "Shaders", lightingMethodMap[lightingMethodComboBox->currentIndex()]); + + // Shadows int cShadowDist = shadowDistanceCheckBox->checkState() != Qt::Unchecked ? shadowDistanceSpinBox->value() : 0; - if (mEngineSettings.getInt("maximum shadow map distance", "Shadows") != cShadowDist) - mEngineSettings.setInt("maximum shadow map distance", "Shadows", cShadowDist); + if (Settings::Manager::getInt("maximum shadow map distance", "Shadows") != cShadowDist) + Settings::Manager::setInt("maximum shadow map distance", "Shadows", cShadowDist); float cFadeStart = fadeStartSpinBox->value(); - if (cShadowDist > 0 && mEngineSettings.getFloat("shadow fade start", "Shadows") != cFadeStart) - mEngineSettings.setFloat("shadow fade start", "Shadows", cFadeStart); + if (cShadowDist > 0 && Settings::Manager::getFloat("shadow fade start", "Shadows") != cFadeStart) + Settings::Manager::setFloat("shadow fade start", "Shadows", cFadeStart); bool cActorShadows = actorShadowsCheckBox->checkState(); bool cObjectShadows = objectShadowsCheckBox->checkState(); @@ -233,42 +249,42 @@ void Launcher::GraphicsPage::saveSettings() bool cPlayerShadows = playerShadowsCheckBox->checkState(); if (cActorShadows || cObjectShadows || cTerrainShadows || cPlayerShadows) { - 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); - if (mEngineSettings.getBool("player shadows", "Shadows") != cPlayerShadows) - mEngineSettings.setBool("player shadows", "Shadows", cPlayerShadows); - if (mEngineSettings.getBool("object shadows", "Shadows") != cObjectShadows) - mEngineSettings.setBool("object shadows", "Shadows", cObjectShadows); - if (mEngineSettings.getBool("terrain shadows", "Shadows") != cTerrainShadows) - mEngineSettings.setBool("terrain shadows", "Shadows", cTerrainShadows); + if (!Settings::Manager::getBool("enable shadows", "Shadows")) + Settings::Manager::setBool("enable shadows", "Shadows", true); + if (Settings::Manager::getBool("actor shadows", "Shadows") != cActorShadows) + Settings::Manager::setBool("actor shadows", "Shadows", cActorShadows); + if (Settings::Manager::getBool("player shadows", "Shadows") != cPlayerShadows) + Settings::Manager::setBool("player shadows", "Shadows", cPlayerShadows); + if (Settings::Manager::getBool("object shadows", "Shadows") != cObjectShadows) + Settings::Manager::setBool("object shadows", "Shadows", cObjectShadows); + if (Settings::Manager::getBool("terrain shadows", "Shadows") != cTerrainShadows) + Settings::Manager::setBool("terrain shadows", "Shadows", cTerrainShadows); } else { - if (mEngineSettings.getBool("enable shadows", "Shadows")) - mEngineSettings.setBool("enable shadows", "Shadows", false); - if (mEngineSettings.getBool("actor shadows", "Shadows")) - mEngineSettings.setBool("actor shadows", "Shadows", false); - if (mEngineSettings.getBool("player shadows", "Shadows")) - mEngineSettings.setBool("player shadows", "Shadows", false); - if (mEngineSettings.getBool("object shadows", "Shadows")) - mEngineSettings.setBool("object shadows", "Shadows", false); - if (mEngineSettings.getBool("terrain shadows", "Shadows")) - mEngineSettings.setBool("terrain shadows", "Shadows", false); + if (Settings::Manager::getBool("enable shadows", "Shadows")) + Settings::Manager::setBool("enable shadows", "Shadows", false); + if (Settings::Manager::getBool("actor shadows", "Shadows")) + Settings::Manager::setBool("actor shadows", "Shadows", false); + if (Settings::Manager::getBool("player shadows", "Shadows")) + Settings::Manager::setBool("player shadows", "Shadows", false); + if (Settings::Manager::getBool("object shadows", "Shadows")) + Settings::Manager::setBool("object shadows", "Shadows", false); + if (Settings::Manager::getBool("terrain shadows", "Shadows")) + Settings::Manager::setBool("terrain shadows", "Shadows", false); } bool cIndoorShadows = indoorShadowsCheckBox->checkState(); - if (mEngineSettings.getBool("enable indoor shadows", "Shadows") != cIndoorShadows) - mEngineSettings.setBool("enable indoor shadows", "Shadows", cIndoorShadows); + if (Settings::Manager::getBool("enable indoor shadows", "Shadows") != cIndoorShadows) + Settings::Manager::setBool("enable indoor shadows", "Shadows", cIndoorShadows); int cShadowRes = shadowResolutionComboBox->currentText().toInt(); - if (cShadowRes != mEngineSettings.getInt("shadow map resolution", "Shadows")) - mEngineSettings.setInt("shadow map resolution", "Shadows", cShadowRes); + if (cShadowRes != Settings::Manager::getInt("shadow map resolution", "Shadows")) + Settings::Manager::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); + if (cComputeSceneBounds != Settings::Manager::getString("compute scene bounds", "Shadows")) + Settings::Manager::setString("compute scene bounds", "Shadows", cComputeSceneBounds); } QStringList Launcher::GraphicsPage::getAvailableResolutions(int screen) diff --git a/apps/launcher/graphicspage.hpp b/apps/launcher/graphicspage.hpp index 7801e6475..a6754ccb0 100644 --- a/apps/launcher/graphicspage.hpp +++ b/apps/launcher/graphicspage.hpp @@ -18,7 +18,7 @@ namespace Launcher Q_OBJECT public: - GraphicsPage(Settings::Manager &engineSettings, QWidget *parent = nullptr); + explicit GraphicsPage(QWidget *parent = nullptr); void saveSettings(); bool loadSettings(); @@ -33,8 +33,6 @@ namespace Launcher void slotShadowDistLimitToggled(bool checked); private: - Settings::Manager &mEngineSettings; - QVector mResolutionsPerScreen; static QStringList getAvailableResolutions(int screen); diff --git a/apps/launcher/maindialog.cpp b/apps/launcher/maindialog.cpp index 2592af817..47dbbab8e 100644 --- a/apps/launcher/maindialog.cpp +++ b/apps/launcher/maindialog.cpp @@ -123,9 +123,9 @@ void Launcher::MainDialog::createPages() mPlayPage = new PlayPage(this); mDataFilesPage = new DataFilesPage(mCfgMgr, mGameSettings, mLauncherSettings, this); - mGraphicsPage = new GraphicsPage(mEngineSettings, this); + mGraphicsPage = new GraphicsPage(this); mSettingsPage = new SettingsPage(mCfgMgr, mGameSettings, mLauncherSettings, this); - mAdvancedPage = new AdvancedPage(mGameSettings, mEngineSettings, this); + mAdvancedPage = new AdvancedPage(mGameSettings, this); // Set the combobox of the play page to imitate the combobox on the datafilespage mPlayPage->setProfilesModel(mDataFilesPage->profilesModel()); @@ -424,11 +424,11 @@ bool Launcher::MainDialog::setupGraphicsSettings() mEngineSettings.clear(); // Create the settings manager and load default settings file - const std::string localDefault = (mCfgMgr.getLocalPath() / "settings-default.cfg").string(); - const std::string globalDefault = (mCfgMgr.getGlobalPath() / "settings-default.cfg").string(); + const std::string localDefault = (mCfgMgr.getLocalPath() / "defaults.bin").string(); + const std::string globalDefault = (mCfgMgr.getGlobalPath() / "defaults.bin").string(); std::string defaultPath; - // Prefer the settings-default.cfg in the current directory. + // Prefer the defaults.bin in the current directory. if (boost::filesystem::exists(localDefault)) defaultPath = localDefault; else if (boost::filesystem::exists(globalDefault)) @@ -436,7 +436,7 @@ bool Launcher::MainDialog::setupGraphicsSettings() // Something's very wrong if we can't find the file at all. else { cfgError(tr("Error reading OpenMW configuration file"), - tr("
Could not find settings-default.cfg

\ + tr("
Could not find defaults.bin

\ The problem may be due to an incomplete installation of OpenMW.
\ Reinstalling OpenMW may resolve the problem.")); return false; @@ -447,7 +447,7 @@ bool Launcher::MainDialog::setupGraphicsSettings() mEngineSettings.loadDefault(defaultPath); } catch (std::exception& e) { - std::string msg = std::string("
Error reading settings-default.cfg

") + e.what(); + std::string msg = std::string("
Error reading defaults.bin

") + e.what(); cfgError(tr("Error reading OpenMW configuration file"), tr(msg.c_str())); return false; } diff --git a/apps/mwiniimporter/importer.cpp b/apps/mwiniimporter/importer.cpp index 23aea2deb..2763d8ad9 100644 --- a/apps/mwiniimporter/importer.cpp +++ b/apps/mwiniimporter/importer.cpp @@ -676,7 +676,7 @@ MwIniImporter::multistrmap MwIniImporter::loadIniFile(const boost::filesystem::p } if(line[0] == '[') { - int pos = line.find(']'); + int pos = static_cast(line.find(']')); if(pos < 2) { std::cout << "Warning: ini file wrongly formatted (" << line << "). Line ignored." << std::endl; continue; @@ -686,12 +686,12 @@ MwIniImporter::multistrmap MwIniImporter::loadIniFile(const boost::filesystem::p continue; } - int comment_pos = line.find(";"); + int comment_pos = static_cast(line.find(';')); if(comment_pos > 0) { line = line.substr(0,comment_pos); } - int pos = line.find("="); + int pos = static_cast(line.find('=')); if(pos < 1) { continue; } @@ -722,7 +722,7 @@ MwIniImporter::multistrmap MwIniImporter::loadCfgFile(const boost::filesystem::p while (std::getline(file, line)) { // we cant say comment by only looking at first char anymore - int comment_pos = line.find("#"); + int comment_pos = static_cast(line.find('#')); if(comment_pos > 0) { line = line.substr(0,comment_pos); } @@ -731,7 +731,7 @@ MwIniImporter::multistrmap MwIniImporter::loadCfgFile(const boost::filesystem::p continue; } - int pos = line.find("="); + int pos = static_cast(line.find('=')); if(pos < 1) { continue; } diff --git a/apps/niftest/niftest.cpp b/apps/niftest/niftest.cpp index e9484d5f5..e403562d3 100644 --- a/apps/niftest/niftest.cpp +++ b/apps/niftest/niftest.cpp @@ -20,7 +20,7 @@ namespace bfs = boost::filesystem; ///See if the file has the named extension bool hasExtension(std::string filename, std::string extensionToFind) { - std::string extension = filename.substr(filename.find_last_of(".")+1); + std::string extension = filename.substr(filename.find_last_of('.')+1); //Convert strings to lower case for comparison std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower); diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index b73cd37b8..19c32df60 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -89,7 +89,7 @@ opencs_units (view/render scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget previewwidget editmode instancemode instanceselectionmode instancemovemode orbitcameramode pathgridmode selectionmode pathgridselectionmode cameracontroller - cellwater terraintexturemode actor terrainselection terrainshapemode brushdraw + cellwater terraintexturemode actor terrainselection terrainshapemode brushdraw commands ) opencs_units_noqt (view/render diff --git a/apps/opencs/model/doc/blacklist.cpp b/apps/opencs/model/doc/blacklist.cpp index b1d402c69..690d79983 100644 --- a/apps/opencs/model/doc/blacklist.cpp +++ b/apps/opencs/model/doc/blacklist.cpp @@ -21,7 +21,7 @@ void CSMDoc::Blacklist::add (CSMWorld::UniversalId::Type type, { std::vector& list = mIds[type]; - int size = list.size(); + size_t size = list.size(); list.resize (size+ids.size()); diff --git a/apps/opencs/model/doc/loader.cpp b/apps/opencs/model/doc/loader.cpp index 69c78bd5e..1c5a7348c 100644 --- a/apps/opencs/model/doc/loader.cpp +++ b/apps/opencs/model/doc/loader.cpp @@ -5,7 +5,6 @@ #include "../tools/reportmodel.hpp" #include "document.hpp" -#include "state.hpp" CSMDoc::Loader::Stage::Stage() : mFile (0), mRecordsLoaded (0), mRecordsLeft (false) {} diff --git a/apps/opencs/model/doc/operation.cpp b/apps/opencs/model/doc/operation.cpp index 218e13e38..369c6bb10 100644 --- a/apps/opencs/model/doc/operation.cpp +++ b/apps/opencs/model/doc/operation.cpp @@ -7,7 +7,6 @@ #include "../world/universalid.hpp" -#include "state.hpp" #include "stage.hpp" void CSMDoc::Operation::prepareStages() diff --git a/apps/opencs/model/filter/narynode.cpp b/apps/opencs/model/filter/narynode.cpp index 2fa9ac6cc..9415f1daf 100644 --- a/apps/opencs/model/filter/narynode.cpp +++ b/apps/opencs/model/filter/narynode.cpp @@ -9,7 +9,7 @@ CSMFilter::NAryNode::NAryNode (const std::vector >& nodes, int CSMFilter::NAryNode::getSize() const { - return mNodes.size(); + return static_cast(mNodes.size()); } const CSMFilter::Node& CSMFilter::NAryNode::operator[] (int index) const diff --git a/apps/opencs/model/prefs/shortcutmanager.cpp b/apps/opencs/model/prefs/shortcutmanager.cpp index 781ad4de3..4c5f77900 100644 --- a/apps/opencs/model/prefs/shortcutmanager.cpp +++ b/apps/opencs/model/prefs/shortcutmanager.cpp @@ -178,7 +178,7 @@ namespace CSMPrefs { const int MaxKeys = 4; // A limitation of QKeySequence - size_t end = data.find(";"); + size_t end = data.find(';'); size_t size = std::min(end, data.size()); std::string value = data.substr(0, size); @@ -191,7 +191,7 @@ namespace CSMPrefs while (start < value.size()) { - end = data.find("+", start); + end = data.find('+', start); end = std::min(end, value.size()); std::string name = value.substr(start, end - start); @@ -243,7 +243,7 @@ namespace CSMPrefs void ShortcutManager::convertFromString(const std::string& data, int& modifier) const { - size_t start = data.find(";") + 1; + size_t start = data.find(';') + 1; start = std::min(start, data.size()); std::string name = data.substr(start); diff --git a/apps/opencs/model/tools/mandatoryid.cpp b/apps/opencs/model/tools/mandatoryid.cpp index 23adb9d37..d0d9cc0b9 100644 --- a/apps/opencs/model/tools/mandatoryid.cpp +++ b/apps/opencs/model/tools/mandatoryid.cpp @@ -11,7 +11,7 @@ CSMTools::MandatoryIdStage::MandatoryIdStage (const CSMWorld::CollectionBase& id int CSMTools::MandatoryIdStage::setup() { - return mIds.size(); + return static_cast(mIds.size()); } void CSMTools::MandatoryIdStage::perform (int stage, CSMDoc::Messages& messages) diff --git a/apps/opencs/model/tools/reportmodel.cpp b/apps/opencs/model/tools/reportmodel.cpp index f9a1fdb0c..a2901a663 100644 --- a/apps/opencs/model/tools/reportmodel.cpp +++ b/apps/opencs/model/tools/reportmodel.cpp @@ -24,7 +24,7 @@ int CSMTools::ReportModel::rowCount (const QModelIndex & parent) const if (parent.isValid()) return 0; - return mRows.size(); + return static_cast(mRows.size()); } int CSMTools::ReportModel::columnCount (const QModelIndex & parent) const @@ -140,7 +140,7 @@ bool CSMTools::ReportModel::removeRows (int row, int count, const QModelIndex& p void CSMTools::ReportModel::add (const CSMDoc::Message& message) { - beginInsertRows (QModelIndex(), mRows.size(), mRows.size()); + beginInsertRows (QModelIndex(), static_cast(mRows.size()), static_cast(mRows.size())); mRows.push_back (message); @@ -176,7 +176,7 @@ void CSMTools::ReportModel::clear() { if (!mRows.empty()) { - beginRemoveRows (QModelIndex(), 0, mRows.size()-1); + beginRemoveRows (QModelIndex(), 0, static_cast(mRows.size())-1); mRows.clear(); endRemoveRows(); } diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index 27d60ae98..30fe6f639 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -270,7 +270,7 @@ void CSMWorld::IdTable::reorderRows (int baseIndex, const std::vector& newO if (!newOrder.empty()) if (mIdCollection->reorderRows (baseIndex, newOrder)) emit dataChanged (index (baseIndex, 0), - index (baseIndex+newOrder.size()-1, mIdCollection->getColumns()-1)); + index (baseIndex+static_cast(newOrder.size())-1, mIdCollection->getColumns()-1)); } std::pair CSMWorld::IdTable::view (int row) const diff --git a/apps/opencs/model/world/refcollection.cpp b/apps/opencs/model/world/refcollection.cpp index 126a0ea78..dfdb8e73b 100644 --- a/apps/opencs/model/world/refcollection.cpp +++ b/apps/opencs/model/world/refcollection.cpp @@ -1,6 +1,5 @@ #include "refcollection.hpp" -#include #include #include "ref.hpp" @@ -109,11 +108,13 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool Record record; record.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly; - (base ? record.mBase : record.mModified) = ref; + const ESM::RefNum refNum = ref.mRefNum; + std::string refId = ref.mId; + (base ? record.mBase : record.mModified) = std::move(ref); appendRecord (record); - cache.insert (std::make_pair (ref.mRefNum, ref.mId)); + cache.emplace(refNum, std::move(refId)); } else { @@ -124,7 +125,7 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool Record record = getRecord (index); record.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_Modified; - (base ? record.mBase : record.mModified) = ref; + (base ? record.mBase : record.mModified) = std::move(ref); setRecord (index, record); } diff --git a/apps/opencs/model/world/resources.cpp b/apps/opencs/model/world/resources.cpp index b40ab1389..c3eb9762e 100644 --- a/apps/opencs/model/world/resources.cpp +++ b/apps/opencs/model/world/resources.cpp @@ -20,13 +20,13 @@ void CSMWorld::Resources::recreate(const VFS::Manager* vfs, const char * const * mFiles.clear(); mIndex.clear(); - int baseSize = mBaseDirectory.size(); + size_t baseSize = mBaseDirectory.size(); const std::map& index = vfs->getIndex(); for (std::map::const_iterator it = index.begin(); it != index.end(); ++it) { std::string filepath = it->first; - if (static_cast (filepath.size())(mFiles.size()); } std::string CSMWorld::Resources::getId (int index) const diff --git a/apps/opencs/view/doc/operations.cpp b/apps/opencs/view/doc/operations.cpp index 7ee4b8726..9ed77ff9c 100644 --- a/apps/opencs/view/doc/operations.cpp +++ b/apps/opencs/view/doc/operations.cpp @@ -30,7 +30,7 @@ void CSVDoc::Operations::setProgress (int current, int max, int type, int thread return; } - int oldCount = mOperations.size(); + int oldCount = static_cast(mOperations.size()); int newCount = oldCount + 1; Operation *operation = new Operation (type, this); @@ -51,7 +51,7 @@ void CSVDoc::Operations::quitOperation (int type) for (std::vector::iterator iter (mOperations.begin()); iter!=mOperations.end(); ++iter) if ((*iter)->getType()==type) { - int oldCount = mOperations.size(); + int oldCount = static_cast(mOperations.size()); int newCount = oldCount - 1; mLayout->removeItem ((*iter)->getLayout()); diff --git a/apps/opencs/view/doc/viewmanager.cpp b/apps/opencs/view/doc/viewmanager.cpp index 8cca3a849..95f9f606d 100644 --- a/apps/opencs/view/doc/viewmanager.cpp +++ b/apps/opencs/view/doc/viewmanager.cpp @@ -11,7 +11,6 @@ #include "../../model/doc/documentmanager.hpp" #include "../../model/doc/document.hpp" #include "../../model/world/columns.hpp" -#include "../../model/world/universalid.hpp" #include "../../model/world/idcompletionmanager.hpp" #include "../../model/prefs/state.hpp" diff --git a/apps/opencs/view/render/cellborder.cpp b/apps/opencs/view/render/cellborder.cpp index 6073807ce..d8ff63801 100644 --- a/apps/opencs/view/render/cellborder.cpp +++ b/apps/opencs/view/render/cellborder.cpp @@ -2,7 +2,6 @@ #include #include -#include #include #include @@ -13,15 +12,23 @@ #include "../../model/world/cellcoordinates.hpp" const int CSVRender::CellBorder::CellSize = ESM::Land::REAL_SIZE; -const int CSVRender::CellBorder::VertexCount = (ESM::Land::LAND_SIZE * 4) - 3; + +/* + The number of vertices per cell border is equal to the number of vertices per edge + minus the duplicated corner vertices. An additional vertex to close the loop is NOT needed. +*/ +const int CSVRender::CellBorder::VertexCount = (ESM::Land::LAND_SIZE * 4) - 4; CSVRender::CellBorder::CellBorder(osg::Group* cellNode, const CSMWorld::CellCoordinates& coords) : mParentNode(cellNode) { + mBorderGeometry = new osg::Geometry(); + mBaseNode = new osg::PositionAttitudeTransform(); mBaseNode->setNodeMask(Mask_CellBorder); mBaseNode->setPosition(osg::Vec3f(coords.getX() * CellSize, coords.getY() * CellSize, 10)); + mBaseNode->addChild(mBorderGeometry); mParentNode->addChild(mBaseNode); } @@ -38,56 +45,59 @@ void CSVRender::CellBorder::buildShape(const ESM::Land& esmLand) if (!landData) return; - osg::ref_ptr geometry = new osg::Geometry(); + mBaseNode->removeChild(mBorderGeometry); + mBorderGeometry = new osg::Geometry(); - // Vertices osg::ref_ptr vertices = new osg::Vec3Array(); - int x = 0, y = 0; - for (; x < ESM::Land::LAND_SIZE; ++x) + int x = 0; + int y = 0; + + /* + Traverse the cell border counter-clockwise starting at the SW corner vertex (0, 0). + Each loop starts at a corner vertex and ends right before the next corner vertex. + */ + for (; x < ESM::Land::LAND_SIZE - 1; ++x) vertices->push_back(osg::Vec3f(scaleToWorld(x), scaleToWorld(y), landData->mHeights[landIndex(x, y)])); x = ESM::Land::LAND_SIZE - 1; - for (; y < ESM::Land::LAND_SIZE; ++y) + for (; y < ESM::Land::LAND_SIZE - 1; ++y) vertices->push_back(osg::Vec3f(scaleToWorld(x), scaleToWorld(y), landData->mHeights[landIndex(x, y)])); y = ESM::Land::LAND_SIZE - 1; - for (; x >= 0; --x) + for (; x > 0; --x) vertices->push_back(osg::Vec3f(scaleToWorld(x), scaleToWorld(y), landData->mHeights[landIndex(x, y)])); x = 0; - for (; y >= 0; --y) + for (; y > 0; --y) vertices->push_back(osg::Vec3f(scaleToWorld(x), scaleToWorld(y), landData->mHeights[landIndex(x, y)])); - geometry->setVertexArray(vertices); + mBorderGeometry->setVertexArray(vertices); - // Color osg::ref_ptr colors = new osg::Vec4Array(); colors->push_back(osg::Vec4f(0.f, 0.5f, 0.f, 1.f)); - geometry->setColorArray(colors, osg::Array::BIND_PER_PRIMITIVE_SET); + mBorderGeometry->setColorArray(colors, osg::Array::BIND_PER_PRIMITIVE_SET); - // Primitive osg::ref_ptr primitives = - new osg::DrawElementsUShort(osg::PrimitiveSet::LINE_STRIP, VertexCount+1); + new osg::DrawElementsUShort(osg::PrimitiveSet::LINE_STRIP, VertexCount + 1); + // Assign one primitive to each vertex. for (size_t i = 0; i < VertexCount; ++i) primitives->setElement(i, i); + // Assign the last primitive to the first vertex to close the loop. primitives->setElement(VertexCount, 0); - geometry->addPrimitiveSet(primitives); - geometry->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF); - + mBorderGeometry->addPrimitiveSet(primitives); + mBorderGeometry->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF); - osg::ref_ptr geode = new osg::Geode(); - geode->addDrawable(geometry); - mBaseNode->addChild(geode); + mBaseNode->addChild(mBorderGeometry); } size_t CSVRender::CellBorder::landIndex(int x, int y) { - return y * ESM::Land::LAND_SIZE + x; + return static_cast(y) * ESM::Land::LAND_SIZE + x; } float CSVRender::CellBorder::scaleToWorld(int value) diff --git a/apps/opencs/view/render/cellborder.hpp b/apps/opencs/view/render/cellborder.hpp index c91aa46c6..be2e18eee 100644 --- a/apps/opencs/view/render/cellborder.hpp +++ b/apps/opencs/view/render/cellborder.hpp @@ -7,6 +7,7 @@ namespace osg { + class Geometry; class Group; class PositionAttitudeTransform; } @@ -47,7 +48,7 @@ namespace CSVRender osg::Group* mParentNode; osg::ref_ptr mBaseNode; - + osg::ref_ptr mBorderGeometry; }; } diff --git a/apps/opencs/view/render/commands.cpp b/apps/opencs/view/render/commands.cpp new file mode 100644 index 000000000..7b3760296 --- /dev/null +++ b/apps/opencs/view/render/commands.cpp @@ -0,0 +1,19 @@ +#include "commands.hpp" + +#include + +#include "terrainselection.hpp" + +CSVRender::DrawTerrainSelectionCommand::DrawTerrainSelectionCommand(TerrainSelection& terrainSelection, QUndoCommand* parent) + : mTerrainSelection(terrainSelection) +{ } + +void CSVRender::DrawTerrainSelectionCommand::redo() +{ + mTerrainSelection.update(); +} + +void CSVRender::DrawTerrainSelectionCommand::undo() +{ + mTerrainSelection.update(); +} diff --git a/apps/opencs/view/render/commands.hpp b/apps/opencs/view/render/commands.hpp new file mode 100644 index 000000000..cdc389e33 --- /dev/null +++ b/apps/opencs/view/render/commands.hpp @@ -0,0 +1,35 @@ +#ifndef CSV_RENDER_COMMANDS_HPP +#define CSV_RENDER_COMMANDS_HPP + +#include + +namespace CSVRender +{ + class TerrainSelection; + + /* + Current solution to force a redrawing of the terrain-selection grid + when undoing/redoing changes in the editor. + This only triggers a simple redraw of the grid, so only use it in + conjunction with actual data changes which deform the grid. + + Please note that this command needs to be put onto the QUndoStack twice: + at the start and at the end of the related terrain manipulation. + This makes sure that the grid is always updated after all changes have + been undone or redone -- but it also means that the selection is redrawn + once at the beginning of either action. Future refinement may solve that. + */ + class DrawTerrainSelectionCommand : public QUndoCommand + { + private: + TerrainSelection& mTerrainSelection; + + public: + DrawTerrainSelectionCommand(TerrainSelection& terrainSelection, QUndoCommand* parent = nullptr); + + void redo() override; + void undo() override; + }; +} + +#endif diff --git a/apps/opencs/view/render/orbitcameramode.cpp b/apps/opencs/view/render/orbitcameramode.cpp index 79ac0167a..c81402ed1 100644 --- a/apps/opencs/view/render/orbitcameramode.cpp +++ b/apps/opencs/view/render/orbitcameramode.cpp @@ -3,7 +3,6 @@ #include #include "../../model/prefs/shortcut.hpp" -#include "../../model/prefs/shortcuteventhandler.hpp" #include "worldspacewidget.hpp" diff --git a/apps/opencs/view/render/pagedworldspacewidget.cpp b/apps/opencs/view/render/pagedworldspacewidget.cpp index ed3558422..a0b4de979 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.cpp +++ b/apps/opencs/view/render/pagedworldspacewidget.cpp @@ -7,18 +7,14 @@ #include #include -#include - #include #include "../../model/prefs/shortcut.hpp" -#include "../../model/world/tablemimedata.hpp" #include "../../model/world/idtable.hpp" #include "../widget/scenetooltoggle2.hpp" #include "../widget/scenetoolmode.hpp" -#include "../widget/scenetooltoggle2.hpp" #include "editmode.hpp" #include "mask.hpp" diff --git a/apps/opencs/view/render/pathgridmode.cpp b/apps/opencs/view/render/pathgridmode.cpp index 7a3fc8ecf..193cb664d 100644 --- a/apps/opencs/view/render/pathgridmode.cpp +++ b/apps/opencs/view/render/pathgridmode.cpp @@ -9,8 +9,6 @@ #include "../../model/world/commands.hpp" #include "../../model/world/commandmacro.hpp" -#include "../../model/world/idtable.hpp" -#include "../../model/world/idtree.hpp" #include "../widget/scenetoolbar.hpp" diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index 534093996..dbed1ba97 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -25,7 +25,6 @@ #include "../../model/prefs/state.hpp" #include "../../model/prefs/shortcut.hpp" -#include "../../model/prefs/shortcuteventhandler.hpp" #include "lighting.hpp" #include "mask.hpp" diff --git a/apps/opencs/view/render/terrainselection.cpp b/apps/opencs/view/render/terrainselection.cpp index 4e209af57..0593917e0 100644 --- a/apps/opencs/view/render/terrainselection.cpp +++ b/apps/opencs/view/render/terrainselection.cpp @@ -39,64 +39,23 @@ std::vector> CSVRender::TerrainSelection::getTerrainSelectio void CSVRender::TerrainSelection::onlySelect(const std::vector> &localPositions) { mSelection = localPositions; + update(); } -void CSVRender::TerrainSelection::addSelect(const std::pair &localPos) +void CSVRender::TerrainSelection::addSelect(const std::vector>& localPositions, bool toggleInProgress) { - if (std::find(mSelection.begin(), mSelection.end(), localPos) == mSelection.end()) - { - mSelection.emplace_back(localPos); - update(); - } + handleSelection(localPositions, toggleInProgress, SelectionMethod::AddSelect); } -void CSVRender::TerrainSelection::toggleSelect(const std::vector> &localPositions, bool toggleInProgress) +void CSVRender::TerrainSelection::removeSelect(const std::vector>& localPositions, bool toggleInProgress) { - if (toggleInProgress) - { - for(auto const& localPos: localPositions) - { - auto iterTemp = std::find(mTemporarySelection.begin(), mTemporarySelection.end(), localPos); - mDraggedOperationFlag = true; - - if (iterTemp == mTemporarySelection.end()) - { - auto iter = std::find(mSelection.begin(), mSelection.end(), localPos); - if (iter != mSelection.end()) - { - mSelection.erase(iter); - } - else - { - mSelection.emplace_back(localPos); - } - } + handleSelection(localPositions, toggleInProgress, SelectionMethod::RemoveSelect); +} - mTemporarySelection.push_back(localPos); - } - } - else if (mDraggedOperationFlag == false) - { - for(auto const& localPos: localPositions) - { - const auto iter = std::find(mSelection.begin(), mSelection.end(), localPos); - if (iter != mSelection.end()) - { - mSelection.erase(iter); - } - else - { - mSelection.emplace_back(localPos); - } - } - } - else - { - mDraggedOperationFlag = false; - mTemporarySelection.clear(); - } - update(); +void CSVRender::TerrainSelection::toggleSelect(const std::vector>& localPositions, bool toggleInProgress) +{ + handleSelection(localPositions, toggleInProgress, SelectionMethod::ToggleSelect); } void CSVRender::TerrainSelection::activate() @@ -239,6 +198,100 @@ void CSVRender::TerrainSelection::drawTextureSelection(const osg::ref_ptr>& localPositions, bool toggleInProgress, SelectionMethod selectionMethod) +{ + if (toggleInProgress) + { + for (auto const& localPos : localPositions) + { + auto iterTemp = std::find(mTemporarySelection.begin(), mTemporarySelection.end(), localPos); + mDraggedOperationFlag = true; + + if (iterTemp == mTemporarySelection.end()) + { + auto iter = std::find(mSelection.begin(), mSelection.end(), localPos); + + switch (selectionMethod) + { + case SelectionMethod::AddSelect: + if (iter == mSelection.end()) + { + mSelection.emplace_back(localPos); + } + + break; + case SelectionMethod::RemoveSelect: + if (iter != mSelection.end()) + { + mSelection.erase(iter); + } + + break; + case SelectionMethod::ToggleSelect: + if (iter == mSelection.end()) + { + mSelection.emplace_back(localPos); + } + else + { + mSelection.erase(iter); + } + + break; + default: break; + } + } + + mTemporarySelection.push_back(localPos); + } + } + else if (mDraggedOperationFlag == false) + { + for (auto const& localPos : localPositions) + { + const auto iter = std::find(mSelection.begin(), mSelection.end(), localPos); + + switch (selectionMethod) + { + case SelectionMethod::AddSelect: + if (iter == mSelection.end()) + { + mSelection.emplace_back(localPos); + } + + break; + case SelectionMethod::RemoveSelect: + if (iter != mSelection.end()) + { + mSelection.erase(iter); + } + + break; + case SelectionMethod::ToggleSelect: + if (iter == mSelection.end()) + { + mSelection.emplace_back(localPos); + } + else + { + mSelection.erase(iter); + } + + break; + default: break; + } + } + } + else + { + mDraggedOperationFlag = false; + + mTemporarySelection.clear(); + } + + update(); +} + bool CSVRender::TerrainSelection::noCell(const std::string& cellId) { CSMDoc::Document& document = mWorldspaceWidget->getDocument(); diff --git a/apps/opencs/view/render/terrainselection.hpp b/apps/opencs/view/render/terrainselection.hpp index 84ee6f25a..18622ad13 100644 --- a/apps/opencs/view/render/terrainselection.hpp +++ b/apps/opencs/view/render/terrainselection.hpp @@ -27,6 +27,14 @@ namespace CSVRender Shape }; + enum class SelectionMethod + { + OnlySelect, + AddSelect, + RemoveSelect, + ToggleSelect + }; + /// \brief Class handling the terrain selection data and rendering class TerrainSelection { @@ -36,7 +44,8 @@ namespace CSVRender ~TerrainSelection(); void onlySelect(const std::vector> &localPositions); - void addSelect(const std::pair &localPos); + void addSelect(const std::vector>& localPositions, bool toggleInProgress); + void removeSelect(const std::vector>& localPositions, bool toggleInProgress); void toggleSelect(const std::vector> &localPositions, bool toggleInProgress); void activate(); @@ -55,6 +64,8 @@ namespace CSVRender private: + void handleSelection(const std::vector>& localPositions, bool toggleInProgress, SelectionMethod selectionMethod); + bool noCell(const std::string& cellId); bool noLand(const std::string& cellId); diff --git a/apps/opencs/view/render/terrainshapemode.cpp b/apps/opencs/view/render/terrainshapemode.cpp index e495d2355..866ff69cd 100644 --- a/apps/opencs/view/render/terrainshapemode.cpp +++ b/apps/opencs/view/render/terrainshapemode.cpp @@ -25,18 +25,16 @@ #include "../../model/doc/document.hpp" #include "../../model/prefs/state.hpp" -#include "../../model/world/columnbase.hpp" -#include "../../model/world/commandmacro.hpp" #include "../../model/world/commands.hpp" #include "../../model/world/data.hpp" #include "../../model/world/idtable.hpp" #include "../../model/world/idtree.hpp" #include "../../model/world/land.hpp" -#include "../../model/world/resourcetable.hpp" #include "../../model/world/tablemimedata.hpp" #include "../../model/world/universalid.hpp" #include "brushdraw.hpp" +#include "commands.hpp" #include "editmode.hpp" #include "pagedworldspacewidget.hpp" #include "mask.hpp" @@ -45,7 +43,7 @@ #include "worldspacewidget.hpp" CSVRender::TerrainShapeMode::TerrainShapeMode (WorldspaceWidget *worldspaceWidget, osg::Group* parentNode, QWidget *parent) -: EditMode (worldspaceWidget, QIcon {":scenetoolbar/editing-terrain-shape"}, Mask_Terrain | Mask_Reference, "Terrain land editing", parent), +: EditMode (worldspaceWidget, QIcon {":scenetoolbar/editing-terrain-shape"}, Mask_Terrain, "Terrain land editing", parent), mParentNode(parentNode) { } @@ -288,6 +286,9 @@ void CSVRender::TerrainShapeMode::applyTerrainEditChanges() undoStack.beginMacro ("Edit shape and normal records"); + // One command at the beginning of the macro for redrawing the terrain-selection grid when undoing the changes. + undoStack.push(new DrawTerrainSelectionCommand(*mTerrainShapeSelection)); + for(CSMWorld::CellCoordinates cellCoordinates: mAlteredCells) { std::string cellId = CSMWorld::CellCoordinates::generateId(cellCoordinates.getX(), cellCoordinates.getY()); @@ -356,6 +357,9 @@ void CSVRender::TerrainShapeMode::applyTerrainEditChanges() } pushNormalsEditToCommand(landNormalsNew, document, landTable, cellId); } + // One command at the end of the macro for redrawing the terrain-selection grid when redoing the changes. + undoStack.push(new DrawTerrainSelectionCommand(*mTerrainShapeSelection)); + undoStack.endMacro(); clearTransientEdits(); } @@ -433,7 +437,9 @@ void CSVRender::TerrainShapeMode::editTerrainShapeGrid(const std::pair float smoothedByDistance = 0.0f; if (mShapeEditTool == ShapeEditTool_Drag) smoothedByDistance = calculateBumpShape(distance, r, mTotalDiffY); if (mShapeEditTool == ShapeEditTool_PaintToRaise || mShapeEditTool == ShapeEditTool_PaintToLower) smoothedByDistance = calculateBumpShape(distance, r, r + mShapeEditToolStrength); - if (distance <= r) + + // Using floating-point radius here to prevent selecting too few vertices. + if (distance <= mBrushSize / 2.0f) { if (mShapeEditTool == ShapeEditTool_Drag) alterHeight(cellCoords, x, y, smoothedByDistance); if (mShapeEditTool == ShapeEditTool_PaintToRaise || mShapeEditTool == ShapeEditTool_PaintToLower) @@ -1036,10 +1042,35 @@ void CSVRender::TerrainShapeMode::handleSelection(int globalSelectionX, int glob return; int selectionX = globalSelectionX; int selectionY = globalSelectionY; - if (xIsAtCellBorder) + + /* + The northern and eastern edges don't belong to the current cell. + If the corresponding adjacent cell is not loaded, some special handling is necessary to select border vertices. + */ + if (xIsAtCellBorder && yIsAtCellBorder) + { + /* + Handle the NW, NE, and SE corner vertices. + NW corner: (+1, -1) offset to reach current cell. + NE corner: (-1, -1) offset to reach current cell. + SE corner: (-1, +1) offset to reach current cell. + */ + if (isInCellSelection(globalSelectionX - 1, globalSelectionY - 1) + || isInCellSelection(globalSelectionX + 1, globalSelectionY - 1) + || isInCellSelection(globalSelectionX - 1, globalSelectionY + 1)) + { + selections->emplace_back(globalSelectionX, globalSelectionY); + } + } + else if (xIsAtCellBorder) + { selectionX--; - if (yIsAtCellBorder) + } + else if (yIsAtCellBorder) + { selectionY--; + } + if (isInCellSelection(selectionX, selectionY)) selections->emplace_back(globalSelectionX, globalSelectionY); } @@ -1074,8 +1105,11 @@ void CSVRender::TerrainShapeMode::selectTerrainShapes(const std::pair& { int distanceX = abs(i - vertexCoords.first); int distanceY = abs(j - vertexCoords.second); - int distance = std::round(sqrt(pow(distanceX, 2)+pow(distanceY, 2))); - if (distance <= r) handleSelection(i, j, &selections); + float distance = sqrt(pow(distanceX, 2)+pow(distanceY, 2)); + + // Using floating-point radius here to prevent selecting too few vertices. + if (distance <= mBrushSize / 2.0f) + handleSelection(i, j, &selections); } } } @@ -1092,9 +1126,21 @@ void CSVRender::TerrainShapeMode::selectTerrainShapes(const std::pair& } } - if(selectMode == 0) mTerrainShapeSelection->onlySelect(selections); - if(selectMode == 1) mTerrainShapeSelection->toggleSelect(selections, dragOperation); + std::string selectAction; + if (selectMode == 0) + selectAction = CSMPrefs::get()["3D Scene Editing"]["primary-select-action"].toString(); + else + selectAction = CSMPrefs::get()["3D Scene Editing"]["secondary-select-action"].toString(); + + if (selectAction == "Select only") + mTerrainShapeSelection->onlySelect(selections); + else if (selectAction == "Add to selection") + mTerrainShapeSelection->addSelect(selections, dragOperation); + else if (selectAction == "Remove from selection") + mTerrainShapeSelection->removeSelect(selections, dragOperation); + else if (selectAction == "Invert selection") + mTerrainShapeSelection->toggleSelect(selections, dragOperation); } void CSVRender::TerrainShapeMode::pushEditToCommand(const CSMWorld::LandHeightsColumn::DataType& newLandGrid, CSMDoc::Document& document, diff --git a/apps/opencs/view/render/terraintexturemode.cpp b/apps/opencs/view/render/terraintexturemode.cpp index fa46cf673..4e267e942 100644 --- a/apps/opencs/view/render/terraintexturemode.cpp +++ b/apps/opencs/view/render/terraintexturemode.cpp @@ -12,23 +12,17 @@ #include -#include - #include "../widget/modebutton.hpp" #include "../widget/scenetoolbar.hpp" #include "../widget/scenetooltexturebrush.hpp" #include "../../model/doc/document.hpp" #include "../../model/prefs/state.hpp" -#include "../../model/world/columnbase.hpp" -#include "../../model/world/commandmacro.hpp" #include "../../model/world/commands.hpp" #include "../../model/world/data.hpp" #include "../../model/world/idtable.hpp" #include "../../model/world/idtree.hpp" -#include "../../model/world/land.hpp" #include "../../model/world/landtexture.hpp" -#include "../../model/world/resourcetable.hpp" #include "../../model/world/tablemimedata.hpp" #include "../../model/world/universalid.hpp" #include "../widget/brushshapes.hpp" @@ -332,7 +326,7 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe int textureColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandTexturesIndex); - std::size_t hashlocation = mBrushTexture.find("#"); + std::size_t hashlocation = mBrushTexture.find('#'); std::string mBrushTextureInt = mBrushTexture.substr (hashlocation+1); int brushInt = stoi(mBrushTexture.substr (hashlocation+1))+1; // All indices are offset by +1 diff --git a/apps/opencs/view/render/unpagedworldspacewidget.cpp b/apps/opencs/view/render/unpagedworldspacewidget.cpp index c4fef45dd..20dc5b8d1 100644 --- a/apps/opencs/view/render/unpagedworldspacewidget.cpp +++ b/apps/opencs/view/render/unpagedworldspacewidget.cpp @@ -12,7 +12,6 @@ #include "../../model/world/idtable.hpp" #include "../../model/world/tablemimedata.hpp" -#include "../widget/scenetooltoggle.hpp" #include "../widget/scenetooltoggle2.hpp" #include "cameracontroller.hpp" diff --git a/apps/opencs/view/render/worldspacewidget.cpp b/apps/opencs/view/render/worldspacewidget.cpp index 6eb437daf..723ffcb6a 100644 --- a/apps/opencs/view/render/worldspacewidget.cpp +++ b/apps/opencs/view/render/worldspacewidget.cpp @@ -17,7 +17,6 @@ #include "../../model/world/idtable.hpp" #include "../../model/prefs/shortcut.hpp" -#include "../../model/prefs/shortcuteventhandler.hpp" #include "../../model/prefs/state.hpp" #include "../render/orbitcameramode.hpp" diff --git a/apps/opencs/view/widget/scenetoolrun.cpp b/apps/opencs/view/widget/scenetoolrun.cpp index b53282036..24bcf3f13 100644 --- a/apps/opencs/view/widget/scenetoolrun.cpp +++ b/apps/opencs/view/widget/scenetoolrun.cpp @@ -30,7 +30,7 @@ void CSVWidget::SceneToolRun::updateIcon() void CSVWidget::SceneToolRun::updatePanel() { - mTable->setRowCount (mProfiles.size()); + mTable->setRowCount (static_cast(mProfiles.size())); int i = 0; diff --git a/apps/opencs/view/widget/scenetoolshapebrush.cpp b/apps/opencs/view/widget/scenetoolshapebrush.cpp index 4b2d20004..d0419a13a 100644 --- a/apps/opencs/view/widget/scenetoolshapebrush.cpp +++ b/apps/opencs/view/widget/scenetoolshapebrush.cpp @@ -27,11 +27,6 @@ #include "../../model/doc/document.hpp" #include "../../model/prefs/state.hpp" #include "../../model/world/commands.hpp" -#include "../../model/world/data.hpp" -#include "../../model/world/idcollection.hpp" -#include "../../model/world/idtable.hpp" -#include "../../model/world/landtexture.hpp" -#include "../../model/world/universalid.hpp" CSVWidget::ShapeBrushSizeControls::ShapeBrushSizeControls(const QString &title, QWidget *parent) diff --git a/apps/opencs/view/widget/scenetooltoggle.cpp b/apps/opencs/view/widget/scenetooltoggle.cpp index fa0be3155..04ac3322b 100644 --- a/apps/opencs/view/widget/scenetooltoggle.cpp +++ b/apps/opencs/view/widget/scenetooltoggle.cpp @@ -80,7 +80,7 @@ QRect CSVWidget::SceneToolToggle::getIconBox (int index) const int y = index / xMax; int x = index % xMax; - int total = mButtons.size(); + int total = static_cast(mButtons.size()); int actualYIcons = total/xMax; @@ -154,7 +154,7 @@ void CSVWidget::SceneToolToggle::addButton (const std::string& icon, unsigned in desc.mMask = mask; desc.mSmallIcon = smallIcon; desc.mName = name; - desc.mIndex = mButtons.size(); + desc.mIndex = static_cast(mButtons.size()); mButtons.insert (std::make_pair (button, desc)); diff --git a/apps/opencs/view/widget/scenetooltoggle2.cpp b/apps/opencs/view/widget/scenetooltoggle2.cpp index cf9bfe349..363cae570 100644 --- a/apps/opencs/view/widget/scenetooltoggle2.cpp +++ b/apps/opencs/view/widget/scenetooltoggle2.cpp @@ -99,7 +99,7 @@ void CSVWidget::SceneToolToggle2::addButton (unsigned int id, unsigned int mask, desc.mButtonId = id; desc.mMask = mask; desc.mName = name; - desc.mIndex = mButtons.size(); + desc.mIndex = static_cast(mButtons.size()); mButtons.insert (std::make_pair (button, desc)); diff --git a/apps/opencs/view/world/colordelegate.cpp b/apps/opencs/view/world/colordelegate.cpp index 15a07b42c..3b5f692fe 100644 --- a/apps/opencs/view/world/colordelegate.cpp +++ b/apps/opencs/view/world/colordelegate.cpp @@ -3,8 +3,6 @@ #include #include -#include "../widget/coloreditor.hpp" - CSVWorld::ColorDelegate::ColorDelegate(CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index 3d3b3cdbe..f2360b137 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -28,7 +28,6 @@ #include "../../model/world/columns.hpp" #include "../../model/world/record.hpp" #include "../../model/world/tablemimedata.hpp" -#include "../../model/world/idtree.hpp" #include "../../model/world/commands.hpp" #include "../../model/doc/document.hpp" diff --git a/apps/opencs/view/world/idvalidator.cpp b/apps/opencs/view/world/idvalidator.cpp index 1092d7217..442157ac5 100644 --- a/apps/opencs/view/world/idvalidator.cpp +++ b/apps/opencs/view/world/idvalidator.cpp @@ -42,7 +42,7 @@ QValidator::State CSVWorld::IdValidator::validate (QString& input, int& pos) con if (!mNamespace.empty()) { - std::string namespace_ = input.left (mNamespace.size()).toUtf8().constData(); + std::string namespace_ = input.left (static_cast(mNamespace.size())).toUtf8().constData(); if (Misc::StringUtils::lowerCase (namespace_)!=mNamespace) return QValidator::Invalid; // incorrect namespace diff --git a/apps/opencs/view/world/scenesubview.cpp b/apps/opencs/view/world/scenesubview.cpp index 44542c529..58d159a17 100644 --- a/apps/opencs/view/world/scenesubview.cpp +++ b/apps/opencs/view/world/scenesubview.cpp @@ -15,7 +15,6 @@ #include "../render/pagedworldspacewidget.hpp" #include "../render/unpagedworldspacewidget.hpp" -#include "../render/editmode.hpp" #include "../widget/scenetoolbar.hpp" #include "../widget/scenetoolmode.hpp" diff --git a/apps/opencs/view/world/scriptsubview.cpp b/apps/opencs/view/world/scriptsubview.cpp index 096fc8a9e..6eab7aaa5 100644 --- a/apps/opencs/view/world/scriptsubview.cpp +++ b/apps/opencs/view/world/scriptsubview.cpp @@ -12,7 +12,6 @@ #include "../../model/doc/document.hpp" #include "../../model/world/universalid.hpp" #include "../../model/world/data.hpp" -#include "../../model/world/columnbase.hpp" #include "../../model/world/commands.hpp" #include "../../model/world/idtable.hpp" #include "../../model/prefs/state.hpp" diff --git a/apps/openmw-mp/main.cpp b/apps/openmw-mp/main.cpp index 4f527e9f9..334f9fbce 100644 --- a/apps/openmw-mp/main.cpp +++ b/apps/openmw-mp/main.cpp @@ -85,9 +85,9 @@ std::string loadSettings (Settings::Manager & settings) // prefer local if (boost::filesystem::exists(localdefault)) - settings.loadDefault(localdefault); + settings.loadDefault(localdefault, false); else if (boost::filesystem::exists(globaldefault)) - settings.loadDefault(globaldefault); + settings.loadDefault(globaldefault, false); else throw std::runtime_error ("No default settings file found! Make sure the file \"tes3mp-server-default.cfg\" was properly installed."); diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index ddb845b0e..abd5a2e41 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -247,7 +247,7 @@ if(APPLE) add_subdirectory(../../files/ ${CMAKE_CURRENT_BINARY_DIR}/files) - configure_file("${OpenMW_BINARY_DIR}/settings-default.cfg" ${BUNDLE_RESOURCES_DIR} COPYONLY) + configure_file("${OpenMW_BINARY_DIR}/defaults.bin" ${BUNDLE_RESOURCES_DIR} COPYONLY) configure_file("${OpenMW_BINARY_DIR}/openmw.cfg" ${BUNDLE_RESOURCES_DIR} COPYONLY) configure_file("${OpenMW_BINARY_DIR}/gamecontrollerdb.txt" ${BUNDLE_RESOURCES_DIR} COPYONLY) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 4a5076419..efd30017f 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -601,8 +601,8 @@ void OMW::Engine::setSkipMenu (bool skipMenu, bool newGame) std::string OMW::Engine::loadSettings (Settings::Manager & settings) { // Create the settings manager and load default settings file - const std::string localdefault = (mCfgMgr.getLocalPath() / "settings-default.cfg").string(); - const std::string globaldefault = (mCfgMgr.getGlobalPath() / "settings-default.cfg").string(); + const std::string localdefault = (mCfgMgr.getLocalPath() / "defaults.bin").string(); + const std::string globaldefault = (mCfgMgr.getGlobalPath() / "defaults.bin").string(); // prefer local if (boost::filesystem::exists(localdefault)) @@ -610,7 +610,7 @@ std::string OMW::Engine::loadSettings (Settings::Manager & settings) else if (boost::filesystem::exists(globaldefault)) settings.loadDefault(globaldefault); else - throw std::runtime_error ("No default settings file found! Make sure the file \"settings-default.cfg\" was properly installed."); + throw std::runtime_error ("No default settings file found! Make sure the file \"defaults.bin\" was properly installed."); // load user settings if they exist const std::string settingspath = (mCfgMgr.getUserConfigPath() / "settings.cfg").string(); diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 4425ad2c0..24883919b 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -42,7 +42,6 @@ #include "../mwworld/failedaction.hpp" #include "../mwworld/customdata.hpp" #include "../mwworld/containerstore.hpp" -#include "../mwphysics/physicssystem.hpp" #include "../mwworld/cellstore.hpp" #include "../mwworld/localscripts.hpp" diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index d6c268545..1c5dee64f 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -52,7 +52,6 @@ #include "../mwworld/failedaction.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/customdata.hpp" -#include "../mwphysics/physicssystem.hpp" #include "../mwworld/cellstore.hpp" #include "../mwworld/localscripts.hpp" diff --git a/apps/openmw/mwdialogue/journalimp.hpp b/apps/openmw/mwdialogue/journalimp.hpp index f47dae22e..e2bea48de 100644 --- a/apps/openmw/mwdialogue/journalimp.hpp +++ b/apps/openmw/mwdialogue/journalimp.hpp @@ -3,7 +3,6 @@ #include "../mwbase/journal.hpp" -#include "journalentry.hpp" #include "quest.hpp" namespace MWDialogue diff --git a/apps/openmw/mwgui/bookpage.hpp b/apps/openmw/mwgui/bookpage.hpp index f9fd93089..512a43992 100644 --- a/apps/openmw/mwgui/bookpage.hpp +++ b/apps/openmw/mwgui/bookpage.hpp @@ -10,7 +10,6 @@ #include #include -#include #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" diff --git a/apps/openmw/mwgui/console.cpp b/apps/openmw/mwgui/console.cpp index e2e85bb9d..f8dae518e 100644 --- a/apps/openmw/mwgui/console.cpp +++ b/apps/openmw/mwgui/console.cpp @@ -23,6 +23,10 @@ #include #include +#include +#include +#include +#include #include "../mwscript/extensions.hpp" diff --git a/apps/openmw/mwgui/console.hpp b/apps/openmw/mwgui/console.hpp index 63fe74953..a74906ddf 100644 --- a/apps/openmw/mwgui/console.hpp +++ b/apps/openmw/mwgui/console.hpp @@ -6,12 +6,8 @@ #include #include -#include -#include -#include #include #include -#include #include "../mwscript/compilercontext.hpp" #include "../mwscript/interpretercontext.hpp" diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index b72b105ed..ab8afee9c 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -319,7 +319,7 @@ namespace MWGui deleteLater(); for (Link* link : mLinks) delete link; - for (auto link : mTopicLinks) + for (const auto& link : mTopicLinks) delete link.second; for (auto history : mHistoryContents) delete history; diff --git a/apps/openmw/mwgui/itemmodel.cpp b/apps/openmw/mwgui/itemmodel.cpp index cf88efaae..4e4d77da4 100644 --- a/apps/openmw/mwgui/itemmodel.cpp +++ b/apps/openmw/mwgui/itemmodel.cpp @@ -2,7 +2,6 @@ #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" -#include "../mwworld/esmstore.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" diff --git a/apps/openmw/mwgui/itemview.cpp b/apps/openmw/mwgui/itemview.cpp index 94dcc77c5..14f2c1dd9 100644 --- a/apps/openmw/mwgui/itemview.cpp +++ b/apps/openmw/mwgui/itemview.cpp @@ -5,9 +5,7 @@ #include #include #include -#include #include -#include #include "../mwworld/class.hpp" diff --git a/apps/openmw/mwgui/journalbooks.cpp b/apps/openmw/mwgui/journalbooks.cpp index 065a503e6..40053b3c8 100644 --- a/apps/openmw/mwgui/journalbooks.cpp +++ b/apps/openmw/mwgui/journalbooks.cpp @@ -278,7 +278,7 @@ BookTypesetter::Ptr JournalBooks::createCyrillicJournalIndex () mIndexPagesCount = 2; } - unsigned char ch[2] = {0xd0, 0x90}; // CYRILLIC CAPITAL A is a 0xd090 in UTF-8 + unsigned char ch[3] = {0xd0, 0x90, 0x00}; // CYRILLIC CAPITAL A is a 0xd090 in UTF-8 for (int i = 0; i < 32; ++i) { diff --git a/apps/openmw/mwgui/levelupdialog.cpp b/apps/openmw/mwgui/levelupdialog.cpp index 86f92db6f..14de3fd27 100644 --- a/apps/openmw/mwgui/levelupdialog.cpp +++ b/apps/openmw/mwgui/levelupdialog.cpp @@ -12,7 +12,6 @@ #include "../mwbase/soundmanager.hpp" #include "../mwworld/class.hpp" -#include "../mwworld/cellstore.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/npcstats.hpp" diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index aeaacaa63..65d49ffc9 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -25,8 +25,6 @@ #include "../mwbase/windowmanager.hpp" #include "../mwbase/inputmanager.hpp" -#include "../mwrender/vismask.hpp" - #include "backgroundimage.hpp" namespace MWGui diff --git a/apps/openmw/mwgui/loadingscreen.hpp b/apps/openmw/mwgui/loadingscreen.hpp index e1c652386..ac911ab60 100644 --- a/apps/openmw/mwgui/loadingscreen.hpp +++ b/apps/openmw/mwgui/loadingscreen.hpp @@ -3,7 +3,6 @@ #include -#include #include #include diff --git a/apps/openmw/mwgui/pickpocketitemmodel.cpp b/apps/openmw/mwgui/pickpocketitemmodel.cpp index b4de5cb50..5daea8f3f 100644 --- a/apps/openmw/mwgui/pickpocketitemmodel.cpp +++ b/apps/openmw/mwgui/pickpocketitemmodel.cpp @@ -7,7 +7,6 @@ #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/pickpocket.hpp" -#include "../mwworld/containerstore.hpp" #include "../mwworld/class.hpp" #include "../mwbase/environment.hpp" diff --git a/apps/openmw/mwgui/quickkeysmenu.hpp b/apps/openmw/mwgui/quickkeysmenu.hpp index d85bb90e0..e1e28be46 100644 --- a/apps/openmw/mwgui/quickkeysmenu.hpp +++ b/apps/openmw/mwgui/quickkeysmenu.hpp @@ -1,8 +1,6 @@ #ifndef MWGUI_QUICKKEYS_H #define MWGUI_QUICKKEYS_H -#include "../mwworld/ptr.hpp" - #include "windowbase.hpp" #include "spellmodel.hpp" diff --git a/apps/openmw/mwgui/recharge.cpp b/apps/openmw/mwgui/recharge.cpp index f8d89c0cb..df6962c78 100644 --- a/apps/openmw/mwgui/recharge.cpp +++ b/apps/openmw/mwgui/recharge.cpp @@ -1,7 +1,6 @@ #include "recharge.hpp" #include -#include #include @@ -15,10 +14,10 @@ #include "../mwworld/class.hpp" #include "../mwworld/esmstore.hpp" -#include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/actorutil.hpp" #include "../mwmechanics/recharge.hpp" +#include "itemselection.hpp" #include "itemwidget.hpp" #include "itemchargeview.hpp" #include "sortfilteritemmodel.hpp" diff --git a/apps/openmw/mwgui/recharge.hpp b/apps/openmw/mwgui/recharge.hpp index 3d469bac5..c260b1554 100644 --- a/apps/openmw/mwgui/recharge.hpp +++ b/apps/openmw/mwgui/recharge.hpp @@ -3,8 +3,6 @@ #include "windowbase.hpp" -#include "itemselection.hpp" - namespace MWWorld { class Ptr; diff --git a/apps/openmw/mwgui/repair.cpp b/apps/openmw/mwgui/repair.cpp index ea79e0326..351b97603 100644 --- a/apps/openmw/mwgui/repair.cpp +++ b/apps/openmw/mwgui/repair.cpp @@ -3,8 +3,6 @@ #include #include -#include -#include #include @@ -17,6 +15,7 @@ #include "../mwworld/containerstore.hpp" #include "../mwworld/class.hpp" +#include "itemselection.hpp" #include "itemwidget.hpp" #include "itemchargeview.hpp" #include "sortfilteritemmodel.hpp" diff --git a/apps/openmw/mwgui/repair.hpp b/apps/openmw/mwgui/repair.hpp index 594ad2823..701009f54 100644 --- a/apps/openmw/mwgui/repair.hpp +++ b/apps/openmw/mwgui/repair.hpp @@ -3,8 +3,6 @@ #include "windowbase.hpp" -#include "itemselection.hpp" - #include "../mwmechanics/repair.hpp" namespace MWGui diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index 0e67f3075..b13737fa1 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -1,9 +1,7 @@ #include "spellwindow.hpp" -#include #include #include -#include #include #include diff --git a/apps/openmw/mwgui/spellwindow.hpp b/apps/openmw/mwgui/spellwindow.hpp index cf5e88f8e..786a7d877 100644 --- a/apps/openmw/mwgui/spellwindow.hpp +++ b/apps/openmw/mwgui/spellwindow.hpp @@ -2,7 +2,6 @@ #define MWGUI_SPELLWINDOW_H #include "windowpinnablebase.hpp" -#include "../mwworld/ptr.hpp" #include "spellmodel.hpp" diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index c0694e403..a821d0106 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -266,7 +266,7 @@ namespace MWGui std::map userStrings = focus->getUserStrings(); for (auto& userStringPair : userStrings) { - size_t underscorePos = userStringPair.first.find("_"); + size_t underscorePos = userStringPair.first.find('_'); if (underscorePos == std::string::npos) continue; std::string key = userStringPair.first.substr(0, underscorePos); diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index f5c6d4d65..422b17186 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -38,7 +38,6 @@ #include "containeritemmodel.hpp" #include "tradeitemmodel.hpp" #include "countdialog.hpp" -#include "controllers.hpp" #include "tooltips.hpp" namespace diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index bf824a3d2..4832319de 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -34,8 +34,6 @@ #include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/actorutil.hpp" -#include "../mwstate/charactermanager.hpp" - namespace MWGui { diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 4d4fc3263..3a63010c9 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -9,7 +9,6 @@ #include #include -#include #include #include #include @@ -77,7 +76,6 @@ #include "../mwworld/cellstore.hpp" #include "../mwworld/esmstore.hpp" -#include "../mwmechanics/stat.hpp" #include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/actorutil.hpp" diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index cdc19b305..13139e4ef 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -6,7 +6,6 @@ #include #include -#include #include "../mwbase/windowmanager.hpp" #include "../mwbase/environment.hpp" diff --git a/apps/openmw/mwinput/keyboardmanager.hpp b/apps/openmw/mwinput/keyboardmanager.hpp index f97f6b9e6..ca58461a2 100644 --- a/apps/openmw/mwinput/keyboardmanager.hpp +++ b/apps/openmw/mwinput/keyboardmanager.hpp @@ -1,7 +1,6 @@ #ifndef MWINPUT_MWKEYBOARDMANAGER_H #define MWINPUT_MWKEYBOARDMANAGER_H -#include #include namespace MWInput diff --git a/apps/openmw/mwinput/mousemanager.cpp b/apps/openmw/mwinput/mousemanager.cpp index a696332df..cf151dfac 100644 --- a/apps/openmw/mwinput/mousemanager.cpp +++ b/apps/openmw/mwinput/mousemanager.cpp @@ -144,7 +144,8 @@ namespace MWInput void MouseManager::mousePressed(const SDL_MouseButtonEvent &arg, Uint8 id) { - MWBase::Environment::get().getInputManager()->setJoystickLastUsed(false); + MWBase::InputManager* input = MWBase::Environment::get().getInputManager(); + input->setJoystickLastUsed(false); bool guiMode = false; if (id == SDL_BUTTON_LEFT || id == SDL_BUTTON_RIGHT) // MyGUI only uses these mouse events @@ -165,7 +166,8 @@ namespace MWInput mBindingsManager->setPlayerControlsEnabled(!guiMode); // Don't trigger any mouse bindings while in settings menu, otherwise rebinding controls becomes impossible - if (MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_Settings) + // Also do not trigger bindings when input controls are disabled, e.g. during save loading + if (MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_Settings && !input->controlsDisabled()) mBindingsManager->mousePressed(arg, id); } diff --git a/apps/openmw/mwmechanics/activespells.hpp b/apps/openmw/mwmechanics/activespells.hpp index b5baeee0e..164d4ded1 100644 --- a/apps/openmw/mwmechanics/activespells.hpp +++ b/apps/openmw/mwmechanics/activespells.hpp @@ -5,7 +5,6 @@ #include #include -#include #include #include "../mwworld/timestamp.hpp" diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 75a14bfad..bf7d1651c 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -59,7 +59,6 @@ #include "aiwander.hpp" #include "actor.hpp" #include "summoning.hpp" -#include "combat.hpp" #include "actorutil.hpp" #include "tickableeffects.hpp" @@ -623,7 +622,8 @@ namespace MWMechanics { greetingTimer++; - if (greetingTimer <= GREETING_SHOULD_END || MWBase::Environment::get().getSoundManager()->sayActive(actor)) + if (!stats.getMovementFlag(CreatureStats::Flag_ForceJump) && !stats.getMovementFlag(CreatureStats::Flag_ForceSneak) + && (greetingTimer <= GREETING_SHOULD_END || MWBase::Environment::get().getSoundManager()->sayActive(actor))) turnActorToFacePlayer(actor, actorState, dir); if (greetingTimer >= GREETING_COOLDOWN) diff --git a/apps/openmw/mwmechanics/aiavoiddoor.hpp b/apps/openmw/mwmechanics/aiavoiddoor.hpp index 1781c5e4a..183f429f3 100644 --- a/apps/openmw/mwmechanics/aiavoiddoor.hpp +++ b/apps/openmw/mwmechanics/aiavoiddoor.hpp @@ -3,10 +3,6 @@ #include "typedaipackage.hpp" -#include - -#include - #include "../mwworld/class.hpp" #include "pathfinding.hpp" diff --git a/apps/openmw/mwmechanics/aicombat.hpp b/apps/openmw/mwmechanics/aicombat.hpp index 0f42c6e2d..5425f1af0 100644 --- a/apps/openmw/mwmechanics/aicombat.hpp +++ b/apps/openmw/mwmechanics/aicombat.hpp @@ -9,7 +9,6 @@ #include "pathfinding.hpp" #include "movement.hpp" -#include "obstacle.hpp" #include "aitimer.hpp" namespace ESM diff --git a/apps/openmw/mwmechanics/aicombataction.hpp b/apps/openmw/mwmechanics/aicombataction.hpp index 77a19f804..d17d5a313 100644 --- a/apps/openmw/mwmechanics/aicombataction.hpp +++ b/apps/openmw/mwmechanics/aicombataction.hpp @@ -3,8 +3,6 @@ #include -#include - #include "../mwworld/ptr.hpp" #include "../mwworld/containerstore.hpp" diff --git a/apps/openmw/mwmechanics/aifollow.hpp b/apps/openmw/mwmechanics/aifollow.hpp index 1f1589e3f..a378211f4 100644 --- a/apps/openmw/mwmechanics/aifollow.hpp +++ b/apps/openmw/mwmechanics/aifollow.hpp @@ -9,8 +9,6 @@ #include "../mwworld/ptr.hpp" -#include "pathfinding.hpp" - namespace ESM { namespace AiSequence diff --git a/apps/openmw/mwmechanics/aipackage.cpp b/apps/openmw/mwmechanics/aipackage.cpp index 204cf2f3f..a7e8e74b0 100644 --- a/apps/openmw/mwmechanics/aipackage.cpp +++ b/apps/openmw/mwmechanics/aipackage.cpp @@ -2,7 +2,6 @@ #include #include -#include #include #include #include @@ -15,8 +14,6 @@ #include "../mwworld/cellstore.hpp" #include "../mwworld/inventorystore.hpp" -#include "../mwphysics/collisiontype.hpp" - #include "pathgrid.hpp" #include "creaturestats.hpp" #include "movement.hpp" diff --git a/apps/openmw/mwmechanics/aipackage.hpp b/apps/openmw/mwmechanics/aipackage.hpp index 81b09c8b9..5ad73c2da 100644 --- a/apps/openmw/mwmechanics/aipackage.hpp +++ b/apps/openmw/mwmechanics/aipackage.hpp @@ -3,7 +3,6 @@ #include -#include #include #include "pathfinding.hpp" diff --git a/apps/openmw/mwmechanics/aipursue.cpp b/apps/openmw/mwmechanics/aipursue.cpp index 50d79d55c..ccc29c3a6 100644 --- a/apps/openmw/mwmechanics/aipursue.cpp +++ b/apps/openmw/mwmechanics/aipursue.cpp @@ -8,7 +8,6 @@ #include "../mwbase/world.hpp" #include "../mwworld/class.hpp" -#include "../mwworld/action.hpp" /* Start of tes3mp addition diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 575a03434..fada7761d 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -5,8 +5,6 @@ #include #include -#include "../mwbase/world.hpp" - #include "aipackage.hpp" #include "aistate.hpp" #include "aiwander.hpp" diff --git a/apps/openmw/mwmechanics/aitravel.cpp b/apps/openmw/mwmechanics/aitravel.cpp index b2a506ca6..8e5372c46 100644 --- a/apps/openmw/mwmechanics/aitravel.cpp +++ b/apps/openmw/mwmechanics/aitravel.cpp @@ -52,14 +52,15 @@ namespace MWMechanics bool AiTravel::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) { MWBase::MechanicsManager* mechMgr = MWBase::Environment::get().getMechanicsManager(); + auto& stats = actor.getClass().getCreatureStats(actor); - if (mechMgr->isTurningToPlayer(actor) || mechMgr->getGreetingState(actor) == Greet_InProgress) + if (!stats.getMovementFlag(CreatureStats::Flag_ForceJump) && !stats.getMovementFlag(CreatureStats::Flag_ForceSneak) + && (mechMgr->isTurningToPlayer(actor) || mechMgr->getGreetingState(actor) == Greet_InProgress)) return false; const osg::Vec3f actorPos(actor.getRefData().getPosition().asVec3()); const osg::Vec3f targetPos(mX, mY, mZ); - auto& stats = actor.getClass().getCreatureStats(actor); stats.setMovementFlag(CreatureStats::Flag_Run, false); stats.setDrawState(DrawState_Nothing); diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 72b8757bf..0e424b2f8 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -11,7 +11,6 @@ #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" -#include "../mwbase/dialoguemanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/esmstore.hpp" @@ -210,14 +209,17 @@ namespace MWMechanics storage.setState(AiWanderStorage::Wander_Walking); } - GreetingState greetingState = MWBase::Environment::get().getMechanicsManager()->getGreetingState(actor); - if (greetingState == Greet_InProgress) + if(!cStats.getMovementFlag(CreatureStats::Flag_ForceJump) && !cStats.getMovementFlag(CreatureStats::Flag_ForceSneak)) { - if (storage.mState == AiWanderStorage::Wander_Walking) + GreetingState greetingState = MWBase::Environment::get().getMechanicsManager()->getGreetingState(actor); + if (greetingState == Greet_InProgress) { - stopMovement(actor); - mObstacleCheck.clear(); - storage.setState(AiWanderStorage::Wander_IdleNow); + if (storage.mState == AiWanderStorage::Wander_Walking) + { + stopMovement(actor); + mObstacleCheck.clear(); + storage.setState(AiWanderStorage::Wander_IdleNow); + } } } diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index 52a926d14..f8506ff59 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -5,8 +5,6 @@ #include -#include "../mwworld/timestamp.hpp" - #include "pathfinding.hpp" #include "obstacle.hpp" #include "aistate.hpp" @@ -128,7 +126,6 @@ namespace MWMechanics short unsigned getRandomIdle(); void setPathToAnAllowedNode(const MWWorld::Ptr& actor, AiWanderStorage& storage, const ESM::Position& actorPos); void evadeObstacles(const MWWorld::Ptr& actor, AiWanderStorage& storage); - void turnActorToFacePlayer(const osg::Vec3f& actorPosition, const osg::Vec3f& playerPosition, AiWanderStorage& storage); void doPerFrameActionsForState(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage); void onIdleStatePerFrameActions(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage); void onWalkingStatePerFrameActions(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage); diff --git a/apps/openmw/mwmechanics/alchemy.cpp b/apps/openmw/mwmechanics/alchemy.cpp index 32f8faed6..00676e4e6 100644 --- a/apps/openmw/mwmechanics/alchemy.cpp +++ b/apps/openmw/mwmechanics/alchemy.cpp @@ -35,7 +35,6 @@ #include "../mwworld/containerstore.hpp" #include "../mwworld/class.hpp" #include "../mwworld/cellstore.hpp" -#include "../mwworld/manualref.hpp" #include "magiceffects.hpp" #include "creaturestats.hpp" diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 791f4fefa..c6d5d136b 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -2034,8 +2034,10 @@ bool CharacterController::updateWeaponState(CharacterState& idle) MWWorld::ConstContainerStoreIterator torch = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); if(torch != inv.end() && torch->getTypeName() == typeid(ESM::Light).name() && updateCarriedLeftVisible(mWeaponType)) - { + if (mAnimation->isPlaying("shield")) + mAnimation->disable("shield"); + mAnimation->play("torch", Priority_Torch, MWRender::Animation::BlendMask_LeftArm, false, 1.0f, "start", "stop", 0.0f, (~(size_t)0), true); } @@ -2595,9 +2597,6 @@ void CharacterController::update(float duration) if (!mMovementAnimationControlled) world->queueMovement(mPtr, vec); } - else - // We must always queue movement, even if there is none, to apply gravity. - world->queueMovement(mPtr, osg::Vec3f(0.f, 0.f, 0.f)); movement = vec; movementSettings.mPosition[0] = movementSettings.mPosition[1] = 0; @@ -2619,8 +2618,6 @@ void CharacterController::update(float duration) if (cls.isPersistent(mPtr) || cls.getCreatureStats(mPtr).isDeathAnimationFinished()) playDeath(1.f, mDeathState); } - // We must always queue movement, even if there is none, to apply gravity. - world->queueMovement(mPtr, osg::Vec3f(0.f, 0.f, 0.f)); } bool isPersist = isPersistentAnimPlaying(); diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 8efe4962c..105d40a15 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -3,8 +3,6 @@ #include -#include - #include "../mwworld/ptr.hpp" #include "../mwworld/containerstore.hpp" diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index a58a2dbad..949779099 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -22,7 +22,6 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" -#include "../mwbase/mechanicsmanager.hpp" namespace MWMechanics { diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index 781b897a7..3f113802f 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -3,7 +3,6 @@ #include #include -#include #include #include #include diff --git a/apps/openmw/mwmechanics/spellcasting.hpp b/apps/openmw/mwmechanics/spellcasting.hpp index 45431bbc6..cc525d2db 100644 --- a/apps/openmw/mwmechanics/spellcasting.hpp +++ b/apps/openmw/mwmechanics/spellcasting.hpp @@ -2,7 +2,6 @@ #define MWMECHANICS_SPELLCASTING_H #include -#include #include "../mwworld/ptr.hpp" diff --git a/apps/openmw/mwmechanics/spelllist.cpp b/apps/openmw/mwmechanics/spelllist.cpp index d8fbcf25a..9328d533e 100644 --- a/apps/openmw/mwmechanics/spelllist.cpp +++ b/apps/openmw/mwmechanics/spelllist.cpp @@ -3,7 +3,6 @@ #include #include -#include #include "spells.hpp" @@ -71,7 +70,7 @@ namespace MWMechanics auto& id = spell->mId; bool changed = withBaseRecord([&] (auto& spells) { - for(auto it : spells) + for(const auto& it : spells) { if(Misc::StringUtils::ciEqual(id, it)) return false; diff --git a/apps/openmw/mwmechanics/spelllist.hpp b/apps/openmw/mwmechanics/spelllist.hpp index c95ee812b..5920949d6 100644 --- a/apps/openmw/mwmechanics/spelllist.hpp +++ b/apps/openmw/mwmechanics/spelllist.hpp @@ -9,8 +9,6 @@ #include -#include "magiceffects.hpp" - namespace ESM { struct SpellState; diff --git a/apps/openmw/mwmechanics/spellpriority.cpp b/apps/openmw/mwmechanics/spellpriority.cpp index 81658193d..bd9c5f7cb 100644 --- a/apps/openmw/mwmechanics/spellpriority.cpp +++ b/apps/openmw/mwmechanics/spellpriority.cpp @@ -17,7 +17,6 @@ #include "creaturestats.hpp" #include "spellresistance.hpp" #include "weapontype.hpp" -#include "combat.hpp" #include "summoning.hpp" #include "spellutil.hpp" diff --git a/apps/openmw/mwmechanics/spells.hpp b/apps/openmw/mwmechanics/spells.hpp index 055339795..9737b72cd 100644 --- a/apps/openmw/mwmechanics/spells.hpp +++ b/apps/openmw/mwmechanics/spells.hpp @@ -4,7 +4,6 @@ #include #include #include -#include #include #include "../mwworld/timestamp.hpp" diff --git a/apps/openmw/mwmechanics/weaponpriority.cpp b/apps/openmw/mwmechanics/weaponpriority.cpp index 13ce30927..8480dc208 100644 --- a/apps/openmw/mwmechanics/weaponpriority.cpp +++ b/apps/openmw/mwmechanics/weaponpriority.cpp @@ -9,7 +9,6 @@ #include "../mwworld/esmstore.hpp" #include "../mwworld/inventorystore.hpp" -#include "npcstats.hpp" #include "combat.hpp" #include "aicombataction.hpp" #include "spellpriority.hpp" diff --git a/apps/openmw/mwmechanics/weaponpriority.hpp b/apps/openmw/mwmechanics/weaponpriority.hpp index 67de7b50f..9dcef3e2e 100644 --- a/apps/openmw/mwmechanics/weaponpriority.hpp +++ b/apps/openmw/mwmechanics/weaponpriority.hpp @@ -1,8 +1,6 @@ #ifndef OPENMW_WEAPON_PRIORITY_H #define OPENMW_WEAPON_PRIORITY_H -#include - #include "../mwworld/ptr.hpp" namespace MWMechanics diff --git a/apps/openmw/mwmechanics/weapontype.hpp b/apps/openmw/mwmechanics/weapontype.hpp index 056a1dbfd..09fa73c06 100644 --- a/apps/openmw/mwmechanics/weapontype.hpp +++ b/apps/openmw/mwmechanics/weapontype.hpp @@ -3,8 +3,6 @@ #include "../mwworld/inventorystore.hpp" -#include "creaturestats.hpp" - namespace MWMechanics { static std::map sWeaponTypeList = diff --git a/apps/openmw/mwmp/Main.cpp b/apps/openmw/mwmp/Main.cpp index 272a836ba..f5615eef1 100644 --- a/apps/openmw/mwmp/Main.cpp +++ b/apps/openmw/mwmp/Main.cpp @@ -68,9 +68,9 @@ std::string loadSettings(Settings::Manager& settings) // prefer local if (boost::filesystem::exists(localdefault)) - settings.loadDefault(localdefault); + settings.loadDefault(localdefault, false); else if (boost::filesystem::exists(globaldefault)) - settings.loadDefault(globaldefault); + settings.loadDefault(globaldefault, false); else throw std::runtime_error ("No default settings file found! Make sure the file \"tes3mp-client-default.cfg\" was properly installed."); diff --git a/apps/openmw/mwphysics/actor.cpp b/apps/openmw/mwphysics/actor.cpp index c9eac2e1c..38719deb4 100644 --- a/apps/openmw/mwphysics/actor.cpp +++ b/apps/openmw/mwphysics/actor.cpp @@ -35,7 +35,7 @@ namespace MWPhysics Actor::Actor(const MWWorld::Ptr& ptr, const Resource::BulletShape* shape, PhysicsTaskScheduler* scheduler) : mStandingOnPtr(nullptr), mCanWaterWalk(false), mWalkingOnWater(false) , mCollisionObject(nullptr), mMeshTranslation(shape->mCollisionBox.center), mHalfExtents(shape->mCollisionBox.extents) - , mStuckFrames(0), mLastStuckPosition{0, 0, 0} + , mVelocity(0,0,0), mStuckFrames(0), mLastStuckPosition{0, 0, 0} , mForce(0.f, 0.f, 0.f), mOnGround(true), mOnSlope(false) , mInternalCollisionMode(true) , mExternalCollisionMode(true) @@ -152,27 +152,15 @@ int Actor::getCollisionMask() const void Actor::updatePosition() { std::scoped_lock lock(mPositionMutex); - updateWorldPosition(); - mPreviousPosition = mWorldPosition; - mPosition = mWorldPosition; - mSimulationPosition = mWorldPosition; + const auto worldPosition = mPtr.getRefData().getPosition().asVec3(); + mPreviousPosition = worldPosition; + mPosition = worldPosition; + mSimulationPosition = worldPosition; mPositionOffset = osg::Vec3f(); mStandingOnPtr = nullptr; mSkipCollisions = true; } -void Actor::updateWorldPosition() -{ - if (mWorldPosition != mPtr.getRefData().getPosition().asVec3()) - mWorldPositionChanged = true; - mWorldPosition = mPtr.getRefData().getPosition().asVec3(); -} - -osg::Vec3f Actor::getWorldPosition() const -{ - return mWorldPosition; -} - void Actor::setSimulationPosition(const osg::Vec3f& position) { mSimulationPosition = position; @@ -209,7 +197,6 @@ osg::Vec3f Actor::getCollisionObjectPosition() const bool Actor::setPosition(const osg::Vec3f& position) { std::scoped_lock lock(mPositionMutex); - updateWorldPosition(); applyOffsetChange(); bool hasChanged = mPosition != position || mWorldPositionChanged; mPreviousPosition = mPosition; @@ -338,4 +325,14 @@ bool Actor::skipCollisions() return std::exchange(mSkipCollisions, false); } +void Actor::setVelocity(osg::Vec3f velocity) +{ + mVelocity = velocity; +} + +osg::Vec3f Actor::velocity() +{ + return std::exchange(mVelocity, osg::Vec3f()); +} + } diff --git a/apps/openmw/mwphysics/actor.hpp b/apps/openmw/mwphysics/actor.hpp index 54f9009bf..7b53e8812 100644 --- a/apps/openmw/mwphysics/actor.hpp +++ b/apps/openmw/mwphysics/actor.hpp @@ -10,7 +10,6 @@ #include #include #include -#include class btCollisionShape; class btCollisionObject; @@ -56,13 +55,6 @@ namespace MWPhysics */ bool isRotationallyInvariant() const; - /** - * Set mWorldPosition to the position in the Ptr's RefData. This is used by the physics simulation to account for - * when an object is "instantly" moved/teleported as opposed to being moved by the physics simulation. - */ - void updateWorldPosition(); - osg::Vec3f getWorldPosition() const; - /** * Used by the physics simulation to store the simulation result. Used in conjunction with mWorldPosition * to account for e.g. scripted movements @@ -179,6 +171,9 @@ namespace MWPhysics bool skipCollisions(); + void setVelocity(osg::Vec3f velocity); + osg::Vec3f velocity(); + private: MWWorld::Ptr mStandingOnPtr; /// Removes then re-adds the collision object to the dynamics world @@ -202,11 +197,11 @@ namespace MWPhysics osg::Vec3f mScale; osg::Vec3f mRenderingScale; - osg::Vec3f mWorldPosition; osg::Vec3f mSimulationPosition; osg::Vec3f mPosition; osg::Vec3f mPreviousPosition; osg::Vec3f mPositionOffset; + osg::Vec3f mVelocity; bool mWorldPositionChanged; bool mSkipCollisions; btTransform mLocalTransform; diff --git a/apps/openmw/mwphysics/actorconvexcallback.cpp b/apps/openmw/mwphysics/actorconvexcallback.cpp index f99b706d7..ef52e07c2 100644 --- a/apps/openmw/mwphysics/actorconvexcallback.cpp +++ b/apps/openmw/mwphysics/actorconvexcallback.cpp @@ -1,5 +1,3 @@ -#include - #include "actorconvexcallback.hpp" #include "collisiontype.hpp" #include "contacttestwrapper.h" @@ -7,7 +5,6 @@ #include #include -#include "collisiontype.hpp" #include "projectile.hpp" namespace MWPhysics diff --git a/apps/openmw/mwphysics/contacttestresultcallback.hpp b/apps/openmw/mwphysics/contacttestresultcallback.hpp index fbe12d5dc..3d1b3b8aa 100644 --- a/apps/openmw/mwphysics/contacttestresultcallback.hpp +++ b/apps/openmw/mwphysics/contacttestresultcallback.hpp @@ -5,8 +5,6 @@ #include -#include "../mwworld/ptr.hpp" - #include "physicssystem.hpp" class btCollisionObject; diff --git a/apps/openmw/mwphysics/movementsolver.hpp b/apps/openmw/mwphysics/movementsolver.hpp index 76141ec0e..90b10c275 100644 --- a/apps/openmw/mwphysics/movementsolver.hpp +++ b/apps/openmw/mwphysics/movementsolver.hpp @@ -1,8 +1,6 @@ #ifndef OPENMW_MWPHYSICS_MOVEMENTSOLVER_H #define OPENMW_MWPHYSICS_MOVEMENTSOLVER_H -#include - #include #include "constants.hpp" diff --git a/apps/openmw/mwphysics/mtphysics.cpp b/apps/openmw/mwphysics/mtphysics.cpp index 95a741825..9b98e7e8f 100644 --- a/apps/openmw/mwphysics/mtphysics.cpp +++ b/apps/openmw/mwphysics/mtphysics.cpp @@ -106,16 +106,6 @@ namespace return actorData.mPosition * interpolationFactor + actorData.mActorRaw->getPreviousPosition() * (1.f - interpolationFactor); } - struct WorldFrameData - { - WorldFrameData() : mIsInStorm(MWBase::Environment::get().getWorld()->isInStorm()) - , mStormDirection(MWBase::Environment::get().getWorld()->getStormDirection()) - {} - - bool mIsInStorm; - osg::Vec3f mStormDirection; - }; - namespace Config { /// @return either the number of thread as configured by the user, or 1 if Bullet doesn't support multithreading @@ -177,43 +167,11 @@ namespace MWPhysics mDeferAabbUpdate = false; } - mPreStepBarrier = std::make_unique(mNumThreads, [&]() - { - if (mDeferAabbUpdate) - updateAabbs(); - if (!mRemainingSteps) - return; - for (auto& data : mActorsFrameData) - if (data.mActor.lock()) - { - std::unique_lock lock(mCollisionWorldMutex); - MovementSolver::unstuck(data, mCollisionWorld); - } - }); + mPreStepBarrier = std::make_unique(mNumThreads); - mPostStepBarrier = std::make_unique(mNumThreads, [&]() - { - if (mRemainingSteps) - { - --mRemainingSteps; - updateActorsPositions(); - } - mNextJob.store(0, std::memory_order_release); - }); + mPostStepBarrier = std::make_unique(mNumThreads); - mPostSimBarrier = std::make_unique(mNumThreads, [&]() - { - mNewFrame = false; - if (mLOSCacheExpiry >= 0) - { - std::unique_lock lock(mLOSCacheMutex); - mLOSCache.erase( - std::remove_if(mLOSCache.begin(), mLOSCache.end(), - [](const LOSRequest& req) { return req.mStale; }), - mLOSCache.end()); - } - mTimeEnd = mTimer->tick(); - }); + mPostSimBarrier = std::make_unique(mNumThreads); } PhysicsTaskScheduler::~PhysicsTaskScheduler() @@ -525,7 +483,7 @@ namespace MWPhysics if (!mNewFrame) mHasJob.wait(lock, [&]() { return mQuit || mNewFrame; }); - mPreStepBarrier->wait(); + mPreStepBarrier->wait([this] { afterPreStep(); }); int job = 0; while (mRemainingSteps && (job = mNextJob.fetch_add(1, std::memory_order_relaxed)) < mNumJobs) @@ -537,7 +495,7 @@ namespace MWPhysics } } - mPostStepBarrier->wait(); + mPostStepBarrier->wait([this] { afterPostStep(); }); if (!mRemainingSteps) { @@ -552,7 +510,7 @@ namespace MWPhysics if (mLOSCacheExpiry >= 0) refreshLOSCache(); - mPostSimBarrier->wait(); + mPostSimBarrier->wait([this] { afterPostSim(); }); } } } @@ -633,4 +591,42 @@ namespace MWPhysics std::shared_lock lock(mCollisionWorldMutex); mDebugDrawer->step(); } + + void PhysicsTaskScheduler::afterPreStep() + { + if (mDeferAabbUpdate) + updateAabbs(); + if (!mRemainingSteps) + return; + for (auto& data : mActorsFrameData) + if (data.mActor.lock()) + { + std::unique_lock lock(mCollisionWorldMutex); + MovementSolver::unstuck(data, mCollisionWorld); + } + } + + void PhysicsTaskScheduler::afterPostStep() + { + if (mRemainingSteps) + { + --mRemainingSteps; + updateActorsPositions(); + } + mNextJob.store(0, std::memory_order_release); + } + + void PhysicsTaskScheduler::afterPostSim() + { + mNewFrame = false; + if (mLOSCacheExpiry >= 0) + { + std::unique_lock lock(mLOSCacheMutex); + mLOSCache.erase( + std::remove_if(mLOSCache.begin(), mLOSCache.end(), + [](const LOSRequest& req) { return req.mStale; }), + mLOSCache.end()); + } + mTimeEnd = mTimer->tick(); + } } diff --git a/apps/openmw/mwphysics/mtphysics.hpp b/apps/openmw/mwphysics/mtphysics.hpp index 8ce739a42..5b5686cbe 100644 --- a/apps/openmw/mwphysics/mtphysics.hpp +++ b/apps/openmw/mwphysics/mtphysics.hpp @@ -66,6 +66,9 @@ namespace MWPhysics void updatePtrAabb(const std::weak_ptr& ptr); void updateStats(osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats); std::tuple calculateStepConfig(float timeAccum) const; + void afterPreStep(); + void afterPostStep(); + void afterPostSim(); std::unique_ptr mWorldFrameData; std::vector mActorsFrameData; diff --git a/apps/openmw/mwphysics/object.cpp b/apps/openmw/mwphysics/object.cpp index e3615989d..636306532 100644 --- a/apps/openmw/mwphysics/object.cpp +++ b/apps/openmw/mwphysics/object.cpp @@ -119,11 +119,8 @@ namespace MWPhysics assert (mShapeInstance->getCollisionShape()->isCompound()); btCompoundShape* compound = static_cast(mShapeInstance->getCollisionShape()); - for (const auto& shape : mShapeInstance->mAnimatedShapes) + for (const auto& [recIndex, shapeIndex] : mShapeInstance->mAnimatedShapes) { - int recIndex = shape.first; - int shapeIndex = shape.second; - auto nodePathFound = mRecIndexToNodePath.find(recIndex); if (nodePathFound == mRecIndexToNodePath.end()) { diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 2aa8e6d05..aee119c16 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include @@ -56,7 +55,6 @@ #include "closestnotmerayresultcallback.hpp" #include "contacttestresultcallback.hpp" #include "projectileconvexcallback.hpp" -#include "constants.hpp" #include "movementsolver.hpp" #include "mtphysics.hpp" @@ -751,21 +749,15 @@ namespace MWPhysics void PhysicsSystem::queueObjectMovement(const MWWorld::Ptr &ptr, const osg::Vec3f &velocity) { - for(auto& movementItem : mMovementQueue) - { - if (movementItem.first == ptr) - { - movementItem.second = velocity; - return; - } - } - - mMovementQueue.emplace_back(ptr, velocity); + ActorMap::iterator found = mActors.find(ptr); + if (found != mActors.end()) + found->second->setVelocity(velocity); } void PhysicsSystem::clearQueuedMovement() { - mMovementQueue.clear(); + for (const auto& [_, actor] : mActors) + actor->setVelocity(osg::Vec3f()); } const std::vector& PhysicsSystem::applyQueuedMovement(float dt, bool skipSimulation, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats) @@ -782,27 +774,21 @@ namespace MWPhysics std::vector PhysicsSystem::prepareFrameData(bool willSimulate) { std::vector actorsFrameData; - actorsFrameData.reserve(mMovementQueue.size()); + actorsFrameData.reserve(mActors.size()); const MWBase::World *world = MWBase::Environment::get().getWorld(); - for (const auto& [character, movement] : mMovementQueue) + for (const auto& [ptr, physicActor] : mActors) { - const auto foundActor = mActors.find(character); - if (foundActor == mActors.end()) // actor was already removed from the scene - continue; - - auto physicActor = foundActor->second; - float waterlevel = -std::numeric_limits::max(); - const MWWorld::CellStore *cell = character.getCell(); + const MWWorld::CellStore *cell = ptr.getCell(); if(cell->getCell()->hasWater()) waterlevel = cell->getWaterLevel(); - const MWMechanics::MagicEffects& effects = character.getClass().getCreatureStats(character).getMagicEffects(); + const MWMechanics::MagicEffects& effects = ptr.getClass().getCreatureStats(physicActor->getPtr()).getMagicEffects(); bool waterCollision = false; if (cell->getCell()->hasWater() && effects.get(ESM::MagicEffect::WaterWalking).getMagnitude()) { - if (physicActor->getCollisionMode() || !world->isUnderwater(character.getCell(), osg::Vec3f(character.getRefData().getPosition().asVec3()))) + if (physicActor->getCollisionMode() || !world->isUnderwater(ptr.getCell(), osg::Vec3f(ptr.getRefData().getPosition().asVec3()))) waterCollision = true; } @@ -816,9 +802,8 @@ namespace MWPhysics if (!willSimulate) standingOn = physicActor->getStandingOnPtr(); - actorsFrameData.emplace_back(std::move(physicActor), standingOn, waterCollision, movement, slowFall, waterlevel); + actorsFrameData.emplace_back(physicActor, standingOn, waterCollision, slowFall, waterlevel); } - mMovementQueue.clear(); return actorsFrameData; } @@ -959,10 +944,10 @@ namespace MWPhysics } ActorFrameData::ActorFrameData(const std::shared_ptr& actor, const MWWorld::Ptr standingOn, - bool waterCollision, osg::Vec3f movement, float slowFall, float waterlevel) + bool waterCollision, float slowFall, float waterlevel) : mActor(actor), mActorRaw(actor.get()), mStandingOn(standingOn), - mDidJump(false), mNeedLand(false), mWaterCollision(waterCollision), - mWaterlevel(waterlevel), mSlowFall(slowFall), mOldHeight(0), mFallHeight(0), mMovement(movement), mPosition(), mRefpos() + mDidJump(false), mNeedLand(false), mWaterCollision(waterCollision), mSkipCollisionDetection(actor->skipCollisions()), + mWaterlevel(waterlevel), mSlowFall(slowFall), mOldHeight(0), mFallHeight(0), mMovement(actor->velocity()), mPosition(), mRefpos() { const MWBase::World *world = MWBase::Environment::get().getWorld(); const auto ptr = actor->getPtr(); @@ -977,8 +962,6 @@ namespace MWPhysics void ActorFrameData::updatePosition(btCollisionWorld* world) { - mActorRaw->updateWorldPosition(); - mSkipCollisionDetection = mActorRaw->skipCollisions(); mActorRaw->applyOffsetChange(); mPosition = mActorRaw->getPosition(); if (mWaterCollision && mPosition.z() < mWaterlevel && canMoveToWaterSurface(mActorRaw, mWaterlevel, world)) diff --git a/apps/openmw/mwphysics/physicssystem.hpp b/apps/openmw/mwphysics/physicssystem.hpp index 9250f188e..dab72fee8 100644 --- a/apps/openmw/mwphysics/physicssystem.hpp +++ b/apps/openmw/mwphysics/physicssystem.hpp @@ -78,7 +78,7 @@ namespace MWPhysics struct ActorFrameData { - ActorFrameData(const std::shared_ptr& actor, const MWWorld::Ptr standingOn, bool moveToWaterSurface, osg::Vec3f movement, float slowFall, float waterlevel); + ActorFrameData(const std::shared_ptr& actor, const MWWorld::Ptr standingOn, bool moveToWaterSurface, float slowFall, float waterlevel); void updatePosition(btCollisionWorld* world); std::weak_ptr mActor; Actor* mActorRaw; @@ -291,9 +291,6 @@ namespace MWPhysics bool mDebugDrawEnabled; - using PtrVelocityList = std::vector>; - PtrVelocityList mMovementQueue; - float mTimeAccum; unsigned int mProjectileId; diff --git a/apps/openmw/mwphysics/projectile.cpp b/apps/openmw/mwphysics/projectile.cpp index a93121997..252da0a68 100644 --- a/apps/openmw/mwphysics/projectile.cpp +++ b/apps/openmw/mwphysics/projectile.cpp @@ -5,10 +5,7 @@ #include -#include #include -#include -#include #include "../mwworld/class.hpp" diff --git a/apps/openmw/mwphysics/projectile.hpp b/apps/openmw/mwphysics/projectile.hpp index fb50eebde..81c33d2a5 100644 --- a/apps/openmw/mwphysics/projectile.hpp +++ b/apps/openmw/mwphysics/projectile.hpp @@ -6,8 +6,6 @@ #include #include -#include "components/misc/convert.hpp" - #include "ptrholder.hpp" class btCollisionObject; diff --git a/apps/openmw/mwphysics/projectileconvexcallback.hpp b/apps/openmw/mwphysics/projectileconvexcallback.hpp index c687de36c..96c84b1fe 100644 --- a/apps/openmw/mwphysics/projectileconvexcallback.hpp +++ b/apps/openmw/mwphysics/projectileconvexcallback.hpp @@ -1,8 +1,6 @@ #ifndef OPENMW_MWPHYSICS_PROJECTILECONVEXCALLBACK_H #define OPENMW_MWPHYSICS_PROJECTILECONVEXCALLBACK_H -#include - #include class btCollisionObject; diff --git a/apps/openmw/mwphysics/stepper.cpp b/apps/openmw/mwphysics/stepper.cpp index 4f5a27e53..1f53c1ac5 100644 --- a/apps/openmw/mwphysics/stepper.cpp +++ b/apps/openmw/mwphysics/stepper.cpp @@ -1,7 +1,5 @@ #include "stepper.hpp" -#include - #include #include diff --git a/apps/openmw/mwrender/actoranimation.cpp b/apps/openmw/mwrender/actoranimation.cpp index 845e917fb..59bc32765 100644 --- a/apps/openmw/mwrender/actoranimation.cpp +++ b/apps/openmw/mwrender/actoranimation.cpp @@ -11,7 +11,6 @@ #include #include -#include #include #include #include diff --git a/apps/openmw/mwrender/actoranimation.hpp b/apps/openmw/mwrender/actoranimation.hpp index 86929a18a..e149e4414 100644 --- a/apps/openmw/mwrender/actoranimation.hpp +++ b/apps/openmw/mwrender/actoranimation.hpp @@ -6,7 +6,6 @@ #include #include "../mwworld/containerstore.hpp" -#include "../mwworld/inventorystore.hpp" #include "animation.hpp" diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index b75e45906..e88c4cee3 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -23,7 +23,6 @@ #include #include -#include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwworld/class.hpp" #include "../mwworld/inventorystore.hpp" diff --git a/apps/openmw/mwrender/globalmap.cpp b/apps/openmw/mwrender/globalmap.cpp index 0fe5e21ea..46033f530 100644 --- a/apps/openmw/mwrender/globalmap.cpp +++ b/apps/openmw/mwrender/globalmap.cpp @@ -9,7 +9,6 @@ #include -#include #include #include diff --git a/apps/openmw/mwrender/groundcover.cpp b/apps/openmw/mwrender/groundcover.cpp index fd2246253..63df3e249 100644 --- a/apps/openmw/mwrender/groundcover.cpp +++ b/apps/openmw/mwrender/groundcover.cpp @@ -241,7 +241,7 @@ namespace MWRender if (model.empty()) continue; model = "meshes/" + model; - instances[model].emplace_back(ref, model); + instances[model].emplace_back(std::move(ref), std::move(model)); } } } diff --git a/apps/openmw/mwrender/landmanager.hpp b/apps/openmw/mwrender/landmanager.hpp index 1694bd243..f3cc86085 100644 --- a/apps/openmw/mwrender/landmanager.hpp +++ b/apps/openmw/mwrender/landmanager.hpp @@ -3,7 +3,6 @@ #include -#include #include #include diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index ec2bf54a7..686078879 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -22,7 +22,6 @@ #include #include #include -#include #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index 064d3aa35..b6bae8cbb 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -105,6 +105,7 @@ namespace MWRender bool mOptimizeBillboards = true; float mSqrDistance = 0.f; osg::Vec3f mViewVector; + osg::Node::NodeMask mCopyMask = ~0u; mutable std::vector mNodePath; void copy(const osg::Node* toCopy, osg::Group* attachTo) @@ -121,6 +122,9 @@ namespace MWRender osg::Node* operator() (const osg::Node* node) const override { + if (!(node->getNodeMask() & mCopyMask)) + return nullptr; + if (const osg::Drawable* d = node->asDrawable()) return operator()(d); @@ -224,6 +228,9 @@ namespace MWRender } osg::Drawable* operator() (const osg::Drawable* drawable) const override { + if (!(drawable->getNodeMask() & mCopyMask)) + return nullptr; + if (dynamic_cast(drawable)) return nullptr; @@ -261,9 +268,11 @@ namespace MWRender class AnalyzeVisitor : public osg::NodeVisitor { public: - AnalyzeVisitor() + AnalyzeVisitor(osg::Node::NodeMask analyzeMask) : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) - , mCurrentStateSet(nullptr) {} + , mCurrentStateSet(nullptr) + , mCurrentDistance(0.f) + , mAnalyzeMask(analyzeMask) {} typedef std::unordered_map StateSetCounter; struct Result @@ -274,12 +283,34 @@ namespace MWRender void apply(osg::Node& node) override { + if (!(node.getNodeMask() & mAnalyzeMask)) + return; + if (node.getStateSet()) mCurrentStateSet = node.getStateSet(); + + if (osg::Switch* sw = node.asSwitch()) + { + for (unsigned int i=0; igetNumChildren(); ++i) + if (sw->getValue(i)) + traverse(*sw->getChild(i)); + return; + } + if (osg::LOD* lod = dynamic_cast(&node)) + { + for (unsigned int i=0; igetNumChildren(); ++i) + if (lod->getMinRange(i) * lod->getMinRange(i) <= mCurrentDistance && mCurrentDistance < lod->getMaxRange(i) * lod->getMaxRange(i)) + traverse(*lod->getChild(i)); + return; + } + traverse(node); } void apply(osg::Geometry& geom) override { + if (!(geom.getNodeMask() & mAnalyzeMask)) + return; + if (osg::Array* array = geom.getVertexArray()) mResult.mNumVerts += array->getNumElements(); @@ -313,6 +344,8 @@ namespace MWRender Result mResult; osg::StateSet* mCurrentStateSet; StateSetCounter mGlobalStateSetCounter; + float mCurrentDistance; + osg::Node::NodeMask mAnalyzeMask; }; class DebugVisitor : public osg::NodeVisitor @@ -384,7 +417,7 @@ namespace MWRender { try { - unsigned int index = cell->mContextList.at(i).index; + unsigned int index = cell->mContextList[i].index; if (esm.size()<=index) esm.resize(index+1); cell->restore(esm[index], i); @@ -393,13 +426,13 @@ namespace MWRender 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; + Misc::StringUtils::lowerCaseInPlace(ref.mRefID); int type = store.findStatic(ref.mRefID); if (!typeFilter(type,size>=2)) continue; if (deleted) { refs.erase(ref.mRefNum); continue; } if (ref.mRefNum.fromGroundcoverFile()) continue; - refs[ref.mRefNum] = ref; + refs[ref.mRefNum] = std::move(ref); } } catch (std::exception&) @@ -407,15 +440,13 @@ namespace MWRender continue; } } - for (ESM::CellRefTracker::const_iterator it = cell->mLeasedRefs.begin(); it != cell->mLeasedRefs.end(); ++it) + for (auto [ref, deleted] : cell->mLeasedRefs) { - ESM::CellRef ref = it->first; - Misc::StringUtils::lowerCaseInPlace(ref.mRefID); - bool deleted = it->second; if (deleted) { refs.erase(ref.mRefNum); continue; } + Misc::StringUtils::lowerCaseInPlace(ref.mRefID); int type = store.findStatic(ref.mRefID); if (!typeFilter(type,size>=2)) continue; - refs[ref.mRefNum] = ref; + refs[ref.mRefNum] = std::move(ref); } } } @@ -438,7 +469,14 @@ namespace MWRender typedef std::map, InstanceList> NodeMap; NodeMap nodes; osg::ref_ptr refnumSet = activeGrid ? new RefnumSet : nullptr; - AnalyzeVisitor analyzeVisitor; + + // Mask_UpdateVisitor is used in such cases in NIF loader: + // 1. For collision nodes, which is not supposed to be rendered. + // 2. For nodes masked via Flag_Hidden (VisController can change this flag value at runtime). + // Since ObjectPaging does not handle VisController, we can just ignore both types of nodes. + constexpr auto copyMask = ~Mask_UpdateVisitor; + + AnalyzeVisitor analyzeVisitor(copyMask); float minSize = mMinSize; if (mMinSizeMergeFactor) minSize *= mMinSizeMergeFactor; @@ -508,6 +546,7 @@ namespace MWRender continue; } + analyzeVisitor.mCurrentDistance = dSqr; auto emplaced = nodes.emplace(cnode, InstanceList()); if (emplaced.second) { @@ -525,6 +564,7 @@ namespace MWRender osg::ref_ptr templateRefs = new Resource::TemplateMultiRef; osgUtil::StateToCompile stateToCompile(0, nullptr); CopyOp copyop; + copyop.mCopyMask = copyMask; for (const auto& pair : nodes) { const osg::Node* cnode = pair.first; @@ -611,6 +651,7 @@ namespace MWRender } optimizer.setIsOperationPermissibleForObjectCallback(new CanOptimizeCallback); unsigned int options = SceneUtil::Optimizer::FLATTEN_STATIC_TRANSFORMS|SceneUtil::Optimizer::REMOVE_REDUNDANT_NODES|SceneUtil::Optimizer::MERGE_GEOMETRY; + mSceneManager->shareState(mergeGroup); optimizer.optimize(mergeGroup, options); group->addChild(mergeGroup); @@ -701,7 +742,7 @@ namespace MWRender ccf.mCell = cell; mCache->call(ccf); if (ccf.mToClear.empty()) return false; - for (auto chunk : ccf.mToClear) + for (const auto& chunk : ccf.mToClear) mCache->removeFromObjectCache(chunk); return true; } @@ -723,7 +764,7 @@ namespace MWRender ccf.mActiveGridOnly = true; mCache->call(ccf); if (ccf.mToClear.empty()) return false; - for (auto chunk : ccf.mToClear) + for (const auto& chunk : ccf.mToClear) mCache->removeFromObjectCache(chunk); return true; } diff --git a/apps/openmw/mwrender/recastmesh.cpp b/apps/openmw/mwrender/recastmesh.cpp index d07e7d37b..7c7c6b8eb 100644 --- a/apps/openmw/mwrender/recastmesh.cpp +++ b/apps/openmw/mwrender/recastmesh.cpp @@ -1,7 +1,6 @@ #include "recastmesh.hpp" #include -#include #include diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 5d5a349d4..e497fdecd 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -43,14 +43,12 @@ #include #include -#include #include #include "../mwworld/cellstore.hpp" #include "../mwworld/class.hpp" #include "../mwgui/loadingscreen.hpp" -#include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwmechanics/actorutil.hpp" @@ -63,7 +61,6 @@ #include "viewovershoulder.hpp" #include "water.hpp" #include "terrainstorage.hpp" -#include "util.hpp" #include "navmesh.hpp" #include "actorspaths.hpp" #include "recastmesh.hpp" @@ -265,7 +262,7 @@ namespace MWRender globalDefines[itr->first] = itr->second; // Refactor this at some point - most shaders don't care about these defines - float groundcoverDistance = (Constants::CellSizeInUnits * std::max(1, Settings::Manager::getInt("distance", "Groundcover")) - 1024) * 0.93; + float groundcoverDistance = std::max(0.f, Settings::Manager::getFloat("rendering distance", "Groundcover")); globalDefines["groundcoverFadeStart"] = std::to_string(groundcoverDistance * 0.9f); globalDefines["groundcoverFadeEnd"] = std::to_string(groundcoverDistance); globalDefines["groundcoverStompMode"] = std::to_string(std::clamp(Settings::Manager::getInt("stomp mode", "Groundcover"), 0, 2)); @@ -352,6 +349,10 @@ namespace MWRender mGroundcover.reset(new Groundcover(mResourceSystem->getSceneManager(), density)); static_cast(mGroundcoverWorld.get())->addChunkManager(mGroundcover.get()); mResourceSystem->addResourceManager(mGroundcover.get()); + + // Groundcover it is handled in the same way indifferently from if it is from active grid or from distant cell. + // Use a stub grid to avoid splitting between chunks for active grid and chunks for distant cells. + mGroundcoverWorld->setActiveGrid(osg::Vec4i(0, 0, 0, 0)); } // water goes after terrain for correct waterculling order mWater.reset(new Water(sceneRoot->getParent(0), sceneRoot, mResourceSystem, mViewer->getIncrementalCompileOperation(), resourcePath)); @@ -1079,7 +1080,7 @@ namespace MWRender if (mGroundcoverWorld) { - int groundcoverDistance = Constants::CellSizeInUnits * std::max(1, Settings::Manager::getInt("distance", "Groundcover")); + float groundcoverDistance = std::max(0.f, Settings::Manager::getFloat("rendering distance", "Groundcover")); mGroundcoverWorld->setViewDistance(groundcoverDistance * (distanceMult ? 1.f/distanceMult : 1.f)); } } @@ -1314,8 +1315,6 @@ namespace MWRender void RenderingManager::setActiveGrid(const osg::Vec4i &grid) { mTerrain->setActiveGrid(grid); - if (mGroundcoverWorld) - mGroundcoverWorld->setActiveGrid(grid); } bool RenderingManager::pagingEnableObject(int type, const MWWorld::ConstPtr& ptr, bool enabled) { diff --git a/apps/openmw/mwrender/screenshotmanager.cpp b/apps/openmw/mwrender/screenshotmanager.cpp index 89b225da4..af2970fd8 100644 --- a/apps/openmw/mwrender/screenshotmanager.cpp +++ b/apps/openmw/mwrender/screenshotmanager.cpp @@ -36,15 +36,15 @@ namespace MWRender class NotifyDrawCompletedCallback : public osg::Camera::DrawCallback { public: - NotifyDrawCompletedCallback(unsigned int frame) - : mDone(false), mFrame(frame) + NotifyDrawCompletedCallback() + : mDone(false), mFrame(0) { } void operator () (osg::RenderInfo& renderInfo) const override { std::lock_guard lock(mMutex); - if (renderInfo.getState()->getFrameStamp()->getFrameNumber() >= mFrame) + if (renderInfo.getState()->getFrameStamp()->getFrameNumber() >= mFrame && !mDone) { mDone = true; mCondition.notify_one(); @@ -59,6 +59,12 @@ namespace MWRender mCondition.wait(lock); } + void reset(unsigned int frame) + { + mDone = false; + mFrame = frame; + } + mutable std::condition_variable mCondition; mutable std::mutex mMutex; mutable bool mDone; @@ -94,8 +100,18 @@ namespace MWRender : mViewer(viewer) , mRootNode(rootNode) , mSceneRoot(sceneRoot) + , mDrawCompleteCallback(new NotifyDrawCompletedCallback) , mResourceSystem(resourceSystem) , mWater(water) + { + // Note: This assumes no other final draw callbacks are set anywhere and that this callback will remain set until the application exits. + // This works around *DrawCallback manipulation being unsafe in OSG >= 3.5.10 for release 0.47 + // If you need to set other final draw callbacks, read the comments of issue 6013 for a suggestion + // Ref https://gitlab.com/OpenMW/openmw/-/issues/6013 + mViewer->getCamera()->setFinalDrawCallback(mDrawCompleteCallback); + } + + ScreenshotManager::~ScreenshotManager() { } @@ -107,16 +123,10 @@ namespace MWRender tempDrw->setCullingActive(false); tempDrw->getOrCreateStateSet()->setRenderBinDetails(100, "RenderBin", osg::StateSet::USE_RENDERBIN_DETAILS); // so its after all scene bins but before POST_RENDER gui camera camera->addChild(tempDrw); - osg::ref_ptr callback (new NotifyDrawCompletedCallback(mViewer->getFrameStamp()->getFrameNumber())); - camera->setFinalDrawCallback(callback); - mViewer->eventTraversal(); - mViewer->updateTraversal(); - mViewer->renderingTraversals(); - callback->waitTillDone(); + traversalsAndWait(mViewer->getFrameStamp()->getFrameNumber()); // now that we've "used up" the current frame, get a fresh frame number for the next frame() following after the screenshot is completed mViewer->advance(mViewer->getFrameStamp()->getSimulationTime()); camera->removeChild(tempDrw); - camera->setFinalDrawCallback(nullptr); } bool ScreenshotManager::screenshot360(osg::Image* image) @@ -257,6 +267,15 @@ namespace MWRender return true; } + void ScreenshotManager::traversalsAndWait(unsigned int frame) + { + mDrawCompleteCallback->reset(frame); + mViewer->eventTraversal(); + mViewer->updateTraversal(); + mViewer->renderingTraversals(); + mDrawCompleteCallback->waitTillDone(); + } + void ScreenshotManager::renderCameraToImage(osg::Camera *camera, osg::Image *image, int w, int h) { camera->setNodeMask(Mask_RenderToTexture); @@ -280,16 +299,10 @@ namespace MWRender mRootNode->addChild(camera); - // The draw needs to complete before we can copy back our image. - osg::ref_ptr callback (new NotifyDrawCompletedCallback(0)); - camera->setFinalDrawCallback(callback); - MWBase::Environment::get().getWindowManager()->getLoadingScreen()->loadingOn(false); - mViewer->eventTraversal(); - mViewer->updateTraversal(); - mViewer->renderingTraversals(); - callback->waitTillDone(); + // The draw needs to complete before we can copy back our image. + traversalsAndWait(0); MWBase::Environment::get().getWindowManager()->getLoadingScreen()->loadingOff(); diff --git a/apps/openmw/mwrender/screenshotmanager.hpp b/apps/openmw/mwrender/screenshotmanager.hpp index 2ac50bdf0..373fe3be8 100644 --- a/apps/openmw/mwrender/screenshotmanager.hpp +++ b/apps/openmw/mwrender/screenshotmanager.hpp @@ -16,11 +16,13 @@ namespace Resource namespace MWRender { class Water; + class NotifyDrawCompletedCallback; class ScreenshotManager { public: ScreenshotManager(osgViewer::Viewer* viewer, osg::ref_ptr rootNode, osg::ref_ptr sceneRoot, Resource::ResourceSystem* resourceSystem, Water* water); + ~ScreenshotManager(); void screenshot(osg::Image* image, int w, int h); bool screenshot360(osg::Image* image); @@ -29,9 +31,11 @@ namespace MWRender osg::ref_ptr mViewer; osg::ref_ptr mRootNode; osg::ref_ptr mSceneRoot; + osg::ref_ptr mDrawCompleteCallback; Resource::ResourceSystem* mResourceSystem; Water* mWater; + void traversalsAndWait(unsigned int frame); void renderCameraToImage(osg::Camera *camera, osg::Image *image, int w, int h); void makeCubemapScreenshot(osg::Image* image, int w, int h, osg::Matrixd cameraTransform=osg::Matrixd()); }; diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 67b24d60a..8bac90604 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -46,7 +46,6 @@ #include #include -#include #include diff --git a/apps/openmw/mwscript/cellextensions.cpp b/apps/openmw/mwscript/cellextensions.cpp index 356428156..b0977984f 100644 --- a/apps/openmw/mwscript/cellextensions.cpp +++ b/apps/openmw/mwscript/cellextensions.cpp @@ -13,7 +13,6 @@ #include "../mwworld/actionteleport.hpp" #include "../mwworld/cellstore.hpp" #include "../mwbase/environment.hpp" -#include "../mwworld/player.hpp" #include "../mwbase/statemanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/world.hpp" diff --git a/apps/openmw/mwscript/controlextensions.cpp b/apps/openmw/mwscript/controlextensions.cpp index b80552f85..b740438d4 100644 --- a/apps/openmw/mwscript/controlextensions.cpp +++ b/apps/openmw/mwscript/controlextensions.cpp @@ -1,6 +1,5 @@ #include "controlextensions.hpp" -#include #include #include @@ -26,8 +25,6 @@ #include "../mwworld/class.hpp" #include "../mwworld/ptr.hpp" -#include "../mwmechanics/npcstats.hpp" - #include "interpretercontext.hpp" #include "ref.hpp" diff --git a/apps/openmw/mwscript/globalscripts.cpp b/apps/openmw/mwscript/globalscripts.cpp index a5d85504c..52eca6742 100644 --- a/apps/openmw/mwscript/globalscripts.cpp +++ b/apps/openmw/mwscript/globalscripts.cpp @@ -12,8 +12,6 @@ #include "../mwbase/world.hpp" #include "../mwbase/scriptmanager.hpp" -#include "../mwmechanics/creaturestats.hpp" - #include "interpretercontext.hpp" namespace diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index 94ecb22ad..8dbb59b1f 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -5,8 +5,6 @@ #include -#include - /* Start of tes3mp addition diff --git a/apps/openmw/mwsound/ffmpeg_decoder.hpp b/apps/openmw/mwsound/ffmpeg_decoder.hpp index f099c831c..0a67a4758 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.hpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.hpp @@ -2,6 +2,12 @@ #define GAME_SOUND_FFMPEG_DECODER_H #include + +#if defined(_MSC_VER) + #pragma warning (push) + #pragma warning (disable : 4244) +#endif + extern "C" { #include @@ -14,6 +20,10 @@ extern "C" #include } +#if defined(_MSC_VER) + #pragma warning (pop) +#endif + #include #include diff --git a/apps/openmw/mwstate/quicksavemanager.hpp b/apps/openmw/mwstate/quicksavemanager.hpp index cdeff42c2..3272b24b5 100644 --- a/apps/openmw/mwstate/quicksavemanager.hpp +++ b/apps/openmw/mwstate/quicksavemanager.hpp @@ -4,7 +4,6 @@ #include #include "character.hpp" -#include "../mwbase/statemanager.hpp" namespace MWState{ class QuickSaveManager{ diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index dfd69b2e5..44afde22a 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -15,13 +15,9 @@ #include #include -#include "../mwbase/environment.hpp" -#include "../mwbase/world.hpp" - #include "../mwrender/landmanager.hpp" #include "cellstore.hpp" -#include "manualref.hpp" #include "class.hpp" namespace MWWorld diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 8640b3fcc..3da3020c1 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -433,8 +433,8 @@ namespace MWWorld void merge() { - for (std::map::const_iterator it = mMovedHere.begin(); it != mMovedHere.end(); ++it) - mMergeTo.push_back(it->first); + for (const auto & [base, _] : mMovedHere) + mMergeTo.push_back(base); } private: @@ -558,9 +558,9 @@ namespace MWWorld if (Ptr ptr = ::searchViaActorId (mCreatures, id, this, mMovedToAnotherCell)) return ptr; - for (MovedRefTracker::const_iterator it = mMovedHere.begin(); it != mMovedHere.end(); ++it) + for (const auto& [base, _] : mMovedHere) { - MWWorld::Ptr actor (it->first, this); + MWWorld::Ptr actor (base, this); if (!actor.getClass().isActor()) continue; if (actor.getClass().getCreatureStats (actor).matchesActorId (id) && actor.getRefData().getCount() > 0) @@ -764,7 +764,7 @@ namespace MWWorld try { // Reopen the ESM reader and seek to the right position. - int index = mCell->mContextList.at(i).index; + int index = mCell->mContextList[i].index; mCell->restore (esm[index], i); ESM::CellRef ref; @@ -783,7 +783,8 @@ namespace MWWorld continue; } - mIds.push_back (Misc::StringUtils::lowerCase (ref.mRefID)); + Misc::StringUtils::lowerCaseInPlace(ref.mRefID); + mIds.push_back(std::move(ref.mRefID)); } } catch (std::exception& e) @@ -793,11 +794,8 @@ namespace MWWorld } // List moved references, from separately tracked list. - for (ESM::CellRefTracker::const_iterator it = mCell->mLeasedRefs.begin(); it != mCell->mLeasedRefs.end(); ++it) + for (const auto& [ref, deleted]: mCell->mLeasedRefs) { - const ESM::CellRef &ref = it->first; - bool deleted = it->second; - if (!deleted) mIds.push_back(Misc::StringUtils::lowerCase(ref.mRefID)); } @@ -822,7 +820,7 @@ namespace MWWorld try { // Reopen the ESM reader and seek to the right position. - int index = mCell->mContextList.at(i).index; + int index = mCell->mContextList[i].index; mCell->restore (esm[index], i); ESM::CellRef ref; @@ -849,10 +847,10 @@ namespace MWWorld } // Load moved references, from separately tracked list. - for (ESM::CellRefTracker::const_iterator it = mCell->mLeasedRefs.begin(); it != mCell->mLeasedRefs.end(); ++it) + for (const auto& leasedRef : mCell->mLeasedRefs) { - ESM::CellRef &ref = const_cast(it->first); - bool deleted = it->second; + ESM::CellRef &ref = const_cast(leasedRef.first); + bool deleted = leasedRef.second; loadRef (ref, deleted, refNumToID); } @@ -1023,11 +1021,10 @@ namespace MWWorld writeReferenceCollection (writer, mWeapons); writeReferenceCollection (writer, mBodyParts); - for (MovedRefTracker::const_iterator it = mMovedToAnotherCell.begin(); it != mMovedToAnotherCell.end(); ++it) + for (const auto& [base, store] : mMovedToAnotherCell) { - LiveCellRefBase* base = it->first; ESM::RefNum refNum = base->mRef.getRefNum(); - ESM::CellId movedTo = it->second->getCell()->getCellId(); + ESM::CellId movedTo = store->getCell()->getCellId(); refNum.save(writer, true, "MVRF"); movedTo.save(writer); @@ -1354,9 +1351,9 @@ namespace MWWorld updateRechargingItems(); mRechargingItemsUpToDate = true; } - for (TRechargingItems::iterator it = mRechargingItems.begin(); it != mRechargingItems.end(); ++it) + for (const auto& [item, charge] : mRechargingItems) { - MWMechanics::rechargeItem(it->first, it->second, duration); + MWMechanics::rechargeItem(item, charge, duration); } } @@ -1364,38 +1361,22 @@ namespace MWWorld { mRechargingItems.clear(); - for (CellRefList::List::iterator it (mWeapons.mList.begin()); it!=mWeapons.mList.end(); ++it) - { - Ptr ptr = getCurrentPtr(&*it); - if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0) - { - checkItem(ptr); - } - } - for (CellRefList::List::iterator it (mArmors.mList.begin()); it!=mArmors.mList.end(); ++it) - { - Ptr ptr = getCurrentPtr(&*it); - if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0) - { - checkItem(ptr); - } - } - for (CellRefList::List::iterator it (mClothes.mList.begin()); it!=mClothes.mList.end(); ++it) + const auto update = [this](auto& list) { - Ptr ptr = getCurrentPtr(&*it); - if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0) + for (auto & item : list) { - checkItem(ptr); - } - } - for (CellRefList::List::iterator it (mBooks.mList.begin()); it!=mBooks.mList.end(); ++it) - { - Ptr ptr = getCurrentPtr(&*it); - if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0) - { - checkItem(ptr); + Ptr ptr = getCurrentPtr(&item); + if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0) + { + checkItem(ptr); + } } - } + }; + + update(mWeapons.mList); + update(mArmors.mList); + update(mClothes.mList); + update(mBooks.mList); } void MWWorld::CellStore::checkItem(Ptr ptr) diff --git a/apps/openmw/mwworld/esmstore.cpp b/apps/openmw/mwworld/esmstore.cpp index a69d74b05..b1885edaf 100644 --- a/apps/openmw/mwworld/esmstore.cpp +++ b/apps/openmw/mwworld/esmstore.cpp @@ -1,5 +1,6 @@ #include "esmstore.hpp" +#include #include #include @@ -8,12 +9,23 @@ #include #include #include +#include #include "../mwmechanics/spelllist.hpp" namespace { - void readRefs(const ESM::Cell& cell, std::map& refs, std::vector& readers) + struct Ref + { + ESM::RefNum mRefNum; + std::size_t mRefID; + + Ref(ESM::RefNum refNum, std::size_t refID) : mRefNum(refNum), mRefID(refID) {} + }; + + constexpr std::size_t deletedRefID = std::numeric_limits::max(); + + void readRefs(const ESM::Cell& cell, std::vector& refs, std::vector& refIDs, std::vector& readers) { for (size_t i = 0; i < cell.mContextList.size(); i++) { @@ -27,24 +39,22 @@ namespace while(cell.getNextRef(readers[index], ref, deleted)) { if(deleted) - refs.erase(ref.mRefNum); + refs.emplace_back(ref.mRefNum, deletedRefID); else if (std::find(cell.mMovedRefs.begin(), cell.mMovedRefs.end(), ref.mRefNum) == cell.mMovedRefs.end()) { - Misc::StringUtils::lowerCaseInPlace(ref.mRefID); - refs[ref.mRefNum] = ref.mRefID; + refs.emplace_back(ref.mRefNum, refIDs.size()); + refIDs.push_back(std::move(ref.mRefID)); } } } - for(const auto& it : cell.mLeasedRefs) + for(const auto& [value, deleted] : cell.mLeasedRefs) { - bool deleted = it.second; if(deleted) - refs.erase(it.first.mRefNum); + refs.emplace_back(value.mRefNum, deletedRefID); else { - ESM::CellRef ref = it.first; - Misc::StringUtils::lowerCaseInPlace(ref.mRefID); - refs[ref.mRefNum] = ref.mRefID; + refs.emplace_back(value.mRefNum, refIDs.size()); + refIDs.push_back(value.mRefID); } } } @@ -63,7 +73,7 @@ namespace // We will replace invalid entries by fixed ones std::vector npcsToReplace; - for (auto npcIter : npcs) + for (const auto& npcIter : npcs) { ESM::NPC npc = npcIter.second; bool changed = false; @@ -248,14 +258,26 @@ void ESMStore::countRecords() { if(!mRefCount.empty()) return; - std::map refs; + std::vector refs; + std::vector refIDs; std::vector readers; for(auto it = mCells.intBegin(); it != mCells.intEnd(); it++) - readRefs(*it, refs, readers); + readRefs(*it, refs, refIDs, readers); for(auto it = mCells.extBegin(); it != mCells.extEnd(); it++) - readRefs(*it, refs, readers); - for(const auto& pair : refs) - mRefCount[pair.second]++; + readRefs(*it, refs, refIDs, readers); + const auto lessByRefNum = [] (const Ref& l, const Ref& r) { return l.mRefNum < r.mRefNum; }; + std::stable_sort(refs.begin(), refs.end(), lessByRefNum); + const auto equalByRefNum = [] (const Ref& l, const Ref& r) { return l.mRefNum == r.mRefNum; }; + const auto incrementRefCount = [&] (const Ref& value) + { + if (value.mRefID != deletedRefID) + { + std::string& refId = refIDs[value.mRefID]; + Misc::StringUtils::lowerCaseInPlace(refId); + ++mRefCount[std::move(refId)]; + } + }; + Misc::forEachUnique(refs.rbegin(), refs.rend(), equalByRefNum, incrementRefCount); } int ESMStore::getRefCount(const std::string& id) const diff --git a/apps/openmw/mwworld/esmstore.hpp b/apps/openmw/mwworld/esmstore.hpp index 2f68f8de2..55528aed2 100644 --- a/apps/openmw/mwworld/esmstore.hpp +++ b/apps/openmw/mwworld/esmstore.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include "store.hpp" @@ -76,7 +77,7 @@ namespace MWWorld std::map mIds; std::map mStaticIds; - std::map mRefCount; + std::unordered_map mRefCount; std::map mStores; diff --git a/apps/openmw/mwworld/failedaction.cpp b/apps/openmw/mwworld/failedaction.cpp index 45df75a32..ec8314712 100644 --- a/apps/openmw/mwworld/failedaction.cpp +++ b/apps/openmw/mwworld/failedaction.cpp @@ -1,5 +1,4 @@ #include "failedaction.hpp" -#include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" diff --git a/apps/openmw/mwworld/globals.hpp b/apps/openmw/mwworld/globals.hpp index 11793a9eb..9ad3f7475 100644 --- a/apps/openmw/mwworld/globals.hpp +++ b/apps/openmw/mwworld/globals.hpp @@ -7,7 +7,6 @@ #include -#include #include namespace ESM diff --git a/apps/openmw/mwworld/projectilemanager.cpp b/apps/openmw/mwworld/projectilemanager.cpp index b463fc09e..53b1b364b 100644 --- a/apps/openmw/mwworld/projectilemanager.cpp +++ b/apps/openmw/mwworld/projectilemanager.cpp @@ -44,7 +44,6 @@ #include "../mwsound/sound.hpp" -#include "../mwphysics/collisiontype.hpp" #include "../mwphysics/physicssystem.hpp" #include "../mwphysics/projectile.hpp" diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 27bdaf3b9..4172893bc 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -37,8 +37,6 @@ #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/windowmanager.hpp" -#include "../mwmechanics/actorutil.hpp" - #include "../mwrender/renderingmanager.hpp" #include "../mwrender/landmanager.hpp" @@ -677,6 +675,8 @@ namespace MWWorld if (changeEvent) mCellChanged = true; + + mNavigator.wait(*loadingListener, DetourNavigator::WaitConditionType::requiredTilesPresent); } void Scene::testExteriorCells() @@ -919,6 +919,8 @@ namespace MWWorld MWBase::Environment::get().getWindowManager()->fadeScreenIn(0.5); MWBase::Environment::get().getWindowManager()->changeCell(mCurrentCell); + + mNavigator.wait(*loadingListener, DetourNavigator::WaitConditionType::requiredTilesPresent); } void Scene::changeToExteriorCell (const ESM::Position& position, bool adjustPlayerPos, bool changeEvent) diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index 0eca2804f..22da74811 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -58,10 +58,7 @@ namespace MWWorld record.load(esm, isDeleted); - // Try to overwrite existing record - std::pair ret = mStatic.insert(std::make_pair(record.mIndex, record)); - if (!ret.second) - ret.first->second = record; + mStatic.insert_or_assign(record.mIndex, record); } template int IndexedStore::getSize() const @@ -181,11 +178,9 @@ namespace MWWorld record.load(esm, isDeleted); Misc::StringUtils::lowerCaseInPlace(record.mId); - std::pair inserted = mStatic.insert(std::make_pair(record.mId, record)); + std::pair inserted = mStatic.insert_or_assign(record.mId, record); if (inserted.second) mShared.push_back(&inserted.first->second); - else - inserted.first->second = record; return RecordId(record.mId, isDeleted); } @@ -235,28 +230,20 @@ namespace MWWorld if(it == mStatic.end()) return nullptr; } - std::pair result = - mDynamic.insert(std::pair(id, item)); + std::pair result = mDynamic.insert_or_assign(id, item); T *ptr = &result.first->second; - if (result.second) { + if (result.second) mShared.push_back(ptr); - } else { - *ptr = item; - } return ptr; } template T *Store::insertStatic(const T &item) { std::string id = Misc::StringUtils::lowerCase(item.mId); - std::pair result = - mStatic.insert(std::pair(id, item)); + std::pair result = mStatic.insert_or_assign(id, item); T *ptr = &result.first->second; - if (result.second) { + if (result.second) mShared.push_back(ptr); - } else { - *ptr = item; - } return ptr; } template @@ -531,9 +518,9 @@ namespace MWWorld // But there may be duplicates here! ESM::CellRefTracker::iterator iter = std::find_if(cellAlt->mLeasedRefs.begin(), cellAlt->mLeasedRefs.end(), ESM::CellRefTrackerPredicate(ref.mRefNum)); if (iter == cellAlt->mLeasedRefs.end()) - cellAlt->mLeasedRefs.push_back(std::make_pair(ref, deleted)); + cellAlt->mLeasedRefs.emplace_back(std::move(ref), deleted); else - *iter = std::make_pair(ref, deleted); + *iter = std::make_pair(std::move(ref), deleted); } } const ESM::Cell *Store::search(const std::string &id) const @@ -634,20 +621,15 @@ namespace MWWorld void Store::setUp() { - typedef DynamicExt::iterator ExtIterator; - typedef std::map::iterator IntIterator; - mSharedInt.clear(); mSharedInt.reserve(mInt.size()); - for (IntIterator it = mInt.begin(); it != mInt.end(); ++it) { - mSharedInt.push_back(&(it->second)); - } + for (auto & [_, cell] : mInt) + mSharedInt.push_back(&cell); mSharedExt.clear(); mSharedExt.reserve(mExt.size()); - for (ExtIterator it = mExt.begin(); it != mExt.end(); ++it) { - mSharedExt.push_back(&(it->second)); - } + for (auto & [_, cell] : mExt) + mSharedExt.push_back(&cell); } RecordId Store::load(ESM::ESMReader &esm) { @@ -1052,7 +1034,7 @@ namespace MWWorld if (index >= mStatic.size()) { return nullptr; } - return &mStatic.at(index); + return &mStatic[index]; } const ESM::Attribute *Store::find(size_t index) const @@ -1099,18 +1081,13 @@ namespace MWWorld { // DialInfos marked as deleted are kept during the loading phase, so that the linked list // structure is kept intact for inserting further INFOs. Delete them now that loading is done. - for (Static::iterator it = mStatic.begin(); it != mStatic.end(); ++it) - { - ESM::Dialogue& dial = it->second; + for (auto & [_, dial] : mStatic) dial.clearDeletedInfos(); - } mShared.clear(); mShared.reserve(mStatic.size()); - std::map::iterator it = mStatic.begin(); - for (; it != mStatic.end(); ++it) { - mShared.push_back(&(it->second)); - } + for (auto & [_, dial] : mStatic) + mShared.push_back(&dial); } template <> diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index ab803246a..30f2a2645 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -4,7 +4,6 @@ #include #include -#include #include "../mwbase/world.hpp" diff --git a/apps/openmw_test_suite/detournavigator/navigator.cpp b/apps/openmw_test_suite/detournavigator/navigator.cpp index 619276588..bf8b4a002 100644 --- a/apps/openmw_test_suite/detournavigator/navigator.cpp +++ b/apps/openmw_test_suite/detournavigator/navigator.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -11,6 +12,7 @@ #include #include +#include #include MATCHER_P3(Vec3fEq, x, y, z, "") @@ -35,6 +37,7 @@ namespace std::back_insert_iterator> mOut; float mStepSize; AreaCosts mAreaCosts; + Loading::Listener mListener; DetourNavigatorNavigatorTest() : mPlayerPosition(0, 0, 0) @@ -64,6 +67,7 @@ namespace mSettings.mRegionMergeSize = 20; mSettings.mRegionMinSize = 8; mSettings.mTileSize = 64; + mSettings.mWaitUntilMinDistanceToPlayer = std::numeric_limits::max(); mSettings.mAsyncNavMeshUpdaterThreads = 1; mSettings.mMaxNavMeshTilesCacheSize = 1024 * 1024; mSettings.mMaxPolygonPathSize = 1024; @@ -124,7 +128,7 @@ namespace mNavigator->addAgent(mAgentHalfExtents); mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener, WaitConditionType::requiredTilesPresent); EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success); @@ -174,7 +178,7 @@ namespace mNavigator->addAgent(mAgentHalfExtents); mNavigator->addObject(ObjectId(&heightfieldShape), heightfieldShape, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success); @@ -206,7 +210,7 @@ namespace mNavigator->addObject(ObjectId(&compoundShape), compoundShape, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); mPath.clear(); mOut = std::back_inserter(mPath); @@ -259,7 +263,7 @@ namespace mNavigator->addObject(ObjectId(&heightfieldShape), heightfieldShape, btTransform::getIdentity()); mNavigator->addObject(ObjectId(&compoundShape), compoundShape, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success); @@ -293,7 +297,7 @@ namespace mNavigator->updateObject(ObjectId(&compoundShape), compoundShape, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); mPath.clear(); mOut = std::back_inserter(mPath); @@ -352,7 +356,7 @@ namespace mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity()); mNavigator->addObject(ObjectId(&shape2), shape2, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success); @@ -408,7 +412,7 @@ namespace mNavigator->addAgent(mAgentHalfExtents); mNavigator->addObject(ObjectId(&shape), ObjectShapes {shape, &shapeAvoid}, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success); @@ -456,7 +460,7 @@ namespace mNavigator->addWater(osg::Vec2i(0, 0), 128 * 4, 300, btTransform::getIdentity()); mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); mStart.x() = 0; mStart.z() = 300; @@ -504,7 +508,7 @@ namespace mNavigator->addWater(osg::Vec2i(0, 0), 128 * 4, -25, btTransform::getIdentity()); mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); mStart.x() = 0; mEnd.x() = 0; @@ -551,7 +555,7 @@ namespace mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity()); mNavigator->addWater(osg::Vec2i(0, 0), std::numeric_limits::max(), -25, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); mStart.x() = 0; mEnd.x() = 0; @@ -598,7 +602,7 @@ namespace mNavigator->addWater(osg::Vec2i(0, 0), 128 * 4, -25, btTransform::getIdentity()); mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); mStart.x() = 0; mEnd.x() = 0; @@ -642,15 +646,15 @@ namespace mNavigator->addAgent(mAgentHalfExtents); mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->removeObject(ObjectId(&shape)); mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success); @@ -696,7 +700,7 @@ namespace mNavigator->addAgent(mAgentHalfExtents); mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); Misc::Rng::init(42); @@ -745,7 +749,7 @@ namespace } mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), Status::Success); @@ -788,7 +792,7 @@ namespace mNavigator->addObject(ObjectId(&shapes[i]), shapes[i], transform); } mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); const auto start = std::chrono::steady_clock::now(); for (std::size_t i = 0; i < shapes.size(); ++i) @@ -797,7 +801,7 @@ namespace mNavigator->updateObject(ObjectId(&shapes[i]), shapes[i], transform); } mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); for (std::size_t i = 0; i < shapes.size(); ++i) { @@ -805,7 +809,7 @@ namespace mNavigator->updateObject(ObjectId(&shapes[i]), shapes[i], transform); } mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); const auto duration = std::chrono::steady_clock::now() - start; @@ -828,7 +832,7 @@ namespace mNavigator->addAgent(mAgentHalfExtents); mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity()); mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); const auto result = mNavigator->raycast(mAgentHalfExtents, mStart, mEnd, Flag_walk); @@ -859,7 +863,7 @@ namespace mNavigator->addObject(ObjectId(&boderBoxShape), boderBoxShape, btTransform(btMatrix3x3::getIdentity(), oscillatingBoxShapePosition + btVector3(0, 0, 200))); mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); const auto navMeshes = mNavigator->getNavMeshes(); ASSERT_EQ(navMeshes.size(), 1); @@ -875,7 +879,7 @@ namespace oscillatingBoxShapePosition); mNavigator->updateObject(ObjectId(&oscillatingBoxShape), oscillatingBoxShape, transform); mNavigator->update(mPlayerPosition); - mNavigator->wait(); + mNavigator->wait(mListener, WaitConditionType::allJobsDone); } ASSERT_EQ(navMeshes.size(), 1); diff --git a/apps/openmw_test_suite/detournavigator/recastmeshbuilder.cpp b/apps/openmw_test_suite/detournavigator/recastmeshbuilder.cpp index 5f43f4800..b4b664569 100644 --- a/apps/openmw_test_suite/detournavigator/recastmeshbuilder.cpp +++ b/apps/openmw_test_suite/detournavigator/recastmeshbuilder.cpp @@ -14,6 +14,8 @@ #include #include +#include + namespace DetourNavigator { static inline bool operator ==(const RecastMesh::Water& lhs, const RecastMesh::Water& rhs) diff --git a/appveyor.yml b/appveyor.yml index ed6f727be..e2c13ed94 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -50,7 +50,7 @@ install: before_build: - cmd: git submodule update --init --recursive - - cmd: sh %APPVEYOR_BUILD_FOLDER%\CI\before_script.msvc.sh -c %configuration% -p %PLATFORM% -v %msvc% -V -i %APPVEYOR_BUILD_FOLDER%\install -D + - cmd: sh %APPVEYOR_BUILD_FOLDER%\CI\before_script.msvc.sh -c %configuration% -p %PLATFORM% -v %msvc% -V -i %APPVEYOR_BUILD_FOLDER%\install build_script: - cmd: if %PLATFORM%==Win32 set build=MSVC%msvc%_32 diff --git a/cmake/OpenMWMacros.cmake b/cmake/OpenMWMacros.cmake index f9a625754..d70b6cb9d 100644 --- a/cmake/OpenMWMacros.cmake +++ b/cmake/OpenMWMacros.cmake @@ -202,6 +202,18 @@ macro (configure_resource_file source_path destination_dir_base dest_path_relati endif (multi_config) endmacro (configure_resource_file) +macro (pack_resource_file source_path destination_dir_base dest_path_relative) + get_generator_is_multi_config(multi_config) + if (multi_config) + foreach(cfgtype ${CMAKE_CONFIGURATION_TYPES}) + execute_process(COMMAND ${CMAKE_COMMAND} "-DINPUT_FILE=${source_path}" "-DOUTPUT_FILE=${destination_dir_base}/${cfgtype}/${dest_path_relative}" -P "${CMAKE_SOURCE_DIR}/cmake/base64.cmake") + endforeach(cfgtype) + else (multi_config) + execute_process(COMMAND ${CMAKE_COMMAND} "-DINPUT_FILE=${source_path}" "-DOUTPUT_FILE=${destination_dir_base}/${dest_path_relative}" -P "${CMAKE_SOURCE_DIR}/cmake/base64.cmake") + endif (multi_config) + set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${source_path}") +endmacro (pack_resource_file) + macro (copy_all_resource_files source_dir destination_dir_base destination_dir_relative files) foreach (f ${files}) get_filename_component(filename ${f} NAME) diff --git a/cmake/base64.cmake b/cmake/base64.cmake new file mode 100644 index 000000000..7931758bb --- /dev/null +++ b/cmake/base64.cmake @@ -0,0 +1,74 @@ +# math(EXPR "...") can't parse hex until 3.13 +cmake_minimum_required(VERSION 3.13) + +if (NOT DEFINED INPUT_FILE) + message(STATUS "Usage: cmake -DINPUT_FILE=\"infile.ext\" -DOUTPUT_FILE=\"out.txt\" -P base64.cmake") + message(FATAL_ERROR "INPUT_FILE not specified") +endif() + +if (NOT DEFINED OUTPUT_FILE) + message(STATUS "Usage: cmake -DINPUT_FILE=\"infile.ext\" -DOUTPUT_FILE=\"out.txt\" -P base64.cmake") + message(FATAL_ERROR "OUTPUT_FILE not specified") +endif() + +if (NOT EXISTS ${INPUT_FILE}) + message(FATAL_ERROR "INPUT_FILE ${INPUT_FILE} does not exist") +endif() + +set(lut "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/") + +file(READ "${INPUT_FILE}" hexContent HEX) + +set(base64Content "") +while(TRUE) + string(LENGTH "${hexContent}" tailLength) + if (tailLength LESS 1) + break() + endif() + + string(SUBSTRING "${hexContent}" 0 6 head) + # base64 works on three-byte chunks. Pad. + string(LENGTH "${head}" headLength) + if (headLength LESS 6) + set(hexContent "") + math(EXPR padSize "6 - ${headLength}") + set(pad "") + foreach(i RANGE 1 ${padSize}) + string(APPEND pad "0") + endforeach() + string(APPEND head "${pad}") + else() + string(SUBSTRING "${hexContent}" 6 -1 hexContent) + set(padSize 0) + endif() + + # get six-bit slices + math(EXPR first "0x${head} >> 18") + math(EXPR second "(0x${head} & 0x3F000) >> 12") + math(EXPR third "(0x${head} & 0xFC0) >> 6") + math(EXPR fourth "0x${head} & 0x3F") + + # first two characters are always needed to represent the first byte + string(SUBSTRING "${lut}" ${first} 1 char) + string(APPEND base64Content "${char}") + string(SUBSTRING "${lut}" ${second} 1 char) + string(APPEND base64Content "${char}") + + # if there's no second byte, pad with = + if (NOT padSize EQUAL 4) + string(SUBSTRING "${lut}" ${third} 1 char) + string(APPEND base64Content "${char}") + else() + string(APPEND base64Content "=") + endif() + + # if there's no third byte, pad with = + if (padSize EQUAL 0) + string(SUBSTRING "${lut}" ${fourth} 1 char) + string(APPEND base64Content "${char}") + else() + string(APPEND base64Content "=") + endif() +endwhile() + +file(WRITE "${OUTPUT_FILE}" "${base64Content}") \ No newline at end of file diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index b244985d5..cfa04de03 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -256,7 +256,6 @@ if (BUILD_OPENMW OR BUILD_OPENCS) navmeshmanager navigatorimpl asyncnavmeshupdater - chunkytrimesh recastmesh tilecachedrecastmeshmanager recastmeshobject @@ -340,12 +339,15 @@ target_link_libraries(components # Start of tes3mp change (major) # -# Don't require RecastNavigation when building the server +# Don't require RecastNavigation or Base64 when building the server if (BUILD_OPENMW OR BUILD_OPENCS) target_link_libraries(components RecastNavigation::DebugUtils RecastNavigation::Detour - RecastNavigation::Recast) + RecastNavigation::Recast + + Base64 + ) endif () # End of tes3mp change (major) diff --git a/components/bsa/bsa_file.cpp b/components/bsa/bsa_file.cpp index ec455ea1f..3375882f5 100644 --- a/components/bsa/bsa_file.cpp +++ b/components/bsa/bsa_file.cpp @@ -43,7 +43,7 @@ void BSAFile::fail(const std::string &msg) BSAFile::Hash getHash(const std::string& name) { BSAFile::Hash hash; - unsigned l = (name.size() >> 1); + unsigned l = (static_cast(name.size()) >> 1); unsigned sum, off, temp, i, n; for (sum = off = i = 0; i < l; i++) { @@ -163,7 +163,7 @@ void BSAFile::readHeader() { FileStruct &fs = mFiles[i]; fs.fileSize = offsets[i*2]; - fs.offset = offsets[i*2+1] + fileDataOffset; + fs.offset = static_cast(offsets[i*2+1] + fileDataOffset); auto namesOffset = offsets[2*filenum+i]; fs.setNameInfos(namesOffset, &mStringBuf); fs.hash = hashes[i]; @@ -203,11 +203,11 @@ void Bsa::BSAFile::writeHeader() uint32_t head[3]; head[0] = 0x100; auto fileDataOffset = mFiles.empty() ? 12 : mFiles.front().offset; - head[1] = fileDataOffset - 12 - 8*mFiles.size(); + head[1] = static_cast(fileDataOffset - 12 - 8*mFiles.size()); output.seekp(0, std::ios_base::end); - head[2] = mFiles.size(); + head[2] = static_cast(mFiles.size()); output.seekp(0); output.write(reinterpret_cast(head), 12); @@ -239,9 +239,9 @@ int BSAFile::getIndex(const char *str) const if(it == mLookup.end()) return -1; - int res = it->second; - assert(res >= 0 && (size_t)res < mFiles.size()); - return res; + size_t res = it->second; + assert(res < mFiles.size()); + return static_cast(res); } /// Open an archive file. @@ -295,12 +295,12 @@ void Bsa::BSAFile::addFile(const std::string& filename, std::istream& file) FileStruct newFile; file.seekg(0, std::ios::end); - newFile.fileSize = file.tellg(); + newFile.fileSize = static_cast(file.tellg()); newFile.setNameInfos(mStringBuf.size(), &mStringBuf); newFile.hash = getHash(filename); if(mFiles.empty()) - newFile.offset = newStartOfDataBuffer; + newFile.offset = static_cast(newStartOfDataBuffer); else { std::vector buffer; @@ -312,7 +312,7 @@ void Bsa::BSAFile::addFile(const std::string& filename, std::istream& file) stream.read(buffer.data(), firstFile.fileSize); stream.seekp(0, std::ios::end); - firstFile.offset = stream.tellp(); + firstFile.offset = static_cast(stream.tellp()); stream.write(buffer.data(), firstFile.fileSize); @@ -320,7 +320,7 @@ void Bsa::BSAFile::addFile(const std::string& filename, std::istream& file) std::rotate(mFiles.begin(), mFiles.begin() + 1, mFiles.end()); } stream.seekp(0, std::ios::end); - newFile.offset = stream.tellp(); + newFile.offset = static_cast(stream.tellp()); } mStringBuf.insert(mStringBuf.end(), filename.begin(), filename.end()); diff --git a/components/bsa/bsa_file.hpp b/components/bsa/bsa_file.hpp index fa6e5fc1c..f9b4d4fa3 100644 --- a/components/bsa/bsa_file.hpp +++ b/components/bsa/bsa_file.hpp @@ -58,7 +58,7 @@ public: void setNameInfos(size_t index, std::vector* stringBuf ) { - namesOffset = index; + namesOffset = static_cast(index); namesBuffer = stringBuf; } @@ -102,7 +102,7 @@ protected: the files[] vector above. The iltstr ensures that file name checks are case insensitive. */ - typedef std::map Lookup; + typedef std::map Lookup; Lookup mLookup; /// Error handling diff --git a/components/compiler/controlparser.cpp b/components/compiler/controlparser.cpp index ec69fffa2..a5a68edbd 100644 --- a/components/compiler/controlparser.cpp +++ b/components/compiler/controlparser.cpp @@ -39,14 +39,14 @@ namespace Compiler Codes block; if (iter!=mIfCode.rbegin()) - Generator::jump (iter->second, codes.size()+1); + Generator::jump (iter->second, static_cast(codes.size()+1)); if (!iter->first.empty()) { // if or elseif std::copy (iter->first.begin(), iter->first.end(), std::back_inserter (block)); - Generator::jumpOnZero (block, iter->second.size()+1); + Generator::jumpOnZero (block, static_cast(iter->second.size()+1)); } std::copy (iter->second.begin(), iter->second.end(), @@ -113,7 +113,7 @@ namespace Compiler Codes skip; - Generator::jumpOnZero (skip, mCodeBlock.size()+loop.size()+1); + Generator::jumpOnZero (skip, static_cast (mCodeBlock.size()+loop.size()+1)); std::copy (skip.begin(), skip.end(), std::back_inserter (mCode)); diff --git a/components/compiler/literals.cpp b/components/compiler/literals.cpp index 774ca4ca7..a40ff2a02 100644 --- a/components/compiler/literals.cpp +++ b/components/compiler/literals.cpp @@ -6,12 +6,12 @@ namespace Compiler { int Literals::getIntegerSize() const { - return mIntegers.size() * sizeof (Interpreter::Type_Integer); + return static_cast(mIntegers.size() * sizeof (Interpreter::Type_Integer)); } int Literals::getFloatSize() const { - return mFloats.size() * sizeof (Interpreter::Type_Float); + return static_cast(mFloats.size() * sizeof (Interpreter::Type_Float)); } int Literals::getStringSize() const @@ -41,11 +41,11 @@ namespace Compiler code.resize (size+stringBlockSize/4); - int offset = 0; + size_t offset = 0; for (const auto & mString : mStrings) { - int stringSize = mString.size()+1; + size_t stringSize = mString.size()+1; std::copy (mString.c_str(), mString.c_str()+stringSize, reinterpret_cast (&code[size]) + offset); diff --git a/components/compiler/locals.cpp b/components/compiler/locals.cpp index 9b233b8f5..f31acb1a8 100644 --- a/components/compiler/locals.cpp +++ b/components/compiler/locals.cpp @@ -30,7 +30,7 @@ namespace Compiler if (iter==collection.end()) return -1; - return iter-collection.begin(); + return static_cast(iter-collection.begin()); } bool Locals::search (char type, const std::string& name) const diff --git a/components/compiler/scanner.hpp b/components/compiler/scanner.hpp index 9c7bd656e..fd7de5b6b 100644 --- a/components/compiler/scanner.hpp +++ b/components/compiler/scanner.hpp @@ -103,7 +103,7 @@ namespace Compiler { blank(); - char ch = in.peek(); + char ch = static_cast(in.peek()); if (!in.good()) return false; @@ -130,7 +130,7 @@ namespace Compiler { std::streampos p_orig = in.tellg(); - char ch = in.peek(); + char ch = static_cast(in.peek()); if (!in.good()) return false; @@ -156,7 +156,7 @@ namespace Compiler void blank() { - std::fill(mData, mData + sizeof(mData), 0); + std::fill(std::begin(mData), std::end(mData), '\0'); mLength = -1; } diff --git a/components/crashcatcher/windows_crashcatcher.cpp b/components/crashcatcher/windows_crashcatcher.cpp index 185eee6aa..39ac86d7b 100644 --- a/components/crashcatcher/windows_crashcatcher.cpp +++ b/components/crashcatcher/windows_crashcatcher.cpp @@ -130,12 +130,12 @@ namespace Crash DWORD copied = 0; do { executablePath.resize(executablePath.size() + MAX_PATH); - copied = GetModuleFileNameW(nullptr, executablePath.data(), executablePath.size()); + copied = GetModuleFileNameW(nullptr, executablePath.data(), static_cast(executablePath.size())); } while (copied >= executablePath.size()); executablePath.resize(copied); memset(mShm->mStartup.mLogFilePath, 0, sizeof(mShm->mStartup.mLogFilePath)); - int length = crashLogPath.length(); + size_t length = crashLogPath.length(); if (length >= MAX_LONG_PATH) length = MAX_LONG_PATH - 1; strncpy(mShm->mStartup.mLogFilePath, crashLogPath.c_str(), length); mShm->mStartup.mLogFilePath[length] = '\0'; diff --git a/components/debug/gldebug.cpp b/components/debug/gldebug.cpp index 37829a8c1..ccf49edd4 100644 --- a/components/debug/gldebug.cpp +++ b/components/debug/gldebug.cpp @@ -179,7 +179,7 @@ namespace Debug if (str.length() == 0) return true; - return str.find("OFF") == std::string::npos && str.find("0") == std::string::npos && str.find("NO") == std::string::npos; + return str.find("OFF") == std::string::npos && str.find('0') == std::string::npos && str.find("NO") == std::string::npos; } DebugGroup::DebugGroup(const std::string & message, GLuint id) diff --git a/components/detournavigator/asyncnavmeshupdater.cpp b/components/detournavigator/asyncnavmeshupdater.cpp index 7248850a6..11de60745 100644 --- a/components/detournavigator/asyncnavmeshupdater.cpp +++ b/components/detournavigator/asyncnavmeshupdater.cpp @@ -6,10 +6,13 @@ #include #include +#include #include +#include #include +#include namespace { @@ -20,6 +23,18 @@ namespace { return std::abs(lhs.x() - rhs.x()) + std::abs(lhs.y() - rhs.y()); } + + int getMinDistanceTo(const TilePosition& position, int maxDistance, + const std::map>& tilesPerHalfExtents, + const std::set>& presentTiles) + { + int result = maxDistance; + for (const auto& [halfExtents, tiles] : tilesPerHalfExtents) + for (const TilePosition& tile : tiles) + if (presentTiles.find(std::make_tuple(halfExtents, tile)) == presentTiles.end()) + result = std::min(result, getManhattanDistance(position, tile)); + return result; + } } namespace DetourNavigator @@ -77,13 +92,22 @@ namespace DetourNavigator const SharedNavMeshCacheItem& navMeshCacheItem, const TilePosition& playerTile, const std::map& changedTiles) { - *mPlayerTile.lock() = playerTile; + bool playerTileChanged = false; + { + auto locked = mPlayerTile.lock(); + playerTileChanged = *locked != playerTile; + *locked = playerTile; + } - if (changedTiles.empty()) + if (!playerTileChanged && changedTiles.empty()) return; const std::lock_guard lock(mMutex); + if (playerTileChanged) + for (auto& job : mJobs) + job.mDistanceToPlayer = getManhattanDistance(job.mChangedTile, playerTile); + for (const auto& changedTile : changedTiles) { if (mPushed[agentHalfExtents].insert(changedTile.first).second) @@ -101,21 +125,100 @@ namespace DetourNavigator ? mLastUpdates[job.mAgentHalfExtents][job.mChangedTile] + mSettings.get().mMinUpdateInterval : std::chrono::steady_clock::time_point(); - mJobs.push(std::move(job)); + if (playerTileChanged) + { + mJobs.push_back(std::move(job)); + } + else + { + const auto it = std::upper_bound(mJobs.begin(), mJobs.end(), job); + mJobs.insert(it, std::move(job)); + } } } + if (playerTileChanged) + std::sort(mJobs.begin(), mJobs.end()); + Log(Debug::Debug) << "Posted " << mJobs.size() << " navigator jobs"; if (!mJobs.empty()) mHasJob.notify_all(); } - void AsyncNavMeshUpdater::wait() + void AsyncNavMeshUpdater::wait(Loading::Listener& listener, WaitConditionType waitConditionType) + { + if (mSettings.get().mWaitUntilMinDistanceToPlayer == 0) + return; + listener.setLabel("Building navigation mesh"); + const std::size_t initialJobsLeft = getTotalJobs(); + std::size_t maxProgress = initialJobsLeft + mThreads.size(); + listener.setProgressRange(maxProgress); + switch (waitConditionType) + { + case WaitConditionType::requiredTilesPresent: + { + const int minDistanceToPlayer = waitUntilJobsDoneForNotPresentTiles(initialJobsLeft, maxProgress, listener); + if (minDistanceToPlayer < mSettings.get().mWaitUntilMinDistanceToPlayer) + { + mProcessingTiles.wait(mProcessed, [] (const auto& v) { return v.empty(); }); + listener.setProgress(maxProgress); + } + break; + } + case WaitConditionType::allJobsDone: + waitUntilAllJobsDone(); + listener.setProgress(maxProgress); + break; + } + } + + int AsyncNavMeshUpdater::waitUntilJobsDoneForNotPresentTiles(const std::size_t initialJobsLeft, std::size_t& maxProgress, Loading::Listener& listener) + { + std::size_t prevJobsLeft = initialJobsLeft; + std::size_t jobsDone = 0; + std::size_t jobsLeft = 0; + const int maxDistanceToPlayer = mSettings.get().mWaitUntilMinDistanceToPlayer; + const TilePosition playerPosition = *mPlayerTile.lockConst(); + int minDistanceToPlayer = 0; + const auto isDone = [&] + { + jobsLeft = mJobs.size() + getTotalThreadJobsUnsafe(); + if (jobsLeft == 0) + { + minDistanceToPlayer = 0; + return true; + } + minDistanceToPlayer = getMinDistanceTo(playerPosition, maxDistanceToPlayer, mPushed, mPresentTiles); + for (const auto& [threadId, queue] : mThreadsQueues) + minDistanceToPlayer = getMinDistanceTo(playerPosition, minDistanceToPlayer, queue.mPushed, mPresentTiles); + return minDistanceToPlayer >= maxDistanceToPlayer; + }; + std::unique_lock lock(mMutex); + while (!mDone.wait_for(lock, std::chrono::milliseconds(250), isDone)) + { + if (maxProgress < jobsLeft) + { + maxProgress = jobsLeft + mThreads.size(); + listener.setProgressRange(maxProgress); + listener.setProgress(jobsDone); + } + else if (jobsLeft < prevJobsLeft) + { + const std::size_t newJobsDone = prevJobsLeft - jobsLeft; + jobsDone += newJobsDone; + prevJobsLeft = jobsLeft; + listener.increaseProgress(newJobsDone); + } + } + return minDistanceToPlayer; + } + + void AsyncNavMeshUpdater::waitUntilAllJobsDone() { { std::unique_lock lock(mMutex); - mDone.wait(lock, [&] { return mJobs.empty() && getTotalThreadJobsUnsafe() == 0; }); + mDone.wait(lock, [this] { return mJobs.size() + getTotalThreadJobsUnsafe() == 0; }); } mProcessingTiles.wait(mProcessed, [] (const auto& v) { return v.empty(); }); } @@ -194,6 +297,17 @@ namespace DetourNavigator navMeshVersion); } + if (status == UpdateNavMeshStatus::removed || status == UpdateNavMeshStatus::lost) + { + const std::scoped_lock lock(mMutex); + mPresentTiles.erase(std::make_tuple(job.mAgentHalfExtents, job.mChangedTile)); + } + else if (isSuccess(status) && status != UpdateNavMeshStatus::ignored) + { + const std::scoped_lock lock(mMutex); + mPresentTiles.insert(std::make_tuple(job.mAgentHalfExtents, job.mChangedTile)); + } + const auto finish = std::chrono::steady_clock::now(); writeDebugFiles(job, recastMesh.get()); @@ -224,7 +338,7 @@ namespace DetourNavigator while (true) { const auto hasJob = [&] { - return (!mJobs.empty() && mJobs.top().mProcessTime <= std::chrono::steady_clock::now()) + return (!mJobs.empty() && mJobs.front().mProcessTime <= std::chrono::steady_clock::now()) || !threadQueue.mJobs.empty(); }; @@ -259,11 +373,11 @@ namespace DetourNavigator { const auto now = std::chrono::steady_clock::now(); - if (jobs.top().mProcessTime > now) + if (jobs.front().mProcessTime > now) return {}; - Job job = jobs.top(); - jobs.pop(); + Job job = jobs.front(); + jobs.pop_front(); if (changeLastUpdate && job.mChangeType == ChangeType::update) mLastUpdates[job.mAgentHalfExtents][job.mChangedTile] = now; @@ -317,7 +431,7 @@ namespace DetourNavigator if (mPushed[job.mAgentHalfExtents].insert(job.mChangedTile).second) { ++job.mTryNumber; - mJobs.push(std::move(job)); + mJobs.push_back(std::move(job)); mHasJob.notify_all(); } } @@ -326,7 +440,7 @@ namespace DetourNavigator { if (queue.mPushed[job.mAgentHalfExtents].insert(job.mChangedTile).second) { - queue.mJobs.push(std::move(job)); + queue.mJobs.push_back(std::move(job)); mHasJob.notify_all(); } } @@ -381,6 +495,12 @@ namespace DetourNavigator mProcessed.notify_all(); } + std::size_t AsyncNavMeshUpdater::getTotalJobs() const + { + const std::scoped_lock lock(mMutex); + return mJobs.size() + getTotalThreadJobsUnsafe(); + } + std::size_t AsyncNavMeshUpdater::getTotalThreadJobsUnsafe() const { return std::accumulate(mThreadsQueues.begin(), mThreadsQueues.end(), std::size_t(0), diff --git a/components/detournavigator/asyncnavmeshupdater.hpp b/components/detournavigator/asyncnavmeshupdater.hpp index 53e7fd7c1..e8b2611e9 100644 --- a/components/detournavigator/asyncnavmeshupdater.hpp +++ b/components/detournavigator/asyncnavmeshupdater.hpp @@ -6,6 +6,7 @@ #include "tilecachedrecastmeshmanager.hpp" #include "tileposition.hpp" #include "navmeshtilescache.hpp" +#include "waitconditiontype.hpp" #include @@ -14,12 +15,18 @@ #include #include #include -#include +#include #include #include +#include class dtNavMesh; +namespace Loading +{ + class Listener; +} + namespace DetourNavigator { enum class ChangeType @@ -55,7 +62,7 @@ namespace DetourNavigator void post(const osg::Vec3f& agentHalfExtents, const SharedNavMeshCacheItem& mNavMeshCacheItem, const TilePosition& playerTile, const std::map& changedTiles); - void wait(); + void wait(Loading::Listener& listener, WaitConditionType waitConditionType); void reportStats(unsigned int frameNumber, osg::Stats& stats) const; @@ -78,11 +85,11 @@ namespace DetourNavigator friend inline bool operator <(const Job& lhs, const Job& rhs) { - return lhs.getPriority() > rhs.getPriority(); + return lhs.getPriority() < rhs.getPriority(); } }; - using Jobs = std::priority_queue>; + using Jobs = std::deque; using Pushed = std::map>; struct Queue @@ -108,6 +115,7 @@ namespace DetourNavigator NavMeshTilesCache mNavMeshTilesCache; Misc::ScopeGuarded>> mProcessingTiles; std::map> mLastUpdates; + std::set> mPresentTiles; std::map mThreadsQueues; std::vector mThreads; @@ -131,9 +139,15 @@ namespace DetourNavigator void unlockTile(const osg::Vec3f& agentHalfExtents, const TilePosition& changedTile); + inline std::size_t getTotalJobs() const; + inline std::size_t getTotalThreadJobsUnsafe() const; void cleanupLastUpdates(); + + int waitUntilJobsDoneForNotPresentTiles(const std::size_t initialJobsLeft, std::size_t& maxJobsLeft, Loading::Listener& listener); + + void waitUntilAllJobsDone(); }; } diff --git a/components/detournavigator/findrandompointaroundcircle.cpp b/components/detournavigator/findrandompointaroundcircle.cpp index 29d4e9da4..ed73dca61 100644 --- a/components/detournavigator/findrandompointaroundcircle.cpp +++ b/components/detournavigator/findrandompointaroundcircle.cpp @@ -4,7 +4,6 @@ #include -#include #include #include diff --git a/components/detournavigator/findsmoothpath.hpp b/components/detournavigator/findsmoothpath.hpp index 29a3ce805..0a9b5bed8 100644 --- a/components/detournavigator/findsmoothpath.hpp +++ b/components/detournavigator/findsmoothpath.hpp @@ -14,8 +14,6 @@ #include #include -#include - #include #include diff --git a/components/detournavigator/makenavmesh.cpp b/components/detournavigator/makenavmesh.cpp index 70fd44c6c..bcfd1b22f 100644 --- a/components/detournavigator/makenavmesh.cpp +++ b/components/detournavigator/makenavmesh.cpp @@ -1,6 +1,5 @@ #include "makenavmesh.hpp" #include "debug.hpp" -#include "dtstatus.hpp" #include "exceptions.hpp" #include "recastmesh.hpp" #include "settings.hpp" diff --git a/components/detournavigator/makenavmesh.hpp b/components/detournavigator/makenavmesh.hpp index f9cf68a73..95720634c 100644 --- a/components/detournavigator/makenavmesh.hpp +++ b/components/detournavigator/makenavmesh.hpp @@ -2,10 +2,8 @@ #define OPENMW_COMPONENTS_DETOURNAVIGATOR_MAKENAVMESH_H #include "offmeshconnectionsmanager.hpp" -#include "settings.hpp" #include "navmeshcacheitem.hpp" #include "tileposition.hpp" -#include "tilebounds.hpp" #include "sharednavmesh.hpp" #include "navmeshtilescache.hpp" diff --git a/components/detournavigator/navigator.hpp b/components/detournavigator/navigator.hpp index edf597348..8cf4cb80e 100644 --- a/components/detournavigator/navigator.hpp +++ b/components/detournavigator/navigator.hpp @@ -7,6 +7,7 @@ #include "objectid.hpp" #include "navmeshcacheitem.hpp" #include "recastmeshtiles.hpp" +#include "waitconditiontype.hpp" namespace ESM { @@ -14,6 +15,11 @@ namespace ESM struct Pathgrid; } +namespace Loading +{ + class Listener; +} + namespace DetourNavigator { struct ObjectShapes @@ -160,9 +166,10 @@ namespace DetourNavigator virtual void setUpdatesEnabled(bool enabled) = 0; /** - * @brief wait locks thread until all tiles are updated from last update call. + * @brief wait locks thread until tiles are updated from last update call based on passed condition type. + * @param waitConditionType defines when waiting will stop */ - virtual void wait() = 0; + virtual void wait(Loading::Listener& listener, WaitConditionType waitConditionType) = 0; /** * @brief findPath fills output iterator with points of scene surfaces to be used for actor to walk through. diff --git a/components/detournavigator/navigatorimpl.cpp b/components/detournavigator/navigatorimpl.cpp index abfb20ba8..7522fe622 100644 --- a/components/detournavigator/navigatorimpl.cpp +++ b/components/detournavigator/navigatorimpl.cpp @@ -5,8 +5,6 @@ #include #include -#include - namespace DetourNavigator { NavigatorImpl::NavigatorImpl(const Settings& settings) @@ -153,9 +151,9 @@ namespace DetourNavigator mUpdatesEnabled = enabled; } - void NavigatorImpl::wait() + void NavigatorImpl::wait(Loading::Listener& listener, WaitConditionType waitConditionType) { - mNavMeshManager.wait(); + mNavMeshManager.wait(listener, waitConditionType); } SharedNavMeshCacheItem NavigatorImpl::getNavMesh(const osg::Vec3f& agentHalfExtents) const diff --git a/components/detournavigator/navigatorimpl.hpp b/components/detournavigator/navigatorimpl.hpp index 74fff0dea..324946261 100644 --- a/components/detournavigator/navigatorimpl.hpp +++ b/components/detournavigator/navigatorimpl.hpp @@ -48,7 +48,7 @@ namespace DetourNavigator void setUpdatesEnabled(bool enabled) override; - void wait() override; + void wait(Loading::Listener& listener, WaitConditionType waitConditionType) override; SharedNavMeshCacheItem getNavMesh(const osg::Vec3f& agentHalfExtents) const override; diff --git a/components/detournavigator/navigatorstub.hpp b/components/detournavigator/navigatorstub.hpp index f6892bf1b..2c12c45eb 100644 --- a/components/detournavigator/navigatorstub.hpp +++ b/components/detournavigator/navigatorstub.hpp @@ -3,6 +3,11 @@ #include "navigator.hpp" +namespace Loading +{ + class Listener; +} + namespace DetourNavigator { class NavigatorStub final : public Navigator @@ -68,7 +73,7 @@ namespace DetourNavigator void setUpdatesEnabled(bool /*enabled*/) override {} - void wait() override {} + void wait(Loading::Listener& /*listener*/, WaitConditionType /*waitConditionType*/) override {} SharedNavMeshCacheItem getNavMesh(const osg::Vec3f& /*agentHalfExtents*/) const override { diff --git a/components/detournavigator/navmeshmanager.cpp b/components/detournavigator/navmeshmanager.cpp index 43d330648..8f1aa86d4 100644 --- a/components/detournavigator/navmeshmanager.cpp +++ b/components/detournavigator/navmeshmanager.cpp @@ -5,11 +5,14 @@ #include "makenavmesh.hpp" #include "navmeshcacheitem.hpp" #include "settings.hpp" +#include "waitconditiontype.hpp" #include #include +#include + namespace { using DetourNavigator::ChangeType; @@ -188,9 +191,9 @@ namespace DetourNavigator " recastMeshManagerRevision=" << lastRevision; } - void NavMeshManager::wait() + void NavMeshManager::wait(Loading::Listener& listener, WaitConditionType waitConditionType) { - mAsyncNavMeshUpdater.wait(); + mAsyncNavMeshUpdater.wait(listener, waitConditionType); } SharedNavMeshCacheItem NavMeshManager::getNavMesh(const osg::Vec3f& agentHalfExtents) const diff --git a/components/detournavigator/navmeshmanager.hpp b/components/detournavigator/navmeshmanager.hpp index f3861f8f2..760ddb6b3 100644 --- a/components/detournavigator/navmeshmanager.hpp +++ b/components/detournavigator/navmeshmanager.hpp @@ -4,8 +4,8 @@ #include "asyncnavmeshupdater.hpp" #include "cachedrecastmeshmanager.hpp" #include "offmeshconnectionsmanager.hpp" -#include "sharednavmesh.hpp" #include "recastmeshtiles.hpp" +#include "waitconditiontype.hpp" #include @@ -45,7 +45,7 @@ namespace DetourNavigator void update(osg::Vec3f playerPosition, const osg::Vec3f& agentHalfExtents); - void wait(); + void wait(Loading::Listener& listener, WaitConditionType waitConditionType); SharedNavMeshCacheItem getNavMesh(const osg::Vec3f& agentHalfExtents) const; diff --git a/components/detournavigator/raycast.cpp b/components/detournavigator/raycast.cpp index 86fabe9c1..271da2249 100644 --- a/components/detournavigator/raycast.cpp +++ b/components/detournavigator/raycast.cpp @@ -2,7 +2,6 @@ #include "settings.hpp" #include "findsmoothpath.hpp" -#include #include #include diff --git a/components/detournavigator/recastmesh.hpp b/components/detournavigator/recastmesh.hpp index 1961d518d..0e6bc4920 100644 --- a/components/detournavigator/recastmesh.hpp +++ b/components/detournavigator/recastmesh.hpp @@ -11,8 +11,6 @@ #include #include -#include - #include namespace DetourNavigator diff --git a/components/detournavigator/recastmeshmanager.cpp b/components/detournavigator/recastmeshmanager.cpp index 146038ae6..cdb9169d9 100644 --- a/components/detournavigator/recastmeshmanager.cpp +++ b/components/detournavigator/recastmeshmanager.cpp @@ -1,7 +1,5 @@ #include "recastmeshmanager.hpp" -#include - namespace DetourNavigator { RecastMeshManager::RecastMeshManager(const Settings& settings, const TileBounds& bounds, std::size_t generation) diff --git a/components/detournavigator/settings.cpp b/components/detournavigator/settings.cpp index 0c4d0f838..ff99fae20 100644 --- a/components/detournavigator/settings.cpp +++ b/components/detournavigator/settings.cpp @@ -29,6 +29,7 @@ namespace DetourNavigator navigatorSettings.mRegionMergeSize = ::Settings::Manager::getInt("region merge size", "Navigator"); navigatorSettings.mRegionMinSize = ::Settings::Manager::getInt("region min size", "Navigator"); navigatorSettings.mTileSize = ::Settings::Manager::getInt("tile size", "Navigator"); + navigatorSettings.mWaitUntilMinDistanceToPlayer = ::Settings::Manager::getInt("wait until min distance to player", "Navigator"); navigatorSettings.mAsyncNavMeshUpdaterThreads = static_cast(::Settings::Manager::getInt("async nav mesh updater threads", "Navigator")); navigatorSettings.mMaxNavMeshTilesCacheSize = static_cast(::Settings::Manager::getInt("max nav mesh tiles cache size", "Navigator")); navigatorSettings.mMaxPolygonPathSize = static_cast(::Settings::Manager::getInt("max polygon path size", "Navigator")); diff --git a/components/detournavigator/settings.hpp b/components/detournavigator/settings.hpp index ece16e35a..39f5815b8 100644 --- a/components/detournavigator/settings.hpp +++ b/components/detournavigator/settings.hpp @@ -31,6 +31,7 @@ namespace DetourNavigator int mRegionMergeSize = 0; int mRegionMinSize = 0; int mTileSize = 0; + int mWaitUntilMinDistanceToPlayer = 0; std::size_t mAsyncNavMeshUpdaterThreads = 0; std::size_t mMaxNavMeshTilesCacheSize = 0; std::size_t mMaxPolygonPathSize = 0; diff --git a/components/detournavigator/waitconditiontype.hpp b/components/detournavigator/waitconditiontype.hpp new file mode 100644 index 000000000..06a590128 --- /dev/null +++ b/components/detournavigator/waitconditiontype.hpp @@ -0,0 +1,13 @@ +#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_WAITCONDITIONTYPE_H +#define OPENMW_COMPONENTS_DETOURNAVIGATOR_WAITCONDITIONTYPE_H + +namespace DetourNavigator +{ + enum class WaitConditionType + { + requiredTilesPresent, + allJobsDone, + }; +} + +#endif diff --git a/components/esm/aipackage.cpp b/components/esm/aipackage.cpp index abbd2c62c..fa20d271c 100644 --- a/components/esm/aipackage.cpp +++ b/components/esm/aipackage.cpp @@ -15,7 +15,12 @@ namespace ESM { AIPackage pack; if (esm.retSubName() == AI_CNDT) { - mList.back().mCellName = esm.getHString(); + if (mList.empty()) + { + esm.fail("AIPackge with an AI_CNDT applying to no cell."); + } else { + mList.back().mCellName = esm.getHString(); + } } else if (esm.retSubName() == AI_Wander) { pack.mType = AI_Wander; esm.getHExact(&pack.mWander, 14); diff --git a/components/esm/defs.hpp b/components/esm/defs.hpp index 9bf9b01f3..1b623f69f 100644 --- a/components/esm/defs.hpp +++ b/components/esm/defs.hpp @@ -85,7 +85,7 @@ bool inline operator!= (const Position& left, const Position& right) noexcept template struct FourCC { - static const unsigned int value = (((((d << 8) | c) << 8) | b) << 8) | a; + static constexpr unsigned int value = (((((d << 8) | c) << 8) | b) << 8) | a; }; enum RecNameInts diff --git a/components/esm/esmcommon.hpp b/components/esm/esmcommon.hpp index 232a24fcf..749e9a85d 100644 --- a/components/esm/esmcommon.hpp +++ b/components/esm/esmcommon.hpp @@ -1,6 +1,7 @@ #ifndef OPENMW_ESM_COMMON_H #define OPENMW_ESM_COMMON_H +#include #include #include #include @@ -110,15 +111,7 @@ struct FIXED_STRING<4> : public FIXED_STRING_BASE void assign(const std::string& value) { intval = 0; - size_t length = value.size(); - if (length == 0) return; - data[0] = value[0]; - if (length == 1) return; - data[1] = value[1]; - if (length == 2) return; - data[2] = value[2]; - if (length == 3) return; - data[3] = value[3]; + std::memcpy(data, value.data(), std::min(value.size(), sizeof(data))); } char const* ro_data() const { return data; } diff --git a/components/esm/esmreader.cpp b/components/esm/esmreader.cpp index 4e7dce876..b779d7f7f 100644 --- a/components/esm/esmreader.cpp +++ b/components/esm/esmreader.cpp @@ -97,13 +97,6 @@ void ESMReader::open(const std::string &file) open (Files::openConstrainedFileStream (file.c_str ()), file); } -int64_t ESMReader::getHNLong(const char *name) -{ - int64_t val; - getHNT(val, name); - return val; -} - std::string ESMReader::getHNOString(const char* name) { if (isNextSub(name)) @@ -210,21 +203,9 @@ void ESMReader::getSubName() } // reading the subrecord data anyway. - const size_t subNameSize = mCtx.subName.data_size(); + const int subNameSize = static_cast(mCtx.subName.data_size()); getExact(mCtx.subName.rw_data(), subNameSize); - mCtx.leftRec -= subNameSize; -} - -bool ESMReader::isEmptyOrGetName() -{ - if (mCtx.leftRec) - { - const size_t subNameSize = mCtx.subName.data_size(); - getExact(mCtx.subName.rw_data(), subNameSize); - mCtx.leftRec -= subNameSize; - return false; - } - return true; + mCtx.leftRec -= static_cast(subNameSize); } void ESMReader::skipHSub() @@ -343,10 +324,10 @@ std::string ESMReader::getString(int size) mBuffer[s] = 0; // read ESM data - char *ptr = &mBuffer[0]; + char *ptr = mBuffer.data(); getExact(ptr, size); - size = strnlen(ptr, size); + size = static_cast(strnlen(ptr, size)); // Convert to UTF8 and return if (mEncoder) @@ -373,7 +354,7 @@ void ESMReader::setEncoder(ToUTF8::Utf8Encoder* encoder) mEncoder = encoder; } -size_t ESMReader::getFileOffset() +size_t ESMReader::getFileOffset() const { return mEsm->tellg(); } diff --git a/components/esm/esmreader.hpp b/components/esm/esmreader.hpp index 761756e8f..503bb637d 100644 --- a/components/esm/esmreader.hpp +++ b/components/esm/esmreader.hpp @@ -73,7 +73,7 @@ public: void openRaw(const std::string &filename); /// Get the current position in the file. Make sure that the file has been opened! - size_t getFileOffset(); + size_t getFileOffset() const; // This is a quick hack for multiple esm/esp files. Each plugin introduces its own // terrain palette, but ESMReader does not pass a reference to the correct plugin @@ -128,8 +128,6 @@ public: getHT(x); } - int64_t getHNLong(const char *name); - // Get data of a given type/size, including subrecord header template void getHT(X &x) @@ -193,9 +191,6 @@ public: // slightly. void getSubName(); - // This is specially optimized for LoadINFO. - bool isEmptyOrGetName(); - // Skip current sub record, including header (but not including // name.) void skipHSub(); diff --git a/components/esm/esmwriter.cpp b/components/esm/esmwriter.cpp index 09fca4b26..f65340f70 100644 --- a/components/esm/esmwriter.cpp +++ b/components/esm/esmwriter.cpp @@ -217,7 +217,7 @@ namespace ESM if (mCounting && !mRecords.empty()) { for (std::list::iterator it = mRecords.begin(); it != mRecords.end(); ++it) - it->size += size; + it->size += static_cast(size); } mStream->write(data, size); diff --git a/components/esm/globalscript.cpp b/components/esm/globalscript.cpp index 239d162f2..016ea4f0c 100644 --- a/components/esm/globalscript.cpp +++ b/components/esm/globalscript.cpp @@ -13,8 +13,7 @@ void ESM::GlobalScript::load (ESMReader &esm) esm.getHNOT (mRunning, "RUN_"); mTargetRef.unset(); - if (esm.peekNextSub("TARG")) - mTargetId = esm.getHNString ("TARG"); + mTargetId = esm.getHNOString ("TARG"); if (esm.peekNextSub("FRMR")) mTargetRef.load(esm, true, "FRMR"); } diff --git a/components/esm/loadarmo.cpp b/components/esm/loadarmo.cpp index 929c111a9..674370253 100644 --- a/components/esm/loadarmo.cpp +++ b/components/esm/loadarmo.cpp @@ -90,7 +90,7 @@ namespace ESM if (!hasName) esm.fail("Missing NAME subrecord"); if (!hasData && !isDeleted) - esm.fail("Missing CTDT subrecord"); + esm.fail("Missing AODT subrecord"); } void Armor::save(ESMWriter &esm, bool isDeleted) const diff --git a/components/esm/loadtes3.cpp b/components/esm/loadtes3.cpp index d953f1dc2..f5cbcd62f 100644 --- a/components/esm/loadtes3.cpp +++ b/components/esm/loadtes3.cpp @@ -41,7 +41,7 @@ void ESM::Header::load (ESMReader &esm) { MasterData m; m.name = esm.getHString(); - m.size = esm.getHNLong ("DATA"); + esm.getHNT(m.size, "DATA"); mMaster.push_back (m); } @@ -54,14 +54,14 @@ void ESM::Header::load (ESMReader &esm) esm.getSubHeader(); mSCRD.resize(esm.getSubSize()); if (!mSCRD.empty()) - esm.getExact(&mSCRD[0], mSCRD.size()); + esm.getExact(mSCRD.data(), mSCRD.size()); } if (esm.isNextSub("SCRS")) { esm.getSubHeader(); mSCRS.resize(esm.getSubSize()); if (!mSCRS.empty()) - esm.getExact(&mSCRS[0], mSCRS.size()); + esm.getExact(mSCRS.data(), mSCRS.size()); } } @@ -78,10 +78,9 @@ void ESM::Header::save (ESMWriter &esm) esm.writeT(mData.records); esm.endRecord("HEDR"); - for (std::vector::iterator iter = mMaster.begin(); - iter != mMaster.end(); ++iter) + for (const Header::MasterData& data : mMaster) { - esm.writeHNCString ("MAST", iter->name); - esm.writeHNT ("DATA", iter->size); + esm.writeHNCString ("MAST", data.name); + esm.writeHNT ("DATA", data.size); } } diff --git a/components/esm/statstate.cpp b/components/esm/statstate.cpp index b9ddc3efd..30d39e3c6 100644 --- a/components/esm/statstate.cpp +++ b/components/esm/statstate.cpp @@ -16,17 +16,16 @@ namespace ESM { int base = 0; esm.getHNT(base, "STBA"); - mBase = static_cast(base); + mBase = static_cast(base); int mod = 0; esm.getHNOT(mod, "STMO"); - mMod = static_cast(mod); + mMod = static_cast(mod); int current = 0; esm.getHNOT(current, "STCU"); - mCurrent = static_cast(current); + mCurrent = static_cast(current); - // mDamage was changed to a float; ensure backwards compatibility int oldDamage = 0; esm.getHNOT(oldDamage, "STDA"); mDamage = static_cast(oldDamage); diff --git a/components/esm/weatherstate.cpp b/components/esm/weatherstate.cpp index ff2528e58..3d94a445b 100644 --- a/components/esm/weatherstate.cpp +++ b/components/esm/weatherstate.cpp @@ -31,15 +31,15 @@ namespace ESM esm.getHNT(mNextWeather, nextWeatherRecord); esm.getHNT(mQueuedWeather, queuedWeatherRecord); - while(esm.peekNextSub(regionNameRecord)) + while (esm.isNextSub(regionNameRecord)) { - std::string regionID = esm.getHNString(regionNameRecord); + std::string regionID = esm.getHString(); RegionWeatherState region; esm.getHNT(region.mWeather, regionWeatherRecord); - while(esm.peekNextSub(regionChanceRecord)) + while (esm.isNextSub(regionChanceRecord)) { char chance; - esm.getHNT(chance, regionChanceRecord); + esm.getHT(chance); region.mChances.push_back(chance); } diff --git a/components/fallback/validate.cpp b/components/fallback/validate.cpp index 982c709af..a482d40fa 100644 --- a/components/fallback/validate.cpp +++ b/components/fallback/validate.cpp @@ -12,7 +12,7 @@ void Fallback::validate(boost::any& v, std::vector const& tokens, F for (const auto& token : tokens) { std::string temp = Files::EscapeHashString::processString(token); - size_t sep = temp.find(","); + size_t sep = temp.find(','); if (sep < 1 || sep == temp.length() - 1 || sep == std::string::npos) throw boost::program_options::validation_error(boost::program_options::validation_error::invalid_option_value); diff --git a/components/files/lowlevelfile.cpp b/components/files/lowlevelfile.cpp index 07915abba..d89ae5838 100644 --- a/components/files/lowlevelfile.cpp +++ b/components/files/lowlevelfile.cpp @@ -326,7 +326,7 @@ void LowLevelFile::seek (size_t position) { assert (mHandle != INVALID_HANDLE_VALUE); - if (SetFilePointer (mHandle, position, nullptr, SEEK_SET) == INVALID_SET_FILE_POINTER) + if (SetFilePointer (mHandle, static_cast(position), nullptr, SEEK_SET) == INVALID_SET_FILE_POINTER) if (GetLastError () != NO_ERROR) throw std::runtime_error ("A seek operation on a file failed."); } @@ -349,7 +349,7 @@ size_t LowLevelFile::read (void * data, size_t size) DWORD read; - if (!ReadFile (mHandle, data, size, &read, nullptr)) + if (!ReadFile (mHandle, data, static_cast(size), &read, nullptr)) throw std::runtime_error ("A read operation on a file failed."); return read; diff --git a/components/interpreter/runtime.cpp b/components/interpreter/runtime.cpp index 35ad93a8a..1cb8f558c 100644 --- a/components/interpreter/runtime.cpp +++ b/components/interpreter/runtime.cpp @@ -45,7 +45,7 @@ namespace Interpreter for (; index; --index) { - offset += std::strlen (literalBlock+offset) + 1; + offset += static_cast(std::strlen (literalBlock+offset)) + 1; if (offset / 4 >= static_cast (mCode[3])) throw std::out_of_range("out of range"); } diff --git a/components/misc/algorithm.hpp b/components/misc/algorithm.hpp new file mode 100644 index 000000000..4d70afa86 --- /dev/null +++ b/components/misc/algorithm.hpp @@ -0,0 +1,36 @@ +#ifndef OPENMW_COMPONENTS_MISC_ALGORITHM_H +#define OPENMW_COMPONENTS_MISC_ALGORITHM_H + +#include +#include + +namespace Misc +{ + template + inline Iterator forEachUnique(Iterator begin, Iterator end, BinaryPredicate predicate, Function function) + { + static_assert( + std::is_base_of_v< + std::forward_iterator_tag, + typename std::iterator_traits::iterator_category + > + ); + if (begin == end) + return begin; + function(*begin); + auto last = begin; + ++begin; + while (begin != end) + { + if (!predicate(*begin, *last)) + { + function(*begin); + last = begin; + } + ++begin; + } + return begin; + } +} + +#endif diff --git a/components/misc/barrier.hpp b/components/misc/barrier.hpp index a5af9f565..b3fe944b0 100644 --- a/components/misc/barrier.hpp +++ b/components/misc/barrier.hpp @@ -2,7 +2,6 @@ #define OPENMW_BARRIER_H #include -#include #include namespace Misc @@ -11,15 +10,14 @@ namespace Misc class Barrier { public: - using BarrierCallback = std::function; /// @param count number of threads to wait on - /// @param func callable to be executed once after all threads have met - Barrier(int count, BarrierCallback&& func) : mThreadCount(count), mRendezvousCount(0), mGeneration(0) - , mFunc(std::forward(func)) + explicit Barrier(int count) : mThreadCount(count), mRendezvousCount(0), mGeneration(0) {} /// @brief stop execution of threads until count distinct threads reach this point - void wait() + /// @param func callable to be executed once after all threads have met + template + void wait(Callback&& func) { std::unique_lock lock(mMutex); @@ -29,7 +27,7 @@ namespace Misc { ++mGeneration; mRendezvousCount = 0; - mFunc(); + func(); mRendezvous.notify_all(); } else @@ -44,7 +42,6 @@ namespace Misc int mGeneration; mutable std::mutex mMutex; std::condition_variable mRendezvous; - BarrierCallback mFunc; }; } diff --git a/components/misc/guarded.hpp b/components/misc/guarded.hpp index 55a2c670c..7f1005fc3 100644 --- a/components/misc/guarded.hpp +++ b/components/misc/guarded.hpp @@ -75,7 +75,7 @@ namespace Misc return Locked(mMutex, mValue); } - Locked lockConst() + Locked lockConst() const { return Locked(mMutex, mValue); } @@ -88,7 +88,7 @@ namespace Misc } private: - std::mutex mMutex; + mutable std::mutex mMutex; T mValue; }; } diff --git a/components/myguiplatform/scalinglayer.cpp b/components/myguiplatform/scalinglayer.cpp index 99ed6d07a..51c148253 100644 --- a/components/myguiplatform/scalinglayer.cpp +++ b/components/myguiplatform/scalinglayer.cpp @@ -74,8 +74,8 @@ namespace osgMyGUI _left -= globalViewSize.width/2; _top -= globalViewSize.height/2; - _left /= scale; - _top /= scale; + _left = static_cast(_left/scale); + _top = static_cast(_top/scale); _left += mViewSize.width/2; _top += mViewSize.height/2; @@ -84,8 +84,8 @@ namespace osgMyGUI float ScalingLayer::getScaleFactor() const { MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize(); - float w = viewSize.width; - float h = viewSize.height; + float w = static_cast(viewSize.width); + float h = static_cast(viewSize.height); float heightScale = (h / mViewSize.height); float widthScale = (w / mViewSize.width); @@ -103,8 +103,8 @@ namespace osgMyGUI MyGUI::IntSize globalViewSize = MyGUI::RenderManager::getInstance().getViewSize(); MyGUI::IntSize viewSize = globalViewSize; float scale = getScaleFactor(); - viewSize.width /= scale; - viewSize.height /= scale; + viewSize.width = static_cast(viewSize.width / scale); + viewSize.height = static_cast(viewSize.height / scale); float hoffset = (globalViewSize.width - mViewSize.width*getScaleFactor())/2.f / static_cast(globalViewSize.width); float voffset = (globalViewSize.height - mViewSize.height*getScaleFactor())/2.f / static_cast(globalViewSize.height); diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index f7d6fd3ed..ab25fd744 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -954,13 +954,17 @@ namespace NifOsg } // Load the initial state of the particle system, i.e. the initial particles and their positions, velocity and colors. - void handleParticleInitialState(const Nif::Node* nifNode, osgParticle::ParticleSystem* partsys, const Nif::NiParticleSystemController* partctrl) + void handleParticleInitialState(const Nif::Node* nifNode, ParticleSystem* partsys, const Nif::NiParticleSystemController* partctrl) { auto particleNode = static_cast(nifNode); if (particleNode->data.empty() || particleNode->data->recType != Nif::RC_NiParticlesData) + { + partsys->setQuota(partctrl->numParticles); return; + } auto particledata = static_cast(particleNode->data.getPtr()); + partsys->setQuota(particledata->numParticles); osg::BoundingBox box; @@ -1095,8 +1099,6 @@ namespace NifOsg handleParticleInitialState(nifNode, partsys, partctrl); - partsys->setQuota(partctrl->numParticles); - partsys->getDefaultParticleTemplate().setSizeRange(osgParticle::rangef(partctrl->size, partctrl->size)); partsys->getDefaultParticleTemplate().setColorRange(osgParticle::rangev4(osg::Vec4f(1.f,1.f,1.f,1.f), osg::Vec4f(1.f,1.f,1.f,1.f))); partsys->getDefaultParticleTemplate().setAlphaRange(osgParticle::rangef(1.f, 1.f)); diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index a3c751f7a..f6035a47d 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -378,7 +378,7 @@ namespace Resource Resource::ImageManager* mImageManager; }; - osg::ref_ptr load (Files::IStreamPtr file, const std::string& normalizedFilename, Resource::ImageManager* imageManager, Resource::NifFileManager* nifFileManager) + osg::ref_ptr load (const std::string& normalizedFilename, const VFS::Manager* vfs, Resource::ImageManager* imageManager, Resource::NifFileManager* nifFileManager) { std::string ext = Resource::getFileExtension(normalizedFilename); if (ext == "nif") @@ -400,7 +400,7 @@ namespace Resource options->setReadFileCallback(new ImageReadCallback(imageManager)); if (ext == "dae") options->setOptionString("daeUseSequencedTextureUnits"); - osgDB::ReaderWriter::ReadResult result = reader->readNode(*file, options); + osgDB::ReaderWriter::ReadResult result = reader->readNode(*vfs->get(normalizedFilename), options); if (!result.success()) { std::stringstream errormsg; @@ -500,7 +500,7 @@ namespace Resource { std::string str(env); - if(str.find("OFF")!=std::string::npos || str.find("0")!= std::string::npos) options = 0; + if(str.find("OFF")!=std::string::npos || str.find('0')!= std::string::npos) options = 0; if(str.find("~FLATTEN_STATIC_TRANSFORMS")!=std::string::npos) options ^= Optimizer::FLATTEN_STATIC_TRANSFORMS; else if(str.find("FLATTEN_STATIC_TRANSFORMS")!=std::string::npos) options |= Optimizer::FLATTEN_STATIC_TRANSFORMS; @@ -514,6 +514,12 @@ namespace Resource return options; } + void SceneManager::shareState(osg::ref_ptr node) { + mSharedStateMutex.lock(); + mSharedStateManager->share(node.get()); + mSharedStateMutex.unlock(); + } + osg::ref_ptr SceneManager::getTemplate(const std::string &name, bool compile) { std::string normalized = name; @@ -527,9 +533,7 @@ namespace Resource osg::ref_ptr loaded; try { - Files::IStreamPtr file = mVFS->get(normalized); - - loaded = load(file, normalized, mImageManager, mNifFileManager); + loaded = load(normalized, mVFS, mImageManager, mNifFileManager); } catch (std::exception& e) { @@ -541,8 +545,7 @@ namespace Resource if (mVFS->exists(normalized)) { Log(Debug::Error) << "Failed to load '" << name << "': " << e.what() << ", using marker_error." << sMeshTypes[i] << " instead"; - Files::IStreamPtr file = mVFS->get(normalized); - loaded = load(file, normalized, mImageManager, mNifFileManager); + loaded = load(normalized, mVFS, mImageManager, mNifFileManager); break; } } diff --git a/components/resource/scenemanager.hpp b/components/resource/scenemanager.hpp index 7635cd20f..de014165b 100644 --- a/components/resource/scenemanager.hpp +++ b/components/resource/scenemanager.hpp @@ -135,7 +135,7 @@ namespace Resource osg::ref_ptr createInstance(const std::string& name); osg::ref_ptr createInstance(const osg::Node* base); - + void shareState(osg::ref_ptr node); /// Get an instance of the given scene template /// @see getTemplate /// @note Thread safe. diff --git a/components/sceneutil/lightmanager.cpp b/components/sceneutil/lightmanager.cpp index b5c7af780..a69a64ba3 100644 --- a/components/sceneutil/lightmanager.cpp +++ b/components/sceneutil/lightmanager.cpp @@ -114,15 +114,35 @@ namespace SceneUtil mOffsets[AttenuationRadius] = 8; } - LightBuffer(const LightBuffer& copy) - : osg::Referenced() - , mData(copy.mData) - , mEndian(copy.mEndian) - , mCount(copy.mCount) - , mStride(copy.mStride) - , mOffsets(copy.mOffsets) - , mCachedSunPos(copy.mCachedSunPos) - {} + LightBuffer(const LightBuffer&) = delete; + + LightBuffer(const LightBuffer& other, int offsetColors, int offsetPosition, int offsetAttenuationRadius, int size, int stride) + : mData(new osg::FloatArray(size / sizeof(GL_FLOAT))) + , mEndian(other.mEndian) + , mCount(other.mCount) + , mStride((offsetAttenuationRadius + sizeof(GL_FLOAT) * osg::Vec4::num_components + stride) / 4) + , mCachedSunPos(other.mCachedSunPos) + { + mData->setBufferObject(other.mData->getBufferObject()); + + constexpr auto sizeofFloat = sizeof(GL_FLOAT); + const auto diffuseOffset = offsetColors / sizeofFloat; + + mOffsets[Diffuse] = diffuseOffset; + mOffsets[Ambient] = diffuseOffset + 1; + mOffsets[Specular] = diffuseOffset + 2; + mOffsets[DiffuseSign] = diffuseOffset + 3; + mOffsets[Position] = offsetPosition / sizeofFloat; + mOffsets[AttenuationRadius] = offsetAttenuationRadius / sizeofFloat; + + // Copy over previous buffers light data. Buffers populate before we know the layout. + for (int i = 0; i < other.mCount; ++i) + { + std::memcpy(&(*mData)[getOffset(i, Diffuse)], &(*other.mData)[other.getOffset(i, Diffuse)], sizeof(osg::Vec4f)); + std::memcpy(&(*mData)[getOffset(i, Position)], &(*other.mData)[other.getOffset(i, Position)], sizeof(osg::Vec4f)); + std::memcpy(&(*mData)[getOffset(i, AttenuationRadius)], &(*other.mData)[other.getOffset(i, AttenuationRadius)], sizeof(osg::Vec4f)); + } + } void setDiffuse(int index, const osg::Vec4& value) { @@ -192,36 +212,11 @@ namespace SceneUtil return mEndian == osg::BigEndian ? value.asABGR() : value.asRGBA(); } - int getOffset(int index, LayoutOffset slot) + int getOffset(int index, LayoutOffset slot) const { return mStride * index + mOffsets[slot]; } - void configureLayout(int offsetColors, int offsetPosition, int offsetAttenuationRadius, int size, int stride) - { - constexpr auto sizeofFloat = sizeof(GL_FLOAT); - constexpr auto sizeofVec4 = sizeofFloat * osg::Vec4::num_components; - - LightBuffer oldBuffer = LightBuffer(*this); - - mOffsets[Diffuse] = offsetColors / sizeofFloat; - mOffsets[Ambient] = mOffsets[Diffuse] + 1; - mOffsets[Specular] = mOffsets[Diffuse] + 2; - mOffsets[DiffuseSign] = mOffsets[Diffuse] + 3; - mOffsets[Position] = offsetPosition / sizeofFloat; - mOffsets[AttenuationRadius] = offsetAttenuationRadius / sizeofFloat; - mStride = (offsetAttenuationRadius + sizeofVec4 + stride) / 4; - - // Copy over previous buffers light data. Buffers populate before we know the layout. - mData->resize(size / sizeofFloat); - for (int i = 0; i < oldBuffer.mCount; ++i) - { - std::memcpy(&(*mData)[getOffset(i, Diffuse)], &(*oldBuffer.mData)[oldBuffer.getOffset(i, Diffuse)], sizeof(osg::Vec4f)); - std::memcpy(&(*mData)[getOffset(i, Position)], &(*oldBuffer.mData)[oldBuffer.getOffset(i, Position)], sizeof(osg::Vec4f)); - std::memcpy(&(*mData)[getOffset(i, AttenuationRadius)], &(*oldBuffer.mData)[oldBuffer.getOffset(i, AttenuationRadius)], sizeof(osg::Vec4f)); - } - } - private: osg::ref_ptr mData; osg::Endian mEndian; @@ -444,7 +439,11 @@ namespace SceneUtil void apply(osg::State &state) const override { - auto* lightUniform = mLightManager->getStateSet()->getUniform("LightBuffer"); + osg::StateSet* stateSet = mLightManager->getStateSet(); + if (!stateSet) + return; + + auto* lightUniform = stateSet->getUniform("LightBuffer"); for (size_t i = 0; i < mLights.size(); ++i) { auto light = mLights[i]; @@ -754,7 +753,7 @@ namespace SceneUtil for (int i = 0; i < 2; ++i) { auto& buf = mLightManager->getLightBuffer(i); - buf->configureLayout(offsets[0], offsets[1], offsets[2], totalBlockSize, stride); + buf = new LightBuffer(*buf, offsets[0], offsets[1], offsets[2], totalBlockSize, stride); } } @@ -837,6 +836,11 @@ namespace SceneUtil return ""; } + LightManager::~LightManager() + { + getOrCreateStateSet()->removeAttribute(osg::StateAttribute::LIGHT); + } + LightManager::LightManager(bool ffp) : mStartLight(0) , mLightingMask(~0u) diff --git a/components/sceneutil/lightmanager.hpp b/components/sceneutil/lightmanager.hpp index dc7f36e96..6dbe7a3f7 100644 --- a/components/sceneutil/lightmanager.hpp +++ b/components/sceneutil/lightmanager.hpp @@ -137,6 +137,8 @@ namespace SceneUtil LightManager(const LightManager& copy, const osg::CopyOp& copyop); + ~LightManager(); + /// @param mask This mask is compared with the current Camera's cull mask to determine if lighting is desired. /// By default, it's ~0u i.e. always on. /// If you have some views that do not require lighting, then set the Camera's cull mask to not include diff --git a/components/sceneutil/serialize.cpp b/components/sceneutil/serialize.cpp index 7e176be3d..1b5c1feed 100644 --- a/components/sceneutil/serialize.cpp +++ b/components/sceneutil/serialize.cpp @@ -131,9 +131,11 @@ void registerSerializers() "SceneUtil::StateSetUpdater", "SceneUtil::DisableLight", "SceneUtil::MWShadowTechnique", + "SceneUtil::TextKeyMapHolder", + "Shader::RemovedAlphaFunc", + "NifOsg::LightManagerStateAttribute", "NifOsg::FlipController", "NifOsg::KeyframeController", - "NifOsg::TextKeyMapHolder", "NifOsg::Emitter", "NifOsg::ParticleColorAffector", "NifOsg::ParticleSystem", @@ -147,6 +149,7 @@ void registerSerializers() "NifOsg::VisController", "osgMyGUI::Drawable", "osg::DrawCallback", + "osg::UniformBufferObject", "osgOQ::ClearQueriesCallback", "osgOQ::RetrieveQueriesCallback", "osg::DummyObject" diff --git a/components/sdlutil/sdlinputwrapper.cpp b/components/sdlutil/sdlinputwrapper.cpp index 7ebfd710f..42276cc2a 100644 --- a/components/sdlutil/sdlinputwrapper.cpp +++ b/components/sdlutil/sdlinputwrapper.cpp @@ -52,9 +52,14 @@ InputWrapper::InputWrapper(SDL_Window* window, osg::ref_ptr v if (windowEventsOnly) { - // During loading, just handle window events, and keep others for later + // During loading, handle window events, discard button presses and keep others for later while (SDL_PeepEvents(&evt, 1, SDL_GETEVENT, SDL_WINDOWEVENT, SDL_WINDOWEVENT)) handleWindowEvent(evt); + + SDL_FlushEvent(SDL_KEYDOWN); + SDL_FlushEvent(SDL_CONTROLLERBUTTONDOWN); + SDL_FlushEvent(SDL_MOUSEBUTTONDOWN); + return; } diff --git a/components/settings/parser.cpp b/components/settings/parser.cpp index 9693bf511..24f359b31 100644 --- a/components/settings/parser.cpp +++ b/components/settings/parser.cpp @@ -7,19 +7,36 @@ #include -void Settings::SettingsFileParser::loadSettingsFile(const std::string& file, CategorySettingValueMap& settings) +#include + +void Settings::SettingsFileParser::loadSettingsFile(const std::string& file, CategorySettingValueMap& settings, bool base64Encoded) { mFile = file; - boost::filesystem::ifstream stream; - stream.open(boost::filesystem::path(file)); + boost::filesystem::ifstream fstream; + fstream.open(boost::filesystem::path(file)); + auto stream = std::ref(fstream); + + std::istringstream decodedStream; + if (base64Encoded) + { + std::string base64String(std::istreambuf_iterator(fstream), {}); + std::string decodedString; + auto result = Base64::Base64::Decode(base64String, decodedString); + if (!result.empty()) + fail("Could not decode Base64 file: " + result); + // Move won't do anything until C++20, but won't hurt to do it anyway. + decodedStream.str(std::move(decodedString)); + stream = std::ref(decodedStream); + } + Log(Debug::Info) << "Loading settings file: " << file; std::string currentCategory; mLine = 0; - while (!stream.eof() && !stream.fail()) + while (!stream.get().eof() && !stream.get().fail()) { ++mLine; std::string line; - std::getline( stream, line ); + std::getline( stream.get(), line ); size_t i = 0; if (!skipWhiteSpace(i, line)) @@ -255,7 +272,7 @@ void Settings::SettingsFileParser::saveSettingsFile(const std::string& file, con ostream << "# This is the OpenMW user 'settings.cfg' file. This file only contains" << std::endl; ostream << "# explicitly changed settings. If you would like to revert a setting" << std::endl; ostream << "# to its default, simply remove it from this file. For available" << std::endl; - ostream << "# settings, see the file 'settings-default.cfg' or the documentation at:" << std::endl; + ostream << "# settings, see the file 'files/settings-default.cfg' in our source repo or the documentation at:" << std::endl; ostream << "#" << std::endl; ostream << "# https://openmw.readthedocs.io/en/master/reference/modding/settings/index.html" << std::endl; } diff --git a/components/settings/parser.hpp b/components/settings/parser.hpp index 449e54223..69e9cdaa4 100644 --- a/components/settings/parser.hpp +++ b/components/settings/parser.hpp @@ -10,7 +10,7 @@ namespace Settings class SettingsFileParser { public: - void loadSettingsFile(const std::string& file, CategorySettingValueMap& settings); + void loadSettingsFile(const std::string& file, CategorySettingValueMap& settings, bool base64encoded = false); void saveSettingsFile(const std::string& file, const CategorySettingValueMap& settings); diff --git a/components/settings/settings.cpp b/components/settings/settings.cpp index b29dadcdc..65af0fad8 100644 --- a/components/settings/settings.cpp +++ b/components/settings/settings.cpp @@ -19,11 +19,19 @@ void Manager::clear() mChangedSettings.clear(); } -void Manager::loadDefault(const std::string &file) +/* + Start of tes3mp change (major) + + Add a base64encoded argument to this function to allow unencoded files to still be opened +*/ +void Manager::loadDefault(const std::string &file, bool base64encoded) { SettingsFileParser parser; - parser.loadSettingsFile(file, mDefaultSettings); + parser.loadSettingsFile(file, mDefaultSettings, base64encoded); } +/* + End of tes3mp change (major) +*/ void Manager::loadUser(const std::string &file) { @@ -49,7 +57,7 @@ std::string Manager::getString(const std::string &setting, const std::string &ca return it->second; throw std::runtime_error(std::string("Trying to retrieve a non-existing setting: ") + setting - + ".\nMake sure the settings-default.cfg file was properly installed."); + + ".\nMake sure the defaults.bin file was properly installed."); } float Manager::getFloat (const std::string& setting, const std::string& category) diff --git a/components/settings/settings.hpp b/components/settings/settings.hpp index ecc5aa5fd..b9cd7dcf6 100644 --- a/components/settings/settings.hpp +++ b/components/settings/settings.hpp @@ -26,8 +26,16 @@ namespace Settings void clear(); ///< clears all settings and default settings - void loadDefault (const std::string& file); + /* + Start of tes3mp change (major) + + Add a base64encoded argument to this function to allow unencoded files to still be opened + */ + void loadDefault (const std::string& file, bool base64encoded = true); ///< load file as the default settings (can be overridden by user settings) + /* + End of tes3mp change (major) + */ void loadUser (const std::string& file); ///< load file as user settings diff --git a/components/terrain/viewdata.cpp b/components/terrain/viewdata.cpp index e4d043ffc..996bf909c 100644 --- a/components/terrain/viewdata.cpp +++ b/components/terrain/viewdata.cpp @@ -141,9 +141,9 @@ ViewData *ViewDataMap::getViewData(osg::Object *viewer, const osg::Vec3f& viewPo vd = found->second; needsUpdate = false; - if (!(vd->suitableToUse(activeGrid) && (vd->getViewPoint()-viewPoint).length2() < mReuseDistance*mReuseDistance && vd->getWorldUpdateRevision() >= mWorldUpdateRevision)) + if (!vd->suitableToUse(activeGrid) || (vd->getViewPoint()-viewPoint).length2() >= mReuseDistance*mReuseDistance || vd->getWorldUpdateRevision() < mWorldUpdateRevision) { - float shortestDist = std::numeric_limits::max(); + float shortestDist = viewer ? mReuseDistance*mReuseDistance : std::numeric_limits::max(); const ViewData* mostSuitableView = nullptr; for (const ViewData* other : mUsedViews) { @@ -162,6 +162,11 @@ ViewData *ViewDataMap::getViewData(osg::Object *viewer, const osg::Vec3f& viewPo vd->copyFrom(*mostSuitableView); return vd; } + else if (!mostSuitableView) + { + vd->setViewPoint(viewPoint); + needsUpdate = true; + } } if (!vd->suitableToUse(activeGrid)) { diff --git a/components/widgets/imagebutton.cpp b/components/widgets/imagebutton.cpp index 0d1f798da..bf2b1b24c 100644 --- a/components/widgets/imagebutton.cpp +++ b/components/widgets/imagebutton.cpp @@ -118,7 +118,7 @@ namespace Gui void ImageButton::setImage(const std::string &image) { - size_t extpos = image.find_last_of("."); + size_t extpos = image.find_last_of('.'); std::string imageNoExt = image.substr(0, extpos); std::string ext = image.substr(extpos); diff --git a/components/widgets/list.cpp b/components/widgets/list.cpp index 73ca94929..e961b04cb 100644 --- a/components/widgets/list.cpp +++ b/components/widgets/list.cpp @@ -115,7 +115,7 @@ namespace Gui unsigned int MWList::getItemCount() { - return mItems.size(); + return static_cast(mItems.size()); } std::string MWList::getItemNameAt(unsigned int at) diff --git a/docs/source/reference/modding/settings/camera.rst b/docs/source/reference/modding/settings/camera.rst index e15b06e74..a880a2b62 100644 --- a/docs/source/reference/modding/settings/camera.rst +++ b/docs/source/reference/modding/settings/camera.rst @@ -6,7 +6,7 @@ near clip :Type: floating point :Range: > 0 -:Default: 1.0 +:Default: 3.0 This setting controls the distance to the near clipping plane. The value must be greater than zero. Values greater than approximately 18.0 will occasionally clip objects in the world in front of the character. diff --git a/docs/source/reference/modding/settings/groundcover.rst b/docs/source/reference/modding/settings/groundcover.rst index ef97caeec..7c5a965e0 100644 --- a/docs/source/reference/modding/settings/groundcover.rst +++ b/docs/source/reference/modding/settings/groundcover.rst @@ -28,15 +28,14 @@ are used in the game. Can affect performance a lot. This setting can only be configured by editing the settings configuration file. -distance --------- +rendering distance +------------------ -:Type: integer -:Range: > 0 -:Default: 1 +:Type: floating point +:Range: >= 0.0 +:Default: 6144.0 -Determines on which distance in cells grass pages are rendered. -Default 1 value means 3x3 cells area (active grid). +Determines on which distance in game units grass pages are rendered. May affect performance a lot. This setting can only be configured by editing the settings configuration file. diff --git a/docs/source/reference/modding/settings/index.rst b/docs/source/reference/modding/settings/index.rst index 220ee88c4..e9607fd9d 100644 --- a/docs/source/reference/modding/settings/index.rst +++ b/docs/source/reference/modding/settings/index.rst @@ -8,6 +8,14 @@ If you are familiar with ``.ini`` tweaks in Morrowind or the other games, this w All settings described in this section are changed in ``settings.cfg``, located in your OpenMW user directory. See :doc:`../paths` for this location. +When creating a new game based on the OpenMW engine, it may be desirable to change the default settings - the defaults are chosen for compatibility with Morrowind. +This can be done by editing ``defaults.bin`` in the OpenMW installation directory without rebuilding OpenMW itself. +If you're using a custom fork of OpenMW, ``files/settings-default.cfg`` in the source repository should be edited instead. +To edit ``defaults.bin``, base64 decode it, make any changes, and then base64 encode it again. + +If you feel a need to edit the default settings for any other reason than when creating a new OpenMW-based game, you should not. +We may be able to accommodate your desired workflow some other way if you make a feature request. + Changing Settings ################# @@ -25,8 +33,10 @@ Changing Settings Then to the line above, type ``[GUI]``, as the tooltip delay setting comes from the "GUI Settings" section. Although this guide attempts to be comprehensive and up to date, -you will always be able to find the full list of settings available and their default values in ``settings-default.cfg`` -in your main OpenMW installation directory. +you will always be able to find the full list of settings available and their default values in ``settings-default.cfg``, +available in the ``files`` directory of our source repo, or by base64 decoding ``defaults.bin`` in your main OpenMW installation directory. +This has changed compared to previous versions of OpenMW as more users were confused by the existence of a file they weren't supposed to edit +than were helped by the existence of a file listing settings they could edit in a different file. The ranges included with each setting are the physically possible ranges, not recommendations. .. warning:: diff --git a/docs/source/reference/modding/settings/navigator.rst b/docs/source/reference/modding/settings/navigator.rst index b9485c3e9..fee4b2626 100644 --- a/docs/source/reference/modding/settings/navigator.rst +++ b/docs/source/reference/modding/settings/navigator.rst @@ -42,6 +42,18 @@ Increasing this value may decrease performance. This condition is always true: ``max tiles number * max polygons per tile <= 4194304``. It's a limitation of `Recastnavigation `_ library. +wait until min distance to player +------------------------------ + +:Type: integer +:Range: >= 0 +:Default: 5 + +Distance in navmesh tiles around the player to keep loading screen until navigation mesh is generated. +Allows to complete cell loading only when minimal navigation mesh area is generated to correctly find path for actors +nearby the player. Increasing this value will keep loading screen longer but will slightly increase nav mesh generation +speed on systems bound by CPU. Zero means no waiting. + Advanced settings ***************** diff --git a/docs/source/reference/modding/texture-modding/texture-basics.rst b/docs/source/reference/modding/texture-modding/texture-basics.rst index 65e71834c..78ae00770 100644 --- a/docs/source/reference/modding/texture-modding/texture-basics.rst +++ b/docs/source/reference/modding/texture-modding/texture-basics.rst @@ -19,8 +19,10 @@ OpenMW automatically uses shaders for objects with these mapping techniques. Normal Mapping ############## -To plug in a normal map, you name the normal map as the diffuse texture but with a specified suffix after. -OpenMW will then recognise the file and load it as a normal map, provided you have set up your settings file correctly. +To plug in a normal map, you name the normal map as the diffuse texture but with a specified suffix after. OpenMW will then recognise the file and load it as a normal map, provided you have set up your settings file correctly. + +Content creators need to know that OpenMW uses the DX format for normal maps, and not the OpenGL format as one otherwise might expect. See an example of the difference between the two formats `here `_. Make sure your normal maps are created according to the DX style. + See the section `Automatic use`_ further down below for detailed information. Specular Mapping diff --git a/extern/Base64/Base64.h b/extern/Base64/Base64.h new file mode 100644 index 000000000..4e9f51747 --- /dev/null +++ b/extern/Base64/Base64.h @@ -0,0 +1,124 @@ +#ifndef _MACARON_BASE64_H_ +#define _MACARON_BASE64_H_ + +/** + * The MIT License (MIT) + * Copyright (c) 2016 tomykaira + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include + +namespace Base64 { + +class Base64 { + public: + + static std::string Encode(const std::string data) { + static constexpr char sEncodingTable[] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '+', '/' + }; + + size_t in_len = data.size(); + size_t out_len = 4 * ((in_len + 2) / 3); + std::string ret(out_len, '\0'); + size_t i; + char *p = ret.data(); + + for (i = 0; i < in_len - 2; i += 3) { + *p++ = sEncodingTable[(data[i] >> 2) & 0x3F]; + *p++ = sEncodingTable[((data[i] & 0x3) << 4) | ((int) (data[i + 1] & 0xF0) >> 4)]; + *p++ = sEncodingTable[((data[i + 1] & 0xF) << 2) | ((int) (data[i + 2] & 0xC0) >> 6)]; + *p++ = sEncodingTable[data[i + 2] & 0x3F]; + } + if (i < in_len) { + *p++ = sEncodingTable[(data[i] >> 2) & 0x3F]; + if (i == (in_len - 1)) { + *p++ = sEncodingTable[((data[i] & 0x3) << 4)]; + *p++ = '='; + } + else { + *p++ = sEncodingTable[((data[i] & 0x3) << 4) | ((int) (data[i + 1] & 0xF0) >> 4)]; + *p++ = sEncodingTable[((data[i + 1] & 0xF) << 2)]; + } + *p++ = '='; + } + + return ret; + } + + static std::string Decode(const std::string& input, std::string& out) { + static constexpr unsigned char kDecodingTable[] = { + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64, + 64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64, + 64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64 + }; + + size_t in_len = input.size(); + if (in_len % 4 != 0) return "Input data size is not a multiple of 4"; + + size_t out_len = in_len / 4 * 3; + if (input[in_len - 1] == '=') out_len--; + if (input[in_len - 2] == '=') out_len--; + + out.resize(out_len); + + for (size_t i = 0, j = 0; i < in_len;) { + uint32_t a = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast(input[i++])]; + uint32_t b = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast(input[i++])]; + uint32_t c = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast(input[i++])]; + uint32_t d = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast(input[i++])]; + + uint32_t triple = (a << 3 * 6) + (b << 2 * 6) + (c << 1 * 6) + (d << 0 * 6); + + if (j < out_len) out[j++] = (triple >> 2 * 8) & 0xFF; + if (j < out_len) out[j++] = (triple >> 1 * 8) & 0xFF; + if (j < out_len) out[j++] = (triple >> 0 * 8) & 0xFF; + } + + return ""; + } + +}; + +} + +#endif /* _MACARON_BASE64_H_ */ diff --git a/extern/Base64/CMakeLists.txt b/extern/Base64/CMakeLists.txt new file mode 100644 index 000000000..d1adf91bc --- /dev/null +++ b/extern/Base64/CMakeLists.txt @@ -0,0 +1,2 @@ +add_library(Base64 INTERFACE) +target_include_directories(Base64 INTERFACE .) \ No newline at end of file diff --git a/extern/CMakeLists.txt b/extern/CMakeLists.txt index 8257b6b23..5dc9642d7 100644 --- a/extern/CMakeLists.txt +++ b/extern/CMakeLists.txt @@ -44,11 +44,11 @@ if(NOT OPENMW_USE_SYSTEM_BULLET) set(USE_MSVC_RUNTIME_LIBRARY_DLL ON CACHE BOOL "" FORCE) endif() - # master on 12 Mar 2021 + # May 7, 2021 include(FetchContent) FetchContent_Declare(bullet - URL https://github.com/bulletphysics/bullet3/archive/87e668f6b2a883b4ef63db8a07c8e9283916e9d9.zip - URL_HASH MD5=9f13246439968494c2b595cf412d83c8 + URL https://github.com/bulletphysics/bullet3/archive/refs/tags/3.17.tar.gz + URL_HASH MD5=7711bce9a49c289a08ecda34eaa0f32e SOURCE_DIR fetched/bullet ) FetchContent_MakeAvailableExcludeFromAll(bullet) diff --git a/extern/osg-ffmpeg-videoplayer/audiodecoder.cpp b/extern/osg-ffmpeg-videoplayer/audiodecoder.cpp index c32794d2a..b5b3b8a3e 100644 --- a/extern/osg-ffmpeg-videoplayer/audiodecoder.cpp +++ b/extern/osg-ffmpeg-videoplayer/audiodecoder.cpp @@ -4,13 +4,20 @@ #include #include +#if defined(_MSC_VER) + #pragma warning (push) + #pragma warning (disable : 4244) +#endif + extern "C" { - #include - #include } +#if defined(_MSC_VER) + #pragma warning (pop) +#endif + #include "videostate.hpp" namespace @@ -255,7 +262,7 @@ size_t MovieAudioDecoder::read(char *stream, size_t len) size_t sampleSize = av_get_bytes_per_sample(mOutputSampleFormat); char* data[1]; data[0] = stream; - av_samples_set_silence((uint8_t**)data, 0, len/sampleSize, 1, mOutputSampleFormat); + av_samples_set_silence((uint8_t**)data, 0, static_cast(len/sampleSize), 1, mOutputSampleFormat); return len; } @@ -276,7 +283,7 @@ size_t MovieAudioDecoder::read(char *stream, size_t len) mFramePos = std::min(mFrameSize, sample_skip); if(sample_skip > 0 || mFrameSize > -sample_skip) - sample_skip -= mFramePos; + sample_skip -= static_cast(mFramePos); continue; } diff --git a/extern/osg-ffmpeg-videoplayer/audiodecoder.hpp b/extern/osg-ffmpeg-videoplayer/audiodecoder.hpp index afb088431..bbb5a2783 100644 --- a/extern/osg-ffmpeg-videoplayer/audiodecoder.hpp +++ b/extern/osg-ffmpeg-videoplayer/audiodecoder.hpp @@ -6,6 +6,11 @@ #include #include +#if defined(_MSC_VER) + #pragma warning (push) + #pragma warning (disable : 4244) +#endif + extern "C" { #include @@ -14,6 +19,10 @@ extern "C" #include } +#if defined(_MSC_VER) + #pragma warning (pop) +#endif + #if defined(_WIN32) && !defined(__MINGW32__) #include diff --git a/extern/osg-ffmpeg-videoplayer/videostate.cpp b/extern/osg-ffmpeg-videoplayer/videostate.cpp index c153aa14c..c3cb412e9 100644 --- a/extern/osg-ffmpeg-videoplayer/videostate.cpp +++ b/extern/osg-ffmpeg-videoplayer/videostate.cpp @@ -9,6 +9,11 @@ #include +#if defined(_MSC_VER) + #pragma warning (push) + #pragma warning (disable : 4244) +#endif + extern "C" { #include @@ -17,6 +22,10 @@ extern "C" #include } +#if defined(_MSC_VER) + #pragma warning (pop) +#endif + static const char* flushString = "FLUSH"; struct FlushPacket : AVPacket { @@ -37,6 +46,31 @@ namespace { const int MAX_AUDIOQ_SIZE = (5 * 16 * 1024); const int MAX_VIDEOQ_SIZE = (5 * 256 * 1024); + + struct AVPacketUnref + { + void operator()(AVPacket* packet) const + { + av_packet_unref(packet); + } + }; + + struct AVFrameFree + { + void operator()(AVFrame* frame) const + { + av_frame_free(&frame); + } + }; + + template + struct AVFree + { + void operator()(T* frame) const + { + av_free(&frame); + } + }; } namespace Video @@ -80,8 +114,7 @@ void VideoState::setAudioFactory(MovieAudioFactory *factory) void PacketQueue::put(AVPacket *pkt) { - AVPacketList *pkt1; - pkt1 = (AVPacketList*)av_malloc(sizeof(AVPacketList)); + std::unique_ptr> pkt1(static_cast(av_malloc(sizeof(AVPacketList)))); if(!pkt1) throw std::bad_alloc(); if(pkt == &flush_pkt) @@ -91,18 +124,16 @@ void PacketQueue::put(AVPacket *pkt) pkt1->next = nullptr; - this->mutex.lock (); - + std::lock_guard lock(this->mutex); + AVPacketList* ptr = pkt1.release(); if(!last_pkt) - this->first_pkt = pkt1; + this->first_pkt = ptr; else - this->last_pkt->next = pkt1; - this->last_pkt = pkt1; + this->last_pkt->next = ptr; + this->last_pkt = ptr; this->nb_packets++; - this->size += pkt1->pkt.size; + this->size += ptr->pkt.size; this->cond.notify_one(); - - this->mutex.unlock(); } int PacketQueue::get(AVPacket *pkt, VideoState *is) @@ -144,7 +175,7 @@ void PacketQueue::clear() { AVPacketList *pkt, *pkt1; - this->mutex.lock(); + std::lock_guard lock(this->mutex); for(pkt = this->first_pkt; pkt != nullptr; pkt = pkt1) { pkt1 = pkt->next; @@ -156,7 +187,6 @@ void PacketQueue::clear() this->first_pkt = nullptr; this->nb_packets = 0; this->size = 0; - this->mutex.unlock (); } int VideoPicture::set_dimensions(int w, int h) { @@ -312,7 +342,7 @@ void VideoState::video_refresh() } -int VideoState::queue_picture(AVFrame *pFrame, double pts) +int VideoState::queue_picture(const AVFrame &pFrame, double pts) { VideoPicture *vp; @@ -325,7 +355,7 @@ int VideoState::queue_picture(AVFrame *pFrame, double pts) if(this->mQuit) return -1; - this->pictq_mutex.lock(); + std::lock_guard lock(this->pictq_mutex); // windex is set to 0 initially vp = &this->pictq[this->pictq_windex]; @@ -333,8 +363,8 @@ int VideoState::queue_picture(AVFrame *pFrame, double pts) // Convert the image into RGBA format // TODO: we could do this in a pixel shader instead, if the source format // matches a commonly used format (ie YUV420P) - const int w = pFrame->width; - const int h = pFrame->height; + const int w = pFrame.width; + const int h = pFrame.height; if(this->sws_context == nullptr || this->sws_context_w != w || this->sws_context_h != h) { if (this->sws_context != nullptr) @@ -350,23 +380,19 @@ int VideoState::queue_picture(AVFrame *pFrame, double pts) vp->pts = pts; if (vp->set_dimensions(w, h) < 0) - { - this->pictq_mutex.unlock(); return -1; - } - sws_scale(this->sws_context, pFrame->data, pFrame->linesize, + sws_scale(this->sws_context, pFrame.data, pFrame.linesize, 0, this->video_ctx->height, vp->rgbaFrame->data, vp->rgbaFrame->linesize); // now we inform our display thread that we have a pic ready this->pictq_windex = (this->pictq_windex+1) % VIDEO_PICTURE_ARRAY_SIZE; this->pictq_size++; - this->pictq_mutex.unlock(); return 0; } -double VideoState::synchronize_video(AVFrame *src_frame, double pts) +double VideoState::synchronize_video(const AVFrame &src_frame, double pts) { double frame_delay; @@ -380,7 +406,7 @@ double VideoState::synchronize_video(AVFrame *src_frame, double pts) frame_delay = av_q2d(this->video_ctx->pkt_timebase); /* if we are repeating a frame, adjust clock accordingly */ - frame_delay += src_frame->repeat_pict * (frame_delay * 0.5); + frame_delay += src_frame.repeat_pict * (frame_delay * 0.5); this->video_clock += frame_delay; return pts; @@ -391,7 +417,17 @@ class VideoThread public: VideoThread(VideoState* self) : mVideoState(self) - , mThread([this] { run(); }) + , mThread([this] + { + try + { + run(); + } + catch(std::exception& e) + { + std::cerr << "An error occurred playing the video: " << e.what () << std::endl; + } + }) { } @@ -403,13 +439,12 @@ public: void run() { VideoState* self = mVideoState; - AVPacket pkt1, *packet = &pkt1; - av_init_packet(packet); - AVFrame *pFrame; + AVPacket packetData; + av_init_packet(&packetData); + std::unique_ptr packet(&packetData); + std::unique_ptr pFrame{av_frame_alloc()}; - pFrame = av_frame_alloc(); - - while(self->videoq.get(packet, self) >= 0) + while(self->videoq.get(packet.get(), self) >= 0) { if(packet->data == flush_pkt.data) { @@ -426,30 +461,26 @@ public: } // Decode video frame - int ret = avcodec_send_packet(self->video_ctx, packet); + int ret = avcodec_send_packet(self->video_ctx, packet.get()); // EAGAIN is not expected if (ret < 0) throw std::runtime_error("Error decoding video frame"); while (!ret) { - ret = avcodec_receive_frame(self->video_ctx, pFrame); + ret = avcodec_receive_frame(self->video_ctx, pFrame.get()); if (!ret) { double pts = pFrame->best_effort_timestamp; pts *= av_q2d((*self->video_st)->time_base); - pts = self->synchronize_video(pFrame, pts); + pts = self->synchronize_video(*pFrame, pts); - if(self->queue_picture(pFrame, pts) < 0) + if(self->queue_picture(*pFrame, pts) < 0) break; } } } - - av_packet_unref(packet); - - av_frame_free(&pFrame); } private: @@ -476,8 +507,9 @@ public: VideoState* self = mVideoState; AVFormatContext *pFormatCtx = self->format_ctx; - AVPacket pkt1, *packet = &pkt1; - av_init_packet(packet); + AVPacket packetData; + av_init_packet(&packetData); + std::unique_ptr packet(&packetData); try { @@ -560,7 +592,7 @@ public: continue; } - if(av_read_frame(pFormatCtx, packet) < 0) + if(av_read_frame(pFormatCtx, packet.get()) < 0) { if (self->audioq.nb_packets == 0 && self->videoq.nb_packets == 0 && self->pictq_size == 0) self->mVideoEnded = true; @@ -571,11 +603,11 @@ public: // Is this a packet from the video stream? if(self->video_st && packet->stream_index == self->video_st-pFormatCtx->streams) - self->videoq.put(packet); + self->videoq.put(packet.get()); else if(self->audio_st && packet->stream_index == self->audio_st-pFormatCtx->streams) - self->audioq.put(packet); + self->audioq.put(packet.get()); else - av_packet_unref(packet); + av_packet_unref(packet.get()); } } catch(std::exception& e) { diff --git a/extern/osg-ffmpeg-videoplayer/videostate.hpp b/extern/osg-ffmpeg-videoplayer/videostate.hpp index 015656084..32a772f29 100644 --- a/extern/osg-ffmpeg-videoplayer/videostate.hpp +++ b/extern/osg-ffmpeg-videoplayer/videostate.hpp @@ -15,6 +15,11 @@ namespace osg class Texture2D; } +#if defined(_MSC_VER) + #pragma warning (push) + #pragma warning (disable : 4244) +#endif + extern "C" { #include @@ -28,6 +33,10 @@ extern "C" #include } +#if defined(_MSC_VER) + #pragma warning (pop) +#endif + #include "videodefs.hpp" #define VIDEO_PICTURE_QUEUE_SIZE 50 @@ -132,8 +141,8 @@ struct VideoState { void video_display(VideoPicture* vp); void video_refresh(); - int queue_picture(AVFrame *pFrame, double pts); - double synchronize_video(AVFrame *src_frame, double pts); + int queue_picture(const AVFrame &pFrame, double pts); + double synchronize_video(const AVFrame &src_frame, double pts); double get_audio_clock(); double get_video_clock(); diff --git a/files/settings-default.cfg b/files/settings-default.cfg index ea678c70f..88a6b4812 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -1,6 +1,7 @@ -# WARNING: If this file is named settings-default.cfg, then editing -# this file might have no effect, as these settings may be overwritten -# by your user settings.cfg file (see documentation for its location). +# WARNING: If this file is named settings-default.cfg or was generated +# from defaults.bin, then editing this file might have no effect, as +# these settings may be overwritten by your user settings.cfg file +# (see documentation for its location). # # This file provides minimal documentation for each setting, and # ranges of recommended values. For detailed explanations of the @@ -14,7 +15,7 @@ [Camera] # Near clipping plane (>0.0, e.g. 0.01 to 18.0). -near clip = 1 +near clip = 3 # Cull objects that occupy less than 'small feature culling pixel size' on the screen. small feature culling = true @@ -908,6 +909,10 @@ max tiles number = 512 # Min time duration for the same tile update in milliseconds (value >= 0) min update interval ms = 250 +# Keep loading screen until navmesh is generated around the player for all tiles within manhattan distance (value >= 0). +# Distance is measured in the number of tiles and can be only an integer value. +wait until min distance to player = 5 + [Shadows] # Enable or disable shadows. Bear in mind that this will force OpenMW to use shaders as if "[Shaders]/force shaders" was set to true. @@ -1071,8 +1076,8 @@ enabled = false # 1.0 means 100% density density = 1.0 -# A maximum distance in cells on which groundcover is rendered. -distance = 1 +# A maximum distance in game units on which groundcover is rendered. +rendering distance = 6144.0 # A minimum size of groundcover chunk in cells (0.125, 0.25, 0.5, 1.0) min chunk size = 0.5 diff --git a/files/ui/advancedpage.ui b/files/ui/advancedpage.ui index 8c3dea4b3..5859f1f38 100644 --- a/files/ui/advancedpage.ui +++ b/files/ui/advancedpage.ui @@ -272,238 +272,244 @@ - - - - - <html><head/><body><p>By default, the fog becomes thicker proportionally to your distance from the clipping plane set at the clipping distance, which causes distortion at the edges of the screen. -This setting makes the fog use the actual eye point distance (or so called Euclidean distance) to calculate the fog, which makes the fog look less artificial, especially if you have a wide FOV.</p></body></html> - - - Radial fog - - - - - - - <html><head/><body><p>Normally environment map reflections aren't affected by lighting, which makes environment-mapped (and thus bump-mapped objects) glow in the dark. -Morrowind Code Patch includes an option to remedy that by doing environment-mapping before applying lighting, this is the equivalent of that option. -Affected objects will use shaders. -</p></body></html> - - - Bump/reflect map local lighting - - - - - - - <html><head/><body><p>Affects side and diagonal movement. Enabling this setting makes movement more realistic.</p><p>If disabled then the whole character's body is pointed to the direction of view. Diagonal movement has no special animation and causes sliding.</p><p>If enabled then the character turns lower body to the direction of movement. Upper body is turned partially. Head is always pointed to the direction of view. In combat mode it works only for diagonal movement. In non-combat mode it changes straight right and straight left movement as well. Also turns the whole body up or down when swimming according to the movement direction.</p></body></html> - - - Turn to movement direction - - - - - - - <html><head/><body><p>Load per-group KF-files and skeleton files from Animations folder</p></body></html> - - - Use additional animation sources - - - - - - - - - Lighting Method: - - - - - - - - legacy - - - - - shaders compatibility - - - - - shaders - - - - - - - - - - <html><head/><body><p>If true, use paging and LOD algorithms to display the entire terrain. If false, only display terrain of the loaded cells.</p></body></html> - - - Distant land - - - - - - - <html><head/><body><p>Use object paging for active cells grid.</p></body></html> - - - Active grid object paging - - - - - - - - - Viewing distance - - - + + + true + + + + + 0 + 0 + 645 + 413 + + + - - - Cells - - - 0.000000000000000 - - - 0.500000000000000 + + + Animations + + + + + <html><head/><body><p>Use casting animations for magic items, just as for spells.</p></body></html> + + + Use magic item animation + + + + + + + <html><head/><body><p>Makes NPCs and player movement more smooth. Recommended to use with "turn to movement direction" enabled.</p></body></html> + + + Smooth movement + + + + + + + <html><head/><body><p>Load per-group KF-files and skeleton files from Animations folder</p></body></html> + + + Use additional animation sources + + + + + + + <html><head/><body><p>Affects side and diagonal movement. Enabling this setting makes movement more realistic.</p><p>If disabled then the whole character's body is pointed to the direction of view. Diagonal movement has no special animation and causes sliding.</p><p>If enabled then the character turns lower body to the direction of movement. Upper body is turned partially. Head is always pointed to the direction of view. In combat mode it works only for diagonal movement. In non-combat mode it changes straight right and straight left movement as well. Also turns the whole body up or down when swimming according to the movement direction.</p></body></html> + + + Turn to movement direction + + + + + + + 20 + + + + + false + + + <html><head/><body><p>Render holstered weapons (with quivers and scabbards), requires modded assets.</p></body></html> + + + Weapon sheathing + + + + + + + false + + + <html><head/><body><p>Render holstered shield, requires modded assets.</p></body></html> + + + Shield sheathing + + + + + + - - - - - - <html><head/><body><p>See 'auto use object normal maps'. Affects terrain.</p></body></html> - - - Auto use terrain normal maps - - - - - - - <html><head/><body><p>If this option is enabled, specular maps are automatically recognized and used if they are named appropriately -(see 'specular map pattern', e.g. for a base texture foo.dds, -the specular map texture would have to be named foo_spec.dds). -If this option is disabled, normal maps are only used if they are explicitly listed within the mesh file -(.osg file, not supported in .nif files). Affects objects.</p></body></html> - - - Auto use object specular maps - - - - - - - <html><head/><body><p>If a file with pattern 'terrain specular map pattern' exists, use that file as a 'diffuse specular' map. The texture must contain the layer colour in the RGB channel (as usual), and a specular multiplier in the alpha channel.</p></body></html> - - - Auto use terrain specular maps - - - - - - - <html><head/><body><p>Makes NPCs and player movement more smooth. Recommended to use with "turn to movement direction" enabled.</p></body></html> - - - Smooth movement - - - - - - - <html><head/><body><p>Use casting animations for magic items, just as for spells.</p></body></html> - - - Use magic item animation - - - - - - - <html><head/><body><p>If this option is enabled, normal maps are automatically recognized and used if they are named appropriately -(see 'normal map pattern', e.g. for a base texture foo.dds, the normal map texture would have to be named foo_n.dds). -If this option is disabled, normal maps are only used if they are explicitly listed within the mesh file (.nif or .osg file). Affects objects.</p></body></html> - - - Auto use object normal maps - - - - - - - 20 - - - - false - - - <html><head/><body><p>Render holstered weapons (with quivers and scabbards), requires modded assets.</p></body></html> - - - Weapon sheathing + + + Shaders + + + + + <html><head/><body><p>If this option is enabled, normal maps are automatically recognized and used if they are named appropriately + (see 'normal map pattern', e.g. for a base texture foo.dds, the normal map texture would have to be named foo_n.dds). + If this option is disabled, normal maps are only used if they are explicitly listed within the mesh file (.nif or .osg file). Affects objects.</p></body></html> + + + Auto use object normal maps + + + + + + + <html><head/><body><p>See 'auto use object normal maps'. Affects terrain.</p></body></html> + + + Auto use terrain normal maps + + + + + + + <html><head/><body><p>If this option is enabled, specular maps are automatically recognized and used if they are named appropriately + (see 'specular map pattern', e.g. for a base texture foo.dds, + the specular map texture would have to be named foo_spec.dds). + If this option is disabled, normal maps are only used if they are explicitly listed within the mesh file + (.osg file, not supported in .nif files). Affects objects.</p></body></html> + + + Auto use object specular maps + + + + + + + <html><head/><body><p>If a file with pattern 'terrain specular map pattern' exists, use that file as a 'diffuse specular' map. The texture must contain the layer colour in the RGB channel (as usual), and a specular multiplier in the alpha channel.</p></body></html> + + + Auto use terrain specular maps + + + + + + + <html><head/><body><p>Normally environment map reflections aren't affected by lighting, which makes environment-mapped (and thus bump-mapped objects) glow in the dark. + Morrowind Code Patch includes an option to remedy that by doing environment-mapping before applying lighting, this is the equivalent of that option. + Affected objects will use shaders. + </p></body></html> + + + Bump/reflect map local lighting + + + + + + + <html><head/><body><p>By default, the fog becomes thicker proportionally to your distance from the clipping plane set at the clipping distance, which causes distortion at the edges of the screen. + This setting makes the fog use the actual eye point distance (or so called Euclidean distance) to calculate the fog, which makes the fog look less artificial, especially if you have a wide FOV.</p></body></html> + + + Radial fog + + + + - - - false - - - <html><head/><body><p>Render holstered shield, requires modded assets.</p></body></html> - - - Shield sheathing + + + Terrain + + + + + + + Viewing distance + + + + + + + Cells + + + 0.000000000000000 + + + 0.500000000000000 + + + + + + + + + <html><head/><body><p>If true, use paging and LOD algorithms to display the entire terrain. If false, only display terrain of the loaded cells.</p></body></html> + + + Distant land + + + + + + + 20 + + + + + <html><head/><body><p>Use object paging for active cells grid.</p></body></html> + + + Active grid object paging + + + + + + - - - - - - - Qt::Vertical - - - - 20 - 40 - - - + + diff --git a/files/ui/graphicspage.ui b/files/ui/graphicspage.ui index 5ec72611f..508a955d7 100644 --- a/files/ui/graphicspage.ui +++ b/files/ui/graphicspage.ui @@ -187,6 +187,56 @@ + + + Lighting + + + + + + + + Lighting Method: + + + + + + + + legacy + + + + + shaders compatibility + + + + + shaders + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + Shadows diff --git a/files/ui/mainwindow.ui b/files/ui/mainwindow.ui index 13e8593e0..54a2d16a1 100644 --- a/files/ui/mainwindow.ui +++ b/files/ui/mainwindow.ui @@ -52,27 +52,13 @@ - - - + + + + + + Qt::Horizontal - - - 0 - - - 0 - - - 0 - - - 0 - - - - -