diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 3c7787dd3..85002cefc 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -37,6 +37,8 @@ Debian_GCC: CC: gcc CXX: g++ CCACHE_SIZE: 3G + # When CCache doesn't exist (e.g. first build on a fork), build takes more than 1h, which is the default for forks. + timeout: 2h Debian_GCC_tests: extends: .Debian @@ -96,7 +98,7 @@ variables: &cs-targets - windows before_script: - Import-Module "$env:ChocolateyInstall\helpers\chocolateyProfile.psm1" - - choco source add -n=openmw-proxy -s="https://repo.openmw.org/repository/Chocolately/" --priority=1 + - choco source add -n=openmw-proxy -s="https://repo.openmw.org/repository/Chocolatey/" --priority=1 - choco install git --force --params "/GitAndUnixToolsOnPath" -y - choco install 7zip -y - choco install cmake.install --installargs 'ADD_CMAKE_TO_PATH=System' -y @@ -120,6 +122,8 @@ variables: &cs-targets Get-ChildItem -Recurse *.pdb | Remove-Item } - 7z a -tzip ..\..\OpenMW_MSVC2019_64_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}.zip '*' + after_script: + - Copy-Item C:\ProgramData\chocolatey\logs\chocolatey.log cache: key: ninja-v2 paths: @@ -186,7 +190,7 @@ Windows_Ninja_CS_RelWithDebInfo: - windows before_script: - Import-Module "$env:ChocolateyInstall\helpers\chocolateyProfile.psm1" - - choco source add -n=openmw-proxy -s="https://repo.openmw.org/repository/Chocolately/" --priority=1 + - choco source add -n=openmw-proxy -s="https://repo.openmw.org/repository/Chocolatey/" --priority=1 - choco install git --force --params "/GitAndUnixToolsOnPath" -y - choco install 7zip -y - choco install cmake.install --installargs 'ADD_CMAKE_TO_PATH=System' -y @@ -208,6 +212,8 @@ Windows_Ninja_CS_RelWithDebInfo: Get-ChildItem -Recurse *.pdb | Remove-Item } - 7z a -tzip ..\..\OpenMW_MSVC2019_64_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}.zip '*' + after_script: + - Copy-Item C:\ProgramData\chocolatey\logs\chocolatey.log cache: key: msbuild-v2 paths: @@ -299,4 +305,4 @@ Debian_AndroidNDK_arm64-v8a: - ccache -s artifacts: paths: - - build/install/ \ No newline at end of file + - build/install/ diff --git a/.travis.yml b/.travis.yml index 2f457798a..e84a9ad71 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,19 +2,12 @@ language: cpp branches: only: - master - - coverity_scan - /openmw-.*$/ -env: - global: - # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created - # via the "travis encrypt" command using the project repo's public key - - secure: "jybGzAdUbqt9vWR/GEnRd96BgAi/7Zd1+2HK68j/i/8+/1YH2XxLOy4Jv/DUBhBlJIkxs/Xv8dRcUlFOclZDHX1d/9Qnsqd3oUVkD7k1y7cTOWy9TBQaE/v/kZo3LpzA3xPwwthrb0BvqIbOfIELi5fS5s8ba85WFRg3AX70wWE=" cache: ccache addons: apt: sources: - sourceline: 'ppa:openmw/openmw' - # - ubuntu-toolchain-r-test # for GCC-10 packages: [ # Dev build-essential, cmake, clang-tools, ccache, @@ -23,32 +16,21 @@ addons: # FFmpeg libavcodec-dev, libavformat-dev, libavutil-dev, libswresample-dev, libswscale-dev, # Audio, Video and Misc. deps - libsdl2-dev, libqt5opengl5-dev, libopenal-dev, libunshield-dev, libtinyxml-dev, liblz4-dev + libsdl2-dev, libqt5opengl5-dev, libopenal-dev, libunshield-dev, libtinyxml-dev, liblz4-dev, # The other ones from OpenMW ppa libbullet-dev, libopenscenegraph-dev, libmygui-dev ] - coverity_scan: # TODO: currently takes too long, disabled openmw/openmw-cs for now. - project: - name: "OpenMW/openmw" - description: "" - branch_pattern: coverity_scan - notification_email: 1122069+psi29a@users.noreply.github.com - build_command_prepend: "cov-configure --comptype gcc --compiler gcc-5 --template; cmake . -DBUILD_OPENMW=FALSE -DBUILD_OPENCS=FALSE" - build_command: "make VERBOSE=1 -j3" matrix: include: - name: OpenMW (all) on MacOS 10.15 with Xcode 11.6 os: osx osx_image: xcode11.6 - if: branch != coverity_scan - name: OpenMW (all) on Ubuntu Focal with GCC os: linux dist: focal - if: branch != coverity_scan - name: OpenMW (tests only) on Ubuntu Focal with GCC os: linux dist: focal - if: branch != coverity_scan env: - BUILD_TESTS_ONLY: 1 - name: OpenMW (openmw) on Ubuntu Focal with Clang's Static Analysis @@ -57,30 +39,21 @@ matrix: env: - MATRIX_EVAL="CC=clang && CXX=clang++" - ANALYZE="scan-build --force-analyze-debug-code --use-cc clang --use-c++ clang++" - if: branch != coverity_scan compiler: clang - - name: OpenMW Components Coverity Scan - os: linux - dist: focal - if: branch = coverity_scan -# allow_failures: -# - name: OpenMW (openmw) on Ubuntu Focal with GCC-10 -# env: -# - MATRIX_EVAL="CC=gcc-10 && CXX=g++-10" before_install: - if [ "${TRAVIS_OS_NAME}" = "linux" ]; then eval "${MATRIX_EVAL}"; fi - - if [ "${COVERITY_SCAN_BRANCH}" != 1 ]; then ./CI/before_install.${TRAVIS_OS_NAME}.sh; fi + - ./CI/before_install.${TRAVIS_OS_NAME}.sh before_script: - ccache -z - - if [ "${COVERITY_SCAN_BRANCH}" != 1 ]; then ./CI/before_script.${TRAVIS_OS_NAME}.sh; fi + - ./CI/before_script.${TRAVIS_OS_NAME}.sh script: - cd ./build - - if [ "${COVERITY_SCAN_BRANCH}" != 1 ]; then ${ANALYZE} make -j3; fi - - if [ "${COVERITY_SCAN_BRANCH}" != 1 ] && [ "${TRAVIS_OS_NAME}" = "osx" ]; then make package; fi - - if [ "${COVERITY_SCAN_BRANCH}" != 1 ] && [ "${TRAVIS_OS_NAME}" = "osx" ]; then ../CI/check_package.osx.sh; fi - - if [ "${COVERITY_SCAN_BRANCH}" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ] && [ "${BUILD_TESTS_ONLY}" ]; then ./openmw_test_suite; fi - - if [ "${COVERITY_SCAN_BRANCH}" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ]; then cd .. && ./CI/check_tabs.sh; fi + - ${ANALYZE} make -j3; + - if [ "${TRAVIS_OS_NAME}" = "osx" ]; then make package; fi + - if [ "${TRAVIS_OS_NAME}" = "osx" ]; then ../CI/check_package.osx.sh; fi + - if [ "${TRAVIS_OS_NAME}" = "linux" ] && [ "${BUILD_TESTS_ONLY}" ]; then ./openmw_test_suite; fi + - if [ "${TRAVIS_OS_NAME}" = "linux" ]; then cd .. && ./CI/check_tabs.sh; fi - cd "${TRAVIS_BUILD_DIR}" - ccache -s deploy: diff --git a/AUTHORS.md b/AUTHORS.md index 25004078e..09ab78412 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -96,6 +96,7 @@ Programmers Jan Borsodi (am0s) Jason Hooks (jhooks) jeaye + jefetienne Jeffrey Haines (Jyby) Jengerer Jiří Kuneš (kunesj) diff --git a/CHANGELOG.md b/CHANGELOG.md index 32862b20b..8f909a19f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,12 +7,15 @@ Bug #2311: Targeted scripts are not properly supported on non-unique RefIDs Bug #2473: Unable to overstock merchants Bug #2798: Mutable ESM records + Bug #2976 [reopened]: Issues combining settings from the command line and both config files Bug #3676: NiParticleColorModifier isn't applied properly Bug #3714: Savegame fails to load due to conflict between SpellState and MagicEffects + Bug #3789: Crash in visitEffectSources while in battle Bug #3862: Random container contents behave differently than vanilla Bug #3929: Leveled list merchant containers respawn on barter Bug #4021: Attributes and skills are not stored as floats Bug #4055: Local scripts don't inherit variables from their base record + Bug #4083: Door animation freezes when colliding with actors Bug #4623: Corprus implementation is incorrect Bug #4631: Setting MSAA level too high doesn't fall back to highest supported level Bug #4764: Data race in osg ParticleSystem @@ -33,6 +36,7 @@ Bug #5403: Enchantment effect doesn't show on an enemy during death animation Bug #5415: Environment maps in ebony cuirass and HiRez Armors Indoril cuirass don't work Bug #5416: Junk non-node records before the root node are not handled gracefully + Bug #5422: The player loses all spells when resurrected Bug #5424: Creatures do not headtrack player Bug #5425: Poison effect only appears for one frame Bug #5427: GetDistance unknown ID error is misleading @@ -56,8 +60,12 @@ Bug #5603: Setting constant effect cast style doesn't correct effects view Bug #5611: Usable items with "0 Uses" should be used only once Bug #5622: Can't properly interact with the console when in pause menu + Bug #5633: Damage Spells in effect before god mode is enabled continue to hurt the player character and can kill them Bug #5639: Tooltips cover Messageboxes Bug #5644: Summon effects running on the player during game initialization cause crashes + Bug #5656: Sneaking characters block hits while standing + Bug #5661: Region sounds don't play at the right interval + Bug #5688: Water shader broken indoors with enable indoor shadows = false Feature #390: 3rd person look "over the shoulder" Feature #2386: Distant Statics in the form of Object Paging Feature #2404: Levelled List can not be placed into a container @@ -78,6 +86,8 @@ Feature #5610: Actors movement should be smoother Feature #5642: Ability to attach arrows to actor skeleton instead of bow mesh Feature #5649: Skyrim SE compressed BSA format support + Feature #5672: Make stretch menu background configuration more accessible + Feature #5692: Improve spell/magic item search to factor in magic effect names Task #5480: Drop Qt4 support Task #5520: Improve cell name autocompleter implementation diff --git a/CI/before_install.osx.sh b/CI/before_install.osx.sh index fd77e0693..85434fa06 100755 --- a/CI/before_install.osx.sh +++ b/CI/before_install.osx.sh @@ -5,8 +5,5 @@ command -v ccache >/dev/null 2>&1 || brew install ccache command -v cmake >/dev/null 2>&1 || brew install cmake command -v qmake >/dev/null 2>&1 || brew install qt -brew link --overwrite lz4 # overwrite system lz4; use brew -brew reinstall lz4 - -curl -fSL -R -J https://downloads.openmw.org/osx/dependencies/openmw-deps-ef2462c.zip -o ~/openmw-deps.zip +curl -fSL -R -J https://downloads.openmw.org/osx/dependencies/openmw-deps-f8918dd.zip -o ~/openmw-deps.zip unzip -o ~/openmw-deps.zip -d /private/tmp/openmw-deps > /dev/null diff --git a/CI/before_script.msvc.sh b/CI/before_script.msvc.sh index da4ec984b..c2d5bc746 100644 --- a/CI/before_script.msvc.sh +++ b/CI/before_script.msvc.sh @@ -917,7 +917,7 @@ printf "LZ4 1.9.2... " printf "Exists. " elif [ -z $SKIP_EXTRACT ]; then rm -rf LZ4_1.9.2 - eval 7z x -y lz4_win${BITS}_v1_9_2.7z -o./LZ4_1.9.2 $STRIP + eval 7z x -y lz4_win${BITS}_v1_9_2.7z -o$(real_pwd)/LZ4_1.9.2 $STRIP fi export LZ4DIR="$(real_pwd)/LZ4_1.9.2" add_cmake_opts -DLZ4_INCLUDE_DIR="${LZ4DIR}/include" \ diff --git a/CI/before_script.osx.sh b/CI/before_script.osx.sh index e11ef1b58..8f9be16e1 100755 --- a/CI/before_script.osx.sh +++ b/CI/before_script.osx.sh @@ -25,5 +25,6 @@ cmake \ -D BUILD_BSATOOL=TRUE \ -D BUILD_ESSIMPORTER=TRUE \ -D BUILD_NIFTEST=TRUE \ +-D BULLET_USE_DOUBLES=TRUE \ -G"Unix Makefiles" \ .. diff --git a/CMakeLists.txt b/CMakeLists.txt index 7ae9c21e7..328fb6970 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,7 @@ project(OpenMW) cmake_minimum_required(VERSION 3.1.0) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) # for link time optimization, remove if cmake version is >= 3.9 if(POLICY CMP0069) @@ -307,7 +309,7 @@ endif() set(Boost_NO_BOOST_CMAKE ON) -find_package(Boost REQUIRED COMPONENTS ${BOOST_COMPONENTS}) +find_package(Boost 1.6.2 REQUIRED COMPONENTS ${BOOST_COMPONENTS}) find_package(MyGUI 3.2.2 REQUIRED) find_package(SDL2 2.0.9 REQUIRED) find_package(OpenAL REQUIRED) @@ -394,9 +396,6 @@ if (NOT WIN32 AND NOT APPLE) "${OpenMW_BINARY_DIR}/org.openmw.cs.desktop") endif() -# CXX Compiler settings -set(CMAKE_CXX_STANDARD 17) - if(OPENMW_LTO_BUILD) if(NOT CMAKE_VERSION VERSION_LESS 3.9) include(CheckIPOSupported) diff --git a/apps/bsatool/bsatool.cpp b/apps/bsatool/bsatool.cpp index ed86a3a42..3afbd777f 100644 --- a/apps/bsatool/bsatool.cpp +++ b/apps/bsatool/bsatool.cpp @@ -6,7 +6,8 @@ #include #include -#include +#include +#include #define BSATOOL_VERSION 1.1 @@ -25,16 +26,6 @@ struct Arguments bool fullpath; }; -void replaceAll(std::string& str, const std::string& needle, const std::string& substitute) -{ - size_t pos = str.find(needle); - while(pos != std::string::npos) - { - str.replace(pos, needle.size(), substitute); - pos = str.find(needle); - } -} - bool parseOptions (int argc, char** argv, Arguments &info) { bpo::options_description desc("Inspect and extract files from Bethesda BSA archives\n\n" @@ -144,9 +135,9 @@ bool parseOptions (int argc, char** argv, Arguments &info) return true; } -int list(Bsa::BSAFile& bsa, Arguments& info); -int extract(Bsa::BSAFile& bsa, Arguments& info); -int extractAll(Bsa::BSAFile& bsa, Arguments& info); +int list(std::unique_ptr& bsa, Arguments& info); +int extract(std::unique_ptr& bsa, Arguments& info); +int extractAll(std::unique_ptr& bsa, Arguments& info); int main(int argc, char** argv) { @@ -157,8 +148,16 @@ int main(int argc, char** argv) return 1; // Open file - Bsa::BSAFile bsa; - bsa.open(info.filename); + std::unique_ptr bsa; + + Bsa::BsaVersion bsaVersion = Bsa::CompressedBSAFile::detectVersion(info.filename); + + if (bsaVersion == Bsa::BSAVER_COMPRESSED) + bsa = std::make_unique(Bsa::CompressedBSAFile()); + else + bsa = std::make_unique(Bsa::BSAFile()); + + bsa->open(info.filename); if (info.mode == "list") return list(bsa, info); @@ -179,10 +178,10 @@ int main(int argc, char** argv) } } -int list(Bsa::BSAFile& bsa, Arguments& info) +int list(std::unique_ptr& bsa, Arguments& info) { // List all files - const Bsa::BSAFile::FileList &files = bsa.getList(); + const Bsa::BSAFile::FileList &files = bsa->getList(); for (const auto& file : files) { if(info.longformat) @@ -201,15 +200,15 @@ int list(Bsa::BSAFile& bsa, Arguments& info) return 0; } -int extract(Bsa::BSAFile& bsa, Arguments& info) +int extract(std::unique_ptr& bsa, Arguments& info) { std::string archivePath = info.extractfile; - replaceAll(archivePath, "/", "\\"); + Misc::StringUtils::replaceAll(archivePath, "/", "\\"); std::string extractPath = info.extractfile; - replaceAll(extractPath, "\\", "/"); + Misc::StringUtils::replaceAll(extractPath, "\\", "/"); - if (!bsa.exists(archivePath.c_str())) + if (!bsa->exists(archivePath.c_str())) { std::cout << "ERROR: file '" << archivePath << "' not found\n"; std::cout << "In archive: " << info.filename << std::endl; @@ -237,7 +236,7 @@ int extract(Bsa::BSAFile& bsa, Arguments& info) } // Get a stream for the file to extract - Files::IStreamPtr stream = bsa.getFile(archivePath.c_str()); + Files::IStreamPtr stream = bsa->getFile(archivePath.c_str()); bfs::ofstream out(target, std::ios::binary); @@ -250,12 +249,12 @@ int extract(Bsa::BSAFile& bsa, Arguments& info) return 0; } -int extractAll(Bsa::BSAFile& bsa, Arguments& info) +int extractAll(std::unique_ptr& bsa, Arguments& info) { - for (const auto &file : bsa.getList()) + for (const auto &file : bsa->getList()) { std::string extractPath(file.name); - replaceAll(extractPath, "\\", "/"); + Misc::StringUtils::replaceAll(extractPath, "\\", "/"); // Get the target path (the path the file will be extracted to) bfs::path target (info.outdir); @@ -273,7 +272,7 @@ int extractAll(Bsa::BSAFile& bsa, Arguments& info) // Get a stream for the file to extract // (inefficient because getFile iter on the list again) - Files::IStreamPtr data = bsa.getFile(file.name); + Files::IStreamPtr data = bsa->getFile(file.name); bfs::ofstream out(target, std::ios::binary); // Write the file to disk diff --git a/apps/launcher/advancedpage.cpp b/apps/launcher/advancedpage.cpp index 050ab8d21..b35e78639 100644 --- a/apps/launcher/advancedpage.cpp +++ b/apps/launcher/advancedpage.cpp @@ -152,6 +152,7 @@ bool Launcher::AdvancedPage::loadSettings() // 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"); } // Bug fixes @@ -277,6 +278,7 @@ void Launcher::AdvancedPage::saveSettings() int showOwnedCurrentIndex = showOwnedComboBox->currentIndex(); if (showOwnedCurrentIndex != mEngineSettings.getInt("show owned", "Game")) mEngineSettings.setInt("show owned", "Game", showOwnedCurrentIndex); + saveSettingBool(stretchBackgroundCheckBox, "stretch menu background", "GUI"); } // Bug fixes diff --git a/apps/launcher/maindialog.cpp b/apps/launcher/maindialog.cpp index d88a41117..df2ba6891 100644 --- a/apps/launcher/maindialog.cpp +++ b/apps/launcher/maindialog.cpp @@ -211,10 +211,10 @@ void Launcher::MainDialog::setVersionLabel() versionLabel->setText(tr("OpenMW development (%1)").arg(revision.left(10))); // Add the compile date and time - versionLabel->setToolTip(tr("Compiled on %1 %2").arg(QLocale(QLocale::C).toDate(QString(__DATE__).simplified(), - QLatin1String("MMM d yyyy")).toString(Qt::SystemLocaleLongDate), - QLocale(QLocale::C).toTime(QString(__TIME__).simplified(), - QLatin1String("hh:mm:ss")).toString(Qt::SystemLocaleShortDate))); + auto compileDate = QLocale(QLocale::C).toDate(QString(__DATE__).simplified(), QLatin1String("MMM d yyyy")); + auto compileTime = QLocale(QLocale::C).toTime(QString(__TIME__).simplified(), QLatin1String("hh:mm:ss")); + versionLabel->setToolTip(tr("Compiled on %1 %2").arg(QLocale::system().toString(compileDate, QLocale::LongFormat), + QLocale::system().toString(compileTime, QLocale::ShortFormat))); } bool Launcher::MainDialog::setup() diff --git a/apps/mwiniimporter/importer.cpp b/apps/mwiniimporter/importer.cpp index 600b4917a..23aea2deb 100644 --- a/apps/mwiniimporter/importer.cpp +++ b/apps/mwiniimporter/importer.cpp @@ -645,7 +645,7 @@ MwIniImporter::MwIniImporter() } for(int i=0; fallback[i]; i++) { - mMergeFallback.push_back(fallback[i]); + mMergeFallback.emplace_back(fallback[i]); } } @@ -910,7 +910,7 @@ void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini, co std::time_t time = lastWriteTime(path, defaultTime); if (time != defaultTime) { - contentFiles.push_back({time, path}); + contentFiles.emplace_back(time, std::move(path)); found = true; break; } @@ -985,14 +985,7 @@ std::time_t MwIniImporter::lastWriteTime(const boost::filesystem::path& filename std::time_t writeTime(defaultTime); if (boost::filesystem::exists(filename)) { - // FixMe: remove #if when Boost dependency for Linux builds updated - // This allows Linux to build until then -#if (BOOST_VERSION >= 104800) - // need to resolve any symlinks so that we get time of file, not symlink boost::filesystem::path resolved = boost::filesystem::canonical(filename); -#else - boost::filesystem::path resolved = filename; -#endif writeTime = boost::filesystem::last_write_time(resolved); // print timestamp diff --git a/apps/niftest/niftest.cpp b/apps/niftest/niftest.cpp index f848ae330..e9484d5f5 100644 --- a/apps/niftest/niftest.cpp +++ b/apps/niftest/niftest.cpp @@ -132,6 +132,7 @@ int main(int argc, char **argv) if(!parseOptions (argc, argv, files)) return 1; + Nif::NIFFile::setLoadUnsupportedFiles(true); // std::cout << "Reading Files" << std::endl; for(std::vector::const_iterator it=files.begin(); it!=files.end(); ++it) { diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index 0c3d006c4..3f53a523f 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -89,10 +89,10 @@ std::pair > CS::Editor::readConfi desc.add_options() ("data", boost::program_options::value()->default_value(Files::EscapePathContainer(), "data")->multitoken()->composing()) - ("data-local", boost::program_options::value()->default_value("")) + ("data-local", boost::program_options::value()->default_value(Files::EscapePath(), "")) ("fs-strict", boost::program_options::value()->implicit_value(true)->default_value(false)) ("encoding", boost::program_options::value()->default_value("win1252")) - ("resources", boost::program_options::value()->default_value("resources")) + ("resources", boost::program_options::value()->default_value(Files::EscapePath(), "resources")) ("fallback-archive", boost::program_options::value()-> default_value(Files::EscapeStringVector(), "fallback-archive")->multitoken()) ("fallback", boost::program_options::value()->default_value(FallbackMap(), "") @@ -112,7 +112,7 @@ std::pair > CS::Editor::readConfi mDocumentManager.setEncoding(ToUTF8::calculateEncoding(mEncodingName)); mFileDialog.setEncoding (QString::fromUtf8(mEncodingName.c_str())); - mDocumentManager.setResourceDir (mResources = variables["resources"].as().toStdString()); + mDocumentManager.setResourceDir (mResources = variables["resources"].as().mPath); if (variables["script-blacklist-use"].as()) mDocumentManager.setBlacklistedScripts ( @@ -125,14 +125,9 @@ std::pair > CS::Editor::readConfi dataDirs = Files::PathContainer(Files::EscapePath::toPathContainer(variables["data"].as())); } - std::string local = variables["data-local"].as().toStdString(); + Files::PathContainer::value_type local(variables["data-local"].as().mPath); if (!local.empty()) - { - if (local.front() == '\"') - local = local.substr(1, local.length() - 2); - - dataLocal.push_back(Files::PathContainer::value_type(local)); - } + dataLocal.push_back(local); mCfgMgr.processPaths (dataDirs); mCfgMgr.processPaths (dataLocal, true); @@ -229,7 +224,7 @@ void CS::Editor::openFiles (const boost::filesystem::path &savePath, const std:: if(discoveredFiles.empty()) { for (const QString &path : mFileDialog.selectedFilePaths()) - files.push_back(path.toUtf8().constData()); + files.emplace_back(path.toUtf8().constData()); } else { @@ -246,7 +241,7 @@ void CS::Editor::createNewFile (const boost::filesystem::path &savePath) std::vector files; for (const QString &path : mFileDialog.selectedFilePaths()) { - files.push_back(path.toUtf8().constData()); + files.emplace_back(path.toUtf8().constData()); } files.push_back (savePath); diff --git a/apps/opencs/main.cpp b/apps/opencs/main.cpp index d1d4944cf..5287c8b19 100644 --- a/apps/opencs/main.cpp +++ b/apps/opencs/main.cpp @@ -47,9 +47,6 @@ int runApplication(int argc, char *argv[]) setenv("OSG_GL_TEXTURE_STORAGE", "OFF", 0); #endif - // To allow background thread drawing in OSG - QApplication::setAttribute(Qt::AA_X11InitThreads, true); - Q_INIT_RESOURCE (resources); qRegisterMetaType ("std::string"); diff --git a/apps/opencs/model/doc/loader.cpp b/apps/opencs/model/doc/loader.cpp index 44d6883ef..69c78bd5e 100644 --- a/apps/opencs/model/doc/loader.cpp +++ b/apps/opencs/model/doc/loader.cpp @@ -127,7 +127,7 @@ void CSMDoc::Loader::load() void CSMDoc::Loader::loadDocument (CSMDoc::Document *document) { - mDocuments.push_back (std::make_pair (document, Stage())); + mDocuments.emplace_back (document, Stage()); } void CSMDoc::Loader::abortLoading (CSMDoc::Document *document) diff --git a/apps/opencs/model/doc/operation.cpp b/apps/opencs/model/doc/operation.cpp index a27ee9f51..218e13e38 100644 --- a/apps/opencs/model/doc/operation.cpp +++ b/apps/opencs/model/doc/operation.cpp @@ -57,7 +57,7 @@ void CSMDoc::Operation::run() void CSMDoc::Operation::appendStage (Stage *stage) { - mStages.push_back (std::make_pair (stage, 0)); + mStages.emplace_back (stage, 0); } void CSMDoc::Operation::setDefaultSeverity (Message::Severity severity) diff --git a/apps/opencs/model/prefs/enumsetting.cpp b/apps/opencs/model/prefs/enumsetting.cpp index 226b17173..62cac062a 100644 --- a/apps/opencs/model/prefs/enumsetting.cpp +++ b/apps/opencs/model/prefs/enumsetting.cpp @@ -35,7 +35,7 @@ CSMPrefs::EnumValues& CSMPrefs::EnumValues::add (const EnumValue& value) CSMPrefs::EnumValues& CSMPrefs::EnumValues::add (const std::string& value, const std::string& tooltip) { - mValues.push_back (EnumValue (value, tooltip)); + mValues.emplace_back(value, tooltip); return *this; } diff --git a/apps/opencs/model/prefs/shortcuteventhandler.cpp b/apps/opencs/model/prefs/shortcuteventhandler.cpp index 07c48fcaa..a4102e1db 100644 --- a/apps/opencs/model/prefs/shortcuteventhandler.cpp +++ b/apps/opencs/model/prefs/shortcuteventhandler.cpp @@ -182,7 +182,7 @@ namespace CSMPrefs } else if (pos == lastPos) { - potentials.push_back(std::make_pair(result, shortcut)); + potentials.emplace_back(result, shortcut); } } } diff --git a/apps/opencs/model/world/commanddispatcher.cpp b/apps/opencs/model/world/commanddispatcher.cpp index 2493813a6..36b3ba2e0 100644 --- a/apps/opencs/model/world/commanddispatcher.cpp +++ b/apps/opencs/model/world/commanddispatcher.cpp @@ -127,7 +127,7 @@ std::vector CSMWorld::CommandDispatcher::getExtendedTypes if (mId==UniversalId::Type_Cells) { tables.push_back (mId); - tables.push_back (UniversalId::Type_References); + tables.emplace_back(UniversalId::Type_References); /// \todo add other cell-specific types } diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index bc5a8e69d..27d60ae98 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -355,7 +355,7 @@ CSMWorld::LandTextureIdTable::ImportResults CSMWorld::LandTextureIdTable::import // If it does not exist or it is in the current plugin, it can be skipped. if (oldRow < 0 || plugin == 0) { - results.recordMapping.push_back(std::make_pair(id, id)); + results.recordMapping.emplace_back(id, id); continue; } @@ -366,7 +366,7 @@ CSMWorld::LandTextureIdTable::ImportResults CSMWorld::LandTextureIdTable::import auto searchIt = reverseLookupMap.find(texture); if (searchIt != reverseLookupMap.end()) { - results.recordMapping.push_back(std::make_pair(id, searchIt->second)); + results.recordMapping.emplace_back(id, searchIt->second); continue; } @@ -381,7 +381,7 @@ CSMWorld::LandTextureIdTable::ImportResults CSMWorld::LandTextureIdTable::import // Id not taken, clone it cloneRecord(id, newId, UniversalId::Type_LandTexture); results.createdRecords.push_back(newId); - results.recordMapping.push_back(std::make_pair(id, newId)); + results.recordMapping.emplace_back(id, newId); reverseLookupMap.emplace(texture, newId); break; } diff --git a/apps/opencs/model/world/refcollection.cpp b/apps/opencs/model/world/refcollection.cpp index 1d91bbd59..d8f6b391b 100644 --- a/apps/opencs/model/world/refcollection.cpp +++ b/apps/opencs/model/world/refcollection.cpp @@ -64,6 +64,7 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool // ignore content file number std::map::iterator iter = cache.begin(); + ref.mRefNum.mIndex = ref.mRefNum.mIndex & 0x00ffffff; for (; iter != cache.end(); ++iter) { if (ref.mRefNum.mIndex == iter->first.mIndex) diff --git a/apps/opencs/model/world/refidcollection.cpp b/apps/opencs/model/world/refidcollection.cpp index 3502d6319..535a31ddd 100644 --- a/apps/opencs/model/world/refidcollection.cpp +++ b/apps/opencs/model/world/refidcollection.cpp @@ -40,45 +40,45 @@ CSMWorld::RefIdCollection::RefIdCollection() { BaseColumns baseColumns; - mColumns.push_back (RefIdColumn (Columns::ColumnId_Id, ColumnBase::Display_Id, - ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue, false, false)); + mColumns.emplace_back(Columns::ColumnId_Id, ColumnBase::Display_Id, + ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue, false, false); baseColumns.mId = &mColumns.back(); - mColumns.push_back (RefIdColumn (Columns::ColumnId_Modification, ColumnBase::Display_RecordState, - ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue, true, false)); + mColumns.emplace_back(Columns::ColumnId_Modification, ColumnBase::Display_RecordState, + ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue, true, false); baseColumns.mModified = &mColumns.back(); - mColumns.push_back (RefIdColumn (Columns::ColumnId_RecordType, ColumnBase::Display_RefRecordType, - ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue, false, false)); + mColumns.emplace_back(Columns::ColumnId_RecordType, ColumnBase::Display_RefRecordType, + ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue, false, false); baseColumns.mType = &mColumns.back(); ModelColumns modelColumns (baseColumns); - mColumns.push_back (RefIdColumn (Columns::ColumnId_Model, ColumnBase::Display_Mesh)); + mColumns.emplace_back(Columns::ColumnId_Model, ColumnBase::Display_Mesh); modelColumns.mModel = &mColumns.back(); NameColumns nameColumns (modelColumns); - mColumns.push_back (RefIdColumn (Columns::ColumnId_Name, ColumnBase::Display_String)); + mColumns.emplace_back(Columns::ColumnId_Name, ColumnBase::Display_String); nameColumns.mName = &mColumns.back(); - mColumns.push_back (RefIdColumn (Columns::ColumnId_Script, ColumnBase::Display_Script)); + mColumns.emplace_back(Columns::ColumnId_Script, ColumnBase::Display_Script); nameColumns.mScript = &mColumns.back(); InventoryColumns inventoryColumns (nameColumns); - mColumns.push_back (RefIdColumn (Columns::ColumnId_Icon, ColumnBase::Display_Icon)); + mColumns.emplace_back(Columns::ColumnId_Icon, ColumnBase::Display_Icon); inventoryColumns.mIcon = &mColumns.back(); - mColumns.push_back (RefIdColumn (Columns::ColumnId_Weight, ColumnBase::Display_Float)); + mColumns.emplace_back(Columns::ColumnId_Weight, ColumnBase::Display_Float); inventoryColumns.mWeight = &mColumns.back(); - mColumns.push_back (RefIdColumn (Columns::ColumnId_CoinValue, ColumnBase::Display_Integer)); + mColumns.emplace_back(Columns::ColumnId_CoinValue, ColumnBase::Display_Integer); inventoryColumns.mValue = &mColumns.back(); IngredientColumns ingredientColumns (inventoryColumns); - mColumns.push_back (RefIdColumn (Columns::ColumnId_EffectList, - ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue)); + mColumns.emplace_back(Columns::ColumnId_EffectList, + ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue); ingredientColumns.mEffects = &mColumns.back(); std::map ingredientEffectsMap; ingredientEffectsMap.insert(std::make_pair(UniversalId::Type_Ingredient, new IngredEffectRefIdAdapter ())); - mNestedAdapters.push_back (std::make_pair(&mColumns.back(), ingredientEffectsMap)); + mNestedAdapters.emplace_back(&mColumns.back(), ingredientEffectsMap); mColumns.back().addColumn( new NestedChildColumn (Columns::ColumnId_EffectId, ColumnBase::Display_IngredEffectId)); mColumns.back().addColumn( @@ -88,13 +88,13 @@ CSMWorld::RefIdCollection::RefIdCollection() // nested table PotionColumns potionColumns (inventoryColumns); - mColumns.push_back (RefIdColumn (Columns::ColumnId_EffectList, - ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue)); + mColumns.emplace_back(Columns::ColumnId_EffectList, + ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue); potionColumns.mEffects = &mColumns.back(); // see refidadapterimp.hpp std::map effectsMap; effectsMap.insert(std::make_pair(UniversalId::Type_Potion, new EffectsRefIdAdapter (UniversalId::Type_Potion))); - mNestedAdapters.push_back (std::make_pair(&mColumns.back(), effectsMap)); + mNestedAdapters.emplace_back(&mColumns.back(), effectsMap); mColumns.back().addColumn( new NestedChildColumn (Columns::ColumnId_EffectId, ColumnBase::Display_EffectId)); mColumns.back().addColumn( @@ -114,67 +114,67 @@ CSMWorld::RefIdCollection::RefIdCollection() EnchantableColumns enchantableColumns (inventoryColumns); - mColumns.push_back (RefIdColumn (Columns::ColumnId_Enchantment, ColumnBase::Display_Enchantment)); + mColumns.emplace_back(Columns::ColumnId_Enchantment, ColumnBase::Display_Enchantment); enchantableColumns.mEnchantment = &mColumns.back(); - mColumns.push_back (RefIdColumn (Columns::ColumnId_EnchantmentPoints, ColumnBase::Display_Integer)); + mColumns.emplace_back(Columns::ColumnId_EnchantmentPoints, ColumnBase::Display_Integer); enchantableColumns.mEnchantmentPoints = &mColumns.back(); ToolColumns toolsColumns (inventoryColumns); - mColumns.push_back (RefIdColumn (Columns::ColumnId_Quality, ColumnBase::Display_Float)); + mColumns.emplace_back(Columns::ColumnId_Quality, ColumnBase::Display_Float); toolsColumns.mQuality = &mColumns.back(); - mColumns.push_back (RefIdColumn (Columns::ColumnId_Charges, ColumnBase::Display_Integer)); + mColumns.emplace_back(Columns::ColumnId_Charges, ColumnBase::Display_Integer); toolsColumns.mUses = &mColumns.back(); ActorColumns actorsColumns (nameColumns); - mColumns.push_back (RefIdColumn (Columns::ColumnId_AiHello, ColumnBase::Display_UnsignedInteger16)); + mColumns.emplace_back(Columns::ColumnId_AiHello, ColumnBase::Display_UnsignedInteger16); actorsColumns.mHello = &mColumns.back(); - mColumns.push_back (RefIdColumn (Columns::ColumnId_AiFlee, ColumnBase::Display_UnsignedInteger8)); + mColumns.emplace_back(Columns::ColumnId_AiFlee, ColumnBase::Display_UnsignedInteger8); actorsColumns.mFlee = &mColumns.back(); - mColumns.push_back (RefIdColumn (Columns::ColumnId_AiFight, ColumnBase::Display_UnsignedInteger8)); + mColumns.emplace_back(Columns::ColumnId_AiFight, ColumnBase::Display_UnsignedInteger8); actorsColumns.mFight = &mColumns.back(); - mColumns.push_back (RefIdColumn (Columns::ColumnId_AiAlarm, ColumnBase::Display_UnsignedInteger8)); + mColumns.emplace_back(Columns::ColumnId_AiAlarm, ColumnBase::Display_UnsignedInteger8); actorsColumns.mAlarm = &mColumns.back(); // Nested table - mColumns.push_back(RefIdColumn (Columns::ColumnId_ActorInventory, - ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue)); + mColumns.emplace_back(Columns::ColumnId_ActorInventory, + ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue); actorsColumns.mInventory = &mColumns.back(); std::map inventoryMap; inventoryMap.insert(std::make_pair(UniversalId::Type_Npc, new NestedInventoryRefIdAdapter (UniversalId::Type_Npc))); inventoryMap.insert(std::make_pair(UniversalId::Type_Creature, new NestedInventoryRefIdAdapter (UniversalId::Type_Creature))); - mNestedAdapters.push_back (std::make_pair(&mColumns.back(), inventoryMap)); + mNestedAdapters.emplace_back(&mColumns.back(), inventoryMap); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_InventoryItemId, CSMWorld::ColumnBase::Display_Referenceable)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_ItemCount, CSMWorld::ColumnBase::Display_Integer)); // Nested table - mColumns.push_back(RefIdColumn (Columns::ColumnId_SpellList, - ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue)); + mColumns.emplace_back(Columns::ColumnId_SpellList, + ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue); actorsColumns.mSpells = &mColumns.back(); std::map spellsMap; spellsMap.insert(std::make_pair(UniversalId::Type_Npc, new NestedSpellRefIdAdapter (UniversalId::Type_Npc))); spellsMap.insert(std::make_pair(UniversalId::Type_Creature, new NestedSpellRefIdAdapter (UniversalId::Type_Creature))); - mNestedAdapters.push_back (std::make_pair(&mColumns.back(), spellsMap)); + mNestedAdapters.emplace_back(&mColumns.back(), spellsMap); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_SpellId, CSMWorld::ColumnBase::Display_Spell)); // Nested table - mColumns.push_back(RefIdColumn (Columns::ColumnId_NpcDestinations, - ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue)); + mColumns.emplace_back(Columns::ColumnId_NpcDestinations, + ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue); actorsColumns.mDestinations = &mColumns.back(); std::map destMap; destMap.insert(std::make_pair(UniversalId::Type_Npc, new NestedTravelRefIdAdapter (UniversalId::Type_Npc))); destMap.insert(std::make_pair(UniversalId::Type_Creature, new NestedTravelRefIdAdapter (UniversalId::Type_Creature))); - mNestedAdapters.push_back (std::make_pair(&mColumns.back(), destMap)); + mNestedAdapters.emplace_back(&mColumns.back(), destMap); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_DestinationCell, CSMWorld::ColumnBase::Display_Cell)); mColumns.back().addColumn( @@ -191,15 +191,15 @@ CSMWorld::RefIdCollection::RefIdCollection() new RefIdColumn (Columns::ColumnId_RotZ, CSMWorld::ColumnBase::Display_Double)); // Nested table - mColumns.push_back(RefIdColumn (Columns::ColumnId_AiPackageList, - ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue)); + mColumns.emplace_back(Columns::ColumnId_AiPackageList, + ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue); actorsColumns.mAiPackages = &mColumns.back(); std::map aiMap; aiMap.insert(std::make_pair(UniversalId::Type_Npc, new ActorAiRefIdAdapter (UniversalId::Type_Npc))); aiMap.insert(std::make_pair(UniversalId::Type_Creature, new ActorAiRefIdAdapter (UniversalId::Type_Creature))); - mNestedAdapters.push_back (std::make_pair(&mColumns.back(), aiMap)); + mNestedAdapters.emplace_back(&mColumns.back(), aiMap); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_AiPackageType, CSMWorld::ColumnBase::Display_AiPackageType)); mColumns.back().addColumn( @@ -270,56 +270,56 @@ CSMWorld::RefIdCollection::RefIdCollection() for (int i=0; sServiceTable[i].mName!=-1; ++i) { - mColumns.push_back (RefIdColumn (sServiceTable[i].mName, ColumnBase::Display_Boolean)); + mColumns.emplace_back(sServiceTable[i].mName, ColumnBase::Display_Boolean); actorsColumns.mServices.insert (std::make_pair (&mColumns.back(), sServiceTable[i].mFlag)); } - mColumns.push_back (RefIdColumn (Columns::ColumnId_AutoCalc, ColumnBase::Display_Boolean, - ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_Refresh)); + mColumns.emplace_back(Columns::ColumnId_AutoCalc, ColumnBase::Display_Boolean, + ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_Refresh); const RefIdColumn *autoCalc = &mColumns.back(); - mColumns.push_back (RefIdColumn (Columns::ColumnId_ApparatusType, - ColumnBase::Display_ApparatusType)); + mColumns.emplace_back(Columns::ColumnId_ApparatusType, + ColumnBase::Display_ApparatusType); const RefIdColumn *apparatusType = &mColumns.back(); - mColumns.push_back (RefIdColumn (Columns::ColumnId_ArmorType, ColumnBase::Display_ArmorType)); + mColumns.emplace_back(Columns::ColumnId_ArmorType, ColumnBase::Display_ArmorType); const RefIdColumn *armorType = &mColumns.back(); - mColumns.push_back (RefIdColumn (Columns::ColumnId_Health, ColumnBase::Display_Integer)); + mColumns.emplace_back(Columns::ColumnId_Health, ColumnBase::Display_Integer); const RefIdColumn *health = &mColumns.back(); - mColumns.push_back (RefIdColumn (Columns::ColumnId_ArmorValue, ColumnBase::Display_Integer)); + mColumns.emplace_back(Columns::ColumnId_ArmorValue, ColumnBase::Display_Integer); const RefIdColumn *armor = &mColumns.back(); - mColumns.push_back (RefIdColumn (Columns::ColumnId_BookType, ColumnBase::Display_BookType)); + mColumns.emplace_back(Columns::ColumnId_BookType, ColumnBase::Display_BookType); const RefIdColumn *bookType = &mColumns.back(); - mColumns.push_back (RefIdColumn (Columns::ColumnId_Skill, ColumnBase::Display_SkillId)); + mColumns.emplace_back(Columns::ColumnId_Skill, ColumnBase::Display_SkillId); const RefIdColumn *skill = &mColumns.back(); - mColumns.push_back (RefIdColumn (Columns::ColumnId_Text, ColumnBase::Display_LongString)); + mColumns.emplace_back(Columns::ColumnId_Text, ColumnBase::Display_LongString); const RefIdColumn *text = &mColumns.back(); - mColumns.push_back (RefIdColumn (Columns::ColumnId_ClothingType, ColumnBase::Display_ClothingType)); + mColumns.emplace_back(Columns::ColumnId_ClothingType, ColumnBase::Display_ClothingType); const RefIdColumn *clothingType = &mColumns.back(); - mColumns.push_back (RefIdColumn (Columns::ColumnId_WeightCapacity, ColumnBase::Display_Float)); + mColumns.emplace_back(Columns::ColumnId_WeightCapacity, ColumnBase::Display_Float); const RefIdColumn *weightCapacity = &mColumns.back(); - mColumns.push_back (RefIdColumn (Columns::ColumnId_OrganicContainer, ColumnBase::Display_Boolean)); + mColumns.emplace_back(Columns::ColumnId_OrganicContainer, ColumnBase::Display_Boolean); const RefIdColumn *organic = &mColumns.back(); - mColumns.push_back (RefIdColumn (Columns::ColumnId_Respawn, ColumnBase::Display_Boolean)); + mColumns.emplace_back(Columns::ColumnId_Respawn, ColumnBase::Display_Boolean); const RefIdColumn *respawn = &mColumns.back(); // Nested table - mColumns.push_back(RefIdColumn (Columns::ColumnId_ContainerContent, - ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue)); + mColumns.emplace_back(Columns::ColumnId_ContainerContent, + ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue); const RefIdColumn *content = &mColumns.back(); std::map contMap; contMap.insert(std::make_pair(UniversalId::Type_Container, new NestedInventoryRefIdAdapter (UniversalId::Type_Container))); - mNestedAdapters.push_back (std::make_pair(&mColumns.back(), contMap)); + mNestedAdapters.emplace_back(&mColumns.back(), contMap); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_InventoryItemId, CSMWorld::ColumnBase::Display_Referenceable)); mColumns.back().addColumn( @@ -327,11 +327,11 @@ CSMWorld::RefIdCollection::RefIdCollection() CreatureColumns creatureColumns (actorsColumns); - mColumns.push_back (RefIdColumn (Columns::ColumnId_CreatureType, ColumnBase::Display_CreatureType)); + mColumns.emplace_back(Columns::ColumnId_CreatureType, ColumnBase::Display_CreatureType); creatureColumns.mType = &mColumns.back(); - mColumns.push_back (RefIdColumn (Columns::ColumnId_Scale, ColumnBase::Display_Float)); + mColumns.emplace_back(Columns::ColumnId_Scale, ColumnBase::Display_Float); creatureColumns.mScale = &mColumns.back(); - mColumns.push_back (RefIdColumn (Columns::ColumnId_ParentCreature, ColumnBase::Display_Creature)); + mColumns.emplace_back(Columns::ColumnId_ParentCreature, ColumnBase::Display_Creature); creatureColumns.mOriginal = &mColumns.back(); static const struct @@ -354,7 +354,7 @@ CSMWorld::RefIdCollection::RefIdCollection() for (int i=0; sCreatureFlagTable[i].mName!=-1; ++i) { - mColumns.push_back (RefIdColumn (sCreatureFlagTable[i].mName, ColumnBase::Display_Boolean)); + mColumns.emplace_back(sCreatureFlagTable[i].mName, ColumnBase::Display_Boolean); creatureColumns.mFlags.insert (std::make_pair (&mColumns.back(), sCreatureFlagTable[i].mFlag)); switch (sCreatureFlagTable[i].mFlag) @@ -363,7 +363,7 @@ CSMWorld::RefIdCollection::RefIdCollection() } } - mColumns.push_back(RefIdColumn(Columns::ColumnId_BloodType, ColumnBase::Display_BloodType)); + mColumns.emplace_back(Columns::ColumnId_BloodType, ColumnBase::Display_BloodType); // For re-use in NPC records. const RefIdColumn *bloodType = &mColumns.back(); creatureColumns.mBloodType = bloodType; @@ -371,24 +371,24 @@ CSMWorld::RefIdCollection::RefIdCollection() creatureColumns.mFlags.insert (std::make_pair (respawn, ESM::Creature::Respawn)); // Nested table - mColumns.push_back(RefIdColumn (Columns::ColumnId_CreatureAttributes, - ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue)); + mColumns.emplace_back(Columns::ColumnId_CreatureAttributes, + ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue); creatureColumns.mAttributes = &mColumns.back(); std::map creaAttrMap; creaAttrMap.insert(std::make_pair(UniversalId::Type_Creature, new CreatureAttributesRefIdAdapter())); - mNestedAdapters.push_back (std::make_pair(&mColumns.back(), creaAttrMap)); + mNestedAdapters.emplace_back(&mColumns.back(), creaAttrMap); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_Attribute, CSMWorld::ColumnBase::Display_Attribute, false, false)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_AttributeValue, CSMWorld::ColumnBase::Display_Integer)); // Nested table - mColumns.push_back(RefIdColumn (Columns::ColumnId_CreatureAttack, - ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue)); + mColumns.emplace_back(Columns::ColumnId_CreatureAttack, + ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue); creatureColumns.mAttacks = &mColumns.back(); std::map attackMap; attackMap.insert(std::make_pair(UniversalId::Type_Creature, new CreatureAttackRefIdAdapter())); - mNestedAdapters.push_back (std::make_pair(&mColumns.back(), attackMap)); + mNestedAdapters.emplace_back(&mColumns.back(), attackMap); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_CreatureAttack, CSMWorld::ColumnBase::Display_Integer, false, false)); mColumns.back().addColumn( @@ -397,12 +397,12 @@ CSMWorld::RefIdCollection::RefIdCollection() new RefIdColumn (Columns::ColumnId_MaxAttack, CSMWorld::ColumnBase::Display_Integer)); // Nested list - mColumns.push_back(RefIdColumn (Columns::ColumnId_CreatureMisc, - ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_List)); + mColumns.emplace_back(Columns::ColumnId_CreatureMisc, + ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_List); creatureColumns.mMisc = &mColumns.back(); std::map creaMiscMap; creaMiscMap.insert(std::make_pair(UniversalId::Type_Creature, new CreatureMiscRefIdAdapter())); - mNestedAdapters.push_back (std::make_pair(&mColumns.back(), creaMiscMap)); + mNestedAdapters.emplace_back(&mColumns.back(), creaMiscMap); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_Level, CSMWorld::ColumnBase::Display_Integer, ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_Refresh)); @@ -423,27 +423,27 @@ CSMWorld::RefIdCollection::RefIdCollection() mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_Gold, CSMWorld::ColumnBase::Display_Integer)); - mColumns.push_back (RefIdColumn (Columns::ColumnId_OpenSound, ColumnBase::Display_Sound)); + mColumns.emplace_back(Columns::ColumnId_OpenSound, ColumnBase::Display_Sound); const RefIdColumn *openSound = &mColumns.back(); - mColumns.push_back (RefIdColumn (Columns::ColumnId_CloseSound, ColumnBase::Display_Sound)); + mColumns.emplace_back(Columns::ColumnId_CloseSound, ColumnBase::Display_Sound); const RefIdColumn *closeSound = &mColumns.back(); LightColumns lightColumns (inventoryColumns); - mColumns.push_back (RefIdColumn (Columns::ColumnId_Duration, ColumnBase::Display_Integer)); + mColumns.emplace_back(Columns::ColumnId_Duration, ColumnBase::Display_Integer); lightColumns.mTime = &mColumns.back(); - mColumns.push_back (RefIdColumn (Columns::ColumnId_Radius, ColumnBase::Display_Integer)); + mColumns.emplace_back(Columns::ColumnId_Radius, ColumnBase::Display_Integer); lightColumns.mRadius = &mColumns.back(); - mColumns.push_back (RefIdColumn (Columns::ColumnId_Colour, ColumnBase::Display_Colour)); + mColumns.emplace_back(Columns::ColumnId_Colour, ColumnBase::Display_Colour); lightColumns.mColor = &mColumns.back(); - mColumns.push_back (RefIdColumn (Columns::ColumnId_Sound, ColumnBase::Display_Sound)); + mColumns.emplace_back(Columns::ColumnId_Sound, ColumnBase::Display_Sound); lightColumns.mSound = &mColumns.back(); - mColumns.push_back(RefIdColumn(Columns::ColumnId_EmitterType, ColumnBase::Display_EmitterType)); + mColumns.emplace_back(Columns::ColumnId_EmitterType, ColumnBase::Display_EmitterType); lightColumns.mEmitterType = &mColumns.back(); static const struct @@ -462,31 +462,31 @@ CSMWorld::RefIdCollection::RefIdCollection() for (int i=0; sLightFlagTable[i].mName!=-1; ++i) { - mColumns.push_back (RefIdColumn (sLightFlagTable[i].mName, ColumnBase::Display_Boolean)); + mColumns.emplace_back(sLightFlagTable[i].mName, ColumnBase::Display_Boolean); lightColumns.mFlags.insert (std::make_pair (&mColumns.back(), sLightFlagTable[i].mFlag)); } - mColumns.push_back (RefIdColumn (Columns::ColumnId_IsKey, ColumnBase::Display_Boolean)); + mColumns.emplace_back(Columns::ColumnId_IsKey, ColumnBase::Display_Boolean); const RefIdColumn *key = &mColumns.back(); NpcColumns npcColumns (actorsColumns); - mColumns.push_back (RefIdColumn (Columns::ColumnId_Race, ColumnBase::Display_Race)); + mColumns.emplace_back(Columns::ColumnId_Race, ColumnBase::Display_Race); npcColumns.mRace = &mColumns.back(); - mColumns.push_back (RefIdColumn (Columns::ColumnId_Class, ColumnBase::Display_Class)); + mColumns.emplace_back(Columns::ColumnId_Class, ColumnBase::Display_Class); npcColumns.mClass = &mColumns.back(); - mColumns.push_back (RefIdColumn (Columns::ColumnId_Faction, ColumnBase::Display_Faction)); + mColumns.emplace_back(Columns::ColumnId_Faction, ColumnBase::Display_Faction); npcColumns.mFaction = &mColumns.back(); - mColumns.push_back (RefIdColumn (Columns::Columnid_Hair, ColumnBase::Display_BodyPart)); + mColumns.emplace_back(Columns::Columnid_Hair, ColumnBase::Display_BodyPart); npcColumns.mHair = &mColumns.back(); - mColumns.push_back (RefIdColumn (Columns::ColumnId_Head, ColumnBase::Display_BodyPart)); + mColumns.emplace_back(Columns::ColumnId_Head, ColumnBase::Display_BodyPart); npcColumns.mHead = &mColumns.back(); - mColumns.push_back (RefIdColumn (Columns::ColumnId_Gender, ColumnBase::Display_GenderNpc)); + mColumns.emplace_back(Columns::ColumnId_Gender, ColumnBase::Display_GenderNpc); npcColumns.mGender = &mColumns.back(); npcColumns.mFlags.insert (std::make_pair (essential, ESM::NPC::Essential)); @@ -503,36 +503,36 @@ CSMWorld::RefIdCollection::RefIdCollection() // These needs to be driven from the autocalculated setting. // Nested table - mColumns.push_back(RefIdColumn (Columns::ColumnId_NpcAttributes, - ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue)); + mColumns.emplace_back(Columns::ColumnId_NpcAttributes, + ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue); npcColumns.mAttributes = &mColumns.back(); std::map attrMap; attrMap.insert(std::make_pair(UniversalId::Type_Npc, new NpcAttributesRefIdAdapter())); - mNestedAdapters.push_back (std::make_pair(&mColumns.back(), attrMap)); + mNestedAdapters.emplace_back(&mColumns.back(), attrMap); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_Attribute, CSMWorld::ColumnBase::Display_Attribute, false, false)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_UChar, CSMWorld::ColumnBase::Display_UnsignedInteger8)); // Nested table - mColumns.push_back(RefIdColumn (Columns::ColumnId_NpcSkills, - ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue)); + mColumns.emplace_back(Columns::ColumnId_NpcSkills, + ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue); npcColumns.mSkills = &mColumns.back(); std::map skillsMap; skillsMap.insert(std::make_pair(UniversalId::Type_Npc, new NpcSkillsRefIdAdapter())); - mNestedAdapters.push_back (std::make_pair(&mColumns.back(), skillsMap)); + mNestedAdapters.emplace_back(&mColumns.back(), skillsMap); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_Skill, CSMWorld::ColumnBase::Display_SkillId, false, false)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_UChar, CSMWorld::ColumnBase::Display_UnsignedInteger8)); // Nested list - mColumns.push_back(RefIdColumn (Columns::ColumnId_NpcMisc, - ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_List)); + mColumns.emplace_back(Columns::ColumnId_NpcMisc, + ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_List); npcColumns.mMisc = &mColumns.back(); std::map miscMap; miscMap.insert(std::make_pair(UniversalId::Type_Npc, new NpcMiscRefIdAdapter())); - mNestedAdapters.push_back (std::make_pair(&mColumns.back(), miscMap)); + mNestedAdapters.emplace_back(&mColumns.back(), miscMap); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_Level, CSMWorld::ColumnBase::Display_SignedInteger16)); mColumns.back().addColumn( @@ -554,15 +554,15 @@ CSMWorld::RefIdCollection::RefIdCollection() WeaponColumns weaponColumns (enchantableColumns); - mColumns.push_back (RefIdColumn (Columns::ColumnId_WeaponType, ColumnBase::Display_WeaponType)); + mColumns.emplace_back(Columns::ColumnId_WeaponType, ColumnBase::Display_WeaponType); weaponColumns.mType = &mColumns.back(); weaponColumns.mHealth = health; - mColumns.push_back (RefIdColumn (Columns::ColumnId_WeaponSpeed, ColumnBase::Display_Float)); + mColumns.emplace_back(Columns::ColumnId_WeaponSpeed, ColumnBase::Display_Float); weaponColumns.mSpeed = &mColumns.back(); - mColumns.push_back (RefIdColumn (Columns::ColumnId_WeaponReach, ColumnBase::Display_Float)); + mColumns.emplace_back(Columns::ColumnId_WeaponReach, ColumnBase::Display_Float); weaponColumns.mReach = &mColumns.back(); for (int i=0; i<3; ++i) @@ -572,8 +572,7 @@ CSMWorld::RefIdCollection::RefIdCollection() for (int j=0; j<2; ++j) { - mColumns.push_back ( - RefIdColumn (Columns::ColumnId_MinChop+i*2+j, ColumnBase::Display_Integer)); + mColumns.emplace_back(Columns::ColumnId_MinChop+i*2+j, ColumnBase::Display_Integer); column[j] = &mColumns.back(); } } @@ -591,19 +590,19 @@ CSMWorld::RefIdCollection::RefIdCollection() for (int i=0; sWeaponFlagTable[i].mName!=-1; ++i) { - mColumns.push_back (RefIdColumn (sWeaponFlagTable[i].mName, ColumnBase::Display_Boolean)); + mColumns.emplace_back(sWeaponFlagTable[i].mName, ColumnBase::Display_Boolean); weaponColumns.mFlags.insert (std::make_pair (&mColumns.back(), sWeaponFlagTable[i].mFlag)); } // Nested table - mColumns.push_back(RefIdColumn (Columns::ColumnId_PartRefList, ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue)); + mColumns.emplace_back(Columns::ColumnId_PartRefList, ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue); const RefIdColumn *partRef = &mColumns.back(); std::map partMap; partMap.insert(std::make_pair(UniversalId::Type_Armor, new BodyPartRefIdAdapter (UniversalId::Type_Armor))); partMap.insert(std::make_pair(UniversalId::Type_Clothing, new BodyPartRefIdAdapter (UniversalId::Type_Clothing))); - mNestedAdapters.push_back (std::make_pair(&mColumns.back(), partMap)); + mNestedAdapters.emplace_back(&mColumns.back(), partMap); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_PartRefType, CSMWorld::ColumnBase::Display_PartRefType)); mColumns.back().addColumn( @@ -614,30 +613,30 @@ CSMWorld::RefIdCollection::RefIdCollection() LevListColumns levListColumns (baseColumns); // Nested table - mColumns.push_back(RefIdColumn (Columns::ColumnId_LevelledList, - ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue)); + mColumns.emplace_back(Columns::ColumnId_LevelledList, + ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue); levListColumns.mLevList = &mColumns.back(); std::map levListMap; levListMap.insert(std::make_pair(UniversalId::Type_CreatureLevelledList, new NestedLevListRefIdAdapter (UniversalId::Type_CreatureLevelledList))); levListMap.insert(std::make_pair(UniversalId::Type_ItemLevelledList, new NestedLevListRefIdAdapter (UniversalId::Type_ItemLevelledList))); - mNestedAdapters.push_back (std::make_pair(&mColumns.back(), levListMap)); + mNestedAdapters.emplace_back(&mColumns.back(), levListMap); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_LevelledItemId, CSMWorld::ColumnBase::Display_Referenceable)); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_LevelledItemLevel, CSMWorld::ColumnBase::Display_Integer)); // Nested list - mColumns.push_back(RefIdColumn (Columns::ColumnId_LevelledList, - ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_List)); + mColumns.emplace_back(Columns::ColumnId_LevelledList, + ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_List); levListColumns.mNestedListLevList = &mColumns.back(); std::map nestedListLevListMap; nestedListLevListMap.insert(std::make_pair(UniversalId::Type_CreatureLevelledList, new NestedListLevListRefIdAdapter (UniversalId::Type_CreatureLevelledList))); nestedListLevListMap.insert(std::make_pair(UniversalId::Type_ItemLevelledList, new NestedListLevListRefIdAdapter (UniversalId::Type_ItemLevelledList))); - mNestedAdapters.push_back (std::make_pair(&mColumns.back(), nestedListLevListMap)); + mNestedAdapters.emplace_back(&mColumns.back(), nestedListLevListMap); mColumns.back().addColumn( new RefIdColumn (Columns::ColumnId_LevelledItemTypeEach, CSMWorld::ColumnBase::Display_Boolean)); mColumns.back().addColumn( diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp index c18c29efe..23b5aa91e 100644 --- a/apps/opencs/view/render/cell.cpp +++ b/apps/opencs/view/render/cell.cpp @@ -553,7 +553,7 @@ std::vector > CSVRender::Cell::getSelection (un result.push_back (iter->second->getTag()); if (mPathgrid && elementMask & Mask_Pathgrid) if (mPathgrid->isSelected()) - result.push_back(mPathgrid->getTag()); + result.emplace_back(mPathgrid->getTag()); return result; } diff --git a/apps/opencs/view/render/terrainshapemode.hpp b/apps/opencs/view/render/terrainshapemode.hpp index d0fec764f..a88e60c9c 100644 --- a/apps/opencs/view/render/terrainshapemode.hpp +++ b/apps/opencs/view/render/terrainshapemode.hpp @@ -59,38 +59,38 @@ namespace CSVRender /// Editmode for terrain shape grid TerrainShapeMode(WorldspaceWidget*, osg::Group* parentNode, QWidget* parent = nullptr); - void primaryOpenPressed (const WorldspaceHitResult& hit) final; + void primaryOpenPressed (const WorldspaceHitResult& hit) override; /// Create single command for one-click shape editing - void primaryEditPressed (const WorldspaceHitResult& hit) final; + void primaryEditPressed (const WorldspaceHitResult& hit) override; /// Open brush settings window - void primarySelectPressed(const WorldspaceHitResult&) final; + void primarySelectPressed(const WorldspaceHitResult&) override; - void secondarySelectPressed(const WorldspaceHitResult&) final; + void secondarySelectPressed(const WorldspaceHitResult&) override; - void activate(CSVWidget::SceneToolbar*) final; - void deactivate(CSVWidget::SceneToolbar*) final; + void activate(CSVWidget::SceneToolbar*) override; + void deactivate(CSVWidget::SceneToolbar*) override; /// Start shape editing command macro - bool primaryEditStartDrag (const QPoint& pos) final; + bool primaryEditStartDrag (const QPoint& pos) override; - bool secondaryEditStartDrag (const QPoint& pos) final; - bool primarySelectStartDrag (const QPoint& pos) final; - bool secondarySelectStartDrag (const QPoint& pos) final; + bool secondaryEditStartDrag (const QPoint& pos) override; + bool primarySelectStartDrag (const QPoint& pos) override; + bool secondarySelectStartDrag (const QPoint& pos) override; /// Handle shape edit behavior during dragging - void drag (const QPoint& pos, int diffX, int diffY, double speedFactor) final; + void drag (const QPoint& pos, int diffX, int diffY, double speedFactor) override; /// End shape editing command macro - void dragCompleted(const QPoint& pos) final; + void dragCompleted(const QPoint& pos) override; /// Cancel shape editing, and reset all pending changes - void dragAborted() final; + void dragAborted() override; - void dragWheel (int diff, double speedFactor) final; - void dragMoveEvent (QDragMoveEvent *event) final; - void mouseMoveEvent (QMouseEvent *event) final; + void dragWheel (int diff, double speedFactor) override; + void dragMoveEvent (QDragMoveEvent *event) override; + void mouseMoveEvent (QMouseEvent *event) override; private: diff --git a/apps/opencs/view/render/terrainstorage.hpp b/apps/opencs/view/render/terrainstorage.hpp index 21faf9b64..762eb8003 100644 --- a/apps/opencs/view/render/terrainstorage.hpp +++ b/apps/opencs/view/render/terrainstorage.hpp @@ -27,10 +27,10 @@ namespace CSVRender const CSMWorld::Data& mData; std::array mAlteredHeight; - osg::ref_ptr getLand (int cellX, int cellY) final; - const ESM::LandTexture* getLandTexture(int index, short plugin) final; + osg::ref_ptr getLand (int cellX, int cellY) override; + const ESM::LandTexture* getLandTexture(int index, short plugin) override; - void getBounds(float& minX, float& maxX, float& minY, float& maxY) final; + void getBounds(float& minX, float& maxX, float& minY, float& maxY) override; int getThisHeight(int col, int row, const ESM::Land::LandData *heightData) const; int getLeftHeight(int col, int row, const ESM::Land::LandData *heightData) const; @@ -44,8 +44,8 @@ namespace CSVRender bool leftOrUpIsOverTheLimit(int col, int row, int heightWarningLimit, const ESM::Land::LandData *heightData) const; bool rightOrDownIsOverTheLimit(int col, int row, int heightWarningLimit, const ESM::Land::LandData *heightData) const; - void adjustColor(int col, int row, const ESM::Land::LandData *heightData, osg::Vec4ub& color) const final; - float getAlteredHeight(int col, int row) const final; + void adjustColor(int col, int row, const ESM::Land::LandData *heightData, osg::Vec4ub& color) const override; + float getAlteredHeight(int col, int row) const override; }; } diff --git a/apps/opencs/view/render/terraintexturemode.hpp b/apps/opencs/view/render/terraintexturemode.hpp index 0d8c4a94a..31932df21 100644 --- a/apps/opencs/view/render/terraintexturemode.hpp +++ b/apps/opencs/view/render/terraintexturemode.hpp @@ -53,37 +53,37 @@ namespace CSVRender /// \brief Editmode for terrain texture grid TerrainTextureMode(WorldspaceWidget*, osg::Group* parentNode, QWidget* parent = nullptr); - void primaryOpenPressed (const WorldspaceHitResult& hit) final; + void primaryOpenPressed (const WorldspaceHitResult& hit) override; /// \brief Create single command for one-click texture editing - void primaryEditPressed (const WorldspaceHitResult& hit) final; + void primaryEditPressed (const WorldspaceHitResult& hit) override; /// \brief Open brush settings window - void primarySelectPressed(const WorldspaceHitResult&) final; + void primarySelectPressed(const WorldspaceHitResult&) override; - void secondarySelectPressed(const WorldspaceHitResult&) final; + void secondarySelectPressed(const WorldspaceHitResult&) override; - void activate(CSVWidget::SceneToolbar*) final; - void deactivate(CSVWidget::SceneToolbar*) final; + void activate(CSVWidget::SceneToolbar*) override; + void deactivate(CSVWidget::SceneToolbar*) override; /// \brief Start texture editing command macro - bool primaryEditStartDrag (const QPoint& pos) final; + bool primaryEditStartDrag (const QPoint& pos) override; - bool secondaryEditStartDrag (const QPoint& pos) final; - bool primarySelectStartDrag (const QPoint& pos) final; - bool secondarySelectStartDrag (const QPoint& pos) final; + bool secondaryEditStartDrag (const QPoint& pos) override; + bool primarySelectStartDrag (const QPoint& pos) override; + bool secondarySelectStartDrag (const QPoint& pos) override; /// \brief Handle texture edit behavior during dragging - void drag (const QPoint& pos, int diffX, int diffY, double speedFactor) final; + void drag (const QPoint& pos, int diffX, int diffY, double speedFactor) override; /// \brief End texture editing command macro - void dragCompleted(const QPoint& pos) final; + void dragCompleted(const QPoint& pos) override; - void dragAborted() final; - void dragWheel (int diff, double speedFactor) final; - void dragMoveEvent (QDragMoveEvent *event) final; + void dragAborted() override; + void dragWheel (int diff, double speedFactor) override; + void dragMoveEvent (QDragMoveEvent *event) override; - void mouseMoveEvent (QMouseEvent *event) final; + void mouseMoveEvent (QMouseEvent *event) override; private: /// \brief Handle brush mechanics, maths regarding worldspace hit etc. diff --git a/apps/opencs/view/render/worldspacewidget.cpp b/apps/opencs/view/render/worldspacewidget.cpp index 916c349d2..4535b5e8a 100644 --- a/apps/opencs/view/render/worldspacewidget.cpp +++ b/apps/opencs/view/render/worldspacewidget.cpp @@ -255,8 +255,7 @@ CSVWidget::SceneToolRun *CSVRender::WorldspaceWidget::makeRunTool ( bool default_ = debugProfiles.data (debugProfiles.index (i, defaultColumn)).toInt(); if (state!=CSMWorld::RecordBase::State_Deleted && default_) - profiles.push_back ( - debugProfiles.data (debugProfiles.index (i, idColumn)). + profiles.emplace_back(debugProfiles.data (debugProfiles.index (i, idColumn)). toString().toUtf8().constData()); } diff --git a/apps/opencs/view/world/datadisplaydelegate.cpp b/apps/opencs/view/world/datadisplaydelegate.cpp index f0c364bc2..6da62d408 100644 --- a/apps/opencs/view/world/datadisplaydelegate.cpp +++ b/apps/opencs/view/world/datadisplaydelegate.cpp @@ -32,7 +32,7 @@ void CSVWorld::DataDisplayDelegate::buildPixmaps () while (it != mIcons.end()) { - mPixmaps.push_back (std::make_pair (it->mValue, it->mIcon.pixmap (mIconSize) ) ); + mPixmaps.emplace_back (it->mValue, it->mIcon.pixmap (mIconSize) ); ++it; } } diff --git a/apps/opencs/view/world/enumdelegate.cpp b/apps/opencs/view/world/enumdelegate.cpp index 8027cec62..3140adc48 100644 --- a/apps/opencs/view/world/enumdelegate.cpp +++ b/apps/opencs/view/world/enumdelegate.cpp @@ -181,5 +181,5 @@ void CSVWorld::EnumDelegateFactory::add (int value, const QString& name) } } - mValues.push_back(std::make_pair (value, name)); + mValues.emplace_back (value, name); } diff --git a/apps/opencs/view/world/regionmap.cpp b/apps/opencs/view/world/regionmap.cpp index af91464b6..d2d4fbd23 100644 --- a/apps/opencs/view/world/regionmap.cpp +++ b/apps/opencs/view/world/regionmap.cpp @@ -345,18 +345,16 @@ std::vector< CSMWorld::UniversalId > CSVWorld::RegionMap::getDraggedRecords() co std::vector ids; for (const QModelIndex& it : selected) { - ids.push_back( - CSMWorld::UniversalId( + ids.emplace_back( CSMWorld::UniversalId::Type_Cell, - model()->data(it, CSMWorld::RegionMap::Role_CellId).toString().toUtf8().constData())); + model()->data(it, CSMWorld::RegionMap::Role_CellId).toString().toUtf8().constData()); } selected = getSelectedCells(false, true); for (const QModelIndex& it : selected) { - ids.push_back( - CSMWorld::UniversalId( + ids.emplace_back( CSMWorld::UniversalId::Type_Cell_Missing, - model()->data(it, CSMWorld::RegionMap::Role_CellId).toString().toUtf8().constData())); + model()->data(it, CSMWorld::RegionMap::Role_CellId).toString().toUtf8().constData()); } return ids; } diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 9d47e2fe5..e5f4e36c5 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -452,7 +452,7 @@ std::vector CSVWorld::Table::getSelectedIds() const ++iter) { int row = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0)).row(); - ids.push_back (mModel->data (mModel->index (row, columnIndex)).toString().toUtf8().constData()); + ids.emplace_back(mModel->data (mModel->index (row, columnIndex)).toString().toUtf8().constData()); } return ids; } @@ -784,7 +784,7 @@ std::vector CSVWorld::Table::getColumnsWithDisplay(CSMWorld::Column if (display == columndisplay) { - titles.push_back(mModel->headerData (i, Qt::Horizontal).toString().toUtf8().constData()); + titles.emplace_back(mModel->headerData (i, Qt::Horizontal).toString().toUtf8().constData()); } } return titles; diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp index b0bae7fdf..5413f87a6 100644 --- a/apps/opencs/view/world/tablesubview.cpp +++ b/apps/opencs/view/world/tablesubview.cpp @@ -133,12 +133,12 @@ void CSVWorld::TableSubView::createFilterRequest (std::vector< CSMWorld::Univers std::vector col = mTable->getColumnsWithDisplay(CSMWorld::TableMimeData::convertEnums(type)); if(!col.empty()) { - filterSource.push_back(std::make_pair(it->getId(), col)); + filterSource.emplace_back(it->getId(), col); } if(hasRefIdDisplay && CSMWorld::TableMimeData::isReferencable(type)) { - filterSource.push_back(std::make_pair(it->getId(), refIdColumns)); + filterSource.emplace_back(it->getId(), refIdColumns); } } diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index ea29fff53..f6ae860aa 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -100,6 +100,7 @@ namespace Script, Mechanics, Physics, + PhysicsWorker, World, Gui, @@ -130,6 +131,9 @@ namespace template <> const UserStats UserStatsValue::sValue {"Phys", "physics"}; + template <> + const UserStats UserStatsValue::sValue {" -Async", "physicsworker"}; + template <> const UserStats UserStatsValue::sValue {"World", "world"}; @@ -209,6 +213,10 @@ namespace profiler.addUserStatsLine(v.mLabel, textColor, barColor, v.mTaken, multiplier, average, averageInInverseSpace, v.mBegin, v.mEnd, maxValue); }); + // the forEachUserStatsValue loop is "run" at compile time, hence the settings manager is not available. + // Unconditionnally add the async physics stats, and then remove it at runtime if necessary + if (Settings::Manager::getInt("async num threads", "Physics") == 0) + profiler.removeUserStatsLine(" -Async"); } } @@ -324,7 +332,7 @@ bool OMW::Engine::frame(float frametime) if (mEnvironment.getStateManager()->getState() != MWBase::StateManager::State_NoGame) { - mEnvironment.getWorld()->updatePhysics(frametime, guiActive); + mEnvironment.getWorld()->updatePhysics(frametime, guiActive, frameStart, frameNumber, *stats); } } diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index a39dd2e39..d3d984901 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -21,22 +21,6 @@ #include #endif -/** - * Workaround for problems with whitespaces in paths in older versions of Boost library - */ -#if (BOOST_VERSION <= 104600) -namespace boost -{ - -template<> -inline boost::filesystem::path lexical_cast(const std::string& arg) -{ - return boost::filesystem::path(arg); -} - -} /* namespace boost */ -#endif /* (BOOST_VERSION <= 104600) */ - using namespace Fallback; @@ -63,20 +47,20 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat ("data", bpo::value()->default_value(Files::EscapePathContainer(), "data") ->multitoken()->composing(), "set data directories (later directories have higher priority)") - ("data-local", bpo::value()->default_value(""), + ("data-local", bpo::value()->default_value(Files::EscapePath(), ""), "set local data directory (highest priority)") ("fallback-archive", bpo::value()->default_value(Files::EscapeStringVector(), "fallback-archive") - ->multitoken(), "set fallback BSA archives (later archives have higher priority)") + ->multitoken()->composing(), "set fallback BSA archives (later archives have higher priority)") - ("resources", bpo::value()->default_value("resources"), + ("resources", bpo::value()->default_value(Files::EscapePath(), "resources"), "set resources directory") - ("start", bpo::value()->default_value(""), + ("start", bpo::value()->default_value(""), "set initial cell") ("content", bpo::value()->default_value(Files::EscapeStringVector(), "") - ->multitoken(), "content file(s): esm/esp, or omwgame/omwaddon") + ->multitoken()->composing(), "content file(s): esm/esp, or omwgame/omwaddon") ("no-sound", bpo::value()->implicit_value(true) ->default_value(false), "disable all sounds") @@ -90,7 +74,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat ("script-console", bpo::value()->implicit_value(true) ->default_value(false), "enable console-only script functionality") - ("script-run", bpo::value()->default_value(""), + ("script-run", bpo::value()->default_value(""), "select a file containing a list of console commands that is executed on startup") ("script-warn", bpo::value()->implicit_value (1) @@ -101,12 +85,12 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat "\t2 - treat warnings as errors") ("script-blacklist", bpo::value()->default_value(Files::EscapeStringVector(), "") - ->multitoken(), "ignore the specified script (if the use of the blacklist is enabled)") + ->multitoken()->composing(), "ignore the specified script (if the use of the blacklist is enabled)") ("script-blacklist-use", bpo::value()->implicit_value(true) ->default_value(true), "enable script blacklisting") - ("load-savegame", bpo::value()->default_value(""), + ("load-savegame", bpo::value()->default_value(Files::EscapePath(), ""), "load a save game file on game startup (specify an absolute filename or a filename relative to the current working directory)") ("skip-menu", bpo::value()->implicit_value(true) @@ -118,14 +102,14 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat ("fs-strict", bpo::value()->implicit_value(true) ->default_value(false), "strict file system handling (no case folding)") - ("encoding", bpo::value()-> + ("encoding", bpo::value()-> default_value("win1252"), "Character encoding used in OpenMW game messages:\n" "\n\twin1250 - Central and Eastern European such as Polish, Czech, Slovak, Hungarian, Slovene, Bosnian, Croatian, Serbian (Latin script), Romanian and Albanian languages\n" "\n\twin1251 - Cyrillic alphabet such as Russian, Bulgarian, Serbian Cyrillic and other languages\n" "\n\twin1252 - Western European (Latin) alphabet, used by default") - ("fallback", bpo::value()->default_value(FallbackMap(), "") + ("fallback", bpo::value()->default_value(FallbackMap(), "") ->multitoken()->composing(), "fallback values") ("no-grab", bpo::value()->implicit_value(true)->default_value(false), "Don't grab mouse cursor") @@ -159,14 +143,16 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat { cfgMgr.readConfiguration(variables, desc, true); - Version::Version v = Version::getOpenmwVersion(variables["resources"].as().toStdString()); + Version::Version v = Version::getOpenmwVersion(variables["resources"].as().mPath.string()); std::cout << v.describe() << std::endl; return false; } + bpo::variables_map composingVariables = cfgMgr.separateComposingVariables(variables, desc); cfgMgr.readConfiguration(variables, desc); + cfgMgr.mergeComposingVariables(variables, composingVariables, desc); - Version::Version v = Version::getOpenmwVersion(variables["resources"].as().toStdString()); + Version::Version v = Version::getOpenmwVersion(variables["resources"].as().mPath.string()); std::cout << v.describe() << std::endl; engine.setGrabMouse(!variables["no-grab"].as()); @@ -181,18 +167,13 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat Files::PathContainer dataDirs(Files::EscapePath::toPathContainer(variables["data"].as())); - std::string local(variables["data-local"].as().toStdString()); + Files::PathContainer::value_type local(variables["data-local"].as().mPath); if (!local.empty()) - { - if (local.front() == '\"') - local = local.substr(1, local.length() - 2); - - dataDirs.push_back(Files::PathContainer::value_type(local)); - } + dataDirs.push_back(local); cfgMgr.processPaths(dataDirs); - engine.setResourceDir(variables["resources"].as().toStdString()); + engine.setResourceDir(variables["resources"].as().mPath); engine.setDataDirs(dataDirs); // fallback archives @@ -230,7 +211,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat engine.setWarningsMode (variables["script-warn"].as()); engine.setScriptBlacklist (variables["script-blacklist"].as().toStdStringVector()); engine.setScriptBlacklistUse (variables["script-blacklist-use"].as()); - engine.setSaveGameFile (variables["load-savegame"].as().toStdString()); + engine.setSaveGameFile (variables["load-savegame"].as().mPath.string()); // other settings Fallback::Map::init(variables["fallback"].as().mMap); diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 778dab85f..1acf66a2d 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -11,6 +11,8 @@ #include +#include + #include "../mwworld/ptr.hpp" #include "../mwworld/doorstate.hpp" @@ -398,7 +400,7 @@ namespace MWBase /// \return pointer to created record virtual void update (float duration, bool paused) = 0; - virtual void updatePhysics (float duration, bool paused) = 0; + virtual void updatePhysics (float duration, bool paused, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats) = 0; virtual void updateWindowManager () = 0; diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index 5ebb2e81e..bc279daac 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -132,11 +132,11 @@ namespace MWClass void modifyBaseInventory(const std::string& actorId, const std::string& itemId, int amount) const override; - float getWalkSpeed(const MWWorld::Ptr& ptr) const final; + float getWalkSpeed(const MWWorld::Ptr& ptr) const override; - float getRunSpeed(const MWWorld::Ptr& ptr) const final; + float getRunSpeed(const MWWorld::Ptr& ptr) const override; - float getSwimSpeed(const MWWorld::Ptr& ptr) const final; + float getSwimSpeed(const MWWorld::Ptr& ptr) const override; }; } diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 1368570b7..a744745c5 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -432,12 +432,12 @@ namespace MWClass const MWWorld::LiveCellRef *npc = ptr.get(); const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get().search(npc->mBase->mRace); if(race && race->mData.mFlags & ESM::Race::Beast) - models.push_back("meshes\\base_animkna.nif"); + models.emplace_back("meshes\\base_animkna.nif"); // keep these always loaded just in case - models.push_back("meshes/xargonian_swimkna.nif"); - models.push_back("meshes/xbase_anim_female.nif"); - models.push_back("meshes/xbase_anim.nif"); + models.emplace_back("meshes/xargonian_swimkna.nif"); + models.emplace_back("meshes/xbase_anim_female.nif"); + models.emplace_back("meshes/xbase_anim.nif"); if (!npc->mBase->mModel.empty()) models.push_back("meshes/"+npc->mBase->mModel); diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index 4ee1b1ebb..1b8f6d61c 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -166,11 +166,11 @@ namespace MWClass void modifyBaseInventory(const std::string& actorId, const std::string& itemId, int amount) const override; - float getWalkSpeed(const MWWorld::Ptr& ptr) const final; + float getWalkSpeed(const MWWorld::Ptr& ptr) const override; - float getRunSpeed(const MWWorld::Ptr& ptr) const final; + float getRunSpeed(const MWWorld::Ptr& ptr) const override; - float getSwimSpeed(const MWWorld::Ptr& ptr) const final; + float getSwimSpeed(const MWWorld::Ptr& ptr) const override; }; } diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index 047a910b6..34fd5828f 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -468,7 +468,7 @@ namespace MWDialogue void DialogueManager::addChoice (const std::string& text, int choice) { mIsInChoice = true; - mChoices.push_back(std::make_pair(text, choice)); + mChoices.emplace_back(text, choice); } const std::vector >& DialogueManager::getChoices() diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp index 6c6eb9a9d..b35bee6d4 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp @@ -73,9 +73,9 @@ namespace MWDialogue bool startDialogue (const MWWorld::Ptr& actor, ResponseCallback* callback) override; std::list getAvailableTopics() override; - int getTopicFlag(const std::string& topicId) final; + int getTopicFlag(const std::string& topicId) override; - bool inJournal (const std::string& topicId, const std::string& infoId) final; + bool inJournal (const std::string& topicId, const std::string& infoId) override; void addTopic (const std::string& topic) override; diff --git a/apps/openmw/mwdialogue/hypertextparser.cpp b/apps/openmw/mwdialogue/hypertextparser.cpp index aa748e772..28e450e2b 100644 --- a/apps/openmw/mwdialogue/hypertextparser.cpp +++ b/apps/openmw/mwdialogue/hypertextparser.cpp @@ -30,7 +30,7 @@ namespace MWDialogue tokenizeKeywords(text.substr(iteration_pos, pos_begin - iteration_pos), result); std::string link = text.substr(pos_begin + 1, pos_end - pos_begin - 1); - result.push_back(Token(link, Token::ExplicitLink)); + result.emplace_back(link, Token::ExplicitLink); iteration_pos = pos_end + 1; } @@ -65,7 +65,7 @@ namespace MWDialogue for (std::vector::Match>::const_iterator it = matches.begin(); it != matches.end(); ++it) { - tokens.push_back(Token(std::string(it->mBeg, it->mEnd), Token::ImplicitKeyword)); + tokens.emplace_back(std::string(it->mBeg, it->mEnd), Token::ImplicitKeyword); } } diff --git a/apps/openmw/mwgui/backgroundimage.hpp b/apps/openmw/mwgui/backgroundimage.hpp index 3ea8b3bba..32cdf1a65 100644 --- a/apps/openmw/mwgui/backgroundimage.hpp +++ b/apps/openmw/mwgui/backgroundimage.hpp @@ -22,8 +22,8 @@ namespace MWGui */ void setBackgroundImage (const std::string& image, bool fixedRatio=true, bool stretch=true); - void setSize (const MyGUI::IntSize &_value) final; - void setCoord (const MyGUI::IntCoord &_value) final; + void setSize (const MyGUI::IntSize &_value) override; + void setCoord (const MyGUI::IntCoord &_value) override; private: MyGUI::ImageBox* mChild; diff --git a/apps/openmw/mwgui/birth.cpp b/apps/openmw/mwgui/birth.cpp index 6f6a621ad..41711f5e4 100644 --- a/apps/openmw/mwgui/birth.cpp +++ b/apps/openmw/mwgui/birth.cpp @@ -147,7 +147,7 @@ namespace MWGui for (const ESM::BirthSign& sign : signs) { - birthSigns.push_back(std::make_pair(sign.mId, &sign)); + birthSigns.emplace_back(sign.mId, &sign); } std::sort(birthSigns.begin(), birthSigns.end(), sortBirthSigns); diff --git a/apps/openmw/mwgui/bookpage.cpp b/apps/openmw/mwgui/bookpage.cpp index a346526eb..8a6ec998d 100644 --- a/apps/openmw/mwgui/bookpage.cpp +++ b/apps/openmw/mwgui/bookpage.cpp @@ -524,9 +524,9 @@ struct TypesetBookImpl::Typesetter : BookTypesetter break; if ( lead != origin ) - mPartialWhitespace.push_back (PartialText (style, lead, origin, space_width)); + mPartialWhitespace.emplace_back(style, lead, origin, space_width); if ( origin != extent ) - mPartialWord.push_back (PartialText (style, origin, extent, word_width)); + mPartialWord.emplace_back(style, origin, extent, word_width); } } @@ -1152,7 +1152,7 @@ public: i->second->createDrawItem (mNode); } - void setVisible (bool newVisible) final + void setVisible (bool newVisible) override { if (mVisible == newVisible) return; @@ -1174,7 +1174,7 @@ public: } } - void createDrawItem(MyGUI::ITexture* texture, MyGUI::ILayerNode* node) final + void createDrawItem(MyGUI::ITexture* texture, MyGUI::ILayerNode* node) override { mNode = node; @@ -1242,9 +1242,9 @@ public: // ISubWidget should not necessarily be a drawitem // in this case, it is not... - void doRender() final { } + void doRender() override { } - void _updateView () final + void _updateView () override { _checkMargin(); @@ -1253,7 +1253,7 @@ public: mNode->outOfDate (i->second->mRenderItem); } - void _correctView() final + void _correctView() override { _checkMargin (); @@ -1263,7 +1263,7 @@ public: } - void destroyDrawItem() final + void destroyDrawItem() override { for (ActiveTextFormats::iterator i = mActiveTextFormats.begin (); i != mActiveTextFormats.end (); ++i) i->second->destroyDrawItem (mNode); @@ -1283,24 +1283,24 @@ public: { } - void showPage (TypesetBook::Ptr book, size_t page) final + void showPage (TypesetBook::Ptr book, size_t page) override { mPageDisplay->showPage (book, page); } - void adviseLinkClicked (std::function linkClicked) final + void adviseLinkClicked (std::function linkClicked) override { mPageDisplay->mLinkClicked = linkClicked; } - void unadviseLinkClicked () final + void unadviseLinkClicked () override { mPageDisplay->mLinkClicked = std::function (); } protected: - void initialiseOverride() final + void initialiseOverride() override { Base::initialiseOverride(); @@ -1314,24 +1314,24 @@ protected: } } - void onMouseLostFocus(Widget* _new) final + void onMouseLostFocus(Widget* _new) override { // NOTE: MyGUI also fires eventMouseLostFocus for widgets that are about to be destroyed (if they had focus). // Child widgets may already be destroyed! So be careful. mPageDisplay->onMouseLostFocus (); } - void onMouseMove(int left, int top) final + void onMouseMove(int left, int top) override { mPageDisplay->onMouseMove (left, top); } - void onMouseButtonPressed (int left, int top, MyGUI::MouseButton id) final + void onMouseButtonPressed (int left, int top, MyGUI::MouseButton id) override { mPageDisplay->onMouseButtonPressed (left, top, id); } - void onMouseButtonReleased(int left, int top, MyGUI::MouseButton id) final + void onMouseButtonReleased(int left, int top, MyGUI::MouseButton id) override { mPageDisplay->onMouseButtonReleased (left, top, id); } diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index a92ad934c..cfbf5e5fb 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -217,7 +217,7 @@ namespace MWGui if (store.get().isDynamic(classInfo.mId)) continue; // custom-made class not relevant for this dialog - items.push_back(std::make_pair(classInfo.mId, classInfo.mName)); + items.emplace_back(classInfo.mId, classInfo.mName); } std::sort(items.begin(), items.end(), sortClasses); diff --git a/apps/openmw/mwgui/companionwindow.cpp b/apps/openmw/mwgui/companionwindow.cpp index b3f6e3339..926456219 100644 --- a/apps/openmw/mwgui/companionwindow.cpp +++ b/apps/openmw/mwgui/companionwindow.cpp @@ -163,8 +163,8 @@ bool CompanionWindow::exit() if (mModel && mModel->hasProfit(mPtr) && getProfit(mPtr) < 0) { std::vector buttons; - buttons.push_back("#{sCompanionWarningButtonOne}"); - buttons.push_back("#{sCompanionWarningButtonTwo}"); + buttons.emplace_back("#{sCompanionWarningButtonOne}"); + buttons.emplace_back("#{sCompanionWarningButtonTwo}"); mMessageBoxManager->createInteractiveMessageBox("#{sCompanionWarningMessage}", buttons); mMessageBoxManager->eventButtonPressed += MyGUI::newDelegate(this, &CompanionWindow::onMessageBoxButtonClicked); return false; diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index a73813901..b0913219f 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -167,6 +167,10 @@ namespace MWGui { WindowBase::onClose(); + // Make sure the window was actually closed and not temporarily hidden. + if (MWBase::Environment::get().getWindowManager()->containsMode(GM_Container)) + return; + if (mModel) mModel->onClose(); @@ -189,6 +193,7 @@ namespace MWGui // transfer everything into the player's inventory ItemModel* playerModel = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getModel(); + assert(mModel); mModel->update(); // unequip all items to avoid unequipping/reequipping diff --git a/apps/openmw/mwgui/containeritemmodel.cpp b/apps/openmw/mwgui/containeritemmodel.cpp index 31439f349..56f084bb9 100644 --- a/apps/openmw/mwgui/containeritemmodel.cpp +++ b/apps/openmw/mwgui/containeritemmodel.cpp @@ -49,14 +49,14 @@ ContainerItemModel::ContainerItemModel(const std::vector& itemSour for(const MWWorld::Ptr& source : itemSources) { MWWorld::ContainerStore& store = source.getClass().getContainerStore(source); - mItemSources.push_back(std::make_pair(source, store.resolveTemporarily())); + mItemSources.emplace_back(source, store.resolveTemporarily()); } } ContainerItemModel::ContainerItemModel (const MWWorld::Ptr& source) : mTrading(false) { MWWorld::ContainerStore& store = source.getClass().getContainerStore(source); - mItemSources.push_back(std::make_pair(source, store.resolveTemporarily())); + mItemSources.emplace_back(source, store.resolveTemporarily()); } bool ContainerItemModel::allowedToUseItems() const diff --git a/apps/openmw/mwgui/controllers.hpp b/apps/openmw/mwgui/controllers.hpp index bd9646ec2..416f104d9 100644 --- a/apps/openmw/mwgui/controllers.hpp +++ b/apps/openmw/mwgui/controllers.hpp @@ -9,21 +9,17 @@ namespace MyGUI class Widget; } -namespace MWGui -{ - namespace Controllers +namespace MWGui::Controllers { /// Automatically positions a widget below the mouse cursor. - class ControllerFollowMouse final : - public MyGUI::ControllerItem + class ControllerFollowMouse final : public MyGUI::ControllerItem { MYGUI_RTTI_DERIVED( ControllerFollowMouse ) private: - bool addTime(MyGUI::Widget* _widget, float _time) final; - void prepareItem(MyGUI::Widget* _widget) final; + bool addTime(MyGUI::Widget* _widget, float _time) override; + void prepareItem(MyGUI::Widget* _widget) override; }; } -} #endif diff --git a/apps/openmw/mwgui/cursor.hpp b/apps/openmw/mwgui/cursor.hpp index ef5099ef8..7e1f9adba 100644 --- a/apps/openmw/mwgui/cursor.hpp +++ b/apps/openmw/mwgui/cursor.hpp @@ -20,10 +20,10 @@ namespace MWGui ResourceImageSetPointerFix(); virtual ~ResourceImageSetPointerFix(); - void deserialization(MyGUI::xml::ElementPtr _node, MyGUI::Version _version) final; + void deserialization(MyGUI::xml::ElementPtr _node, MyGUI::Version _version) override; - void setImage(MyGUI::ImageBox* _image) final; - void setPosition(MyGUI::ImageBox* _image, const MyGUI::IntPoint& _point) final; + void setImage(MyGUI::ImageBox* _image) override; + void setPosition(MyGUI::ImageBox* _image, const MyGUI::IntPoint& _point) override; //and now for the whole point of this class, allow us to get //the hot spot, the image and the size of the cursor. diff --git a/apps/openmw/mwgui/itemchargeview.hpp b/apps/openmw/mwgui/itemchargeview.hpp index 2522f55d1..039dcaf4e 100644 --- a/apps/openmw/mwgui/itemchargeview.hpp +++ b/apps/openmw/mwgui/itemchargeview.hpp @@ -36,7 +36,7 @@ namespace MWGui /// Register needed components with MyGUI's factory manager static void registerComponents(); - void initialiseOverride() final; + void initialiseOverride() override; /// Takes ownership of \a model void setModel(ItemModel* model); @@ -47,8 +47,8 @@ namespace MWGui void layoutWidgets(); void resetScrollbars(); - void setSize(const MyGUI::IntSize& value) final; - void setCoord(const MyGUI::IntCoord& value) final; + void setSize(const MyGUI::IntSize& value) override; + void setCoord(const MyGUI::IntCoord& value) override; MyGUI::delegates::CMultiDelegate2 eventItemClicked; diff --git a/apps/openmw/mwgui/itemview.hpp b/apps/openmw/mwgui/itemview.hpp index a5e537aa0..4074e55e4 100644 --- a/apps/openmw/mwgui/itemview.hpp +++ b/apps/openmw/mwgui/itemview.hpp @@ -13,7 +13,7 @@ namespace MWGui MYGUI_RTTI_DERIVED(ItemView) public: ItemView(); - virtual ~ItemView(); + ~ItemView() override; /// Register needed components with MyGUI's factory manager static void registerComponents (); @@ -33,12 +33,12 @@ namespace MWGui void resetScrollBars(); private: - void initialiseOverride() final; + void initialiseOverride() override; void layoutWidgets(); - void setSize(const MyGUI::IntSize& _value) final; - void setCoord(const MyGUI::IntCoord& _value) final; + void setSize(const MyGUI::IntSize& _value) override; + void setCoord(const MyGUI::IntCoord& _value) override; void onSelectedItem (MyGUI::Widget* sender); void onSelectedBackground (MyGUI::Widget* sender); diff --git a/apps/openmw/mwgui/itemwidget.hpp b/apps/openmw/mwgui/itemwidget.hpp index 748a44445..550d73643 100644 --- a/apps/openmw/mwgui/itemwidget.hpp +++ b/apps/openmw/mwgui/itemwidget.hpp @@ -41,7 +41,7 @@ namespace MWGui void setFrame (const std::string& frame, const MyGUI::IntCoord& coord); protected: - void initialiseOverride() final; + void initialiseOverride() override; MyGUI::ImageBox* mItem; MyGUI::ImageBox* mItemShadow; diff --git a/apps/openmw/mwgui/jailscreen.cpp b/apps/openmw/mwgui/jailscreen.cpp index 67124884f..cc793073e 100644 --- a/apps/openmw/mwgui/jailscreen.cpp +++ b/apps/openmw/mwgui/jailscreen.cpp @@ -123,7 +123,7 @@ namespace MWGui } std::vector buttons; - buttons.push_back("#{sOk}"); + buttons.emplace_back("#{sOk}"); MWBase::Environment::get().getWindowManager()->interactiveMessageBox(message, buttons); } } diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index 4778ee7b0..a5d8f7344 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -231,25 +231,25 @@ namespace MWGui std::vector buttons; if (state==MWBase::StateManager::State_Running) - buttons.push_back("return"); + buttons.emplace_back("return"); - buttons.push_back("newgame"); + buttons.emplace_back("newgame"); if (state==MWBase::StateManager::State_Running && MWBase::Environment::get().getWorld()->getGlobalInt ("chargenstate")==-1 && MWBase::Environment::get().getWindowManager()->isSavingAllowed()) - buttons.push_back("savegame"); + buttons.emplace_back("savegame"); if (MWBase::Environment::get().getStateManager()->characterBegin()!= MWBase::Environment::get().getStateManager()->characterEnd()) - buttons.push_back("loadgame"); + buttons.emplace_back("loadgame"); - buttons.push_back("options"); + buttons.emplace_back("options"); if (state==MWBase::StateManager::State_NoGame) - buttons.push_back("credits"); + buttons.emplace_back("credits"); - buttons.push_back("exitgame"); + buttons.emplace_back("exitgame"); // Create new buttons if needed std::vector allButtons { "return", "newgame", "savegame", "loadgame", "options", "credits", "exitgame"}; diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index 351358ffe..013384a9f 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -74,12 +74,12 @@ namespace MyGUI::Colour mNormalColour; MyGUI::Colour mHoverColour; - void onMouseLostFocus(MyGUI::Widget* _new) final + void onMouseLostFocus(MyGUI::Widget* _new) override { setColour(mNormalColour); } - void onMouseSetFocus(MyGUI::Widget* _old) final + void onMouseSetFocus(MyGUI::Widget* _old) override { setColour(mHoverColour); } diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index cf69ecca3..457594697 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -358,7 +358,7 @@ namespace MWGui if (!playable) // Only display playable races continue; - items.push_back(std::make_pair(race.mId, race.mName)); + items.emplace_back(race.mId, race.mName); } std::sort(items.begin(), items.end(), sortRaces); diff --git a/apps/openmw/mwgui/resourceskin.hpp b/apps/openmw/mwgui/resourceskin.hpp index bdf0d2f0b..fd1977e66 100644 --- a/apps/openmw/mwgui/resourceskin.hpp +++ b/apps/openmw/mwgui/resourceskin.hpp @@ -10,7 +10,7 @@ namespace MWGui MYGUI_RTTI_DERIVED( AutoSizedResourceSkin ) public: - void deserialization(MyGUI::xml::ElementPtr _node, MyGUI::Version _version) final; + void deserialization(MyGUI::xml::ElementPtr _node, MyGUI::Version _version) override; }; } diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index c5fa17ca4..68dac4a95 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -244,7 +244,7 @@ namespace MWGui { SDL_DisplayMode mode; SDL_GetDisplayMode(screen, i, &mode); - resolutions.push_back(std::make_pair(mode.w, mode.h)); + resolutions.emplace_back(mode.w, mode.h); } std::sort(resolutions.begin(), resolutions.end(), sortResolutions); for (std::pair& resolution : resolutions) diff --git a/apps/openmw/mwgui/sortfilteritemmodel.cpp b/apps/openmw/mwgui/sortfilteritemmodel.cpp index f18cac681..28b13cdf0 100644 --- a/apps/openmw/mwgui/sortfilteritemmodel.cpp +++ b/apps/openmw/mwgui/sortfilteritemmodel.cpp @@ -31,18 +31,18 @@ namespace { // this defines the sorting order of types. types that are first in the vector appear before other types. std::vector mapping; - mapping.push_back( typeid(ESM::Weapon).name() ); - mapping.push_back( typeid(ESM::Armor).name() ); - mapping.push_back( typeid(ESM::Clothing).name() ); - mapping.push_back( typeid(ESM::Potion).name() ); - mapping.push_back( typeid(ESM::Ingredient).name() ); - mapping.push_back( typeid(ESM::Apparatus).name() ); - mapping.push_back( typeid(ESM::Book).name() ); - mapping.push_back( typeid(ESM::Light).name() ); - mapping.push_back( typeid(ESM::Miscellaneous).name() ); - mapping.push_back( typeid(ESM::Lockpick).name() ); - mapping.push_back( typeid(ESM::Repair).name() ); - mapping.push_back( typeid(ESM::Probe).name() ); + mapping.emplace_back(typeid(ESM::Weapon).name() ); + mapping.emplace_back(typeid(ESM::Armor).name() ); + mapping.emplace_back(typeid(ESM::Clothing).name() ); + mapping.emplace_back(typeid(ESM::Potion).name() ); + mapping.emplace_back(typeid(ESM::Ingredient).name() ); + mapping.emplace_back(typeid(ESM::Apparatus).name() ); + mapping.emplace_back(typeid(ESM::Book).name() ); + mapping.emplace_back(typeid(ESM::Light).name() ); + mapping.emplace_back(typeid(ESM::Miscellaneous).name() ); + mapping.emplace_back(typeid(ESM::Lockpick).name() ); + mapping.emplace_back(typeid(ESM::Repair).name() ); + mapping.emplace_back(typeid(ESM::Probe).name() ); assert( std::find(mapping.begin(), mapping.end(), type1) != mapping.end() ); assert( std::find(mapping.begin(), mapping.end(), type2) != mapping.end() ); @@ -166,7 +166,7 @@ namespace MWGui void SortFilterItemModel::addDragItem (const MWWorld::Ptr& dragItem, size_t count) { - mDragItems.push_back(std::make_pair(dragItem, count)); + mDragItems.emplace_back(dragItem, count); } void SortFilterItemModel::clearDragItems() diff --git a/apps/openmw/mwgui/soulgemdialog.cpp b/apps/openmw/mwgui/soulgemdialog.cpp index 104c81eab..345c8b722 100644 --- a/apps/openmw/mwgui/soulgemdialog.cpp +++ b/apps/openmw/mwgui/soulgemdialog.cpp @@ -12,8 +12,8 @@ namespace MWGui { mSoulgem = soulgem; std::vector buttons; - buttons.push_back("#{sRechargeEnchantment}"); - buttons.push_back("#{sMake Enchantment}"); + buttons.emplace_back("#{sRechargeEnchantment}"); + buttons.emplace_back("#{sMake Enchantment}"); mManager->createInteractiveMessageBox("#{sDoYouWantTo}", buttons); mManager->eventButtonPressed += MyGUI::newDelegate(this, &SoulgemDialog::onButtonPressed); } diff --git a/apps/openmw/mwgui/spellmodel.cpp b/apps/openmw/mwgui/spellmodel.cpp index 1dedfa10b..136547c4c 100644 --- a/apps/openmw/mwgui/spellmodel.cpp +++ b/apps/openmw/mwgui/spellmodel.cpp @@ -42,6 +42,44 @@ namespace MWGui { } + bool SpellModel::matchingEffectExists(std::string filter, const ESM::EffectList &effects) + { + auto wm = MWBase::Environment::get().getWindowManager(); + const MWWorld::ESMStore &store = + MWBase::Environment::get().getWorld()->getStore(); + + for (unsigned int i = 0; i < effects.mList.size(); ++i) + { + short effectId = effects.mList[i].mEffectID; + + if (effectId != -1) + { + const ESM::MagicEffect *magicEffect = + store.get().search(effectId); + std::string effectIDStr = ESM::MagicEffect::effectIdToString(effectId); + std::string fullEffectName = wm->getGameSettingString(effectIDStr, ""); + + if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill && effects.mList[i].mSkill != -1) + { + fullEffectName += " " + wm->getGameSettingString(ESM::Skill::sSkillNameIds[effects.mList[i].mSkill], ""); + } + + if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute && effects.mList[i].mAttribute != -1) + { + fullEffectName += " " + wm->getGameSettingString(ESM::Attribute::sGmstAttributeIds[effects.mList[i].mAttribute], ""); + } + + std::string convert = Misc::StringUtils::lowerCaseUtf8(fullEffectName); + if (convert.find(filter) != std::string::npos) + { + return true; + } + } + } + + return false; + } + void SpellModel::update() { mSpells.clear(); @@ -61,8 +99,9 @@ namespace MWGui continue; std::string name = Misc::StringUtils::lowerCaseUtf8(spell->mName); - - if (name.find(filter) == std::string::npos) + + if (name.find(filter) == std::string::npos + && !matchingEffectExists(filter, spell->mEffects)) continue; Spell newSpell; @@ -103,7 +142,8 @@ namespace MWGui std::string name = Misc::StringUtils::lowerCaseUtf8(item.getClass().getName(item)); - if (name.find(filter) == std::string::npos) + if (name.find(filter) == std::string::npos + && !matchingEffectExists(filter, enchant->mEffects)) continue; Spell newSpell; diff --git a/apps/openmw/mwgui/spellmodel.hpp b/apps/openmw/mwgui/spellmodel.hpp index d191cba0e..2404610bf 100644 --- a/apps/openmw/mwgui/spellmodel.hpp +++ b/apps/openmw/mwgui/spellmodel.hpp @@ -2,6 +2,7 @@ #define OPENMW_GUI_SPELLMODEL_H #include "../mwworld/ptr.hpp" +#include namespace MWGui { @@ -57,6 +58,8 @@ namespace MWGui std::vector mSpells; std::string mFilter; + + bool matchingEffectExists(std::string filter, const ESM::EffectList &effects); }; } diff --git a/apps/openmw/mwgui/spellview.cpp b/apps/openmw/mwgui/spellview.cpp index f03c1cedb..a8b7cb639 100644 --- a/apps/openmw/mwgui/spellview.cpp +++ b/apps/openmw/mwgui/spellview.cpp @@ -128,10 +128,10 @@ namespace MWGui group.push_back(costChance); Gui::SharedStateButton::createButtonGroup(group); - mLines.push_back(LineInfo(t, costChance, i)); + mLines.emplace_back(t, costChance, i); } else - mLines.push_back(LineInfo(t, (MyGUI::Widget*)nullptr, i)); + mLines.emplace_back(t, (MyGUI::Widget*)nullptr, i); t->setStateSelected(spell.mSelected); } @@ -236,7 +236,7 @@ namespace MWGui MyGUI::IntCoord(0, 0, mScrollView->getWidth(), 18), MyGUI::Align::Left | MyGUI::Align::Top); separator->setNeedMouseFocus(false); - mLines.push_back(LineInfo(separator, (MyGUI::Widget*)nullptr, NoSpellIndex)); + mLines.emplace_back(separator, (MyGUI::Widget*)nullptr, NoSpellIndex); } MyGUI::TextBox* groupWidget = mScrollView->createWidget("SandBrightText", @@ -255,10 +255,10 @@ namespace MWGui groupWidget2->setTextAlign(MyGUI::Align::Right); groupWidget2->setNeedMouseFocus(false); - mLines.push_back(LineInfo(groupWidget, groupWidget2, NoSpellIndex)); + mLines.emplace_back(groupWidget, groupWidget2, NoSpellIndex); } else - mLines.push_back(LineInfo(groupWidget, (MyGUI::Widget*)nullptr, NoSpellIndex)); + mLines.emplace_back(groupWidget, (MyGUI::Widget*)nullptr, NoSpellIndex); } diff --git a/apps/openmw/mwgui/spellview.hpp b/apps/openmw/mwgui/spellview.hpp index a387cac39..6b3effc45 100644 --- a/apps/openmw/mwgui/spellview.hpp +++ b/apps/openmw/mwgui/spellview.hpp @@ -47,10 +47,10 @@ namespace MWGui /// Fired when a spell was clicked EventHandle_ModelIndex eventSpellClicked; - void initialiseOverride() final; + void initialiseOverride() override; - void setSize(const MyGUI::IntSize& _value) final; - void setCoord(const MyGUI::IntCoord& _value) final; + void setSize(const MyGUI::IntSize& _value) override; + void setCoord(const MyGUI::IntCoord& _value) override; void resetScrollbars(); diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index f585d9019..0ccf1a144 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -523,6 +523,9 @@ namespace MWGui void TradeWindow::onClose() { + // Make sure the window was actually closed and not temporarily hidden. + if (MWBase::Environment::get().getWindowManager()->containsMode(GM_Barter)) + return; resetReference(); } } diff --git a/apps/openmw/mwgui/trainingwindow.cpp b/apps/openmw/mwgui/trainingwindow.cpp index da3c7d186..7fae33bae 100644 --- a/apps/openmw/mwgui/trainingwindow.cpp +++ b/apps/openmw/mwgui/trainingwindow.cpp @@ -84,7 +84,7 @@ namespace MWGui { float value = getSkillForTraining(actorStats, i); - skills.push_back(std::make_pair(i, value)); + skills.emplace_back(i, value); } std::sort(skills.begin(), skills.end(), sortSkills); diff --git a/apps/openmw/mwgui/widgets.hpp b/apps/openmw/mwgui/widgets.hpp index ff3b2311a..731a41a35 100644 --- a/apps/openmw/mwgui/widgets.hpp +++ b/apps/openmw/mwgui/widgets.hpp @@ -116,7 +116,7 @@ namespace MWGui protected: virtual ~MWSkill(); - void initialiseOverride() final; + void initialiseOverride() override; void onClicked(MyGUI::Widget* _sender); @@ -156,7 +156,7 @@ namespace MWGui protected: virtual ~MWAttribute(); - void initialiseOverride() final; + void initialiseOverride() override; void onClicked(MyGUI::Widget* _sender); @@ -199,7 +199,7 @@ namespace MWGui protected: virtual ~MWSpell(); - void initialiseOverride() final; + void initialiseOverride() override; private: void updateWidgets(); @@ -241,7 +241,7 @@ namespace MWGui protected: virtual ~MWEffectList(); - void initialiseOverride() final; + void initialiseOverride() override; private: void updateWidgets(); @@ -265,7 +265,7 @@ namespace MWGui protected: virtual ~MWSpellEffect(); - void initialiseOverride() final; + void initialiseOverride() override; private: static const int sIconOffset = 24; @@ -294,7 +294,7 @@ namespace MWGui protected: virtual ~MWDynamicStat(); - void initialiseOverride() final; + void initialiseOverride() override; private: diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 4d7841010..047741bac 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -199,7 +199,7 @@ namespace MWMechanics if (sourceId != mSpellId) return; - mMagnitudes.push_back(std::make_pair(key, magnitude)); + mMagnitudes.emplace_back(key, magnitude); } std::vector> mMagnitudes; @@ -590,7 +590,9 @@ namespace MWMechanics if (!actorState.isTurningToPlayer()) { - float angle = std::atan2(dir.x(), dir.y()); + float from = dir.x(); + float to = dir.y(); + float angle = std::atan2(from, to); actorState.setAngleToPlayer(angle); float deltaAngle = Misc::normalizeAngle(angle - actor.getRefData().getPosition().rot[2]); if (!mSmoothMovement || std::abs(deltaAngle) > osg::DegreesToRadians(60.f)) @@ -914,12 +916,54 @@ namespace MWMechanics } }; + void Actors::applyCureEffects(const MWWorld::Ptr& actor) + { + CreatureStats &creatureStats = actor.getClass().getCreatureStats(actor); + const MagicEffects &effects = creatureStats.getMagicEffects(); + + if (effects.get(ESM::MagicEffect::CurePoison).getModifier() > 0) + { + creatureStats.getActiveSpells().purgeEffect(ESM::MagicEffect::Poison); + creatureStats.getSpells().purgeEffect(ESM::MagicEffect::Poison); + if (actor.getClass().hasInventoryStore(actor)) + actor.getClass().getInventoryStore(actor).purgeEffect(ESM::MagicEffect::Poison); + } + else if (effects.get(ESM::MagicEffect::CureParalyzation).getModifier() > 0) + { + creatureStats.getActiveSpells().purgeEffect(ESM::MagicEffect::Paralyze); + creatureStats.getSpells().purgeEffect(ESM::MagicEffect::Paralyze); + if (actor.getClass().hasInventoryStore(actor)) + actor.getClass().getInventoryStore(actor).purgeEffect(ESM::MagicEffect::Paralyze); + } + else if (effects.get(ESM::MagicEffect::CureCommonDisease).getModifier() > 0) + { + creatureStats.getSpells().purgeCommonDisease(); + } + else if (effects.get(ESM::MagicEffect::CureBlightDisease).getModifier() > 0) + { + creatureStats.getSpells().purgeBlightDisease(); + } + else if (effects.get(ESM::MagicEffect::CureCorprusDisease).getModifier() > 0) + { + creatureStats.getActiveSpells().purgeCorprusDisease(); + creatureStats.getSpells().purgeCorprusDisease(); + if (actor.getClass().hasInventoryStore(actor)) + actor.getClass().getInventoryStore(actor).purgeEffect(ESM::MagicEffect::Corprus, true); + } + else if (effects.get(ESM::MagicEffect::RemoveCurse).getModifier() > 0) + { + creatureStats.getSpells().purgeCurses(); + } + } + void Actors::calculateCreatureStatModifiers (const MWWorld::Ptr& ptr, float duration) { CreatureStats &creatureStats = ptr.getClass().getCreatureStats(ptr); const MagicEffects &effects = creatureStats.getMagicEffects(); bool godmode = ptr == getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState(); + applyCureEffects(ptr); + bool wasDead = creatureStats.isDead(); if (duration > 0) @@ -1687,6 +1731,9 @@ namespace MWMechanics void Actors::predictAndAvoidCollisions() { + if (!MWBase::Environment::get().getMechanicsManager()->isAIActive()) + return; + const float minGap = 10.f; const float maxDistForPartialAvoiding = 200.f; const float maxDistForStrictAvoiding = 100.f; @@ -1723,7 +1770,7 @@ namespace MWMechanics shouldAvoidCollision = true; else if (package->getTypeId() == AiPackageTypeId::Wander && giveWayWhenIdle) { - if (!dynamic_cast(package.get())->isStationary()) + if (!static_cast(package.get())->isStationary()) shouldAvoidCollision = true; } else if (package->getTypeId() == AiPackageTypeId::Combat || package->getTypeId() == AiPackageTypeId::Pursue) diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 9299d468c..453540001 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -206,6 +206,7 @@ namespace MWMechanics private: void updateVisibility (const MWWorld::Ptr& ptr, CharacterController* ctrl); + void applyCureEffects (const MWWorld::Ptr& actor); PtrActorMap mActors; float mTimerDisposeSummonsCorpses; diff --git a/apps/openmw/mwmechanics/aiactivate.hpp b/apps/openmw/mwmechanics/aiactivate.hpp index c53744e88..dc7e0bb26 100644 --- a/apps/openmw/mwmechanics/aiactivate.hpp +++ b/apps/openmw/mwmechanics/aiactivate.hpp @@ -24,15 +24,15 @@ namespace MWMechanics public: /// Constructor /** \param objectId Reference to object to activate **/ - AiActivate(const std::string &objectId); + explicit AiActivate(const std::string &objectId); - AiActivate(const ESM::AiSequence::AiActivate* activate); + explicit AiActivate(const ESM::AiSequence::AiActivate* activate); - bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; + bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) override; static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::Activate; } - void writeState(ESM::AiSequence::AiSequence& sequence) const final; + void writeState(ESM::AiSequence::AiSequence& sequence) const override; private: const std::string mObjectId; diff --git a/apps/openmw/mwmechanics/aiavoiddoor.hpp b/apps/openmw/mwmechanics/aiavoiddoor.hpp index cd0718e2e..1781c5e4a 100644 --- a/apps/openmw/mwmechanics/aiavoiddoor.hpp +++ b/apps/openmw/mwmechanics/aiavoiddoor.hpp @@ -20,9 +20,9 @@ namespace MWMechanics { public: /// Avoid door until the door is fully open - AiAvoidDoor(const MWWorld::ConstPtr& doorPtr); + explicit AiAvoidDoor(const MWWorld::ConstPtr& doorPtr); - bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; + bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) override; static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::AvoidDoor; } diff --git a/apps/openmw/mwmechanics/aibreathe.hpp b/apps/openmw/mwmechanics/aibreathe.hpp index 7e9ac69da..b84c0eb76 100644 --- a/apps/openmw/mwmechanics/aibreathe.hpp +++ b/apps/openmw/mwmechanics/aibreathe.hpp @@ -10,7 +10,7 @@ namespace MWMechanics class AiBreathe final : public TypedAiPackage { public: - bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; + bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) override; static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::Breathe; } diff --git a/apps/openmw/mwmechanics/aicast.hpp b/apps/openmw/mwmechanics/aicast.hpp index 1175dccd2..9758c2b94 100644 --- a/apps/openmw/mwmechanics/aicast.hpp +++ b/apps/openmw/mwmechanics/aicast.hpp @@ -15,11 +15,11 @@ namespace MWMechanics public: AiCast(const std::string& targetId, const std::string& spellId, bool manualSpell=false); - bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; + bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) override; static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::Cast; } - MWWorld::Ptr getTarget() const final; + MWWorld::Ptr getTarget() const override; static constexpr Options makeDefaultOptions() { diff --git a/apps/openmw/mwmechanics/aicombat.hpp b/apps/openmw/mwmechanics/aicombat.hpp index ff8a0e081..64645ca94 100644 --- a/apps/openmw/mwmechanics/aicombat.hpp +++ b/apps/openmw/mwmechanics/aicombat.hpp @@ -96,13 +96,13 @@ namespace MWMechanics public: ///Constructor /** \param actor Actor to fight **/ - AiCombat(const MWWorld::Ptr& actor); + explicit AiCombat(const MWWorld::Ptr& actor); - AiCombat (const ESM::AiSequence::AiCombat* combat); + explicit AiCombat (const ESM::AiSequence::AiCombat* combat); void init(); - bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; + bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) override; static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::Combat; } @@ -116,9 +116,9 @@ namespace MWMechanics } ///Returns target ID - MWWorld::Ptr getTarget() const final; + MWWorld::Ptr getTarget() const override; - void writeState(ESM::AiSequence::AiSequence &sequence) const final; + void writeState(ESM::AiSequence::AiSequence &sequence) const override; private: /// Returns true if combat should end diff --git a/apps/openmw/mwmechanics/aiescort.hpp b/apps/openmw/mwmechanics/aiescort.hpp index edfd0ca3f..27a177893 100644 --- a/apps/openmw/mwmechanics/aiescort.hpp +++ b/apps/openmw/mwmechanics/aiescort.hpp @@ -30,7 +30,7 @@ namespace MWMechanics AiEscort(const ESM::AiSequence::AiEscort* escort); - bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; + bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) override; static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::Escort; } @@ -42,11 +42,11 @@ namespace MWMechanics return options; } - void writeState(ESM::AiSequence::AiSequence &sequence) const final; + void writeState(ESM::AiSequence::AiSequence &sequence) const override; - void fastForward(const MWWorld::Ptr& actor, AiState& state) final; + void fastForward(const MWWorld::Ptr& actor, AiState& state) override; - osg::Vec3f getDestination() const final { return osg::Vec3f(mX, mY, mZ); } + osg::Vec3f getDestination() const override { return osg::Vec3f(mX, mY, mZ); } private: const std::string mCellId; diff --git a/apps/openmw/mwmechanics/aiface.hpp b/apps/openmw/mwmechanics/aiface.hpp index a5ee67a14..e176eb52e 100644 --- a/apps/openmw/mwmechanics/aiface.hpp +++ b/apps/openmw/mwmechanics/aiface.hpp @@ -10,7 +10,7 @@ namespace MWMechanics public: AiFace(float targetX, float targetY); - bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; + bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) override; static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::Face; } diff --git a/apps/openmw/mwmechanics/aifollow.hpp b/apps/openmw/mwmechanics/aifollow.hpp index d0c2cec97..e6aeebb24 100644 --- a/apps/openmw/mwmechanics/aifollow.hpp +++ b/apps/openmw/mwmechanics/aifollow.hpp @@ -53,7 +53,7 @@ namespace MWMechanics AiFollow(const ESM::AiSequence::AiFollow* follow); - bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; + bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) override; static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::Follow; } @@ -69,15 +69,15 @@ namespace MWMechanics /// Returns the actor being followed std::string getFollowedActor(); - void writeState (ESM::AiSequence::AiSequence& sequence) const final; + void writeState (ESM::AiSequence::AiSequence& sequence) const override; bool isCommanded() const; int getFollowIndex() const; - void fastForward(const MWWorld::Ptr& actor, AiState& state) final; + void fastForward(const MWWorld::Ptr& actor, AiState& state) override; - osg::Vec3f getDestination() const final + osg::Vec3f getDestination() const override { MWWorld::Ptr target = getTarget(); if (target.isEmpty()) diff --git a/apps/openmw/mwmechanics/aipursue.hpp b/apps/openmw/mwmechanics/aipursue.hpp index 64465f530..2fbc13b87 100644 --- a/apps/openmw/mwmechanics/aipursue.hpp +++ b/apps/openmw/mwmechanics/aipursue.hpp @@ -26,7 +26,7 @@ namespace MWMechanics AiPursue(const ESM::AiSequence::AiPursue* pursue); - bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; + bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) override; static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::Pursue; } @@ -38,9 +38,9 @@ namespace MWMechanics return options; } - MWWorld::Ptr getTarget() const final; + MWWorld::Ptr getTarget() const override; - void writeState (ESM::AiSequence::AiSequence& sequence) const final; + void writeState (ESM::AiSequence::AiSequence& sequence) const override; }; } #endif diff --git a/apps/openmw/mwmechanics/aitravel.hpp b/apps/openmw/mwmechanics/aitravel.hpp index 0c19572d2..2ea2a8f71 100644 --- a/apps/openmw/mwmechanics/aitravel.hpp +++ b/apps/openmw/mwmechanics/aitravel.hpp @@ -25,14 +25,14 @@ namespace MWMechanics AiTravel(float x, float y, float z); - AiTravel(const ESM::AiSequence::AiTravel* travel); + explicit AiTravel(const ESM::AiSequence::AiTravel* travel); /// Simulates the passing of time - void fastForward(const MWWorld::Ptr& actor, AiState& state) final; + void fastForward(const MWWorld::Ptr& actor, AiState& state) override; - void writeState(ESM::AiSequence::AiSequence &sequence) const final; + void writeState(ESM::AiSequence::AiSequence &sequence) const override; - bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; + bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) override; static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::Travel; } @@ -44,7 +44,7 @@ namespace MWMechanics return options; } - osg::Vec3f getDestination() const final { return osg::Vec3f(mX, mY, mZ); } + osg::Vec3f getDestination() const override { return osg::Vec3f(mX, mY, mZ); } private: const float mX; @@ -62,7 +62,7 @@ namespace MWMechanics static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::InternalTravel; } - std::unique_ptr clone() const final; + std::unique_ptr clone() const override; }; } diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index 4165cebbd..68bcddf22 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -89,9 +89,9 @@ namespace MWMechanics \param repeat Repeat wander or not **/ AiWander(int distance, int duration, int timeOfDay, const std::vector& idle, bool repeat); - AiWander (const ESM::AiSequence::AiWander* wander); + explicit AiWander (const ESM::AiSequence::AiWander* wander); - bool execute(const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; + bool execute(const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) override; static constexpr AiPackageTypeId getTypeId() { return AiPackageTypeId::Wander; } @@ -103,13 +103,13 @@ namespace MWMechanics return options; } - void writeState(ESM::AiSequence::AiSequence &sequence) const final; + void writeState(ESM::AiSequence::AiSequence &sequence) const override; - void fastForward(const MWWorld::Ptr& actor, AiState& state) final; + void fastForward(const MWWorld::Ptr& actor, AiState& state) override; - osg::Vec3f getDestination(const MWWorld::Ptr& actor) const final; + osg::Vec3f getDestination(const MWWorld::Ptr& actor) const override; - osg::Vec3f getDestination() const final + osg::Vec3f getDestination() const override { if (!mHasDestination) return osg::Vec3f(0, 0, 0); diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 11e2e8a32..f1d302f51 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -276,6 +276,7 @@ void CharacterController::refreshHitRecoilAnims(CharacterState& idle) mCurrentHit = "shield"; MWRender::Animation::AnimPriority priorityBlock (Priority_Hit); priorityBlock[MWRender::Animation::BoneGroup_LeftArm] = Priority_Block; + priorityBlock[MWRender::Animation::BoneGroup_LowerBody] = Priority_WeaponLowerBody; mAnimation->play(mCurrentHit, priorityBlock, MWRender::Animation::BlendMask_All, true, 1, "block start", "block stop", 0.0f, 0); } @@ -295,6 +296,8 @@ void CharacterController::refreshHitRecoilAnims(CharacterState& idle) mUpperBodyState = UpperCharState_Nothing; } } + if (mHitState != CharState_None) + idle = CharState_None; } else if(!mAnimation->isPlaying(mCurrentHit)) { @@ -314,8 +317,6 @@ void CharacterController::refreshHitRecoilAnims(CharacterState& idle) mAnimation->disable(mCurrentHit); mAnimation->play(mCurrentHit, Priority_Knockdown, MWRender::Animation::BlendMask_All, true, 1, "loop stop", "stop", 0.0f, 0); } - if (mHitState != CharState_None) - idle = CharState_None; } void CharacterController::refreshJumpAnims(const std::string& weapShortGroup, JumpingState jump, CharacterState& idle, bool force) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 872a66799..b1db2562b 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -971,7 +971,7 @@ namespace MWMechanics { const OwnerMap& owners = it->second; for (OwnerMap::const_iterator ownerIt = owners.begin(); ownerIt != owners.end(); ++ownerIt) - result.push_back(std::make_pair(ownerIt->first.first, ownerIt->second)); + result.emplace_back(ownerIt->first.first, ownerIt->second); return result; } } diff --git a/apps/openmw/mwmechanics/tickableeffects.cpp b/apps/openmw/mwmechanics/tickableeffects.cpp index fa3b6ac20..5056179f8 100644 --- a/apps/openmw/mwmechanics/tickableeffects.cpp +++ b/apps/openmw/mwmechanics/tickableeffects.cpp @@ -205,24 +205,6 @@ namespace MWMechanics break; } - case ESM::MagicEffect::CurePoison: - actor.getClass().getCreatureStats(actor).getActiveSpells().purgeEffect(ESM::MagicEffect::Poison); - break; - case ESM::MagicEffect::CureParalyzation: - actor.getClass().getCreatureStats(actor).getActiveSpells().purgeEffect(ESM::MagicEffect::Paralyze); - break; - case ESM::MagicEffect::CureCommonDisease: - actor.getClass().getCreatureStats(actor).getSpells().purgeCommonDisease(); - break; - case ESM::MagicEffect::CureBlightDisease: - actor.getClass().getCreatureStats(actor).getSpells().purgeBlightDisease(); - break; - case ESM::MagicEffect::CureCorprusDisease: - actor.getClass().getCreatureStats(actor).getSpells().purgeCorprusDisease(); - break; - case ESM::MagicEffect::RemoveCurse: - actor.getClass().getCreatureStats(actor).getSpells().purgeCurses(); - break; default: return false; } diff --git a/apps/openmw/mwmechanics/tickableeffects.hpp b/apps/openmw/mwmechanics/tickableeffects.hpp index c4abed6a3..ccd42ca19 100644 --- a/apps/openmw/mwmechanics/tickableeffects.hpp +++ b/apps/openmw/mwmechanics/tickableeffects.hpp @@ -12,6 +12,7 @@ namespace MWMechanics struct EffectKey; /// Apply a magic effect that is applied in tick intervals until its remaining time ends or it is removed + /// Note: this function works in loop, so magic effects should not be removed here to avoid iterator invalidation. /// @return Was the effect a tickable effect with a magnitude? bool effectTick(CreatureStats& creatureStats, const MWWorld::Ptr& actor, const EffectKey& effectKey, float magnitude); } diff --git a/apps/openmw/mwphysics/actor.cpp b/apps/openmw/mwphysics/actor.cpp index 5caaba5c9..430bd4dee 100644 --- a/apps/openmw/mwphysics/actor.cpp +++ b/apps/openmw/mwphysics/actor.cpp @@ -19,7 +19,7 @@ namespace MWPhysics Actor::Actor(const MWWorld::Ptr& ptr, const Resource::BulletShape* shape, PhysicsTaskScheduler* scheduler) - : mCanWaterWalk(false), mWalkingOnWater(false) + : mStandingOnPtr(nullptr), mCanWaterWalk(false), mWalkingOnWater(false) , mCollisionObject(nullptr), mMeshTranslation(shape->mCollisionBoxTranslate), mHalfExtents(shape->mCollisionBoxHalfExtents) , mForce(0.f, 0.f, 0.f), mOnGround(true), mOnSlope(false) , mInternalCollisionMode(true) @@ -74,10 +74,8 @@ Actor::Actor(const MWWorld::Ptr& ptr, const Resource::BulletShape* shape, Physic updateRotation(); updateScale(); - updatePosition(); - + resetPosition(); addCollisionMask(getCollisionMask()); - commitPositionChange(); } Actor::~Actor() @@ -122,88 +120,80 @@ int Actor::getCollisionMask() const void Actor::updatePosition() { - std::unique_lock lock(mPositionMutex); - osg::Vec3f position = mPtr.getRefData().getPosition().asVec3(); + std::scoped_lock lock(mPositionMutex); + mWorldPosition = mPtr.getRefData().getPosition().asVec3(); +} - mPosition = position; - mPreviousPosition = position; +osg::Vec3f Actor::getWorldPosition() const +{ + std::scoped_lock lock(mPositionMutex); + return mWorldPosition; +} - mTransformUpdatePending = true; - updateCollisionObjectPosition(); +void Actor::setNextPosition(const osg::Vec3f& position) +{ + mNextPosition = position; +} + +osg::Vec3f Actor::getNextPosition() const +{ + return mNextPosition; } void Actor::updateCollisionObjectPosition() { + std::scoped_lock lock(mPositionMutex); + mShape->setLocalScaling(Misc::Convert::toBullet(mScale)); osg::Vec3f scaledTranslation = mRotation * osg::componentMultiply(mMeshTranslation, mScale); osg::Vec3f newPosition = scaledTranslation + mPosition; mLocalTransform.setOrigin(Misc::Convert::toBullet(newPosition)); mLocalTransform.setRotation(Misc::Convert::toBullet(mRotation)); - + mCollisionObject->setWorldTransform(mLocalTransform); } -void Actor::commitPositionChange() +osg::Vec3f Actor::getCollisionObjectPosition() const { - std::unique_lock lock(mPositionMutex); - if (mScaleUpdatePending) - { - mShape->setLocalScaling(Misc::Convert::toBullet(mScale)); - mScaleUpdatePending = false; - } - if (mTransformUpdatePending) - { - mCollisionObject->setWorldTransform(mLocalTransform); - mTransformUpdatePending = false; - } + std::scoped_lock lock(mPositionMutex); + return Misc::Convert::toOsg(mLocalTransform.getOrigin()); } -osg::Vec3f Actor::getCollisionObjectPosition() const +void Actor::setPosition(const osg::Vec3f& position) { - std::unique_lock lock(mPositionMutex); - return Misc::Convert::toOsg(mLocalTransform.getOrigin()); + mPreviousPosition = mPosition; + mPosition = position; } -void Actor::setPosition(const osg::Vec3f &position, bool updateCollisionObject) +void Actor::adjustPosition(const osg::Vec3f& offset) { - std::unique_lock lock(mPositionMutex); - if (mTransformUpdatePending) - { - mCollisionObject->setWorldTransform(mLocalTransform); - mTransformUpdatePending = false; - } - else - { - mPreviousPosition = mPosition; + mPosition += offset; + mPreviousPosition += offset; +} - mPosition = position; - if (updateCollisionObject) - { - updateCollisionObjectPosition(); - mCollisionObject->setWorldTransform(mLocalTransform); - } - } +void Actor::resetPosition() +{ + updatePosition(); + mPreviousPosition = mWorldPosition; + mPosition = mWorldPosition; + mNextPosition = mWorldPosition; + updateCollisionObjectPosition(); } osg::Vec3f Actor::getPosition() const { - std::unique_lock lock(mPositionMutex); return mPosition; } osg::Vec3f Actor::getPreviousPosition() const { - std::unique_lock lock(mPositionMutex); return mPreviousPosition; } void Actor::updateRotation () { - std::unique_lock lock(mPositionMutex); + std::scoped_lock lock(mPositionMutex); if (mRotation == mPtr.getRefData().getBaseNode()->getAttitude()) return; mRotation = mPtr.getRefData().getBaseNode()->getAttitude(); - - mTransformUpdatePending = true; - updateCollisionObjectPosition(); } bool Actor::isRotationallyInvariant() const @@ -213,37 +203,33 @@ bool Actor::isRotationallyInvariant() const void Actor::updateScale() { - std::unique_lock lock(mPositionMutex); + std::scoped_lock lock(mPositionMutex); float scale = mPtr.getCellRef().getScale(); osg::Vec3f scaleVec(scale,scale,scale); mPtr.getClass().adjustScale(mPtr, scaleVec, false); mScale = scaleVec; - mScaleUpdatePending = true; scaleVec = osg::Vec3f(scale,scale,scale); mPtr.getClass().adjustScale(mPtr, scaleVec, true); mRenderingScale = scaleVec; - - mTransformUpdatePending = true; - updateCollisionObjectPosition(); } osg::Vec3f Actor::getHalfExtents() const { - std::unique_lock lock(mPositionMutex); + std::scoped_lock lock(mPositionMutex); return osg::componentMultiply(mHalfExtents, mScale); } osg::Vec3f Actor::getOriginalHalfExtents() const { - std::unique_lock lock(mPositionMutex); + std::scoped_lock lock(mPositionMutex); return mHalfExtents; } osg::Vec3f Actor::getRenderingHalfExtents() const { - std::unique_lock lock(mPositionMutex); + std::scoped_lock lock(mPositionMutex); return osg::componentMultiply(mHalfExtents, mRenderingScale); } @@ -274,7 +260,7 @@ void Actor::setWalkingOnWater(bool walkingOnWater) void Actor::setCanWaterWalk(bool waterWalk) { - std::unique_lock lock(mPositionMutex); + std::scoped_lock lock(mPositionMutex); if (waterWalk != mCanWaterWalk) { mCanWaterWalk = waterWalk; @@ -282,4 +268,16 @@ void Actor::setCanWaterWalk(bool waterWalk) } } +MWWorld::Ptr Actor::getStandingOnPtr() const +{ + std::scoped_lock lock(mPositionMutex); + return mStandingOnPtr; +} + +void Actor::setStandingOnPtr(const MWWorld::Ptr& ptr) +{ + std::scoped_lock lock(mPositionMutex); + mStandingOnPtr = ptr; +} + } diff --git a/apps/openmw/mwphysics/actor.hpp b/apps/openmw/mwphysics/actor.hpp index ef7b368b9..3d6f93ae0 100644 --- a/apps/openmw/mwphysics/actor.hpp +++ b/apps/openmw/mwphysics/actor.hpp @@ -57,13 +57,20 @@ namespace MWPhysics bool isRotationallyInvariant() const; /** - * Set mPosition and mPreviousPosition to the position in the Ptr's RefData. This should be used + * 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 updatePosition(); + 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 + */ + void setNextPosition(const osg::Vec3f& position); + osg::Vec3f getNextPosition() const; void updateCollisionObjectPosition(); - void commitPositionChange(); /** * Returns the half extents of the collision body (scaled according to collision scale) @@ -83,9 +90,10 @@ namespace MWPhysics /** * Store the current position into mPreviousPosition, then move to this position. - * Optionally, inform the physics engine about the change of position. */ - void setPosition(const osg::Vec3f& position, bool updateCollisionObject=true); + void setPosition(const osg::Vec3f& position); + void resetPosition(); + void adjustPosition(const osg::Vec3f& offset); osg::Vec3f getPosition() const; @@ -137,7 +145,11 @@ namespace MWPhysics void setWalkingOnWater(bool walkingOnWater); bool isWalkingOnWater() const; + MWWorld::Ptr getStandingOnPtr() const; + void setStandingOnPtr(const MWWorld::Ptr& ptr); + private: + MWWorld::Ptr mStandingOnPtr; /// Removes then re-adds the collision object to the dynamics world void updateCollisionMask(); void addCollisionMask(int collisionMask); @@ -159,11 +171,11 @@ namespace MWPhysics osg::Vec3f mScale; osg::Vec3f mRenderingScale; + osg::Vec3f mWorldPosition; + osg::Vec3f mNextPosition; osg::Vec3f mPosition; osg::Vec3f mPreviousPosition; btTransform mLocalTransform; - bool mScaleUpdatePending; - bool mTransformUpdatePending; mutable std::mutex mPositionMutex; osg::Vec3f mForce; diff --git a/apps/openmw/mwphysics/contacttestresultcallback.cpp b/apps/openmw/mwphysics/contacttestresultcallback.cpp index f8209e363..5829ee02f 100644 --- a/apps/openmw/mwphysics/contacttestresultcallback.cpp +++ b/apps/openmw/mwphysics/contacttestresultcallback.cpp @@ -2,6 +2,8 @@ #include +#include "components/misc/convert.hpp" + #include "ptrholder.hpp" namespace MWPhysics @@ -20,7 +22,7 @@ namespace MWPhysics collisionObject = col1Wrap->m_collisionObject; PtrHolder* holder = static_cast(collisionObject->getUserPointer()); if (holder) - mResult.push_back(holder->getPtr()); + mResult.emplace_back(ContactPoint{holder->getPtr(), Misc::Convert::toOsg(cp.m_positionWorldOnB), Misc::Convert::toOsg(cp.m_normalWorldOnB)}); return 0.f; } diff --git a/apps/openmw/mwphysics/contacttestresultcallback.hpp b/apps/openmw/mwphysics/contacttestresultcallback.hpp index 03fc36299..fbe12d5dc 100644 --- a/apps/openmw/mwphysics/contacttestresultcallback.hpp +++ b/apps/openmw/mwphysics/contacttestresultcallback.hpp @@ -7,6 +7,8 @@ #include "../mwworld/ptr.hpp" +#include "physicssystem.hpp" + class btCollisionObject; struct btCollisionObjectWrapper; @@ -23,7 +25,7 @@ namespace MWPhysics const btCollisionObjectWrapper* col0Wrap,int partId0,int index0, const btCollisionObjectWrapper* col1Wrap,int partId1,int index1) override; - std::vector mResult; + std::vector mResult; }; } diff --git a/apps/openmw/mwphysics/deepestnotmecontacttestresultcallback.cpp b/apps/openmw/mwphysics/deepestnotmecontacttestresultcallback.cpp index 0baaa6241..7744af14b 100644 --- a/apps/openmw/mwphysics/deepestnotmecontacttestresultcallback.cpp +++ b/apps/openmw/mwphysics/deepestnotmecontacttestresultcallback.cpp @@ -39,6 +39,7 @@ namespace MWPhysics mObject = collisionObject; mLeastDistSqr = distsqr; mContactPoint = cp.getPositionWorldOnA(); + mContactNormal = cp.m_normalWorldOnB; } } diff --git a/apps/openmw/mwphysics/deepestnotmecontacttestresultcallback.hpp b/apps/openmw/mwphysics/deepestnotmecontacttestresultcallback.hpp index 9b2e97e65..00cb5c01f 100644 --- a/apps/openmw/mwphysics/deepestnotmecontacttestresultcallback.hpp +++ b/apps/openmw/mwphysics/deepestnotmecontacttestresultcallback.hpp @@ -20,6 +20,7 @@ namespace MWPhysics public: const btCollisionObject *mObject{nullptr}; btVector3 mContactPoint{0,0,0}; + btVector3 mContactNormal{0,0,0}; btScalar mLeastDistSqr; DeepestNotMeContactTestResultCallback(const btCollisionObject* me, const std::vector& targets, const btVector3 &origin); diff --git a/apps/openmw/mwphysics/hasspherecollisioncallback.hpp b/apps/openmw/mwphysics/hasspherecollisioncallback.hpp index 58e7373e5..275325cf6 100644 --- a/apps/openmw/mwphysics/hasspherecollisioncallback.hpp +++ b/apps/openmw/mwphysics/hasspherecollisioncallback.hpp @@ -35,7 +35,7 @@ namespace MWPhysics { } - bool process(const btBroadphaseProxy* proxy) final + bool process(const btBroadphaseProxy* proxy) override { if (mResult) return false; diff --git a/apps/openmw/mwphysics/mtphysics.cpp b/apps/openmw/mwphysics/mtphysics.cpp index 1ad0ae9c0..8c06e0140 100644 --- a/apps/openmw/mwphysics/mtphysics.cpp +++ b/apps/openmw/mwphysics/mtphysics.cpp @@ -1,6 +1,8 @@ #include #include +#include + #include "components/debug/debuglog.hpp" #include #include "components/misc/convert.hpp" @@ -82,14 +84,6 @@ namespace ptr.getClass().getMovementSettings(ptr).mPosition[2] = 0; } - void updateStandingCollision(MWPhysics::ActorFrameData& actorData, MWPhysics::CollisionMap& standingCollisions) - { - if (!actorData.mStandingOn.isEmpty()) - standingCollisions[actorData.mPtr] = actorData.mStandingOn; - else - standingCollisions.erase(actorData.mPtr); - } - void updateMechanics(MWPhysics::ActorFrameData& actorData) { if (actorData.mDidJump) @@ -102,9 +96,18 @@ namespace stats.addToFallHeight(-actorData.mFallHeight); } - osg::Vec3f interpolateMovements(const MWPhysics::ActorFrameData& actorData, float timeAccum, float physicsDt) + osg::Vec3f interpolateMovements(MWPhysics::ActorFrameData& actorData, float timeAccum, float physicsDt) { const float interpolationFactor = timeAccum / physicsDt; + + // account for force change of actor's position in the main thread + const auto correction = actorData.mActorRaw->getWorldPosition() - actorData.mOrigin; + if (correction.length() != 0) + { + actorData.mActorRaw->adjustPosition(correction); + actorData.mPosition = actorData.mActorRaw->getPosition(); + } + return actorData.mPosition * interpolationFactor + actorData.mActorRaw->getPreviousPosition() * (1.f - interpolationFactor); } @@ -142,6 +145,7 @@ namespace MWPhysics { PhysicsTaskScheduler::PhysicsTaskScheduler(float physicsDt, std::shared_ptr collisionWorld) : mPhysicsDt(physicsDt) + , mTimeAccum(0.f) , mCollisionWorld(std::move(collisionWorld)) , mNumJobs(0) , mRemainingSteps(0) @@ -152,6 +156,8 @@ namespace MWPhysics , mQuit(false) , mNextJob(0) , mNextLOS(0) + , mFrameNumber(0) + , mTimer(osg::Timer::instance()) { mNumThreads = Config::computeNumThreads(mThreadSafeBullet); @@ -181,22 +187,22 @@ namespace MWPhysics mPostSimBarrier = std::make_unique(mNumThreads, [&]() { - udpateActorsAabbs(); mNewFrame = false; if (mLOSCacheExpiry >= 0) { - std::unique_lock lock(mLOSCacheMutex); + 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(); }); } PhysicsTaskScheduler::~PhysicsTaskScheduler() { - std::unique_lock lock(mSimulationMutex); + std::unique_lock lock(mSimulationMutex); mQuit = true; mNumJobs = 0; mRemainingSteps = 0; @@ -206,19 +212,16 @@ namespace MWPhysics thread.join(); } - const PtrPositionList& PhysicsTaskScheduler::moveActors(int numSteps, float timeAccum, std::vector&& actorsData, CollisionMap& standingCollisions, bool skipSimulation) + const PtrPositionList& PhysicsTaskScheduler::moveActors(int numSteps, float timeAccum, std::vector&& actorsData, bool skipSimulation, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats) { // This function run in the main thread. // While the mSimulationMutex is held, background physics threads can't run. - std::unique_lock lock(mSimulationMutex); + std::unique_lock lock(mSimulationMutex); // start by finishing previous background computation if (mNumThreads != 0) { - if (mAdvanceSimulation) - standingCollisions.clear(); - for (auto& data : mActorsFrameData) { // Ignore actors that were deleted while the background thread was running @@ -227,8 +230,21 @@ namespace MWPhysics updateMechanics(data); if (mAdvanceSimulation) - updateStandingCollision(data, standingCollisions); + data.mActorRaw->setStandingOnPtr(data.mStandingOn); + + if (mMovementResults.find(data.mPtr) != mMovementResults.end()) + data.mActorRaw->setNextPosition(mMovementResults[data.mPtr]); + } + + if (mFrameNumber == frameNumber - 1) + { + stats.setAttribute(mFrameNumber, "physicsworker_time_begin", mTimer->delta_s(mFrameStart, mTimeBegin)); + stats.setAttribute(mFrameNumber, "physicsworker_time_taken", mTimer->delta_s(mTimeBegin, mTimeEnd)); + stats.setAttribute(mFrameNumber, "physicsworker_time_end", mTimer->delta_s(mFrameStart, mTimeEnd)); } + mFrameStart = frameStart; + mTimeBegin = mTimer->tick(); + mFrameNumber = frameNumber; } // init @@ -244,18 +260,17 @@ namespace MWPhysics if (mAdvanceSimulation) mWorldFrameData = std::make_unique(); - // update each actor position based on latest data - for (auto& data : mActorsFrameData) - data.updatePosition(); - // we are asked to skip the simulation (load a savegame for instance) // just return the actors' reference position without applying the movements if (skipSimulation) { - standingCollisions.clear(); mMovementResults.clear(); for (const auto& m : mActorsFrameData) - mMovementResults[m.mPtr] = m.mPosition; + { + m.mActorRaw->setStandingOnPtr(nullptr); + m.mActorRaw->resetPosition(); + mMovementResults[m.mPtr] = m.mActorRaw->getWorldPosition(); + } return mMovementResults; } @@ -264,11 +279,12 @@ namespace MWPhysics mMovementResults.clear(); syncComputation(); - if (mAdvanceSimulation) + for (auto& data : mActorsFrameData) { - standingCollisions.clear(); - for (auto& data : mActorsFrameData) - updateStandingCollision(data, standingCollisions); + if (mAdvanceSimulation) + data.mActorRaw->setStandingOnPtr(data.mStandingOn); + if (mMovementResults.find(data.mPtr) != mMovementResults.end()) + data.mActorRaw->setNextPosition(mMovementResults[data.mPtr]); } return mMovementResults; } @@ -294,25 +310,25 @@ namespace MWPhysics void PhysicsTaskScheduler::rayTest(const btVector3& rayFromWorld, const btVector3& rayToWorld, btCollisionWorld::RayResultCallback& resultCallback) const { - MaybeSharedLock lock(mCollisionWorldMutex, mThreadSafeBullet); + MaybeSharedLock lock(mCollisionWorldMutex, mThreadSafeBullet); mCollisionWorld->rayTest(rayFromWorld, rayToWorld, resultCallback); } void PhysicsTaskScheduler::convexSweepTest(const btConvexShape* castShape, const btTransform& from, const btTransform& to, btCollisionWorld::ConvexResultCallback& resultCallback) const { - MaybeSharedLock lock(mCollisionWorldMutex, mThreadSafeBullet); + MaybeSharedLock lock(mCollisionWorldMutex, mThreadSafeBullet); mCollisionWorld->convexSweepTest(castShape, from, to, resultCallback); } void PhysicsTaskScheduler::contactTest(btCollisionObject* colObj, btCollisionWorld::ContactResultCallback& resultCallback) { - std::shared_lock lock(mCollisionWorldMutex); + std::shared_lock lock(mCollisionWorldMutex); mCollisionWorld->contactTest(colObj, resultCallback); } - boost::optional PhysicsTaskScheduler::getHitPoint(const btTransform& from, btCollisionObject* target) + std::optional PhysicsTaskScheduler::getHitPoint(const btTransform& from, btCollisionObject* target) { - MaybeSharedLock lock(mCollisionWorldMutex, mThreadSafeBullet); + MaybeSharedLock lock(mCollisionWorldMutex, mThreadSafeBullet); // target the collision object's world origin, this should be the center of the collision object btTransform rayTo; rayTo.setIdentity(); @@ -323,37 +339,37 @@ namespace MWPhysics mCollisionWorld->rayTestSingle(from, rayTo, target, target->getCollisionShape(), target->getWorldTransform(), cb); if (!cb.hasHit()) // didn't hit the target. this could happen if point is already inside the collision box - return boost::none; + return std::nullopt; return {cb.m_hitPointWorld}; } void PhysicsTaskScheduler::aabbTest(const btVector3& aabbMin, const btVector3& aabbMax, btBroadphaseAabbCallback& callback) { - std::shared_lock lock(mCollisionWorldMutex); + std::shared_lock lock(mCollisionWorldMutex); mCollisionWorld->getBroadphase()->aabbTest(aabbMin, aabbMax, callback); } void PhysicsTaskScheduler::getAabb(const btCollisionObject* obj, btVector3& min, btVector3& max) { - std::shared_lock lock(mCollisionWorldMutex); + std::shared_lock lock(mCollisionWorldMutex); obj->getCollisionShape()->getAabb(obj->getWorldTransform(), min, max); } void PhysicsTaskScheduler::setCollisionFilterMask(btCollisionObject* collisionObject, int collisionFilterMask) { - std::unique_lock lock(mCollisionWorldMutex); + std::unique_lock lock(mCollisionWorldMutex); collisionObject->getBroadphaseHandle()->m_collisionFilterMask = collisionFilterMask; } void PhysicsTaskScheduler::addCollisionObject(btCollisionObject* collisionObject, int collisionFilterGroup, int collisionFilterMask) { - std::unique_lock lock(mCollisionWorldMutex); + std::unique_lock lock(mCollisionWorldMutex); mCollisionWorld->addCollisionObject(collisionObject, collisionFilterGroup, collisionFilterMask); } void PhysicsTaskScheduler::removeCollisionObject(btCollisionObject* collisionObject) { - std::unique_lock lock(mCollisionWorldMutex); + std::unique_lock lock(mCollisionWorldMutex); mCollisionWorld->removeCollisionObject(collisionObject); } @@ -361,19 +377,19 @@ namespace MWPhysics { if (mDeferAabbUpdate) { - std::unique_lock lock(mUpdateAabbMutex); + std::unique_lock lock(mUpdateAabbMutex); mUpdateAabb.insert(std::move(ptr)); } else { - std::unique_lock lock(mCollisionWorldMutex); + std::unique_lock lock(mCollisionWorldMutex); updatePtrAabb(ptr); } } bool PhysicsTaskScheduler::getLineOfSight(const std::weak_ptr& actor1, const std::weak_ptr& actor2) { - std::unique_lock lock(mLOSCacheMutex); + std::unique_lock lock(mLOSCacheMutex); auto actorPtr1 = actor1.lock(); auto actorPtr2 = actor2.lock(); @@ -395,7 +411,7 @@ namespace MWPhysics void PhysicsTaskScheduler::refreshLOSCache() { - std::shared_lock lock(mLOSCacheMutex); + std::shared_lock lock(mLOSCacheMutex); int job = 0; int numLOS = mLOSCache.size(); while ((job = mNextLOS.fetch_add(1, std::memory_order_relaxed)) < numLOS) @@ -414,9 +430,7 @@ namespace MWPhysics void PhysicsTaskScheduler::updateAabbs() { - std::unique_lock lock1(mCollisionWorldMutex, std::defer_lock); - std::unique_lock lock2(mUpdateAabbMutex, std::defer_lock); - std::lock(lock1, lock2); + std::scoped_lock lock(mCollisionWorldMutex, mUpdateAabbMutex); std::for_each(mUpdateAabb.begin(), mUpdateAabb.end(), [this](const std::weak_ptr& ptr) { updatePtrAabb(ptr); }); mUpdateAabb.clear(); @@ -428,7 +442,7 @@ namespace MWPhysics { if (const auto actor = std::dynamic_pointer_cast(p)) { - actor->commitPositionChange(); + actor->updateCollisionObjectPosition(); mCollisionWorld->updateSingleAabb(actor->getCollisionObject()); } else if (const auto object = std::dynamic_pointer_cast(p)) @@ -441,7 +455,7 @@ namespace MWPhysics void PhysicsTaskScheduler::worker() { - std::shared_lock lock(mSimulationMutex); + std::shared_lock lock(mSimulationMutex); while (!mQuit) { if (!mNewFrame) @@ -453,7 +467,7 @@ namespace MWPhysics int job = 0; while (mRemainingSteps && (job = mNextJob.fetch_add(1, std::memory_order_relaxed)) < mNumJobs) { - MaybeSharedLock lockColWorld(mCollisionWorldMutex, mThreadSafeBullet); + MaybeSharedLock lockColWorld(mCollisionWorldMutex, mThreadSafeBullet); if(const auto actor = mActorsFrameData[job].mActor.lock()) MovementSolver::move(mActorsFrameData[job], mPhysicsDt, mCollisionWorld.get(), *mWorldFrameData); } @@ -481,33 +495,22 @@ namespace MWPhysics void PhysicsTaskScheduler::updateActorsPositions() { - std::unique_lock lock(mCollisionWorldMutex); + std::unique_lock lock(mCollisionWorldMutex); for (auto& actorData : mActorsFrameData) { if(const auto actor = actorData.mActor.lock()) { - if (actorData.mPosition == actor->getPosition()) - actor->setPosition(actorData.mPosition, false); // update previous position to make sure interpolation is correct - else + bool positionChanged = actorData.mPosition != actorData.mActorRaw->getPosition(); + actorData.mActorRaw->setPosition(actorData.mPosition); + if (positionChanged) { - actorData.mPositionChanged = true; - actor->setPosition(actorData.mPosition); + actor->updateCollisionObjectPosition(); + mCollisionWorld->updateSingleAabb(actor->getCollisionObject()); } } } } - void PhysicsTaskScheduler::udpateActorsAabbs() - { - std::unique_lock lock(mCollisionWorldMutex); - for (const auto& actorData : mActorsFrameData) - if (actorData.mPositionChanged) - { - if(const auto actor = actorData.mActor.lock()) - mCollisionWorld->updateSingleAabb(actor->getCollisionObject()); - } - } - bool PhysicsTaskScheduler::hasLineOfSight(const Actor* actor1, const Actor* actor2) { btVector3 pos1 = Misc::Convert::toBullet(actor1->getCollisionObjectPosition() + osg::Vec3f(0,0,actor1->getHalfExtents().z() * 0.9)); // eye level @@ -517,7 +520,7 @@ namespace MWPhysics resultCallback.m_collisionFilterGroup = 0xFF; resultCallback.m_collisionFilterMask = CollisionType_World|CollisionType_HeightMap|CollisionType_Door; - MaybeSharedLock lockColWorld(mCollisionWorldMutex, mThreadSafeBullet); + MaybeSharedLock lockColWorld(mCollisionWorldMutex, mThreadSafeBullet); mCollisionWorld->rayTest(pos1, pos2, resultCallback); return !resultCallback.hasHit(); @@ -539,6 +542,5 @@ namespace MWPhysics mMovementResults[actorData.mPtr] = interpolateMovements(actorData, mTimeAccum, mPhysicsDt); updateMechanics(actorData); } - udpateActorsAabbs(); } } diff --git a/apps/openmw/mwphysics/mtphysics.hpp b/apps/openmw/mwphysics/mtphysics.hpp index 4862393f3..c061fe01d 100644 --- a/apps/openmw/mwphysics/mtphysics.hpp +++ b/apps/openmw/mwphysics/mtphysics.hpp @@ -3,12 +3,14 @@ #include #include -#include +#include #include +#include -#include #include +#include + #include "physicssystem.hpp" #include "ptrholder.hpp" @@ -30,13 +32,13 @@ namespace MWPhysics /// @param timeAccum accumulated time from previous run to interpolate movements /// @param actorsData per actor data needed to compute new positions /// @return new position of each actor - const PtrPositionList& moveActors(int numSteps, float timeAccum, std::vector&& actorsData, CollisionMap& standingCollisions, bool skip); + const PtrPositionList& moveActors(int numSteps, float timeAccum, std::vector&& actorsData, bool skip, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats); // Thread safe wrappers void rayTest(const btVector3& rayFromWorld, const btVector3& rayToWorld, btCollisionWorld::RayResultCallback& resultCallback) const; void convexSweepTest(const btConvexShape* castShape, const btTransform& from, const btTransform& to, btCollisionWorld::ConvexResultCallback& resultCallback) const; void contactTest(btCollisionObject* colObj, btCollisionWorld::ContactResultCallback& resultCallback); - boost::optional getHitPoint(const btTransform& from, btCollisionObject* target); + std::optional getHitPoint(const btTransform& from, btCollisionObject* target); void aabbTest(const btVector3& aabbMin, const btVector3& aabbMax, btBroadphaseAabbCallback& callback); void getAabb(const btCollisionObject* obj, btVector3& min, btVector3& max); void setCollisionFilterMask(btCollisionObject* collisionObject, int collisionFilterMask); @@ -49,7 +51,6 @@ namespace MWPhysics void syncComputation(); void worker(); void updateActorsPositions(); - void udpateActorsAabbs(); bool hasLineOfSight(const Actor* actor1, const Actor* actor2); void refreshLOSCache(); void updateAabbs(); @@ -83,11 +84,17 @@ namespace MWPhysics std::atomic mNextLOS; std::vector mThreads; - mutable std::shared_timed_mutex mSimulationMutex; - mutable std::shared_timed_mutex mCollisionWorldMutex; - mutable std::shared_timed_mutex mLOSCacheMutex; + mutable std::shared_mutex mSimulationMutex; + mutable std::shared_mutex mCollisionWorldMutex; + mutable std::shared_mutex mLOSCacheMutex; mutable std::mutex mUpdateAabbMutex; std::condition_variable_any mHasJob; + + unsigned int mFrameNumber; + const osg::Timer* mTimer; + osg::Timer_t mTimeBegin; + osg::Timer_t mTimeEnd; + osg::Timer_t mFrameStart; }; } diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 00068c1e6..1ceba2f31 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -1,7 +1,11 @@ #include "physicssystem.hpp" +#include +#include +#include #include #include +#include #include #include @@ -90,6 +94,7 @@ namespace MWPhysics } mTaskScheduler = std::make_unique(mPhysicsDt, mCollisionWorld); + mDebugDrawer = std::make_unique(mParentNode, mCollisionWorld.get(), mDebugDrawEnabled); } PhysicsSystem::~PhysicsSystem() @@ -124,14 +129,8 @@ namespace MWPhysics { mDebugDrawEnabled = !mDebugDrawEnabled; - if (mDebugDrawEnabled && !mDebugDrawer) - { - mDebugDrawer.reset(new MWRender::DebugDrawer(mParentNode, mCollisionWorld.get())); - mCollisionWorld->setDebugDrawer(mDebugDrawer.get()); - mDebugDrawer->setDebugMode(mDebugDrawEnabled); - } - else if (mDebugDrawer) - mDebugDrawer->setDebugMode(mDebugDrawEnabled); + mCollisionWorld->setDebugDrawer(mDebugDrawEnabled ? mDebugDrawer.get() : nullptr); + mDebugDrawer->setDebugMode(mDebugDrawEnabled); return mDebugDrawEnabled; } @@ -150,11 +149,11 @@ namespace MWPhysics if (!physactor || !physactor->getOnGround()) return false; - CollisionMap::const_iterator found = mStandingCollisions.find(actor); - if (found == mStandingCollisions.end()) + const auto obj = physactor->getStandingOnPtr(); + if (obj.isEmpty()) return true; // assume standing on terrain (which is a non-object, so not collision tracked) - ObjectMap::const_iterator foundObj = mObjects.find(found->second); + ObjectMap::const_iterator foundObj = mObjects.find(obj); if (foundObj == mObjects.end()) return false; @@ -175,6 +174,7 @@ namespace MWPhysics if (result.mHit) { + reportCollision(Misc::Convert::toBullet(result.mHitPos), Misc::Convert::toBullet(result.mHitNormal)); return std::make_pair(result.mHitObject, result.mHitPos); } @@ -219,7 +219,10 @@ namespace MWPhysics { PtrHolder* holder = static_cast(resultCallback.mObject->getUserPointer()); if (holder) + { + reportCollision(resultCallback.mContactPoint, resultCallback.mContactNormal); return std::make_pair(holder->getPtr(), Misc::Convert::toOsg(resultCallback.mContactPoint)); + } } return std::make_pair(MWWorld::Ptr(), osg::Vec3f()); } @@ -239,7 +242,7 @@ namespace MWPhysics auto hitpoint = mTaskScheduler->getHitPoint(rayFrom, targetCollisionObj); if (hitpoint) - return (point - Misc::Convert::toOsg(hitpoint.get())).length(); + return (point - Misc::Convert::toOsg(*hitpoint)).length(); // didn't hit the target. this could happen if point is already inside the collision box return 0.f; @@ -401,15 +404,15 @@ namespace MWPhysics return osg::Vec3f(); } - std::vector PhysicsSystem::getCollisions(const MWWorld::ConstPtr &ptr, int collisionGroup, int collisionMask) const + std::vector PhysicsSystem::getCollisionsPoints(const MWWorld::ConstPtr &ptr, int collisionGroup, int collisionMask) const { btCollisionObject* me = nullptr; - ObjectMap::const_iterator found = mObjects.find(ptr); + auto found = mObjects.find(ptr); if (found != mObjects.end()) me = found->second->getCollisionObject(); else - return std::vector(); + return {}; ContactTestResultCallback resultCallback (me); resultCallback.m_collisionFilterGroup = collisionGroup; @@ -418,13 +421,21 @@ namespace MWPhysics return resultCallback.mResult; } + std::vector PhysicsSystem::getCollisions(const MWWorld::ConstPtr &ptr, int collisionGroup, int collisionMask) const + { + std::vector actors; + for (auto& [actor, point, normal] : getCollisionsPoints(ptr, collisionGroup, collisionMask)) + actors.emplace_back(actor); + return actors; + } + osg::Vec3f PhysicsSystem::traceDown(const MWWorld::Ptr &ptr, const osg::Vec3f& position, float maxHeight) { ActorMap::iterator found = mActors.find(ptr); if (found == mActors.end()) return ptr.getRefData().getPosition().asVec3(); - else - return MovementSolver::traceDown(ptr, position, found->second.get(), mCollisionWorld.get(), maxHeight); + found->second->resetPosition(); + return MovementSolver::traceDown(ptr, position, found->second.get(), mCollisionWorld.get(), maxHeight); } void PhysicsSystem::addHeightField (const float* heights, int x, int y, float triSize, float sqrtVerts, float minH, float maxH, const osg::Object* holdObject) @@ -491,22 +502,6 @@ namespace MWPhysics } } - void PhysicsSystem::updateCollisionMapPtr(CollisionMap& map, const MWWorld::Ptr &old, const MWWorld::Ptr &updated) - { - CollisionMap::iterator found = map.find(old); - if (found != map.end()) - { - map[updated] = found->second; - map.erase(found); - } - - for (auto& collision : map) - { - if (collision.second == old) - collision.second = updated; - } - } - void PhysicsSystem::updatePtr(const MWWorld::Ptr &old, const MWWorld::Ptr &updated) { ObjectMap::iterator found = mObjects.find(old); @@ -527,7 +522,11 @@ namespace MWPhysics mActors.emplace(updated, std::move(actor)); } - updateCollisionMapPtr(mStandingCollisions, old, updated); + for (auto& [_, actor] : mActors) + { + if (actor->getStandingOnPtr() == old) + actor->setStandingOnPtr(updated); + } } Actor *PhysicsSystem::getActor(const MWWorld::Ptr &ptr) @@ -648,27 +647,26 @@ namespace MWPhysics return false; } - void PhysicsSystem::queueObjectMovement(const MWWorld::Ptr &ptr, const osg::Vec3f &movement) + void PhysicsSystem::queueObjectMovement(const MWWorld::Ptr &ptr, const osg::Vec3f &velocity) { for(auto& movementItem : mMovementQueue) { if (movementItem.first == ptr) { - movementItem.second = movement; + movementItem.second = velocity; return; } } - mMovementQueue.emplace_back(ptr, movement); + mMovementQueue.emplace_back(ptr, velocity); } void PhysicsSystem::clearQueuedMovement() { mMovementQueue.clear(); - mStandingCollisions.clear(); } - const PtrPositionList& PhysicsSystem::applyQueuedMovement(float dt, bool skipSimulation) + const PtrPositionList& PhysicsSystem::applyQueuedMovement(float dt, bool skipSimulation, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats) { mTimeAccum += dt; @@ -678,24 +676,20 @@ namespace MWPhysics mTimeAccum -= numSteps * mPhysicsDt; - return mTaskScheduler->moveActors(numSteps, mTimeAccum, prepareFrameData(), mStandingCollisions, skipSimulation); + return mTaskScheduler->moveActors(numSteps, mTimeAccum, prepareFrameData(numSteps), skipSimulation, frameStart, frameNumber, stats); } - std::vector PhysicsSystem::prepareFrameData() + std::vector PhysicsSystem::prepareFrameData(int numSteps) { std::vector actorsFrameData; actorsFrameData.reserve(mMovementQueue.size()); const MWBase::World *world = MWBase::Environment::get().getWorld(); - for (const auto& m : mMovementQueue) + for (const auto& [character, movement] : mMovementQueue) { - const auto& character = m.first; - const auto& movement = m.second; const auto foundActor = mActors.find(character); if (foundActor == mActors.end()) // actor was already removed from the scene - { - mStandingCollisions.erase(character); continue; - } + auto physicActor = foundActor->second; float waterlevel = -std::numeric_limits::max(); @@ -723,7 +717,12 @@ namespace MWPhysics // Slow fall reduces fall speed by a factor of (effect magnitude / 200) const float slowFall = 1.f - std::max(0.f, std::min(1.f, effects.get(ESM::MagicEffect::SlowFall).getMagnitude() * 0.005f)); - actorsFrameData.emplace_back(std::move(physicActor), character, mStandingCollisions[character], moveToWaterSurface, movement, slowFall, waterlevel); + // Ue current value only if we don't advance the simulation. Otherwise we might get a stale value. + MWWorld::Ptr standingOn; + if (numSteps == 0) + standingOn = physicActor->getStandingOnPtr(); + + actorsFrameData.emplace_back(std::move(physicActor), character, standingOn, moveToWaterSurface, movement, slowFall, waterlevel); } mMovementQueue.clear(); return actorsFrameData; @@ -755,26 +754,24 @@ namespace MWPhysics void PhysicsSystem::debugDraw() { - if (mDebugDrawer) + if (mDebugDrawEnabled) mDebugDrawer->step(); } bool PhysicsSystem::isActorStandingOn(const MWWorld::Ptr &actor, const MWWorld::ConstPtr &object) const { - for (const auto& standingActor : mStandingCollisions) - { - if (standingActor.first == actor && standingActor.second == object) - return true; - } + const auto physActor = mActors.find(actor); + if (physActor != mActors.end()) + return physActor->second->getStandingOnPtr() == object; return false; } void PhysicsSystem::getActorsStandingOn(const MWWorld::ConstPtr &object, std::vector &out) const { - for (const auto& standingActor : mStandingCollisions) + for (const auto& [_, actor] : mActors) { - if (standingActor.second == object) - out.push_back(standingActor.first); + if (actor->getStandingOnPtr() == object) + out.emplace_back(actor->getPtr()); } } @@ -861,10 +858,16 @@ namespace MWPhysics stats.setAttribute(frameNumber, "Physics HeightFields", mHeightFields.size()); } + void PhysicsSystem::reportCollision(const btVector3& position, const btVector3& normal) + { + if (mDebugDrawEnabled) + mDebugDrawer->addCollision(position, normal); + } + ActorFrameData::ActorFrameData(const std::shared_ptr& actor, const MWWorld::Ptr character, const MWWorld::Ptr standingOn, bool moveToWaterSurface, osg::Vec3f movement, float slowFall, float waterlevel) : mActor(actor), mActorRaw(actor.get()), mStandingOn(standingOn), - mPositionChanged(false), mDidJump(false), mNeedLand(false), mMoveToWaterSurface(moveToWaterSurface), + mDidJump(false), mNeedLand(false), mMoveToWaterSurface(moveToWaterSurface), mWaterlevel(waterlevel), mSlowFall(slowFall), mOldHeight(0), mFallHeight(0), mMovement(movement), mPosition(), mRefpos() { const MWBase::World *world = MWBase::Environment::get().getWorld(); @@ -874,10 +877,9 @@ namespace MWPhysics mWantJump = mPtr.getClass().getMovementSettings(mPtr).mPosition[2] != 0; mIsDead = mPtr.getClass().getCreatureStats(mPtr).isDead(); mWasOnGround = actor->getOnGround(); - } - void ActorFrameData::updatePosition() - { + mActorRaw->updatePosition(); + mOrigin = mActorRaw->getNextPosition(); mPosition = mActorRaw->getPosition(); if (mMoveToWaterSurface) { diff --git a/apps/openmw/mwphysics/physicssystem.hpp b/apps/openmw/mwphysics/physicssystem.hpp index f89a29cae..379aea1dd 100644 --- a/apps/openmw/mwphysics/physicssystem.hpp +++ b/apps/openmw/mwphysics/physicssystem.hpp @@ -10,6 +10,7 @@ #include #include #include +#include #include "../mwworld/ptr.hpp" @@ -45,17 +46,24 @@ class btDefaultCollisionConfiguration; class btCollisionDispatcher; class btCollisionObject; class btCollisionShape; +class btVector3; namespace MWPhysics { using PtrPositionList = std::map; - using CollisionMap = std::map; class HeightField; class Object; class Actor; class PhysicsTaskScheduler; + struct ContactPoint + { + MWWorld::Ptr mObject; + osg::Vec3f mPoint; + osg::Vec3f mNormal; + }; + struct LOSRequest { LOSRequest(const std::weak_ptr& a1, const std::weak_ptr& a2); @@ -70,14 +78,12 @@ namespace MWPhysics struct ActorFrameData { ActorFrameData(const std::shared_ptr& actor, const MWWorld::Ptr character, const MWWorld::Ptr standingOn, bool moveToWaterSurface, osg::Vec3f movement, float slowFall, float waterlevel); - void updatePosition(); std::weak_ptr mActor; Actor* mActorRaw; MWWorld::Ptr mPtr; MWWorld::Ptr mStandingOn; bool mFlying; bool mSwimming; - bool mPositionChanged; bool mWasOnGround; bool mWantJump; bool mDidJump; @@ -89,6 +95,7 @@ namespace MWPhysics float mOldHeight; float mFallHeight; osg::Vec3f mMovement; + osg::Vec3f mOrigin; osg::Vec3f mPosition; ESM::Position mRefpos; }; @@ -144,6 +151,7 @@ namespace MWPhysics void debugDraw(); std::vector getCollisions(const MWWorld::ConstPtr &ptr, int collisionGroup, int collisionMask) const; ///< get handles this object collides with + std::vector getCollisionsPoints(const MWWorld::ConstPtr &ptr, int collisionGroup, int collisionMask) const; osg::Vec3f traceDown(const MWWorld::Ptr &ptr, const osg::Vec3f& position, float maxHeight); std::pair getHitContact(const MWWorld::ConstPtr& actor, @@ -156,17 +164,17 @@ namespace MWPhysics /// target vector hits the collision shape and then calculates distance from the intersection point. /// This can be used to find out how much nearer we need to move to the target for a "getHitContact" to be successful. /// \note Only Actor targets are supported at the moment. - float getHitDistance(const osg::Vec3f& point, const MWWorld::ConstPtr& target) const final; + float getHitDistance(const osg::Vec3f& point, const MWWorld::ConstPtr& target) const override; /// @param me Optional, a Ptr to ignore in the list of results. targets are actors to filter for, ignoring all other actors. RayCastingResult castRay(const osg::Vec3f &from, const osg::Vec3f &to, const MWWorld::ConstPtr& ignore = MWWorld::ConstPtr(), std::vector targets = std::vector(), - int mask = CollisionType_World|CollisionType_HeightMap|CollisionType_Actor|CollisionType_Door, int group=0xff) const final; + int mask = CollisionType_World|CollisionType_HeightMap|CollisionType_Actor|CollisionType_Door, int group=0xff) const override; - RayCastingResult castSphere(const osg::Vec3f& from, const osg::Vec3f& to, float radius) const final; + RayCastingResult castSphere(const osg::Vec3f& from, const osg::Vec3f& to, float radius) const override; /// Return true if actor1 can see actor2. - bool getLineOfSight(const MWWorld::ConstPtr& actor1, const MWWorld::ConstPtr& actor2) const final; + bool getLineOfSight(const MWWorld::ConstPtr& actor1, const MWWorld::ConstPtr& actor2) const override; bool isOnGround (const MWWorld::Ptr& actor); @@ -193,7 +201,7 @@ namespace MWPhysics void queueObjectMovement(const MWWorld::Ptr &ptr, const osg::Vec3f &velocity); /// Apply all queued movements, then clear the list. - const PtrPositionList& applyQueuedMovement(float dt, bool skipSimulation); + const PtrPositionList& applyQueuedMovement(float dt, bool skipSimulation, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats); /// Clear the queued movements list without applying. void clearQueuedMovement(); @@ -232,12 +240,13 @@ namespace MWPhysics bool isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius, const MWWorld::ConstPtr& ignore) const; void reportStats(unsigned int frameNumber, osg::Stats& stats) const; + void reportCollision(const btVector3& position, const btVector3& normal); private: void updateWater(); - std::vector prepareFrameData(); + std::vector prepareFrameData(int numSteps); osg::ref_ptr mUnrefQueue; @@ -263,13 +272,6 @@ namespace MWPhysics bool mDebugDrawEnabled; - // Tracks standing collisions happening during a single frame. - // This will detect standing on an object, but won't detect running e.g. against a wall. - CollisionMap mStandingCollisions; - - // replaces all occurrences of 'old' in the map by 'updated', no matter if it's a key or value - void updateCollisionMapPtr(CollisionMap& map, const MWWorld::Ptr &old, const MWWorld::Ptr &updated); - using PtrVelocityList = std::vector>; PtrVelocityList mMovementQueue; diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 6b93d9a17..4d46ed739 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -16,7 +16,6 @@ #include #include -#include #include #include @@ -37,8 +36,6 @@ #include -#include - #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwworld/esmstore.hpp" @@ -66,7 +63,7 @@ namespace void apply(osg::Node &node) override { if (dynamic_cast(&node)) - mToRemove.push_back(&node); + mToRemove.emplace_back(&node); traverse(node); } @@ -74,7 +71,7 @@ namespace void apply(osg::Drawable& drw) override { if (osgParticle::ParticleSystem* partsys = dynamic_cast(&drw)) - mToRemove.push_back(partsys); + mToRemove.emplace_back(partsys); } void remove() @@ -278,7 +275,7 @@ namespace if (vfxCallback) { if (vfxCallback->mFinished) - mToRemove.push_back(std::make_pair(group.asNode(), group.getParent(0))); + mToRemove.emplace_back(group.asNode(), group.getParent(0)); else mHasMagicEffects = true; } @@ -331,7 +328,7 @@ namespace { bool toRemove = mEffectId < 0 || vfxCallback->mParams.mEffectId == mEffectId; if (toRemove) - mToRemove.push_back(std::make_pair(group.asNode(), group.getParent(0))); + mToRemove.emplace_back(group.asNode(), group.getParent(0)); else mHasMagicEffects = true; } @@ -432,7 +429,7 @@ namespace node.setStateSet(nullptr); if (node.getNodeMask() == 0x1 && node.getNumParents() == 1) - mToRemove.push_back(std::make_pair(&node, node.getParent(0))); + mToRemove.emplace_back(&node, node.getParent(0)); else traverse(node); } @@ -450,12 +447,12 @@ namespace osg::Group* parentParent = static_cast(*(parent - 1)); if (parentGroup->getNumChildren() == 1 && parentGroup->getDataVariance() == osg::Object::STATIC) { - mToRemove.push_back(std::make_pair(parentGroup, parentParent)); + mToRemove.emplace_back(parentGroup, parentParent); return; } } - mToRemove.push_back(std::make_pair(&node, parentGroup)); + mToRemove.emplace_back(&node, parentGroup); } }; @@ -483,7 +480,7 @@ namespace { osg::Group* parent = static_cast(*(getNodePath().end()-2)); // Not safe to remove in apply(), since the visitor is still iterating the child list - mToRemove.push_back(std::make_pair(&node, parent)); + mToRemove.emplace_back(&node, parent); } } }; @@ -494,9 +491,8 @@ namespace MWRender class TransparencyUpdater : public SceneUtil::StateSetUpdater { public: - TransparencyUpdater(const float alpha, osg::ref_ptr shadowUniform) + TransparencyUpdater(const float alpha) : mAlpha(alpha) - , mShadowUniform(shadowUniform) { } @@ -510,9 +506,6 @@ namespace MWRender { osg::BlendFunc* blendfunc (new osg::BlendFunc); stateset->setAttributeAndModes(blendfunc, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); - // TODO: don't do this anymore once custom shadow renderbin is handling it - if (mShadowUniform) - stateset->addUniform(mShadowUniform); stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); stateset->setRenderBinMode(osg::StateSet::OVERRIDE_RENDERBIN_DETAILS); @@ -534,7 +527,6 @@ namespace MWRender private: float mAlpha; - osg::ref_ptr mShadowUniform; }; struct Animation::AnimSource @@ -1827,7 +1819,7 @@ namespace MWRender { if (mTransparencyUpdater == nullptr) { - mTransparencyUpdater = new TransparencyUpdater(alpha, mResourceSystem->getSceneManager()->getShaderManager().getShadowMapAlphaTestEnableUniform()); + mTransparencyUpdater = new TransparencyUpdater(alpha); mObjectRoot->addCullCallback(mTransparencyUpdater); } else diff --git a/apps/openmw/mwrender/bulletdebugdraw.cpp b/apps/openmw/mwrender/bulletdebugdraw.cpp index 4cf76e473..00529ef80 100644 --- a/apps/openmw/mwrender/bulletdebugdraw.cpp +++ b/apps/openmw/mwrender/bulletdebugdraw.cpp @@ -1,4 +1,4 @@ -#include "bulletdebugdraw.hpp" +#include #include @@ -6,27 +6,19 @@ #include #include +#include +#include "bulletdebugdraw.hpp" #include "vismask.hpp" -namespace -{ - osg::Vec3f toOsg(const btVector3& vec) - { - return osg::Vec3f(vec.x(), vec.y(), vec.z()); - } -} - namespace MWRender { -DebugDrawer::DebugDrawer(osg::ref_ptr parentNode, btCollisionWorld *world) +DebugDrawer::DebugDrawer(osg::ref_ptr parentNode, btCollisionWorld *world, int debugMode) : mParentNode(parentNode), - mWorld(world), - mDebugOn(true) + mWorld(world) { - - createGeometry(); + setDebugMode(debugMode); } void DebugDrawer::createGeometry() @@ -37,11 +29,14 @@ void DebugDrawer::createGeometry() mGeometry->setNodeMask(Mask_Debug); mVertices = new osg::Vec3Array; + mColors = new osg::Vec4Array; mDrawArrays = new osg::DrawArrays(osg::PrimitiveSet::LINES); mGeometry->setUseDisplayList(false); mGeometry->setVertexArray(mVertices); + mGeometry->setColorArray(mColors); + mGeometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX); mGeometry->setDataVariance(osg::Object::DYNAMIC); mGeometry->addPrimitiveSet(mDrawArrays); @@ -70,23 +65,53 @@ void DebugDrawer::step() if (mDebugOn) { mVertices->clear(); + mColors->clear(); mWorld->debugDrawWorld(); + showCollisions(); mDrawArrays->setCount(mVertices->size()); mVertices->dirty(); + mColors->dirty(); mGeometry->dirtyBound(); } } void DebugDrawer::drawLine(const btVector3 &from, const btVector3 &to, const btVector3 &color) { - mVertices->push_back(toOsg(from)); - mVertices->push_back(toOsg(to)); + mVertices->push_back(Misc::Convert::toOsg(from)); + mVertices->push_back(Misc::Convert::toOsg(to)); + mColors->push_back({1,1,1,1}); + mColors->push_back({1,1,1,1}); +} + +void DebugDrawer::addCollision(const btVector3& orig, const btVector3& normal) +{ + mCollisionViews.emplace_back(orig, normal); +} + +void DebugDrawer::showCollisions() +{ + const auto now = std::chrono::steady_clock::now(); + for (auto& [from, to , created] : mCollisionViews) + { + if (now - created < std::chrono::seconds(2)) + { + mVertices->push_back(Misc::Convert::toOsg(from)); + mVertices->push_back(Misc::Convert::toOsg(to)); + mColors->push_back({1,0,0,1}); + mColors->push_back({1,0,0,1}); + } + } + mCollisionViews.erase(std::remove_if(mCollisionViews.begin(), mCollisionViews.end(), + [&now](const CollisionView& view) { return now - view.mCreated >= std::chrono::seconds(2); }), + mCollisionViews.end()); } void DebugDrawer::drawContactPoint(const btVector3 &PointOnB, const btVector3 &normalOnB, btScalar distance, int lifeTime, const btVector3 &color) { - mVertices->push_back(toOsg(PointOnB)); - mVertices->push_back(toOsg(PointOnB) + (toOsg(normalOnB) * distance * 20)); + mVertices->push_back(Misc::Convert::toOsg(PointOnB)); + mVertices->push_back(Misc::Convert::toOsg(PointOnB) + (Misc::Convert::toOsg(normalOnB) * distance * 20)); + mColors->push_back({1,1,1,1}); + mColors->push_back({1,1,1,1}); } void DebugDrawer::reportErrorWarning(const char *warningString) @@ -96,7 +121,7 @@ void DebugDrawer::reportErrorWarning(const char *warningString) void DebugDrawer::setDebugMode(int isOn) { - mDebugOn = (isOn == 0) ? false : true; + mDebugOn = (isOn != 0); if (!mDebugOn) destroyGeometry(); @@ -109,6 +134,4 @@ int DebugDrawer::getDebugMode() const return mDebugOn; } - - } diff --git a/apps/openmw/mwrender/bulletdebugdraw.hpp b/apps/openmw/mwrender/bulletdebugdraw.hpp index 30cabc4f9..ec421bd74 100644 --- a/apps/openmw/mwrender/bulletdebugdraw.hpp +++ b/apps/openmw/mwrender/bulletdebugdraw.hpp @@ -1,6 +1,9 @@ #ifndef OPENMW_MWRENDER_BULLETDEBUGDRAW_H #define OPENMW_MWRENDER_BULLETDEBUGDRAW_H +#include +#include + #include #include #include @@ -20,11 +23,22 @@ namespace MWRender class DebugDrawer : public btIDebugDraw { +private: + struct CollisionView + { + btVector3 mOrig; + btVector3 mEnd; + std::chrono::time_point mCreated; + CollisionView(btVector3 orig, btVector3 normal) : mOrig(orig), mEnd(orig + normal * 20), mCreated(std::chrono::steady_clock::now()) {}; + }; + std::vector mCollisionViews; + protected: osg::ref_ptr mParentNode; btCollisionWorld *mWorld; osg::ref_ptr mGeometry; osg::ref_ptr mVertices; + osg::ref_ptr mColors; osg::ref_ptr mDrawArrays; bool mDebugOn; @@ -34,13 +48,17 @@ protected: public: - DebugDrawer(osg::ref_ptr parentNode, btCollisionWorld *world); + DebugDrawer(osg::ref_ptr parentNode, btCollisionWorld *world, int debugMode = 1); ~DebugDrawer(); void step(); void drawLine(const btVector3& from,const btVector3& to,const btVector3& color) override; + void addCollision(const btVector3& orig, const btVector3& normal); + + void showCollisions(); + void drawContactPoint(const btVector3& PointOnB,const btVector3& normalOnB,btScalar distance,int lifeTime,const btVector3& color) override; void reportErrorWarning(const char* warningString) override; diff --git a/apps/openmw/mwrender/camera.cpp b/apps/openmw/mwrender/camera.cpp index c16324115..77be2d7b5 100644 --- a/apps/openmw/mwrender/camera.cpp +++ b/apps/openmw/mwrender/camera.cpp @@ -69,6 +69,7 @@ namespace MWRender mBaseCameraDistance(Settings::Manager::getFloat("third person camera distance", "Camera")), mPitch(0.f), mYaw(0.f), + mRoll(0.f), mVanityToggleQueued(false), mVanityToggleQueuedValue(false), mViewModeToggleQueued(false), @@ -84,6 +85,9 @@ namespace MWRender mDynamicCameraDistanceEnabled(false), mShowCrosshairInThirdPersonMode(false), mHeadBobbingEnabled(Settings::Manager::getBool("head bobbing", "Camera")), + mHeadBobbingOffset(0.f), + mHeadBobbingWeight(0.f), + mTotalMovement(0.f), mDeferredRotation(osg::Vec3f()), mDeferredRotationDisabled(false) { diff --git a/apps/openmw/mwrender/camera.hpp b/apps/openmw/mwrender/camera.hpp index f59b4a3d2..201e80bce 100644 --- a/apps/openmw/mwrender/camera.hpp +++ b/apps/openmw/mwrender/camera.hpp @@ -75,8 +75,8 @@ namespace MWRender bool mHeadBobbingEnabled; float mHeadBobbingOffset; - float mHeadBobbingWeight = 0; // Value from 0 to 1 for smooth enabling/disabling. - float mTotalMovement = 0; // Needed for head bobbing. + float mHeadBobbingWeight; // Value from 0 to 1 for smooth enabling/disabling. + float mTotalMovement; // Needed for head bobbing. void updateHeadBobbing(float duration); void updateFocalPointOffset(float duration); diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index feefb29f5..401e21ae4 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -118,7 +118,7 @@ void LocalMap::saveFogOfWar(MWWorld::CellStore* cell) if (segment.mFogOfWarImage && segment.mHasFogState) { std::unique_ptr fog (new ESM::FogState()); - fog->mFogTextures.push_back(ESM::FogTexture()); + fog->mFogTextures.emplace_back(); segment.saveFogOfWar(fog->mFogTextures.back()); @@ -150,7 +150,7 @@ void LocalMap::saveFogOfWar(MWWorld::CellStore* cell) { const MapSegment& segment = mSegments[std::make_pair(x,y)]; - fog->mFogTextures.push_back(ESM::FogTexture()); + fog->mFogTextures.emplace_back(); // saving even if !segment.mHasFogState so we don't mess up the segmenting // plus, older openmw versions can't deal with empty images diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index f74455d59..a5015f377 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -596,7 +596,7 @@ namespace MWRender if (numinstances > 0) { // add a ref to the original template, to hint to the cache that it's still being used and should be kept in cache - templateRefs->mObjects.push_back(cnode); + templateRefs->mObjects.emplace_back(cnode); if (pair.second.mNeedCompile) { diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 561ad2e0e..212901e66 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -224,6 +224,7 @@ namespace MWRender resourceSystem->getSceneManager()->setNormalHeightMapPattern(Settings::Manager::getString("normal height map pattern", "Shaders")); resourceSystem->getSceneManager()->setAutoUseSpecularMaps(Settings::Manager::getBool("auto use object specular maps", "Shaders")); resourceSystem->getSceneManager()->setSpecularMapPattern(Settings::Manager::getString("specular map pattern", "Shaders")); + resourceSystem->getSceneManager()->setApplyLightingToEnvMaps(Settings::Manager::getBool("apply lighting to environment maps", "Shaders")); osg::ref_ptr sceneRoot = new SceneUtil::LightManager; sceneRoot->setLightingMask(Mask_Lighting); @@ -372,6 +373,7 @@ namespace MWRender mViewer->getCamera()->setCullMask(~(Mask_UpdateVisitor|Mask_SimpleWater)); NifOsg::Loader::setHiddenNodeMask(Mask_UpdateVisitor); NifOsg::Loader::setIntersectionDisabledNodeMask(Mask_Effect); + Nif::NIFFile::setLoadUnsupportedFiles(Settings::Manager::getBool("load unsupported nif files", "Models")); mNearClip = Settings::Manager::getFloat("near clip", "Camera"); mViewDistance = Settings::Manager::getFloat("viewing distance", "Camera"); @@ -439,7 +441,7 @@ namespace MWRender workItem->mKeyframes.push_back(std::string("meshes/") + basemodels[i] + ".kf"); } - workItem->mTextures.push_back("textures/_land_default.dds"); + workItem->mTextures.emplace_back("textures/_land_default.dds"); mWorkQueue->addWorkItem(workItem); } diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 7ba490b92..2920e07dd 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -941,10 +941,10 @@ public: Moon(osg::Group* parentNode, Resource::ImageManager& imageManager, float scaleFactor, Type type) : CelestialBody(parentNode, scaleFactor, 2) , mType(type) - , mPhase(MoonState::Phase_Unspecified) + , mPhase(MoonState::Phase::Unspecified) , mUpdater(new Updater(imageManager)) { - setPhase(MoonState::Phase_Full); + setPhase(MoonState::Phase::Full); setVisible(true); mGeom->addUpdateCallback(mUpdater); @@ -993,14 +993,14 @@ public: unsigned int getPhaseInt() const { - if (mPhase == MoonState::Phase_New) return 0; - else if (mPhase == MoonState::Phase_WaxingCrescent) return 1; - else if (mPhase == MoonState::Phase_WaningCrescent) return 1; - else if (mPhase == MoonState::Phase_FirstQuarter) return 2; - else if (mPhase == MoonState::Phase_ThirdQuarter) return 2; - else if (mPhase == MoonState::Phase_WaxingGibbous) return 3; - else if (mPhase == MoonState::Phase_WaningGibbous) return 3; - else if (mPhase == MoonState::Phase_Full) return 4; + if (mPhase == MoonState::Phase::New) return 0; + else if (mPhase == MoonState::Phase::WaxingCrescent) return 1; + else if (mPhase == MoonState::Phase::WaningCrescent) return 1; + else if (mPhase == MoonState::Phase::FirstQuarter) return 2; + else if (mPhase == MoonState::Phase::ThirdQuarter) return 2; + else if (mPhase == MoonState::Phase::WaxingGibbous) return 3; + else if (mPhase == MoonState::Phase::WaningGibbous) return 3; + else if (mPhase == MoonState::Phase::Full) return 4; return 0; } @@ -1090,14 +1090,14 @@ private: else textureName += "masser_"; - if (phase == MoonState::Phase_New) textureName += "new"; - else if(phase == MoonState::Phase_WaxingCrescent) textureName += "one_wax"; - else if(phase == MoonState::Phase_FirstQuarter) textureName += "half_wax"; - else if(phase == MoonState::Phase_WaxingGibbous) textureName += "three_wax"; - else if(phase == MoonState::Phase_WaningCrescent) textureName += "one_wan"; - else if(phase == MoonState::Phase_ThirdQuarter) textureName += "half_wan"; - else if(phase == MoonState::Phase_WaningGibbous) textureName += "three_wan"; - else if(phase == MoonState::Phase_Full) textureName += "full"; + if (phase == MoonState::Phase::New) textureName += "new"; + else if(phase == MoonState::Phase::WaxingCrescent) textureName += "one_wax"; + else if(phase == MoonState::Phase::FirstQuarter) textureName += "half_wax"; + else if(phase == MoonState::Phase::WaxingGibbous) textureName += "three_wax"; + else if(phase == MoonState::Phase::WaningCrescent) textureName += "one_wan"; + else if(phase == MoonState::Phase::ThirdQuarter) textureName += "half_wan"; + else if(phase == MoonState::Phase::WaningGibbous) textureName += "three_wan"; + else if(phase == MoonState::Phase::Full) textureName += "full"; textureName += ".dds"; @@ -1911,42 +1911,42 @@ void SkyManager::setWaterHeight(float height) void SkyManager::listAssetsToPreload(std::vector& models, std::vector& textures) { - models.push_back("meshes/sky_atmosphere.nif"); + models.emplace_back("meshes/sky_atmosphere.nif"); if (mSceneManager->getVFS()->exists("meshes/sky_night_02.nif")) - models.push_back("meshes/sky_night_02.nif"); - models.push_back("meshes/sky_night_01.nif"); - models.push_back("meshes/sky_clouds_01.nif"); - - models.push_back("meshes\\ashcloud.nif"); - models.push_back("meshes\\blightcloud.nif"); - models.push_back("meshes\\snow.nif"); - models.push_back("meshes\\blizzard.nif"); - - textures.push_back("textures/tx_mooncircle_full_s.dds"); - textures.push_back("textures/tx_mooncircle_full_m.dds"); - - textures.push_back("textures/tx_masser_new.dds"); - textures.push_back("textures/tx_masser_one_wax.dds"); - textures.push_back("textures/tx_masser_half_wax.dds"); - textures.push_back("textures/tx_masser_three_wax.dds"); - textures.push_back("textures/tx_masser_one_wan.dds"); - textures.push_back("textures/tx_masser_half_wan.dds"); - textures.push_back("textures/tx_masser_three_wan.dds"); - textures.push_back("textures/tx_masser_full.dds"); - - textures.push_back("textures/tx_secunda_new.dds"); - textures.push_back("textures/tx_secunda_one_wax.dds"); - textures.push_back("textures/tx_secunda_half_wax.dds"); - textures.push_back("textures/tx_secunda_three_wax.dds"); - textures.push_back("textures/tx_secunda_one_wan.dds"); - textures.push_back("textures/tx_secunda_half_wan.dds"); - textures.push_back("textures/tx_secunda_three_wan.dds"); - textures.push_back("textures/tx_secunda_full.dds"); - - textures.push_back("textures/tx_sun_05.dds"); - textures.push_back("textures/tx_sun_flash_grey_05.dds"); - - textures.push_back("textures/tx_raindrop_01.dds"); + models.emplace_back("meshes/sky_night_02.nif"); + models.emplace_back("meshes/sky_night_01.nif"); + models.emplace_back("meshes/sky_clouds_01.nif"); + + models.emplace_back("meshes\\ashcloud.nif"); + models.emplace_back("meshes\\blightcloud.nif"); + models.emplace_back("meshes\\snow.nif"); + models.emplace_back("meshes\\blizzard.nif"); + + textures.emplace_back("textures/tx_mooncircle_full_s.dds"); + textures.emplace_back("textures/tx_mooncircle_full_m.dds"); + + textures.emplace_back("textures/tx_masser_new.dds"); + textures.emplace_back("textures/tx_masser_one_wax.dds"); + textures.emplace_back("textures/tx_masser_half_wax.dds"); + textures.emplace_back("textures/tx_masser_three_wax.dds"); + textures.emplace_back("textures/tx_masser_one_wan.dds"); + textures.emplace_back("textures/tx_masser_half_wan.dds"); + textures.emplace_back("textures/tx_masser_three_wan.dds"); + textures.emplace_back("textures/tx_masser_full.dds"); + + textures.emplace_back("textures/tx_secunda_new.dds"); + textures.emplace_back("textures/tx_secunda_one_wax.dds"); + textures.emplace_back("textures/tx_secunda_half_wax.dds"); + textures.emplace_back("textures/tx_secunda_three_wax.dds"); + textures.emplace_back("textures/tx_secunda_one_wan.dds"); + textures.emplace_back("textures/tx_secunda_half_wan.dds"); + textures.emplace_back("textures/tx_secunda_three_wan.dds"); + textures.emplace_back("textures/tx_secunda_full.dds"); + + textures.emplace_back("textures/tx_sun_05.dds"); + textures.emplace_back("textures/tx_sun_flash_grey_05.dds"); + + textures.emplace_back("textures/tx_raindrop_01.dds"); } void SkyManager::setWaterEnabled(bool enabled) diff --git a/apps/openmw/mwrender/sky.hpp b/apps/openmw/mwrender/sky.hpp index 9727529d9..cf697bd44 100644 --- a/apps/openmw/mwrender/sky.hpp +++ b/apps/openmw/mwrender/sky.hpp @@ -99,17 +99,17 @@ namespace MWRender struct MoonState { - enum Phase + enum class Phase { - Phase_Full = 0, - Phase_WaningGibbous, - Phase_ThirdQuarter, - Phase_WaningCrescent, - Phase_New, - Phase_WaxingCrescent, - Phase_FirstQuarter, - Phase_WaxingGibbous, - Phase_Unspecified + Full = 0, + WaningGibbous, + ThirdQuarter, + WaningCrescent, + New, + WaxingCrescent, + FirstQuarter, + WaxingGibbous, + Unspecified }; float mRotationFromHorizon; diff --git a/apps/openmw/mwscript/containerextensions.cpp b/apps/openmw/mwscript/containerextensions.cpp index 2e78fe374..186940dd9 100644 --- a/apps/openmw/mwscript/containerextensions.cpp +++ b/apps/openmw/mwscript/containerextensions.cpp @@ -246,7 +246,7 @@ namespace MWScript auto& store = container.getClass().getContainerStore(container); // Note that unlike AddItem, RemoveItem only removes from unresolved containers if(!store.isResolved()) - store.remove(item, count, ptr, false); + store.remove(item, count, ptr, false, false); } } return; diff --git a/apps/openmw/mwscript/globalscripts.cpp b/apps/openmw/mwscript/globalscripts.cpp index 41f153b28..1a7e3ebbc 100644 --- a/apps/openmw/mwscript/globalscripts.cpp +++ b/apps/openmw/mwscript/globalscripts.cpp @@ -186,7 +186,7 @@ namespace MWScript // make list of global scripts to be added std::vector scripts; - scripts.push_back ("main"); + scripts.emplace_back("main"); for (MWWorld::Store::iterator iter = mStore.get().begin(); diff --git a/apps/openmw/mwscript/locals.cpp b/apps/openmw/mwscript/locals.cpp index 381d73ec8..352dc67b3 100644 --- a/apps/openmw/mwscript/locals.cpp +++ b/apps/openmw/mwscript/locals.cpp @@ -191,7 +191,7 @@ namespace MWScript case 2: value.setType (ESM::VT_Float); value.setFloat (mFloats.at (i2)); break; } - locals.mVariables.push_back (std::make_pair (names[i2], value)); + locals.mVariables.emplace_back (names[i2], value); } } } diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index 1606b2979..a7be5a743 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -579,7 +579,7 @@ std::vector OpenAL_Output::enumerate() devnames = alcGetString(nullptr, ALC_DEVICE_SPECIFIER); while(devnames && *devnames) { - devlist.push_back(devnames); + devlist.emplace_back(devnames); devnames += strlen(devnames)+1; } return devlist; @@ -878,7 +878,7 @@ std::vector OpenAL_Output::enumerateHrtf() for(ALCint i = 0;i < num_hrtf;++i) { const ALCchar *entry = alcGetStringiSOFT(mDevice, ALC_HRTF_SPECIFIER_SOFT, i); - ret.push_back(entry); + ret.emplace_back(entry); } return ret; diff --git a/apps/openmw/mwsound/regionsoundselector.cpp b/apps/openmw/mwsound/regionsoundselector.cpp index ac5f66721..752c4a26b 100644 --- a/apps/openmw/mwsound/regionsoundselector.cpp +++ b/apps/openmw/mwsound/regionsoundselector.cpp @@ -1,5 +1,6 @@ #include "regionsoundselector.hpp" +#include #include #include @@ -18,7 +19,12 @@ namespace MWSound } } - boost::optional RegionSoundSelector::getNextRandom(float duration, const std::string& regionName, + RegionSoundSelector::RegionSoundSelector() + : mMinTimeBetweenSounds(Fallback::Map::getFloat("Weather_Minimum_Time_Between_Environmental_Sounds")) + , mMaxTimeBetweenSounds(Fallback::Map::getFloat("Weather_Maximum_Time_Between_Environmental_Sounds")) + {} + + std::optional RegionSoundSelector::getNextRandom(float duration, const std::string& regionName, const MWBase::World& world) { mTimePassed += duration; @@ -27,9 +33,7 @@ namespace MWSound return {}; const float a = Misc::Rng::rollClosedProbability(); - // NOTE: We should use the "Minimum Time Between Environmental Sounds" and - // "Maximum Time Between Environmental Sounds" fallback settings here. - mTimeToNextEnvSound = 5.0f * a + 15.0f * (1.0f - a); + mTimeToNextEnvSound = mMinTimeBetweenSounds + (mMaxTimeBetweenSounds - mMinTimeBetweenSounds) * a; mTimePassed = 0; if (mLastRegionName != regionName) @@ -50,7 +54,7 @@ namespace MWSound return {}; } - const int r = Misc::Rng::rollDice(mSumChance); + const int r = Misc::Rng::rollDice(std::max(mSumChance, 100)); int pos = 0; const auto isSelected = [&] (const ESM::Region::SoundRef& sound) diff --git a/apps/openmw/mwsound/regionsoundselector.hpp b/apps/openmw/mwsound/regionsoundselector.hpp index 00a2d5ca8..35df8a531 100644 --- a/apps/openmw/mwsound/regionsoundselector.hpp +++ b/apps/openmw/mwsound/regionsoundselector.hpp @@ -1,8 +1,7 @@ #ifndef GAME_SOUND_REGIONSOUNDSELECTOR_H #define GAME_SOUND_REGIONSOUNDSELECTOR_H -#include - +#include #include namespace MWBase @@ -15,14 +14,18 @@ namespace MWSound class RegionSoundSelector { public: - boost::optional getNextRandom(float duration, const std::string& regionName, + std::optional getNextRandom(float duration, const std::string& regionName, const MWBase::World& world); + RegionSoundSelector(); + private: float mTimeToNextEnvSound = 0.0f; int mSumChance = 0; std::string mLastRegionName; float mTimePassed = 0.0; + float mMinTimeBetweenSounds; + float mMaxTimeBetweenSounds; }; } diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 17aa184a7..1a90dceed 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -65,6 +65,9 @@ namespace MWSound , mUnderwaterSound(nullptr) , mNearWaterSound(nullptr) , mPlaybackPaused(false) + , mTimePassed(0.f) + , mLastCell(nullptr) + , mCurrentRegionSound(nullptr) { mBufferCacheMin = std::max(Settings::Manager::getInt("buffer cache min", "Sound"), 1); mBufferCacheMax = std::max(Settings::Manager::getInt("buffer cache max", "Sound"), 1); @@ -895,9 +898,11 @@ namespace MWSound if (!cell->isExterior()) return; + if (mCurrentRegionSound && mOutput->isSoundPlaying(mCurrentRegionSound)) + return; if (const auto next = mRegionSoundSelector.getNextRandom(duration, cell->mRegion, *world)) - playSound(*next, 1.0f, 1.0f); + mCurrentRegionSound = playSound(*next, 1.0f, 1.0f); } void SoundManager::updateWaterSound() diff --git a/apps/openmw/mwsound/soundmanagerimp.hpp b/apps/openmw/mwsound/soundmanagerimp.hpp index 37b099c02..f69171a09 100644 --- a/apps/openmw/mwsound/soundmanagerimp.hpp +++ b/apps/openmw/mwsound/soundmanagerimp.hpp @@ -116,9 +116,11 @@ namespace MWSound RegionSoundSelector mRegionSoundSelector; - float mTimePassed = 0; + float mTimePassed; - const ESM::Cell *mLastCell = nullptr; + const ESM::Cell *mLastCell; + + Sound* mCurrentRegionSound; Sound_Buffer *insertSound(const std::string &soundId, const ESM::Sound *sound); diff --git a/apps/openmw/mwstate/charactermanager.cpp b/apps/openmw/mwstate/charactermanager.cpp index 856264d03..b5868c3e5 100644 --- a/apps/openmw/mwstate/charactermanager.cpp +++ b/apps/openmw/mwstate/charactermanager.cpp @@ -77,7 +77,7 @@ MWState::Character* MWState::CharacterManager::createCharacter(const std::string path = mPath / test.str(); } - mCharacters.push_back (Character (path, mGame)); + mCharacters.emplace_back(path, mGame); return &mCharacters.back(); } diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index d59704bf1..72e4b1ae0 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -48,8 +48,8 @@ void MWState::StateManager::cleanup (bool force) MWBase::Environment::get().getDialogueManager()->clear(); MWBase::Environment::get().getJournal()->clear(); MWBase::Environment::get().getScriptManager()->clear(); - MWBase::Environment::get().getWorld()->clear(); MWBase::Environment::get().getWindowManager()->clear(); + MWBase::Environment::get().getWorld()->clear(); MWBase::Environment::get().getInputManager()->clear(); MWBase::Environment::get().getMechanicsManager()->clear(); @@ -118,8 +118,8 @@ void MWState::StateManager::askLoadRecent() { MWState::Slot lastSave = *character->begin(); std::vector buttons; - buttons.push_back("#{sYes}"); - buttons.push_back("#{sNo}"); + buttons.emplace_back("#{sYes}"); + buttons.emplace_back("#{sNo}"); std::string tag("%s"); std::string message = MWBase::Environment::get().getWindowManager()->getGameSettingString("sLoadLastSaveMsg", tag); size_t pos = message.find(tag); @@ -165,7 +165,7 @@ void MWState::StateManager::newGame (bool bypass) MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); std::vector buttons; - buttons.push_back("#{sOk}"); + buttons.emplace_back("#{sOk}"); MWBase::Environment::get().getWindowManager()->interactiveMessageBox(error.str(), buttons); } } @@ -306,7 +306,7 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot Log(Debug::Error) << error.str(); std::vector buttons; - buttons.push_back("#{sOk}"); + buttons.emplace_back("#{sOk}"); MWBase::Environment::get().getWindowManager()->interactiveMessageBox(error.str(), buttons); // If no file was written, clean up the slot @@ -451,6 +451,7 @@ void MWState::StateManager::loadGame (const Character *character, const std::str case ESM::REC_LEVC: case ESM::REC_LEVI: case ESM::REC_CREA: + case ESM::REC_CONT: MWBase::Environment::get().getWorld()->readRecord(reader, n.intval, contentFileMap); break; @@ -557,7 +558,7 @@ void MWState::StateManager::loadGame (const Character *character, const std::str MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); std::vector buttons; - buttons.push_back("#{sOk}"); + buttons.emplace_back("#{sOk}"); MWBase::Environment::get().getWindowManager()->interactiveMessageBox(error.str(), buttons); } } @@ -633,8 +634,8 @@ bool MWState::StateManager::verifyProfile(const ESM::SavedGame& profile) const if (notFound) { std::vector buttons; - buttons.push_back("#{sYes}"); - buttons.push_back("#{sNo}"); + buttons.emplace_back("#{sYes}"); + buttons.emplace_back("#{sNo}"); MWBase::Environment::get().getWindowManager()->interactiveMessageBox("#{sMissingMastersMsg}", buttons, true); int selectedButton = MWBase::Environment::get().getWindowManager()->readPressedButton(); if (selectedButton == 1 || selectedButton == -1) diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index c6f71df6c..31af5b24b 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -496,7 +496,7 @@ namespace MWWorld else if (mTerrainViews.size() < positions.size()) { for (unsigned int i=mTerrainViews.size(); icreateView()); + mTerrainViews.emplace_back(mTerrain->createView()); } mTerrainPreloadPositions = positions; diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 3852b1abe..6fad42926 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -463,7 +463,7 @@ void MWWorld::ContainerStore::updateRechargingItems() } } -int MWWorld::ContainerStore::remove(const std::string& itemId, int count, const Ptr& actor, bool resolveFirst) +int MWWorld::ContainerStore::remove(const std::string& itemId, int count, const Ptr& actor, bool equipReplacement, bool resolveFirst) { if(resolveFirst) resolve(); @@ -471,7 +471,7 @@ int MWWorld::ContainerStore::remove(const std::string& itemId, int count, const for (ContainerStoreIterator iter(begin()); iter != end() && toRemove > 0; ++iter) if (Misc::StringUtils::ciEqual(iter->getCellRef().getRefId(), itemId)) - toRemove -= removeImp(*iter, toRemove, actor); + toRemove -= remove(*iter, toRemove, actor, equipReplacement, resolveFirst); flagAsModified(); @@ -490,16 +490,12 @@ bool MWWorld::ContainerStore::hasVisibleItems() const return false; } -int MWWorld::ContainerStore::remove(const Ptr& item, int count, const Ptr& actor) +int MWWorld::ContainerStore::remove(const Ptr& item, int count, const Ptr& actor, bool equipReplacement, bool resolveFirst) { assert(this == item.getContainerStore()); - resolve(); - - return removeImp(item, count, actor); -} + if(resolveFirst) + resolve(); -int MWWorld::ContainerStore::removeImp(const Ptr& item, int count, const Ptr& actor) -{ int toRemove = count; RefData& itemRef = item.getRefData(); diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index 9092d41fc..e0843efba 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -129,8 +129,6 @@ namespace MWWorld void addInitialItem (const std::string& id, const std::string& owner, int count, Misc::Rng::Seed* seed, bool topLevel=true); void addInitialItemImp (const MWWorld::Ptr& ptr, const std::string& owner, int count, Misc::Rng::Seed* seed, bool topLevel=true); - int removeImp(const Ptr& item, int count, const Ptr& actor); - template ContainerStoreIterator getState (CellRefList& collection, const ESM::ObjectState& state); @@ -180,12 +178,12 @@ namespace MWWorld ContainerStoreIterator add(const std::string& id, int count, const Ptr& actorPtr); ///< Utility to construct a ManualRef and call add(ptr, count, actorPtr, true) - int remove(const std::string& itemId, int count, const Ptr& actor, bool resolve = true); + int remove(const std::string& itemId, int count, const Ptr& actor, bool equipReplacement = 0, bool resolve = true); ///< Remove \a count item(s) designated by \a itemId from this container. /// /// @return the number of items actually removed - virtual int remove(const Ptr& item, int count, const Ptr& actor); + virtual int remove(const Ptr& item, int count, const Ptr& actor, bool equipReplacement = 0, bool resolve = true); ///< Remove \a count item(s) designated by \a item from this inventory. /// /// @return the number of items actually removed diff --git a/apps/openmw/mwworld/esmstore.cpp b/apps/openmw/mwworld/esmstore.cpp index aea9a5e4f..942d5feeb 100644 --- a/apps/openmw/mwworld/esmstore.cpp +++ b/apps/openmw/mwworld/esmstore.cpp @@ -345,7 +345,8 @@ void ESMStore::validate() +mWeapons.getDynamicSize() +mCreatureLists.getDynamicSize() +mItemLists.getDynamicSize() - +mCreatures.getDynamicSize(); + +mCreatures.getDynamicSize() + +mContainers.getDynamicSize(); } void ESMStore::write (ESM::ESMWriter& writer, Loading::Listener& progress) const @@ -368,6 +369,7 @@ void ESMStore::validate() mItemLists.write (writer, progress); mCreatureLists.write (writer, progress); mCreatures.write (writer, progress); + mContainers.write (writer, progress); } bool ESMStore::readRecord (ESM::ESMReader& reader, uint32_t type) @@ -386,6 +388,7 @@ void ESMStore::validate() case ESM::REC_LEVI: case ESM::REC_LEVC: case ESM::REC_CREA: + case ESM::REC_CONT: mStores[type]->read (reader); return true; diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index c2785579b..386bbb30a 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -710,33 +710,9 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::getSelectedEnchantItem( return mSelectedEnchantItem; } -int MWWorld::InventoryStore::remove(const std::string& itemId, int count, const Ptr& actor) +int MWWorld::InventoryStore::remove(const Ptr& item, int count, const Ptr& actor, bool equipReplacement, bool resolve) { - return remove(itemId, count, actor, false); -} - -int MWWorld::InventoryStore::remove(const Ptr& item, int count, const Ptr& actor) -{ - return remove(item, count, actor, false); -} - -int MWWorld::InventoryStore::remove(const std::string& itemId, int count, const Ptr& actor, bool equipReplacement) -{ - int toRemove = count; - - for (ContainerStoreIterator iter(begin()); iter != end() && toRemove > 0; ++iter) - if (Misc::StringUtils::ciEqual(iter->getCellRef().getRefId(), itemId)) - toRemove -= remove(*iter, toRemove, actor, equipReplacement); - - flagAsModified(); - - // number of removed items - return count - toRemove; -} - -int MWWorld::InventoryStore::remove(const Ptr& item, int count, const Ptr& actor, bool equipReplacement) -{ - int retCount = ContainerStore::remove(item, count, actor); + int retCount = ContainerStore::remove(item, count, actor, equipReplacement, resolve); bool wasEquipped = false; if (!item.getRefData().getCount()) @@ -1012,7 +988,7 @@ void MWWorld::InventoryStore::writeState(ESM::InventoryState &state) const std::vector > params; for (std::vector::const_iterator pIt = it->second.begin(); pIt != it->second.end(); ++pIt) { - params.push_back(std::make_pair(pIt->mRandom, pIt->mMultiplier)); + params.emplace_back(pIt->mRandom, pIt->mMultiplier); } state.mPermanentMagicEffectMagnitudes[it->first] = params; diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp index 12306f809..e70c21480 100644 --- a/apps/openmw/mwworld/inventorystore.hpp +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -165,11 +165,8 @@ namespace MWWorld bool stacks (const ConstPtr& ptr1, const ConstPtr& ptr2) const override; ///< @return true if the two specified objects can stack with each other - virtual int remove(const std::string& itemId, int count, const Ptr& actor); - virtual int remove(const std::string& itemId, int count, const Ptr& actor, bool equipReplacement); - - int remove(const Ptr& item, int count, const Ptr& actor) override; - virtual int remove(const Ptr& item, int count, const Ptr& actor, bool equipReplacement); + using ContainerStore::remove; + int remove(const Ptr& item, int count, const Ptr& actor, bool equipReplacement = 0, bool resolve = true) override; ///< Remove \a count item(s) designated by \a item from this inventory. /// /// @return the number of items actually removed diff --git a/apps/openmw/mwworld/localscripts.cpp b/apps/openmw/mwworld/localscripts.cpp index 8a511030d..42914d4ac 100644 --- a/apps/openmw/mwworld/localscripts.cpp +++ b/apps/openmw/mwworld/localscripts.cpp @@ -101,7 +101,7 @@ void MWWorld::LocalScripts::add (const std::string& scriptName, const Ptr& ptr) break; } - mScripts.push_back (std::make_pair (scriptName, ptr)); + mScripts.emplace_back (scriptName, ptr); } catch (const std::exception& exception) { diff --git a/apps/openmw/mwworld/projectilemanager.cpp b/apps/openmw/mwworld/projectilemanager.cpp index b80ab49ba..42287ba05 100644 --- a/apps/openmw/mwworld/projectilemanager.cpp +++ b/apps/openmw/mwworld/projectilemanager.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -86,7 +87,7 @@ namespace continue; if (magicEffect->mBolt.empty()) - projectileIDs.push_back("VFX_DefaultBolt"); + projectileIDs.emplace_back("VFX_DefaultBolt"); else projectileIDs.push_back(magicEffect->mBolt); @@ -456,6 +457,7 @@ namespace MWWorld cast.mSourceName = it->mSourceName; cast.mStack = false; cast.inflict(result.mHitObject, caster, it->mEffects, ESM::RT_Target, false, true); + mPhysics->reportCollision(Misc::Convert::toBullet(result.mHitPos), Misc::Convert::toBullet(result.mHitNormal)); } } @@ -536,6 +538,7 @@ namespace MWWorld caster = result.mHitObject; MWMechanics::projectileHit(caster, result.mHitObject, bow, projectileRef.getPtr(), result.mHit ? result.mHitPos : newPos, it->mAttackStrength); + mPhysics->reportCollision(Misc::Convert::toBullet(result.mHitPos), Misc::Convert::toBullet(result.mHitNormal)); if (underwater) mRendering->emitWaterRipple(newPos); diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index bbf41ad2e..d01cc741c 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -563,7 +563,7 @@ namespace MWWorld if (iter==mActiveCells.end()) { refsToLoad += MWBase::Environment::get().getWorld()->getExterior(x, y)->count(); - cellsPositionsToLoad.push_back(std::make_pair(x, y)); + cellsPositionsToLoad.emplace_back(x, y); } } } @@ -1027,7 +1027,7 @@ namespace MWWorld { continue; } - teleportDoors.push_back(MWWorld::ConstPtr(&door, cellStore)); + teleportDoors.emplace_back(&door, cellStore); } } diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index 4c4417587..f8ec8c7c2 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -352,7 +352,7 @@ namespace MWWorld //========================================================================= Store::Store() { - mStatic.push_back(LandTextureList()); + mStatic.emplace_back(); LandTextureList <exl = mStatic[0]; // More than enough to hold Morrowind.esm. Extra lists for plugins will we // added on-the-fly in a different method. diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index 2f054488b..6a4a227a4 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -370,7 +370,7 @@ MWRender::MoonState MoonModel::calculateState(const TimeStamp& gameTime) const { rotationFromHorizon, mAxisOffset, // Reverse engineered from Morrowind's scene graph rotation matrices. - static_cast(phase(gameTime)), + phase(gameTime), shadowBlend(rotationFromHorizon), earlyMoonShadowAlpha(rotationFromHorizon) * hourlyAlpha(gameTime.getHour()) }; @@ -439,17 +439,15 @@ inline float MoonModel::rotation(float hours) const return 15.0f * mSpeed * hours; } -inline unsigned int MoonModel::phase(const TimeStamp& gameTime) const +MWRender::MoonState::Phase MoonModel::phase(const TimeStamp& gameTime) const { // Morrowind starts with a full moon on 16 Last Seed and then begins to wane 17 Last Seed, working on 3 day phase cycle. - // Note: this is an internal helper, and as such we don't want to return MWRender::MoonState::Phase since we can't - // forward declare it (C++11 strongly typed enums solve this). // If the moon didn't rise yet today, use yesterday's moon phase. if(gameTime.getHour() < moonRiseHour(gameTime.getDay())) - return (gameTime.getDay() / 3) % 8; + return static_cast((gameTime.getDay() / 3) % 8); else - return ((gameTime.getDay() + 1) / 3) % 8; + return static_cast(((gameTime.getDay() + 1) / 3) % 8); } inline float MoonModel::shadowBlend(float angle) const diff --git a/apps/openmw/mwworld/weather.hpp b/apps/openmw/mwworld/weather.hpp index 696f7c688..a3928465c 100644 --- a/apps/openmw/mwworld/weather.hpp +++ b/apps/openmw/mwworld/weather.hpp @@ -261,7 +261,7 @@ namespace MWWorld float angle(const TimeStamp& gameTime) const; float moonRiseHour(unsigned int daysPassed) const; float rotation(float hours) const; - unsigned int phase(const TimeStamp& gameTime) const; + MWRender::MoonState::Phase phase(const TimeStamp& gameTime) const; float shadowBlend(float angle) const; float hourlyAlpha(float gameHour) const; float earlyMoonShadowAlpha(float angle) const; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 1f8e2a831..bb36c9983 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -898,6 +899,7 @@ namespace MWWorld { mRendering->notifyWorldSpaceChanged(); mProjectileManager->clear(); + mDiscardMovements = true; } } @@ -1535,21 +1537,21 @@ namespace MWWorld mPhysics->updateAnimatedCollisionShape(ptr); } - void World::doPhysics(float duration) + void World::doPhysics(float duration, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats) { mPhysics->stepSimulation(); processDoors(duration); mProjectileManager->update(duration); - const auto results = mPhysics->applyQueuedMovement(duration, mDiscardMovements); + const auto results = mPhysics->applyQueuedMovement(duration, mDiscardMovements, frameStart, frameNumber, stats); mDiscardMovements = false; - for(const auto& result : results) + for(const auto& [actor, position]: results) { // Handle player last, in case a cell transition occurs - if(result.first != getPlayerPtr()) - moveObjectImp(result.first, result.second.x(), result.second.y(), result.second.z(), false); + if(actor != getPlayerPtr()) + moveObjectImp(actor, position.x(), position.y(), position.z(), false); } const auto player = results.find(getPlayerPtr()); @@ -1618,21 +1620,28 @@ namespace MWWorld float minRot = door.getCellRef().getPosition().rot[2]; float maxRot = minRot + osg::DegreesToRadians(90.f); - float diff = duration * osg::DegreesToRadians(90.f); - float targetRot = std::min(std::max(minRot, oldRot + diff * (state == MWWorld::DoorState::Opening ? 1 : -1)), maxRot); + float diff = duration * osg::DegreesToRadians(90.f) * (state == MWWorld::DoorState::Opening ? 1 : -1); + float targetRot = std::min(std::max(minRot, oldRot + diff), maxRot); rotateObject(door, objPos.rot[0], objPos.rot[1], targetRot, MWBase::RotationFlag_none); bool reached = (targetRot == maxRot && state != MWWorld::DoorState::Idle) || targetRot == minRot; /// \todo should use convexSweepTest here bool collisionWithActor = false; - std::vector collisions = mPhysics->getCollisions(door, MWPhysics::CollisionType_Door, MWPhysics::CollisionType_Actor); - for (MWWorld::Ptr& ptr : collisions) + for (auto& [ptr, point, normal] : mPhysics->getCollisionsPoints(door, MWPhysics::CollisionType_Door, MWPhysics::CollisionType_Actor)) { + if (ptr.getClass().isActor()) { + auto localPoint = objPos.asVec3() - point; + osg::Vec3f direction = osg::Quat(diff, osg::Vec3f(0, 0, 1)) * localPoint - localPoint; + direction.normalize(); + mPhysics->reportCollision(Misc::Convert::toBullet(point), Misc::Convert::toBullet(normal)); + if (direction * normal < 0) // door is turning away from actor + continue; + collisionWithActor = true; - + // Collided with actor, ask actor to try to avoid door if(ptr != getPlayerPtr() ) { @@ -1864,11 +1873,11 @@ namespace MWWorld } } - void World::updatePhysics (float duration, bool paused) + void World::updatePhysics (float duration, bool paused, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats) { if (!paused) { - doPhysics (duration); + doPhysics (duration, frameStart, frameNumber, stats); } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 820d89806..cca3f0955 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -159,7 +159,7 @@ namespace MWWorld void processDoors(float duration); ///< Run physics simulation and modify \a world accordingly. - void doPhysics(float duration); + void doPhysics(float duration, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats); ///< Run physics simulation and modify \a world accordingly. void updateNavigator(); @@ -497,7 +497,7 @@ namespace MWWorld /// \return pointer to created record void update (float duration, bool paused) override; - void updatePhysics (float duration, bool paused) override; + void updatePhysics (float duration, bool paused, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats) override; void updateWindowManager () override; diff --git a/apps/openmw_test_suite/detournavigator/navigator.cpp b/apps/openmw_test_suite/detournavigator/navigator.cpp index 5a92d5d28..ae345d187 100644 --- a/apps/openmw_test_suite/detournavigator/navigator.cpp +++ b/apps/openmw_test_suite/detournavigator/navigator.cpp @@ -8,12 +8,9 @@ #include #include -#include - #include #include -#include #include MATCHER_P3(Vec3fEq, x, y, z, "") diff --git a/apps/openmw_test_suite/misc/test_stringops.cpp b/apps/openmw_test_suite/misc/test_stringops.cpp index 081ca8da6..086908692 100644 --- a/apps/openmw_test_suite/misc/test_stringops.cpp +++ b/apps/openmw_test_suite/misc/test_stringops.cpp @@ -5,14 +5,14 @@ struct PartialBinarySearchTest : public ::testing::Test { protected: std::vector mDataVec; - virtual void SetUp() + void SetUp() override { const char* data[] = { "Head", "Chest", "Tri Head", "Tri Chest", "Bip01", "Tri Bip01" }; mDataVec = std::vector(data, data+sizeof(data)/sizeof(data[0])); std::sort(mDataVec.begin(), mDataVec.end(), Misc::StringUtils::ciLess); } - virtual void TearDown() + void TearDown() override { } diff --git a/apps/openmw_test_suite/mwdialogue/test_keywordsearch.cpp b/apps/openmw_test_suite/mwdialogue/test_keywordsearch.cpp index e0e1871d2..431725be2 100644 --- a/apps/openmw_test_suite/mwdialogue/test_keywordsearch.cpp +++ b/apps/openmw_test_suite/mwdialogue/test_keywordsearch.cpp @@ -4,11 +4,11 @@ struct KeywordSearchTest : public ::testing::Test { protected: - virtual void SetUp() + void SetUp() override { } - virtual void TearDown() + void TearDown() override { } }; diff --git a/apps/openmw_test_suite/mwworld/test_store.cpp b/apps/openmw_test_suite/mwworld/test_store.cpp index 60f60adb3..77aaccfdd 100644 --- a/apps/openmw_test_suite/mwworld/test_store.cpp +++ b/apps/openmw_test_suite/mwworld/test_store.cpp @@ -22,7 +22,7 @@ struct ContentFileTest : public ::testing::Test { protected: - virtual void SetUp() + void SetUp() override { readContentFiles(); @@ -31,13 +31,13 @@ struct ContentFileTest : public ::testing::Test readerList.resize(mContentFiles.size()); int index=0; - for (std::vector::const_iterator it = mContentFiles.begin(); it != mContentFiles.end(); ++it) + for (const auto & mContentFile : mContentFiles) { ESM::ESMReader lEsm; lEsm.setEncoder(nullptr); lEsm.setIndex(index); lEsm.setGlobalReaderList(&readerList); - lEsm.open(it->string()); + lEsm.open(mContentFile.string()); readerList[index] = lEsm; mEsmStore.load(readerList[index], &dummyListener); @@ -47,7 +47,7 @@ struct ContentFileTest : public ::testing::Test mEsmStore.setUp(); } - virtual void TearDown() + void TearDown() override { } @@ -86,8 +86,8 @@ struct ContentFileTest : public ::testing::Test Files::Collections collections (dataDirs, true); std::vector contentFiles = variables["content"].as >(); - for (std::vector::iterator it = contentFiles.begin(); it != contentFiles.end(); ++it) - mContentFiles.push_back(collections.getPath(*it)); + for (auto & contentFile : contentFiles) + mContentFiles.push_back(collections.getPath(contentFile)); } protected: @@ -111,14 +111,12 @@ TEST_F(ContentFileTest, dialogue_merging_test) stream.open(file); const MWWorld::Store& dialStore = mEsmStore.get(); - for (MWWorld::Store::iterator it = dialStore.begin(); it != dialStore.end(); ++it) + for (const auto & dial : dialStore) { - const ESM::Dialogue& dial = *it; stream << "Dialogue: " << dial.mId << std::endl; - for (ESM::Dialogue::InfoContainer::const_iterator infoIt = dial.mInfo.begin(); infoIt != dial.mInfo.end(); ++infoIt) + for (const auto & info : dial.mInfo) { - const ESM::DialInfo& info = *infoIt; stream << info.mId << std::endl; } stream << std::endl; @@ -229,7 +227,7 @@ template Files::IStreamPtr getEsmFile(T record, bool deleted) { ESM::ESMWriter writer; - std::stringstream* stream = new std::stringstream; + auto* stream = new std::stringstream; writer.setFormat(0); writer.save(*stream); writer.startRecord(T::sRecordId); diff --git a/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp b/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp index 72dcd3066..7da8f0fd5 100644 --- a/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp +++ b/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp @@ -373,6 +373,7 @@ namespace TEST_F(TestBulletNifLoader, for_zero_num_roots_should_return_default) { EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(0)); + EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); const auto result = mLoader.load(mNifFile); Resource::BulletShape expected; @@ -422,11 +423,13 @@ namespace { mNode.hasBounds = true; mNode.flags |= Nif::NiNode::Flag_BBoxCollision; - mNode.boundXYZ = osg::Vec3f(1, 2, 3); - mNode.boundPos = osg::Vec3f(-1, -2, -3); + mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV; + mNode.bounds.box.extents = osg::Vec3f(1, 2, 3); + mNode.bounds.box.center = osg::Vec3f(-1, -2, -3); EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNode)); + EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); const auto result = mLoader.load(mNifFile); Resource::BulletShape expected; @@ -444,12 +447,14 @@ namespace { mNode.hasBounds = true; mNode.flags |= Nif::NiNode::Flag_BBoxCollision; - mNode.boundXYZ = osg::Vec3f(1, 2, 3); - mNode.boundPos = osg::Vec3f(-1, -2, -3); + mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV; + mNode.bounds.box.extents = osg::Vec3f(1, 2, 3); + mNode.bounds.box.center = osg::Vec3f(-1, -2, -3); mNiNode.children = Nif::NodeList(std::vector({Nif::NodePtr(&mNode)})); EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); + EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); const auto result = mLoader.load(mNifFile); Resource::BulletShape expected; @@ -467,16 +472,19 @@ namespace { mNode.hasBounds = true; mNode.flags |= Nif::NiNode::Flag_BBoxCollision; - mNode.boundXYZ = osg::Vec3f(1, 2, 3); - mNode.boundPos = osg::Vec3f(-1, -2, -3); + mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV; + mNode.bounds.box.extents = osg::Vec3f(1, 2, 3); + mNode.bounds.box.center = osg::Vec3f(-1, -2, -3); mNiNode.hasBounds = true; - mNiNode.boundXYZ = osg::Vec3f(4, 5, 6); - mNiNode.boundPos = osg::Vec3f(-4, -5, -6); + mNiNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV; + mNiNode.bounds.box.extents = osg::Vec3f(4, 5, 6); + mNiNode.bounds.box.center = osg::Vec3f(-4, -5, -6); mNiNode.children = Nif::NodeList(std::vector({Nif::NodePtr(&mNode)})); EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); + EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); const auto result = mLoader.load(mNifFile); Resource::BulletShape expected; @@ -494,20 +502,24 @@ namespace { mNode.hasBounds = true; mNode.flags |= Nif::NiNode::Flag_BBoxCollision; - mNode.boundXYZ = osg::Vec3f(1, 2, 3); - mNode.boundPos = osg::Vec3f(-1, -2, -3); + mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV; + mNode.bounds.box.extents = osg::Vec3f(1, 2, 3); + mNode.bounds.box.center = osg::Vec3f(-1, -2, -3); mNode2.hasBounds = true; - mNode2.boundXYZ = osg::Vec3f(4, 5, 6); - mNode2.boundPos = osg::Vec3f(-4, -5, -6); + mNode2.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV; + mNode2.bounds.box.extents = osg::Vec3f(4, 5, 6); + mNode2.bounds.box.center = osg::Vec3f(-4, -5, -6); mNiNode.hasBounds = true; - mNiNode.boundXYZ = osg::Vec3f(7, 8, 9); - mNiNode.boundPos = osg::Vec3f(-7, -8, -9); + mNiNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV; + mNiNode.bounds.box.extents = osg::Vec3f(7, 8, 9); + mNiNode.bounds.box.center = osg::Vec3f(-7, -8, -9); mNiNode.children = Nif::NodeList(std::vector({Nif::NodePtr(&mNode), Nif::NodePtr(&mNode2)})); EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); + EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); const auto result = mLoader.load(mNifFile); Resource::BulletShape expected; @@ -524,21 +536,25 @@ namespace TEST_F(TestBulletNifLoader, for_root_and_two_children_where_both_with_bounds_but_only_second_with_flag_should_use_second_bounds) { mNode.hasBounds = true; - mNode.boundXYZ = osg::Vec3f(1, 2, 3); - mNode.boundPos = osg::Vec3f(-1, -2, -3); + mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV; + mNode.bounds.box.extents = osg::Vec3f(1, 2, 3); + mNode.bounds.box.center = osg::Vec3f(-1, -2, -3); mNode2.hasBounds = true; mNode2.flags |= Nif::NiNode::Flag_BBoxCollision; - mNode2.boundXYZ = osg::Vec3f(4, 5, 6); - mNode2.boundPos = osg::Vec3f(-4, -5, -6); + mNode2.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV; + mNode2.bounds.box.extents = osg::Vec3f(4, 5, 6); + mNode2.bounds.box.center = osg::Vec3f(-4, -5, -6); mNiNode.hasBounds = true; - mNiNode.boundXYZ = osg::Vec3f(7, 8, 9); - mNiNode.boundPos = osg::Vec3f(-7, -8, -9); + mNiNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV; + mNiNode.bounds.box.extents = osg::Vec3f(7, 8, 9); + mNiNode.bounds.box.center = osg::Vec3f(-7, -8, -9); mNiNode.children = Nif::NodeList(std::vector({Nif::NodePtr(&mNode), Nif::NodePtr(&mNode2)})); EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); + EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); const auto result = mLoader.load(mNifFile); Resource::BulletShape expected; @@ -555,8 +571,9 @@ namespace TEST_F(TestBulletNifLoader, for_root_nif_node_with_bounds_but_without_flag_should_return_shape_with_bounds_but_with_null_collision_shape) { mNode.hasBounds = true; - mNode.boundXYZ = osg::Vec3f(1, 2, 3); - mNode.boundPos = osg::Vec3f(-1, -2, -3); + mNode.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV; + mNode.bounds.box.extents = osg::Vec3f(1, 2, 3); + mNode.bounds.box.center = osg::Vec3f(-1, -2, -3); EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNode)); @@ -588,8 +605,9 @@ namespace TEST_F(TestBulletNifLoader, for_tri_shape_root_node_with_bounds_should_return_shape_with_bounds_but_with_null_collision_shape) { mNiTriShape.hasBounds = true; - mNiTriShape.boundXYZ = osg::Vec3f(1, 2, 3); - mNiTriShape.boundPos = osg::Vec3f(-1, -2, -3); + mNiTriShape.bounds.type = Nif::NiBoundingVolume::Type::BOX_BV; + mNiTriShape.bounds.box.extents = osg::Vec3f(1, 2, 3); + mNiTriShape.bounds.box.center = osg::Vec3f(-1, -2, -3); EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiTriShape)); diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index a221af51d..5913bd641 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -51,7 +51,7 @@ add_component_dir (shader add_component_dir (sceneutil clone attach visitor util statesetupdater controller skeleton riggeometry morphgeometry lightcontroller lightmanager lightutil positionattitudetransform workqueue unrefqueue pathgridutil waterutil writescene serialize optimizer - actorutil detourdebugdraw navmesh agentpath shadow mwshadowtechnique recastmesh + actorutil detourdebugdraw navmesh agentpath shadow mwshadowtechnique recastmesh shadowsbin ) add_component_dir (nif diff --git a/components/bsa/bsa_file.cpp b/components/bsa/bsa_file.cpp index abeca5326..3fd74dd83 100644 --- a/components/bsa/bsa_file.cpp +++ b/components/bsa/bsa_file.cpp @@ -150,7 +150,7 @@ void BSAFile::readHeader() /// Get the index of a given file name, or -1 if not found int BSAFile::getIndex(const char *str) const { - Lookup::const_iterator it = mLookup.find(str); + auto it = mLookup.find(str); if(it == mLookup.end()) return -1; diff --git a/components/bsa/bsa_file.hpp b/components/bsa/bsa_file.hpp index d12d01b0c..037802739 100644 --- a/components/bsa/bsa_file.hpp +++ b/components/bsa/bsa_file.hpp @@ -24,7 +24,7 @@ #ifndef BSA_BSA_FILE_H #define BSA_BSA_FILE_H -#include +#include #include #include #include @@ -106,8 +106,7 @@ public: : mIsLoaded(false) { } - virtual ~BSAFile() - { } + virtual ~BSAFile() = default; /// Open an archive file. void open(const std::string &file); diff --git a/components/bsa/compressedbsafile.cpp b/components/bsa/compressedbsafile.cpp index adc98c454..77e477ac5 100644 --- a/components/bsa/compressedbsafile.cpp +++ b/components/bsa/compressedbsafile.cpp @@ -36,7 +36,6 @@ #include #include #include -#include #include #include @@ -103,8 +102,7 @@ CompressedBSAFile::CompressedBSAFile() : mCompressedByDefault(false), mEmbeddedFileNames(false) { } -CompressedBSAFile::~CompressedBSAFile() -{ } +CompressedBSAFile::~CompressedBSAFile() = default; /// Read header information from the input source void CompressedBSAFile::readHeader() @@ -183,7 +181,7 @@ void CompressedBSAFile::readHeader() else input.read(reinterpret_cast(&fr.offset), 4); // not sure purpose of offset - std::map::const_iterator lb = mFolders.lower_bound(hash); + auto lb = mFolders.lower_bound(hash); if (lb != mFolders.end() && !(mFolders.key_comp()(hash, lb->first))) fail("Archive found duplicate folder name hash"); else @@ -194,7 +192,7 @@ void CompressedBSAFile::readHeader() std::uint64_t fileHash; FileRecord file; - std::string folder(""); + std::string folder; std::uint64_t folderHash; if ((archiveFlags & 0x1) == 0) folderCount = 1; // TODO: not tested - unit test necessary @@ -209,7 +207,7 @@ void CompressedBSAFile::readHeader() folderHash = generateHash(folder, std::string()); - std::map::iterator iter = mFolders.find(folderHash); + auto iter = mFolders.find(folderHash); if (iter == mFolders.end()) fail("Archive folder name hash not found"); @@ -219,13 +217,13 @@ void CompressedBSAFile::readHeader() input.read(reinterpret_cast(&file.size), 4); input.read(reinterpret_cast(&file.offset), 4); - std::map::const_iterator lb = iter->second.files.lower_bound(fileHash); + auto lb = iter->second.files.lower_bound(fileHash); if (lb != iter->second.files.end() && !(iter->second.files.key_comp()(fileHash, lb->first))) fail("Archive found duplicate file name hash"); iter->second.files.insert(lb, std::pair(fileHash, file)); - FileStruct fileStruct; + FileStruct fileStruct{}; fileStruct.fileSize = file.getSizeWithoutCompressionFlag(); fileStruct.offset = file.offset; fileStruct.name = nullptr; @@ -308,12 +306,12 @@ CompressedBSAFile::FileRecord CompressedBSAFile::getFileRecord(const std::string std::string folder = p.string(); std::uint64_t folderHash = generateHash(folder, std::string()); - std::map::const_iterator it = mFolders.find(folderHash); + auto it = mFolders.find(folderHash); if (it == mFolders.end()) return FileRecord(); // folder not found, return default which has offset of sInvalidOffset std::uint64_t fileHash = generateHash(stem, ext); - std::map::const_iterator iter = it->second.files.find(fileHash); + auto iter = it->second.files.find(fileHash); if (iter == it->second.files.end()) return FileRecord(); // file not found, return default which has offset of sInvalidOffset @@ -430,12 +428,12 @@ BsaVersion CompressedBSAFile::detectVersion(std::string filePath) //mFiles used by OpenMW expects uncompressed sizes void CompressedBSAFile::convertCompressedSizesToUncompressed() { - for (auto iter = mFiles.begin(); iter != mFiles.end(); ++iter) + for (auto & mFile : mFiles) { - const FileRecord& fileRecord = getFileRecord(iter->name); + const FileRecord& fileRecord = getFileRecord(mFile.name); if (!fileRecord.isValid()) { - fail("Could not find file " + std::string(iter->name) + " in BSA"); + fail("Could not find file " + std::string(mFile.name) + " in BSA"); } if (!fileRecord.isCompressed(mCompressedByDefault)) @@ -452,47 +450,37 @@ void CompressedBSAFile::convertCompressedSizesToUncompressed() getBZString(embeddedFileName, *(dataBegin.get())); } - dataBegin->read(reinterpret_cast(&(iter->fileSize)), sizeof(iter->fileSize)); + dataBegin->read(reinterpret_cast(&(mFile.fileSize)), sizeof(mFile.fileSize)); } } -std::uint64_t CompressedBSAFile::generateHash(std::string stem, std::string extension) const +std::uint64_t CompressedBSAFile::generateHash(std::string stem, std::string extension) { size_t len = stem.length(); if (len == 0) return 0; - std::uint64_t hash = 0; - unsigned int hash2 = 0; + std::replace(stem.begin(), stem.end(), '/', '\\'); Misc::StringUtils::lowerCaseInPlace(stem); - if (extension.empty()) // It's a folder. - std::replace(stem.begin(), stem.end(), '/', '\\'); - else - { - Misc::StringUtils::lowerCaseInPlace(extension); - for (const char &c : extension) - hash = hash * 0x1003f + c; - } + uint64_t result = stem[len-1] | (len >= 3 ? (stem[len-2] << 8) : 0) | (len << 16) | (stem[0] << 24); if (len >= 4) { - for (size_t i = 1; i < len-2; i++) - hash2 = hash2 * 0x1003f + stem[i]; - } - hash = (hash + hash2) << 32; - hash2 = (stem[0] << 24) | (len << 16); - if (len >= 2) - { - if (len >= 3) - hash2 |= stem[len-2] << 8; - hash2 |= stem[len-1]; - } - if (!extension.empty()) - { - if (extension == ".kf") hash2 |= 0x80; - else if (extension == ".nif") hash2 |= 0x8000; - else if (extension == ".dds") hash2 |= 0x8080; - else if (extension == ".wav") hash2 |= 0x80000000; + uint32_t hash = 0; + for (size_t i = 1; i <= len-3; ++i) + hash = hash * 0x1003f + stem[i]; + result += static_cast(hash) << 32; } - return hash + hash2; + if (extension.empty()) + return result; + Misc::StringUtils::lowerCaseInPlace(extension); + if (extension == ".kf") result |= 0x80; + else if (extension == ".nif") result |= 0x8000; + else if (extension == ".dds") result |= 0x8080; + else if (extension == ".wav") result |= 0x80000000; + uint32_t hash = 0; + for (const char &c : extension) + hash = hash * 0x1003f + c; + result += static_cast(hash) << 32; + return result; } } //namespace Bsa diff --git a/components/bsa/compressedbsafile.hpp b/components/bsa/compressedbsafile.hpp index f3ad584d8..deddfae38 100644 --- a/components/bsa/compressedbsafile.hpp +++ b/components/bsa/compressedbsafile.hpp @@ -80,7 +80,7 @@ namespace Bsa //mFiles used by OpenMW will contain uncompressed file sizes void convertCompressedSizesToUncompressed(); /// \brief Normalizes given filename or folder and generates format-compatible hash. See https://en.uesp.net/wiki/Tes4Mod:Hash_Calculation. - std::uint64_t generateHash(std::string stem, std::string extension) const; + static std::uint64_t generateHash(std::string stem, std::string extension) ; Files::IStreamPtr getFile(const FileRecord& fileRecord); public: CompressedBSAFile(); diff --git a/components/bsa/memorystream.hpp b/components/bsa/memorystream.hpp index 5dbe16ebe..d168e93d6 100644 --- a/components/bsa/memorystream.hpp +++ b/components/bsa/memorystream.hpp @@ -37,8 +37,8 @@ Class used internally by MemoryInputStream. class MemoryInputStreamBuf : public std::streambuf { public: - MemoryInputStreamBuf(size_t bufferSize); - char* getRawData(); + explicit MemoryInputStreamBuf(size_t bufferSize); + virtual char* getRawData(); private: //correct call to delete [] on C++ 11 std::vector mBufferPtr; @@ -54,8 +54,8 @@ private: */ class MemoryInputStream : virtual MemoryInputStreamBuf, std::istream { public: - MemoryInputStream(size_t bufferSize); - char* getRawData(); + explicit MemoryInputStream(size_t bufferSize); + char* getRawData() override; }; } diff --git a/components/bullethelpers/processtrianglecallback.hpp b/components/bullethelpers/processtrianglecallback.hpp index b0d156754..22ab30b2a 100644 --- a/components/bullethelpers/processtrianglecallback.hpp +++ b/components/bullethelpers/processtrianglecallback.hpp @@ -11,11 +11,11 @@ namespace BulletHelpers class ProcessTriangleCallback : public btTriangleCallback { public: - ProcessTriangleCallback(Impl impl) + explicit ProcessTriangleCallback(Impl impl) : mImpl(std::move(impl)) {} - void processTriangle(btVector3* triangle, int partId, int triangleIndex) final + void processTriangle(btVector3* triangle, int partId, int triangleIndex) override { return mImpl(triangle, partId, triangleIndex); } diff --git a/components/compiler/context.hpp b/components/compiler/context.hpp index 84bb89bdc..2d6af0e45 100644 --- a/components/compiler/context.hpp +++ b/components/compiler/context.hpp @@ -15,7 +15,7 @@ namespace Compiler Context() : mExtensions (0) {} - virtual ~Context() {} + virtual ~Context() = default; virtual bool canDeclareLocals() const = 0; ///< Is the compiler allowed to declare local variables? diff --git a/components/compiler/controlparser.cpp b/components/compiler/controlparser.cpp index ebadcbbc6..ec69fffa2 100644 --- a/components/compiler/controlparser.cpp +++ b/components/compiler/controlparser.cpp @@ -33,7 +33,7 @@ namespace Compiler // store code for if-cascade Codes codes; - for (IfCodes::reverse_iterator iter (mIfCode.rbegin()); + for (auto iter (mIfCode.rbegin()); iter!=mIfCode.rend(); ++iter) { Codes block; diff --git a/components/compiler/errorhandler.cpp b/components/compiler/errorhandler.cpp index 7f02255db..f02097736 100644 --- a/components/compiler/errorhandler.cpp +++ b/components/compiler/errorhandler.cpp @@ -5,7 +5,7 @@ namespace Compiler ErrorHandler::ErrorHandler() : mWarnings (0), mErrors (0), mWarningsMode (1), mDowngradeErrors (false) {} - ErrorHandler::~ErrorHandler() {} + ErrorHandler::~ErrorHandler() = default; // Was compiling successful? diff --git a/components/compiler/errorhandler.hpp b/components/compiler/errorhandler.hpp index ea904e385..dfd29f273 100644 --- a/components/compiler/errorhandler.hpp +++ b/components/compiler/errorhandler.hpp @@ -83,7 +83,7 @@ namespace Compiler public: - ErrorDowngrade (ErrorHandler& handler); + explicit ErrorDowngrade (ErrorHandler& handler); ~ErrorDowngrade(); }; diff --git a/components/compiler/exception.hpp b/components/compiler/exception.hpp index f21f2e586..3aa8997c7 100644 --- a/components/compiler/exception.hpp +++ b/components/compiler/exception.hpp @@ -21,7 +21,7 @@ namespace Compiler { public: - const char *what() const noexcept final { return "Can't read file"; } + const char *what() const noexcept override { return "Can't read file"; } ///< Return error message }; @@ -31,7 +31,7 @@ namespace Compiler { public: - const char *what() const noexcept final { return "End of file"; } + const char *what() const noexcept override { return "End of file"; } ///< Return error message }; } diff --git a/components/compiler/exprparser.cpp b/components/compiler/exprparser.cpp index 8c8fe640e..0afe3de6d 100644 --- a/components/compiler/exprparser.cpp +++ b/components/compiler/exprparser.cpp @@ -21,7 +21,7 @@ namespace Compiler { - int ExprParser::getPriority (char op) const + int ExprParser::getPriority (char op) { switch (op) { @@ -654,28 +654,27 @@ namespace Compiler std::stack > stack; - for (std::string::const_iterator iter (arguments.begin()); iter!=arguments.end(); - ++iter) + for (char argument : arguments) { - if (*iter=='/') + if (argument=='/') { optional = true; } - else if (*iter=='S' || *iter=='c' || *iter=='x') + else if (argument=='S' || argument=='c' || argument=='x') { stringParser.reset(); - if (optional || *iter=='x') + if (optional || argument=='x') stringParser.setOptional (true); - if (*iter=='c') stringParser.smashCase(); - if (*iter=='x') stringParser.discard(); + if (argument=='c') stringParser.smashCase(); + if (argument=='x') stringParser.discard(); scanner.scan (stringParser); - if ((optional || *iter=='x') && stringParser.isEmpty()) + if ((optional || argument=='x') && stringParser.isEmpty()) break; - if (*iter!='x') + if (argument!='x') { std::vector tmp; stringParser.append (tmp); @@ -689,7 +688,7 @@ namespace Compiler getErrorHandler().warning ("Extra argument", stringParser.getTokenLoc()); } - else if (*iter=='X') + else if (argument=='X') { parser.reset(); @@ -702,7 +701,7 @@ namespace Compiler else getErrorHandler().warning("Extra argument", parser.getTokenLoc()); } - else if (*iter=='z') + else if (argument=='z') { discardParser.reset(); discardParser.setOptional (true); @@ -714,7 +713,7 @@ namespace Compiler else getErrorHandler().warning("Extra argument", discardParser.getTokenLoc()); } - else if (*iter=='j') + else if (argument=='j') { /// \todo disable this when operating in strict mode junkParser.reset(); @@ -737,8 +736,8 @@ namespace Compiler char type = parser.append (tmp); - if (type!=*iter) - Generator::convert (tmp, type, *iter); + if (type!=argument) + Generator::convert (tmp, type, argument); stack.push (tmp); diff --git a/components/compiler/exprparser.hpp b/components/compiler/exprparser.hpp index 0e8ad88c6..2f3eaa8a9 100644 --- a/components/compiler/exprparser.hpp +++ b/components/compiler/exprparser.hpp @@ -28,7 +28,7 @@ namespace Compiler bool mRefOp; bool mMemberOp; - int getPriority (char op) const; + static int getPriority (char op) ; char getOperandType (int Index = 0) const; diff --git a/components/compiler/extensions.cpp b/components/compiler/extensions.cpp index dbb953e20..9646d54a7 100644 --- a/components/compiler/extensions.cpp +++ b/components/compiler/extensions.cpp @@ -12,8 +12,7 @@ namespace Compiler int Extensions::searchKeyword (const std::string& keyword) const { - std::map::const_iterator iter = mKeywords.find (keyword); - + auto iter = mKeywords.find (keyword); if (iter==mKeywords.end()) return 0; @@ -23,8 +22,7 @@ namespace Compiler bool Extensions::isFunction (int keyword, ScriptReturn& returnType, ScriptArgs& argumentType, bool& explicitReference) const { - std::map::const_iterator iter = mFunctions.find (keyword); - + auto iter = mFunctions.find (keyword); if (iter==mFunctions.end()) return false; @@ -39,8 +37,7 @@ namespace Compiler bool Extensions::isInstruction (int keyword, ScriptArgs& argumentType, bool& explicitReference) const { - std::map::const_iterator iter = mInstructions.find (keyword); - + auto iter = mInstructions.find (keyword); if (iter==mInstructions.end()) return false; @@ -115,8 +112,7 @@ namespace Compiler { assert (optionalArguments>=0); - std::map::const_iterator iter = mFunctions.find (keyword); - + auto iter = mFunctions.find (keyword); if (iter==mFunctions.end()) throw std::logic_error ("unknown custom function keyword"); @@ -164,8 +160,7 @@ namespace Compiler { assert (optionalArguments>=0); - std::map::const_iterator iter = mInstructions.find (keyword); - + auto iter = mInstructions.find (keyword); if (iter==mInstructions.end()) throw std::logic_error ("unknown custom instruction keyword"); @@ -209,8 +204,7 @@ namespace Compiler void Extensions::listKeywords (std::vector& keywords) const { - for (std::map::const_iterator iter (mKeywords.begin()); - iter!=mKeywords.end(); ++iter) - keywords.push_back (iter->first); + for (const auto & mKeyword : mKeywords) + keywords.push_back (mKeyword.first); } } diff --git a/components/compiler/generator.cpp b/components/compiler/generator.cpp index 2787488c2..34da1c412 100644 --- a/components/compiler/generator.cpp +++ b/components/compiler/generator.cpp @@ -283,9 +283,7 @@ namespace } } -namespace Compiler -{ - namespace Generator +namespace Compiler::Generator { void pushInt (CodeContainer& code, Literals& literals, int value) { @@ -732,4 +730,3 @@ namespace Compiler } } } -} diff --git a/components/compiler/lineparser.cpp b/components/compiler/lineparser.cpp index 829ad6f08..77afaee8b 100644 --- a/components/compiler/lineparser.cpp +++ b/components/compiler/lineparser.cpp @@ -260,7 +260,7 @@ namespace Compiler /// \todo add option to disable this std::unique_ptr errorDowngrade (nullptr); if (Misc::StringUtils::lowerCase (loc.mLiteral)=="positioncell") - errorDowngrade.reset (new ErrorDowngrade (getErrorHandler())); + errorDowngrade = std::make_unique (getErrorHandler()); std::vector code; int optionals = mExprParser.parseArguments (argumentType, scanner, code, keyword); diff --git a/components/compiler/literals.cpp b/components/compiler/literals.cpp index ee2c4d345..774ca4ca7 100644 --- a/components/compiler/literals.cpp +++ b/components/compiler/literals.cpp @@ -30,13 +30,11 @@ namespace Compiler void Literals::append (std::vector& code) const { - for (std::vector::const_iterator iter (mIntegers.begin()); - iter!=mIntegers.end(); ++iter) - code.push_back (*reinterpret_cast (&*iter)); + for (const int & mInteger : mIntegers) + code.push_back (*reinterpret_cast (&mInteger)); - for (std::vector::const_iterator iter (mFloats.begin()); - iter!=mFloats.end(); ++iter) - code.push_back (*reinterpret_cast (&*iter)); + for (const float & mFloat : mFloats) + code.push_back (*reinterpret_cast (&mFloat)); int stringBlockSize = getStringSize(); int size = static_cast (code.size()); @@ -45,12 +43,11 @@ namespace Compiler int offset = 0; - for (std::vector::const_iterator iter (mStrings.begin()); - iter!=mStrings.end(); ++iter) + for (const auto & mString : mStrings) { - int stringSize = iter->size()+1; + int stringSize = mString.size()+1; - std::copy (iter->c_str(), iter->c_str()+stringSize, + std::copy (mString.c_str(), mString.c_str()+stringSize, reinterpret_cast (&code[size]) + offset); offset += stringSize; } diff --git a/components/compiler/locals.cpp b/components/compiler/locals.cpp index a7102c38d..9b233b8f5 100644 --- a/components/compiler/locals.cpp +++ b/components/compiler/locals.cpp @@ -25,8 +25,7 @@ namespace Compiler { const std::vector& collection = get (type); - std::vector::const_iterator iter = - std::find (collection.begin(), collection.end(), name); + auto iter = std::find (collection.begin(), collection.end(), name); if (iter==collection.end()) return -1; diff --git a/components/compiler/opcodes.cpp b/components/compiler/opcodes.cpp index 03081dff9..9e2356d0e 100644 --- a/components/compiler/opcodes.cpp +++ b/components/compiler/opcodes.cpp @@ -1,8 +1,6 @@ #include "opcodes.hpp" -namespace Compiler -{ - namespace Control +namespace Compiler::Control { const char *controls[numberOfControls] = { @@ -10,4 +8,3 @@ namespace Compiler "playerviewswitch", "vanitymode" }; } -} diff --git a/components/compiler/parser.cpp b/components/compiler/parser.cpp index 62265d8a1..ffa393a29 100644 --- a/components/compiler/parser.cpp +++ b/components/compiler/parser.cpp @@ -58,7 +58,7 @@ namespace Compiler // destructor - Parser::~Parser() {} + Parser::~Parser() = default; // Handle an int token. // \return fetch another token? diff --git a/components/compiler/scanner.cpp b/components/compiler/scanner.cpp index 9f2865868..573c8cc93 100644 --- a/components/compiler/scanner.cpp +++ b/components/compiler/scanner.cpp @@ -1,7 +1,6 @@ #include "scanner.hpp" #include -#include #include "exception.hpp" #include "errorhandler.hpp" @@ -266,7 +265,7 @@ namespace Compiler "messagebox", "set", "to", "getsquareroot", - 0 + nullptr }; bool Scanner::scanName (MultiChar& c, Parser& parser, bool& cont) @@ -638,7 +637,7 @@ namespace Compiler void Scanner::listKeywords (std::vector& keywords) { for (int i=0; Compiler::sKeywords[i]; ++i) - keywords.push_back (Compiler::sKeywords[i]); + keywords.emplace_back(Compiler::sKeywords[i]); if (mExtensions) mExtensions->listKeywords (keywords); diff --git a/components/compiler/scanner.hpp b/components/compiler/scanner.hpp index b6321a92d..2139f04b2 100644 --- a/components/compiler/scanner.hpp +++ b/components/compiler/scanner.hpp @@ -28,7 +28,7 @@ namespace Compiler blank(); } - MultiChar(const char ch) + explicit MultiChar(const char ch) { blank(); mData[0] = ch; @@ -36,7 +36,7 @@ namespace Compiler mLength = getCharLength(ch); } - int getCharLength(const char ch) + static int getCharLength(const char ch) { unsigned char c = ch; if (c<=127) return 0; @@ -170,8 +170,8 @@ namespace Compiler } private: - char mData[4]; - int mLength; + char mData[4]{}; + int mLength{}; }; class Scanner @@ -251,7 +251,7 @@ namespace Compiler public: Scanner (ErrorHandler& errorHandler, std::istream& inputStream, - const Extensions *extensions = 0); + const Extensions *extensions = nullptr); ///< constructor void scan (Parser& parser); diff --git a/components/compiler/scriptparser.hpp b/components/compiler/scriptparser.hpp index d6dfe768c..0433a23a3 100644 --- a/components/compiler/scriptparser.hpp +++ b/components/compiler/scriptparser.hpp @@ -1,7 +1,6 @@ #ifndef COMPILER_SCRIPTPARSER_H_INCLUDED #define COMPILER_SCRIPTPARSER_H_INCLUDED - #include "parser.hpp" #include "lineparser.hpp" #include "controlparser.hpp" diff --git a/components/compiler/streamerrorhandler.cpp b/components/compiler/streamerrorhandler.cpp index 1c41d3f7f..030d02d1d 100644 --- a/components/compiler/streamerrorhandler.cpp +++ b/components/compiler/streamerrorhandler.cpp @@ -61,7 +61,7 @@ namespace Compiler mContext = context; } - StreamErrorHandler::StreamErrorHandler() {} + StreamErrorHandler::StreamErrorHandler() = default; ContextOverride::ContextOverride(StreamErrorHandler& handler, const std::string& context) : mHandler(handler), mContext(handler.mContext) { diff --git a/components/compiler/tokenloc.hpp b/components/compiler/tokenloc.hpp index 62b5cdee5..ff715c5e9 100644 --- a/components/compiler/tokenloc.hpp +++ b/components/compiler/tokenloc.hpp @@ -13,7 +13,7 @@ namespace Compiler int mLine; std::string mLiteral; - TokenLoc() : mColumn (0), mLine (0), mLiteral ("") {} + TokenLoc() : mColumn (0), mLine (0), mLiteral () {} }; } diff --git a/components/config/gamesettings.cpp b/components/config/gamesettings.cpp index b771b7fc8..85c090a3f 100644 --- a/components/config/gamesettings.cpp +++ b/components/config/gamesettings.cpp @@ -7,24 +7,6 @@ #include -#include - -/** - * Workaround for problems with whitespaces in paths in older versions of Boost library - */ -#if (BOOST_VERSION <= 104600) -namespace boost -{ - - template<> - inline boost::filesystem::path lexical_cast(const std::string& arg) - { - return boost::filesystem::path(arg); - } - -} /* namespace boost */ -#endif /* (BOOST_VERSION <= 104600) */ - const char Config::GameSettings::sContentKey[] = "content"; Config::GameSettings::GameSettings(Files::ConfigurationManager &cfg) @@ -32,9 +14,7 @@ Config::GameSettings::GameSettings(Files::ConfigurationManager &cfg) { } -Config::GameSettings::~GameSettings() -{ -} +Config::GameSettings::~GameSettings() = default; void Config::GameSettings::validatePaths() { @@ -51,8 +31,8 @@ void Config::GameSettings::validatePaths() mCfgMgr.processPaths(dataDirs); mDataDirs.clear(); - for (Files::PathContainer::iterator it = dataDirs.begin(); it != dataDirs.end(); ++it) { - QString path = QString::fromUtf8(it->string().c_str()); + for (auto & dataDir : dataDirs) { + QString path = QString::fromUtf8(dataDir.string().c_str()); QDir dir(path); if (dir.exists()) @@ -118,12 +98,19 @@ bool Config::GameSettings::readFile(QTextStream &stream, QMultiMap comments; - std::vector::iterator commentStart = fileCopy.end(); + auto commentStart = fileCopy.end(); std::map > commentsMap; - for (std::vector::iterator iter = fileCopy.begin(); iter != fileCopy.end(); ++iter) + for (auto iter = fileCopy.begin(); iter != fileCopy.end(); ++iter) { if (isOrderedLine(*iter)) { @@ -339,9 +315,9 @@ bool Config::GameSettings::writeFileWithComments(QFile &file) if (commentStart == fileCopy.end()) throw std::runtime_error("Config::GameSettings: failed to parse settings - iterator is past of end of settings file"); - for (std::vector::const_iterator it = comments.begin(); it != comments.end(); ++it) + for (const auto & comment : comments) { - *commentStart = *it; + *commentStart = comment; ++commentStart; } comments.clear(); @@ -383,16 +359,16 @@ bool Config::GameSettings::writeFileWithComments(QFile &file) } // comments at top of file - for (std::vector::iterator iter = fileCopy.begin(); iter != fileCopy.end(); ++iter) + for (auto & iter : fileCopy) { - if ((*iter).isNull()) + if (iter.isNull()) continue; // Below is based on readFile() code, if that changes corresponding change may be // required (for example duplicates may be inserted if the rules don't match) - if (/*(*iter).isEmpty() ||*/ (*iter).contains(QRegExp("^\\s*#"))) + if (/*(*iter).isEmpty() ||*/ iter.contains(QRegExp("^\\s*#"))) { - stream << *iter << "\n"; + stream << iter << "\n"; continue; } } @@ -406,7 +382,10 @@ bool Config::GameSettings::writeFileWithComments(QFile &file) { it.previous(); - if (it.key() == QLatin1String("data")) + if (it.key() == QLatin1String("data") + || it.key() == QLatin1String("data-local") + || it.key() == QLatin1String("resources") + || it.key() == QLatin1String("load-savegame")) { settingLine = it.key() + "="; @@ -416,30 +395,20 @@ bool Config::GameSettings::writeFileWithComments(QFile &file) QString string = it.value(); settingLine += delim; - for (QString::const_iterator iter = string.begin(); iter != string.end(); ++iter) + for (auto iter : string) { - if (*iter == delim || *iter == escape) + if (iter == delim || iter == escape) settingLine += escape; - settingLine += *iter; + settingLine += iter; } settingLine += delim; } - // Quote paths with spaces - else if ((it.key() == QLatin1String("data-local") - || it.key() == QLatin1String("resources")) && it.value().contains(QChar(' '))) - { - QString stripped = it.value(); - stripped.remove(QChar('\"')); // Remove quotes - - settingLine = it.key() + "=\"" + stripped + "\""; - } else settingLine = it.key() + "=" + it.value(); if (settingRegex.indexIn(settingLine) != -1) { - std::map >::iterator i = - commentsMap.find(settingRegex.cap(1)+"="+settingRegex.cap(2)); + auto i = commentsMap.find(settingRegex.cap(1)+"="+settingRegex.cap(2)); // check if previous removed content item with comments if (i == commentsMap.end()) @@ -448,8 +417,8 @@ bool Config::GameSettings::writeFileWithComments(QFile &file) if (i != commentsMap.end()) { std::vector cLines = i->second; - for (std::vector::const_iterator ci = cLines.begin(); ci != cLines.end(); ++ci) - stream << *ci << "\n"; + for (const auto & cLine : cLines) + stream << cLine << "\n"; commentsMap.erase(i); } @@ -461,14 +430,14 @@ bool Config::GameSettings::writeFileWithComments(QFile &file) // flush any removed settings if (!commentsMap.empty()) { - std::map >::const_iterator i = commentsMap.begin(); + auto i = commentsMap.begin(); for (; i != commentsMap.end(); ++i) { if (i->first.contains(QRegExp("^\\s*content\\s*="))) { std::vector cLines = i->second; - for (std::vector::const_iterator ci = cLines.begin(); ci != cLines.end(); ++ci) - stream << *ci << "\n"; + for (const auto & cLine : cLines) + stream << cLine << "\n"; // mark the content line entry for future preocessing stream << "##" << i->first << "\n"; @@ -481,8 +450,8 @@ bool Config::GameSettings::writeFileWithComments(QFile &file) // flush any end comments if (!comments.empty()) { - for (std::vector::const_iterator ci = comments.begin(); ci != comments.end(); ++ci) - stream << *ci << "\n"; + for (const auto & comment : comments) + stream << comment << "\n"; } file.resize(file.pos()); diff --git a/components/config/gamesettings.hpp b/components/config/gamesettings.hpp index a6876b83d..ccb1d5fd2 100644 --- a/components/config/gamesettings.hpp +++ b/components/config/gamesettings.hpp @@ -88,7 +88,7 @@ namespace Config static const char sContentKey[]; - bool isOrderedLine(const QString& line) const; + static bool isOrderedLine(const QString& line) ; }; } #endif // GAMESETTINGS_HPP diff --git a/components/config/launchersettings.cpp b/components/config/launchersettings.cpp index ae591ef58..ff2c38438 100644 --- a/components/config/launchersettings.cpp +++ b/components/config/launchersettings.cpp @@ -12,13 +12,9 @@ const char Config::LauncherSettings::sLauncherConfigFileName[] = "launcher.cfg"; const char Config::LauncherSettings::sContentListsSectionPrefix[] = "Profiles/"; const char Config::LauncherSettings::sContentListSuffix[] = "/content"; -Config::LauncherSettings::LauncherSettings() -{ -} +Config::LauncherSettings::LauncherSettings() = default; -Config::LauncherSettings::~LauncherSettings() -{ -} +Config::LauncherSettings::~LauncherSettings() = default; QStringList Config::LauncherSettings::subKeys(const QString &key) { diff --git a/components/config/settingsbase.hpp b/components/config/settingsbase.hpp index 119971716..86fa962ae 100644 --- a/components/config/settingsbase.hpp +++ b/components/config/settingsbase.hpp @@ -15,7 +15,7 @@ namespace Config public: SettingsBase() { mMultiValue = false; } - ~SettingsBase() {} + ~SettingsBase() = default; inline QString value(const QString &key, const QString &defaultValue = QString()) const { diff --git a/components/crashcatcher/crashcatcher.cpp b/components/crashcatcher/crashcatcher.cpp index 99df2cfd8..307c08d95 100644 --- a/components/crashcatcher/crashcatcher.cpp +++ b/components/crashcatcher/crashcatcher.cpp @@ -143,8 +143,14 @@ static void gdb_info(pid_t pid) FILE *f; int fd; - /* Create a temp file to put gdb commands into */ + /* + * Create a temp file to put gdb commands into. + * Note: POSIX.1-2008 declares that the file should be already created with mode 0600 by default. + * Modern systems implement it and and suggest to do not touch masks in multithreaded applications. + * So CoverityScan warning is valid only for ancient versions of stdlib. + */ strcpy(respfile, "/tmp/gdb-respfile-XXXXXX"); + // coverity[secure_temp] if((fd=mkstemp(respfile)) >= 0 && (f=fdopen(fd, "w")) != nullptr) { fprintf(f, "attach %d\n" diff --git a/components/debug/debugging.cpp b/components/debug/debugging.cpp index dfed077e3..c4f3af307 100644 --- a/components/debug/debugging.cpp +++ b/components/debug/debugging.cpp @@ -11,11 +11,22 @@ namespace Debug { #ifdef _WIN32 + bool isRedirected(DWORD nStdHandle) + { + DWORD fileType = GetFileType(GetStdHandle(nStdHandle)); + + return (fileType == FILE_TYPE_DISK) || (fileType == FILE_TYPE_PIPE); + } + bool attachParentConsole() { if (GetConsoleWindow() != nullptr) return true; + bool inRedirected = isRedirected(STD_INPUT_HANDLE); + bool outRedirected = isRedirected(STD_OUTPUT_HANDLE); + bool errRedirected = isRedirected(STD_ERROR_HANDLE); + if (AttachConsole(ATTACH_PARENT_PROCESS)) { fflush(stdout); @@ -24,12 +35,21 @@ namespace Debug std::cerr.flush(); // this looks dubious but is really the right way - _wfreopen(L"CON", L"w", stdout); - _wfreopen(L"CON", L"w", stderr); - _wfreopen(L"CON", L"r", stdin); - freopen("CON", "w", stdout); - freopen("CON", "w", stderr); - freopen("CON", "r", stdin); + if (!inRedirected) + { + _wfreopen(L"CON", L"r", stdin); + freopen("CON", "r", stdin); + } + if (!outRedirected) + { + _wfreopen(L"CON", L"w", stdout); + freopen("CON", "w", stdout); + } + if (!errRedirected) + { + _wfreopen(L"CON", L"w", stderr); + freopen("CON", "w", stderr); + } return true; } diff --git a/components/detournavigator/asyncnavmeshupdater.cpp b/components/detournavigator/asyncnavmeshupdater.cpp index 878b8130a..1ac928f07 100644 --- a/components/detournavigator/asyncnavmeshupdater.cpp +++ b/components/detournavigator/asyncnavmeshupdater.cpp @@ -198,7 +198,7 @@ namespace DetourNavigator return isSuccess(status); } - boost::optional AsyncNavMeshUpdater::getNextJob() + std::optional AsyncNavMeshUpdater::getNextJob() { std::unique_lock lock(mMutex); @@ -217,7 +217,7 @@ namespace DetourNavigator mFirstStart.lock()->reset(); if (mJobs.empty() && getTotalThreadJobsUnsafe() == 0) mDone.notify_all(); - return boost::none; + return std::nullopt; } Log(Debug::Debug) << "Got " << mJobs.size() << " navigator jobs and " @@ -239,7 +239,7 @@ namespace DetourNavigator } } - boost::optional AsyncNavMeshUpdater::getJob(Jobs& jobs, Pushed& pushed, bool changeLastUpdate) + std::optional AsyncNavMeshUpdater::getJob(Jobs& jobs, Pushed& pushed, bool changeLastUpdate) { const auto now = std::chrono::steady_clock::now(); diff --git a/components/detournavigator/asyncnavmeshupdater.hpp b/components/detournavigator/asyncnavmeshupdater.hpp index 26f886512..53e7fd7c1 100644 --- a/components/detournavigator/asyncnavmeshupdater.hpp +++ b/components/detournavigator/asyncnavmeshupdater.hpp @@ -9,8 +9,6 @@ #include -#include - #include #include #include @@ -106,7 +104,7 @@ namespace DetourNavigator Jobs mJobs; std::map> mPushed; Misc::ScopeGuarded mPlayerTile; - Misc::ScopeGuarded> mFirstStart; + Misc::ScopeGuarded> mFirstStart; NavMeshTilesCache mNavMeshTilesCache; Misc::ScopeGuarded>> mProcessingTiles; std::map> mLastUpdates; @@ -117,9 +115,9 @@ namespace DetourNavigator bool processJob(const Job& job); - boost::optional getNextJob(); + std::optional getNextJob(); - boost::optional getJob(Jobs& jobs, Pushed& pushed, bool changeLastUpdate); + std::optional getJob(Jobs& jobs, Pushed& pushed, bool changeLastUpdate); void postThreadJob(Job&& job, Queue& queue); diff --git a/components/detournavigator/cachedrecastmeshmanager.cpp b/components/detournavigator/cachedrecastmeshmanager.cpp index da0f6c874..90b426610 100644 --- a/components/detournavigator/cachedrecastmeshmanager.cpp +++ b/components/detournavigator/cachedrecastmeshmanager.cpp @@ -25,7 +25,7 @@ namespace DetourNavigator return true; } - boost::optional CachedRecastMeshManager::removeObject(const ObjectId id) + std::optional CachedRecastMeshManager::removeObject(const ObjectId id) { const auto object = mImpl.removeObject(id); if (object) @@ -42,7 +42,7 @@ namespace DetourNavigator return true; } - boost::optional CachedRecastMeshManager::removeWater(const osg::Vec2i& cellPosition) + std::optional CachedRecastMeshManager::removeWater(const osg::Vec2i& cellPosition) { const auto water = mImpl.removeWater(cellPosition); if (water) diff --git a/components/detournavigator/cachedrecastmeshmanager.hpp b/components/detournavigator/cachedrecastmeshmanager.hpp index 5efb315e8..54574aa99 100644 --- a/components/detournavigator/cachedrecastmeshmanager.hpp +++ b/components/detournavigator/cachedrecastmeshmanager.hpp @@ -3,8 +3,6 @@ #include "recastmeshmanager.hpp" -#include - namespace DetourNavigator { class CachedRecastMeshManager @@ -19,9 +17,9 @@ namespace DetourNavigator bool addWater(const osg::Vec2i& cellPosition, const int cellSize, const btTransform& transform); - boost::optional removeWater(const osg::Vec2i& cellPosition); + std::optional removeWater(const osg::Vec2i& cellPosition); - boost::optional removeObject(const ObjectId id); + std::optional removeObject(const ObjectId id); std::shared_ptr getMesh(); diff --git a/components/detournavigator/chunkytrimesh.cpp b/components/detournavigator/chunkytrimesh.cpp index 3a8fc3480..ffd39d0a9 100644 --- a/components/detournavigator/chunkytrimesh.cpp +++ b/components/detournavigator/chunkytrimesh.cpp @@ -51,7 +51,7 @@ namespace DetourNavigator const auto inum = imax - imin; const auto icur = curNode; - if (curNode > nodes.size()) + if (curNode >= nodes.size()) return; ChunkyTriMeshNode& node = nodes[curNode++]; diff --git a/components/detournavigator/findrandompointaroundcircle.cpp b/components/detournavigator/findrandompointaroundcircle.cpp index 263dba68e..f2e815c91 100644 --- a/components/detournavigator/findrandompointaroundcircle.cpp +++ b/components/detournavigator/findrandompointaroundcircle.cpp @@ -10,12 +10,12 @@ namespace DetourNavigator { - boost::optional findRandomPointAroundCircle(const dtNavMesh& navMesh, const osg::Vec3f& halfExtents, + std::optional findRandomPointAroundCircle(const dtNavMesh& navMesh, const osg::Vec3f& halfExtents, const osg::Vec3f& start, const float maxRadius, const Flags includeFlags, const Settings& settings) { dtNavMeshQuery navMeshQuery; if (!initNavMeshQuery(navMeshQuery, navMesh, settings.mMaxNavMeshQueryNodes)) - return boost::optional(); + return std::optional(); dtQueryFilter queryFilter; queryFilter.setIncludeFlags(includeFlags); @@ -31,7 +31,7 @@ namespace DetourNavigator } if (startRef == 0) - return boost::optional(); + return std::optional(); dtPolyRef resultRef = 0; osg::Vec3f resultPosition; @@ -39,8 +39,8 @@ namespace DetourNavigator []() { return Misc::Rng::rollProbability(); }, &resultRef, resultPosition.ptr()); if (resultRef == 0) - return boost::optional(); + return std::optional(); - return boost::optional(resultPosition); + return std::optional(resultPosition); } } diff --git a/components/detournavigator/findrandompointaroundcircle.hpp b/components/detournavigator/findrandompointaroundcircle.hpp index 841508f67..d0dc2bbbc 100644 --- a/components/detournavigator/findrandompointaroundcircle.hpp +++ b/components/detournavigator/findrandompointaroundcircle.hpp @@ -3,8 +3,7 @@ #include "flags.hpp" -#include - +#include #include class dtNavMesh; @@ -13,7 +12,7 @@ namespace DetourNavigator { struct Settings; - boost::optional findRandomPointAroundCircle(const dtNavMesh& navMesh, const osg::Vec3f& halfExtents, + std::optional findRandomPointAroundCircle(const dtNavMesh& navMesh, const osg::Vec3f& halfExtents, const osg::Vec3f& start, const float maxRadius, const Flags includeFlags, const Settings& settings); } diff --git a/components/detournavigator/findsmoothpath.cpp b/components/detournavigator/findsmoothpath.cpp index ef0341d72..8e443def9 100644 --- a/components/detournavigator/findsmoothpath.cpp +++ b/components/detournavigator/findsmoothpath.cpp @@ -103,7 +103,7 @@ namespace DetourNavigator return result; } - boost::optional getSteerTarget(const dtNavMeshQuery& navQuery, const osg::Vec3f& startPos, + std::optional getSteerTarget(const dtNavMeshQuery& navQuery, const osg::Vec3f& startPos, const osg::Vec3f& endPos, const float minTargetDist, const std::vector& path) { // Find steer target. @@ -117,7 +117,7 @@ namespace DetourNavigator steerPathFlags.data(), steerPathPolys.data(), &nsteerPath, MAX_STEER_POINTS); assert(nsteerPath >= 0); if (!nsteerPath) - return boost::none; + return std::nullopt; // Find vertex far enough to steer to. std::size_t ns = 0; @@ -131,7 +131,7 @@ namespace DetourNavigator } // Failed to find good point to steer to. if (ns >= static_cast(nsteerPath)) - return boost::none; + return std::nullopt; dtVcopy(result.steerPos.ptr(), &steerPath[ns * 3]); result.steerPos.y() = startPos[1]; diff --git a/components/detournavigator/findsmoothpath.hpp b/components/detournavigator/findsmoothpath.hpp index f1de71207..dcdddce8a 100644 --- a/components/detournavigator/findsmoothpath.hpp +++ b/components/detournavigator/findsmoothpath.hpp @@ -18,10 +18,8 @@ #include -#include - #include - +#include #include class dtNavMesh; @@ -58,7 +56,7 @@ namespace DetourNavigator dtPolyRef steerPosRef; }; - boost::optional getSteerTarget(const dtNavMeshQuery& navQuery, const osg::Vec3f& startPos, + std::optional getSteerTarget(const dtNavMeshQuery& navQuery, const osg::Vec3f& startPos, const osg::Vec3f& endPos, const float minTargetDist, const std::vector& path); template @@ -111,7 +109,7 @@ namespace DetourNavigator std::vector mVisited; }; - inline boost::optional moveAlongSurface(const dtNavMeshQuery& navMeshQuery, + inline std::optional moveAlongSurface(const dtNavMeshQuery& navMeshQuery, const dtPolyRef startRef, const osg::Vec3f& startPos, const osg::Vec3f& endPos, const dtQueryFilter& filter, const std::size_t maxVisitedSize) { @@ -128,7 +126,7 @@ namespace DetourNavigator return {std::move(result)}; } - inline boost::optional> findPath(const dtNavMeshQuery& navMeshQuery, const dtPolyRef startRef, + inline std::optional> findPath(const dtNavMeshQuery& navMeshQuery, const dtPolyRef startRef, const dtPolyRef endRef, const osg::Vec3f& startPos, const osg::Vec3f& endPos, const dtQueryFilter& queryFilter, const std::size_t maxSize) { @@ -144,7 +142,7 @@ namespace DetourNavigator return {std::move(result)}; } - inline boost::optional getPolyHeight(const dtNavMeshQuery& navMeshQuery, const dtPolyRef ref, const osg::Vec3f& pos) + inline std::optional getPolyHeight(const dtNavMeshQuery& navMeshQuery, const dtPolyRef ref, const osg::Vec3f& pos) { float result = 0.0f; const auto status = navMeshQuery.getPolyHeight(ref, pos.ptr(), &result); diff --git a/components/detournavigator/navigator.cpp b/components/detournavigator/navigator.cpp index a58a4f876..658e539ad 100644 --- a/components/detournavigator/navigator.cpp +++ b/components/detournavigator/navigator.cpp @@ -3,18 +3,18 @@ namespace DetourNavigator { - boost::optional Navigator::findRandomPointAroundCircle(const osg::Vec3f& agentHalfExtents, + std::optional Navigator::findRandomPointAroundCircle(const osg::Vec3f& agentHalfExtents, const osg::Vec3f& start, const float maxRadius, const Flags includeFlags) const { const auto navMesh = getNavMesh(agentHalfExtents); if (!navMesh) - return boost::optional(); + return std::optional(); const auto settings = getSettings(); const auto result = DetourNavigator::findRandomPointAroundCircle(navMesh->lockConst()->getImpl(), toNavMeshCoordinates(settings, agentHalfExtents), toNavMeshCoordinates(settings, start), toNavMeshCoordinates(settings, maxRadius), includeFlags, settings); if (!result) - return boost::optional(); - return boost::optional(fromNavMeshCoordinates(settings, *result)); + return std::optional(); + return std::optional(fromNavMeshCoordinates(settings, *result)); } } diff --git a/components/detournavigator/navigator.hpp b/components/detournavigator/navigator.hpp index 3e7f54178..a79aa59d4 100644 --- a/components/detournavigator/navigator.hpp +++ b/components/detournavigator/navigator.hpp @@ -220,7 +220,7 @@ namespace DetourNavigator * @param includeFlags setup allowed surfaces for actor to walk. * @return not empty optional with position if point is found and empty optional if point is not found. */ - boost::optional findRandomPointAroundCircle(const osg::Vec3f& agentHalfExtents, + std::optional findRandomPointAroundCircle(const osg::Vec3f& agentHalfExtents, const osg::Vec3f& start, const float maxRadius, const Flags includeFlags) const; virtual RecastMeshTiles getRecastMeshTiles() = 0; diff --git a/components/detournavigator/navigatorimpl.hpp b/components/detournavigator/navigatorimpl.hpp index a1d66463c..e197c71b7 100644 --- a/components/detournavigator/navigatorimpl.hpp +++ b/components/detournavigator/navigatorimpl.hpp @@ -15,7 +15,7 @@ namespace DetourNavigator * @brief Navigator constructor initializes all internal data. Constructed object is ready to build a scene. * @param settings allows to customize navigator work. Constructor is only place to set navigator settings. */ - NavigatorImpl(const Settings& settings); + explicit NavigatorImpl(const Settings& settings); void addAgent(const osg::Vec3f& agentHalfExtents) override; @@ -40,9 +40,9 @@ namespace DetourNavigator bool removeWater(const osg::Vec2i& cellPosition) override; - void addPathgrid(const ESM::Cell& cell, const ESM::Pathgrid& pathgrid) final; + void addPathgrid(const ESM::Cell& cell, const ESM::Pathgrid& pathgrid) override; - void removePathgrid(const ESM::Pathgrid& pathgrid) final; + void removePathgrid(const ESM::Pathgrid& pathgrid) override; void update(const osg::Vec3f& playerPosition) override; diff --git a/components/detournavigator/navigatorstub.hpp b/components/detournavigator/navigatorstub.hpp index 9279e77e3..8b81bde19 100644 --- a/components/detournavigator/navigatorstub.hpp +++ b/components/detournavigator/navigatorstub.hpp @@ -60,9 +60,9 @@ namespace DetourNavigator return false; } - void addPathgrid(const ESM::Cell& /*cell*/, const ESM::Pathgrid& /*pathgrid*/) final {} + void addPathgrid(const ESM::Cell& /*cell*/, const ESM::Pathgrid& /*pathgrid*/) override {} - void removePathgrid(const ESM::Pathgrid& /*pathgrid*/) final {} + void removePathgrid(const ESM::Pathgrid& /*pathgrid*/) override {} void update(const osg::Vec3f& /*playerPosition*/) override {} diff --git a/components/detournavigator/recastmeshbuilder.cpp b/components/detournavigator/recastmeshbuilder.cpp index 59f60394d..ee014b932 100644 --- a/components/detournavigator/recastmeshbuilder.cpp +++ b/components/detournavigator/recastmeshbuilder.cpp @@ -17,6 +17,7 @@ #include #include +#include #include namespace DetourNavigator diff --git a/components/detournavigator/recastmeshmanager.cpp b/components/detournavigator/recastmeshmanager.cpp index 05f250596..3796c9816 100644 --- a/components/detournavigator/recastmeshmanager.cpp +++ b/components/detournavigator/recastmeshmanager.cpp @@ -34,11 +34,11 @@ namespace DetourNavigator return true; } - boost::optional RecastMeshManager::removeObject(const ObjectId id) + std::optional RecastMeshManager::removeObject(const ObjectId id) { const auto object = mObjects.find(id); if (object == mObjects.end()) - return boost::none; + return std::nullopt; const RemovedRecastMeshObject result {object->second->getShape(), object->second->getTransform()}; mObjectsOrder.erase(object->second); mObjects.erase(object); @@ -59,11 +59,11 @@ namespace DetourNavigator return true; } - boost::optional RecastMeshManager::removeWater(const osg::Vec2i& cellPosition) + std::optional RecastMeshManager::removeWater(const osg::Vec2i& cellPosition) { const auto water = mWater.find(cellPosition); if (water == mWater.end()) - return boost::none; + return std::nullopt; ++mRevision; const auto result = *water->second; mWaterOrder.erase(water->second); diff --git a/components/detournavigator/recastmeshmanager.hpp b/components/detournavigator/recastmeshmanager.hpp index f1870ec6b..5b568e004 100644 --- a/components/detournavigator/recastmeshmanager.hpp +++ b/components/detournavigator/recastmeshmanager.hpp @@ -9,11 +9,10 @@ #include -#include - +#include #include +#include #include -#include class btCollisionShape; @@ -43,9 +42,9 @@ namespace DetourNavigator bool addWater(const osg::Vec2i& cellPosition, const int cellSize, const btTransform& transform); - boost::optional removeWater(const osg::Vec2i& cellPosition); + std::optional removeWater(const osg::Vec2i& cellPosition); - boost::optional removeObject(const ObjectId id); + std::optional removeObject(const ObjectId id); std::shared_ptr getMesh(); diff --git a/components/detournavigator/settings.cpp b/components/detournavigator/settings.cpp index 49aec41ff..977b80cf5 100644 --- a/components/detournavigator/settings.cpp +++ b/components/detournavigator/settings.cpp @@ -4,10 +4,10 @@ namespace DetourNavigator { - boost::optional makeSettingsFromSettingsManager() + std::optional makeSettingsFromSettingsManager() { if (!::Settings::Manager::getBool("enable", "Navigator")) - return boost::optional(); + return std::optional(); Settings navigatorSettings; diff --git a/components/detournavigator/settings.hpp b/components/detournavigator/settings.hpp index 939d825a5..d73087b21 100644 --- a/components/detournavigator/settings.hpp +++ b/components/detournavigator/settings.hpp @@ -1,10 +1,9 @@ #ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_SETTINGS_H #define OPENMW_COMPONENTS_DETOURNAVIGATOR_SETTINGS_H -#include - -#include #include +#include +#include namespace DetourNavigator { @@ -42,7 +41,7 @@ namespace DetourNavigator std::chrono::milliseconds mMinUpdateInterval; }; - boost::optional makeSettingsFromSettingsManager(); + std::optional makeSettingsFromSettingsManager(); } #endif diff --git a/components/detournavigator/tilecachedrecastmeshmanager.cpp b/components/detournavigator/tilecachedrecastmeshmanager.cpp index 9debe5dea..fddaa88f1 100644 --- a/components/detournavigator/tilecachedrecastmeshmanager.cpp +++ b/components/detournavigator/tilecachedrecastmeshmanager.cpp @@ -31,12 +31,12 @@ namespace DetourNavigator return result; } - boost::optional TileCachedRecastMeshManager::removeObject(const ObjectId id) + std::optional TileCachedRecastMeshManager::removeObject(const ObjectId id) { const auto object = mObjectsTilesPositions.find(id); if (object == mObjectsTilesPositions.end()) - return boost::none; - boost::optional result; + return std::nullopt; + std::optional result; { auto tiles = mTiles.lock(); for (const auto& tilePosition : object->second) @@ -100,12 +100,12 @@ namespace DetourNavigator return result; } - boost::optional TileCachedRecastMeshManager::removeWater(const osg::Vec2i& cellPosition) + std::optional TileCachedRecastMeshManager::removeWater(const osg::Vec2i& cellPosition) { const auto object = mWaterTilesPositions.find(cellPosition); if (object == mWaterTilesPositions.end()) - return boost::none; - boost::optional result; + return std::nullopt; + std::optional result; for (const auto& tilePosition : object->second) { const auto tiles = mTiles.lock(); @@ -168,12 +168,12 @@ namespace DetourNavigator return tile != tiles.end() && tile->second.updateObject(id, transform, areaType); } - boost::optional TileCachedRecastMeshManager::removeTile(const ObjectId id, + std::optional TileCachedRecastMeshManager::removeTile(const ObjectId id, const TilePosition& tilePosition, std::map& tiles) { const auto tile = tiles.find(tilePosition); if (tile == tiles.end()) - return boost::optional(); + return std::optional(); const auto tileResult = tile->second.removeObject(id); if (tile->second.isEmpty()) { diff --git a/components/detournavigator/tilecachedrecastmeshmanager.hpp b/components/detournavigator/tilecachedrecastmeshmanager.hpp index 557cde1be..fa192bd73 100644 --- a/components/detournavigator/tilecachedrecastmeshmanager.hpp +++ b/components/detournavigator/tilecachedrecastmeshmanager.hpp @@ -69,11 +69,11 @@ namespace DetourNavigator return changed; } - boost::optional removeObject(const ObjectId id); + std::optional removeObject(const ObjectId id); bool addWater(const osg::Vec2i& cellPosition, const int cellSize, const btTransform& transform); - boost::optional removeWater(const osg::Vec2i& cellPosition); + std::optional removeWater(const osg::Vec2i& cellPosition); std::shared_ptr getMesh(const TilePosition& tilePosition); @@ -103,7 +103,7 @@ namespace DetourNavigator bool updateTile(const ObjectId id, const btTransform& transform, const AreaType areaType, const TilePosition& tilePosition, std::map& tiles); - boost::optional removeTile(const ObjectId id, const TilePosition& tilePosition, + std::optional removeTile(const ObjectId id, const TilePosition& tilePosition, std::map& tiles); }; } diff --git a/components/esm/aisequence.cpp b/components/esm/aisequence.cpp index 7891299e3..df3c1ca9d 100644 --- a/components/esm/aisequence.cpp +++ b/components/esm/aisequence.cpp @@ -171,7 +171,7 @@ namespace AiSequence int type; esm.getHT(type); - mPackages.push_back(AiPackageContainer()); + mPackages.emplace_back(); mPackages.back().mType = type; switch (type) diff --git a/components/esm/containerstate.hpp b/components/esm/containerstate.hpp index b0c4da2d1..7f1afd077 100644 --- a/components/esm/containerstate.hpp +++ b/components/esm/containerstate.hpp @@ -12,14 +12,14 @@ namespace ESM { InventoryState mInventory; - void load (ESMReader &esm) final; - void save (ESMWriter &esm, bool inInventory = false) const final; + void load (ESMReader &esm) override; + void save (ESMWriter &esm, bool inInventory = false) const override; - ContainerState& asContainerState() final + ContainerState& asContainerState() override { return *this; } - const ContainerState& asContainerState() const final + const ContainerState& asContainerState() const override { return *this; } diff --git a/components/esm/creaturelevliststate.hpp b/components/esm/creaturelevliststate.hpp index ec0bc8667..2ee9e511f 100644 --- a/components/esm/creaturelevliststate.hpp +++ b/components/esm/creaturelevliststate.hpp @@ -12,14 +12,14 @@ namespace ESM int mSpawnActorId; bool mSpawn; - void load (ESMReader &esm) final; - void save (ESMWriter &esm, bool inInventory = false) const final; + void load (ESMReader &esm) override; + void save (ESMWriter &esm, bool inInventory = false) const override; - CreatureLevListState& asCreatureLevListState() final + CreatureLevListState& asCreatureLevListState() override { return *this; } - const CreatureLevListState& asCreatureLevListState() const final + const CreatureLevListState& asCreatureLevListState() const override { return *this; } diff --git a/components/esm/creaturestate.hpp b/components/esm/creaturestate.hpp index 3e9a44d56..4b4c59d71 100644 --- a/components/esm/creaturestate.hpp +++ b/components/esm/creaturestate.hpp @@ -15,16 +15,16 @@ namespace ESM CreatureStats mCreatureStats; /// Initialize to default state - void blank(); + void blank() override; - void load (ESMReader &esm) final; - void save (ESMWriter &esm, bool inInventory = false) const final; + void load (ESMReader &esm) override; + void save (ESMWriter &esm, bool inInventory = false) const override; - CreatureState& asCreatureState() final + CreatureState& asCreatureState() override { return *this; } - const CreatureState& asCreatureState() const final + const CreatureState& asCreatureState() const override { return *this; } diff --git a/components/esm/doorstate.hpp b/components/esm/doorstate.hpp index 04ad110d6..c3aeb42e3 100644 --- a/components/esm/doorstate.hpp +++ b/components/esm/doorstate.hpp @@ -11,14 +11,14 @@ namespace ESM { int mDoorState = 0; - void load (ESMReader &esm) final; - void save (ESMWriter &esm, bool inInventory = false) const final; + void load (ESMReader &esm) override; + void save (ESMWriter &esm, bool inInventory = false) const override; - DoorState& asDoorState() final + DoorState& asDoorState() override { return *this; } - const DoorState& asDoorState() const final + const DoorState& asDoorState() const override { return *this; } diff --git a/components/esm/inventorystate.cpp b/components/esm/inventorystate.cpp index 2392d263f..980d67f7e 100644 --- a/components/esm/inventorystate.cpp +++ b/components/esm/inventorystate.cpp @@ -76,7 +76,7 @@ void ESM::InventoryState::load (ESMReader &esm) float rand, multiplier; esm.getHT (rand); esm.getHNT (multiplier, "MULT"); - params.push_back(std::make_pair(rand, multiplier)); + params.emplace_back(rand, multiplier); } mPermanentMagicEffectMagnitudes[id] = params; } diff --git a/components/esm/loadland.cpp b/components/esm/loadland.cpp index 3064d0c31..2aa8f21db 100644 --- a/components/esm/loadland.cpp +++ b/components/esm/loadland.cpp @@ -331,9 +331,10 @@ namespace ESM std::copy(land.mWnam, land.mWnam + LAND_GLOBAL_MAP_LOD_SIZE, mWnam); } - Land& Land::operator= (Land land) + Land& Land::operator= (const Land& land) { - swap (land); + Land tmp(land); + swap(tmp); return *this; } diff --git a/components/esm/loadland.hpp b/components/esm/loadland.hpp index e5faf4b31..2a1140ad2 100644 --- a/components/esm/loadland.hpp +++ b/components/esm/loadland.hpp @@ -148,7 +148,7 @@ struct Land Land (const Land& land); - Land& operator= (Land land); + Land& operator= (const Land& land); void swap (Land& land); diff --git a/components/esm/locals.cpp b/components/esm/locals.cpp index bd51be08f..4149695fe 100644 --- a/components/esm/locals.cpp +++ b/components/esm/locals.cpp @@ -12,7 +12,7 @@ void ESM::Locals::load (ESMReader &esm) Variant value; value.read (esm, Variant::Format_Local); - mVariables.push_back (std::make_pair (id, value)); + mVariables.emplace_back (id, value); } } diff --git a/components/esm/npcstate.hpp b/components/esm/npcstate.hpp index 6c0469050..fc6b91764 100644 --- a/components/esm/npcstate.hpp +++ b/components/esm/npcstate.hpp @@ -17,16 +17,16 @@ namespace ESM CreatureStats mCreatureStats; /// Initialize to default state - void blank(); + void blank() override; - void load (ESMReader &esm) final; - void save (ESMWriter &esm, bool inInventory = false) const final; + void load (ESMReader &esm) override; + void save (ESMWriter &esm, bool inInventory = false) const override; - NpcState& asNpcState() final + NpcState& asNpcState() override { return *this; } - const NpcState& asNpcState() const final + const NpcState& asNpcState() const override { return *this; } diff --git a/components/esm/objectstate.hpp b/components/esm/objectstate.hpp index 1079d6c72..6b0fca5ea 100644 --- a/components/esm/objectstate.hpp +++ b/components/esm/objectstate.hpp @@ -49,7 +49,7 @@ namespace ESM virtual void save (ESMWriter &esm, bool inInventory = false) const; - /// Initialize to default state + virtual /// Initialize to default state void blank(); virtual ~ObjectState(); diff --git a/components/fallback/validate.cpp b/components/fallback/validate.cpp new file mode 100644 index 000000000..982c709af --- /dev/null +++ b/components/fallback/validate.cpp @@ -0,0 +1,24 @@ +#include "validate.hpp" + +void Fallback::validate(boost::any& v, std::vector const& tokens, FallbackMap*, int) +{ + if (v.empty()) + { + v = boost::any(FallbackMap()); + } + + FallbackMap *map = boost::any_cast(&v); + + for (const auto& token : tokens) + { + std::string temp = Files::EscapeHashString::processString(token); + 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); + + std::string key(temp.substr(0, sep)); + std::string value(temp.substr(sep + 1)); + + map->mMap[key] = value; + } +} diff --git a/components/fallback/validate.hpp b/components/fallback/validate.hpp index cef52e462..96690f50a 100644 --- a/components/fallback/validate.hpp +++ b/components/fallback/validate.hpp @@ -16,32 +16,7 @@ namespace Fallback std::map mMap; }; - void validate(boost::any &v, std::vector const &tokens, FallbackMap*, int) - { - if (v.empty()) - { - v = boost::any(FallbackMap()); - } - - FallbackMap *map = boost::any_cast(&v); - - for (std::vector::const_iterator it = tokens.begin(); it != tokens.end(); ++it) - { - std::string temp = Files::EscapeHashString::processString(*it); - int sep = temp.find(","); - if (sep < 1 || sep == (int)temp.length() - 1) -#if (BOOST_VERSION < 104200) - throw boost::program_options::validation_error("invalid value"); -#else - throw boost::program_options::validation_error(boost::program_options::validation_error::invalid_option_value); -#endif - - std::string key(temp.substr(0, sep)); - std::string value(temp.substr(sep + 1)); - - map->mMap[key] = value; - } - } + void validate(boost::any &v, std::vector const &tokens, FallbackMap*, int); } #endif diff --git a/components/files/configurationmanager.cpp b/components/files/configurationmanager.cpp index 0ba2d1519..92d35a6b6 100644 --- a/components/files/configurationmanager.cpp +++ b/components/files/configurationmanager.cpp @@ -2,6 +2,7 @@ #include #include +#include #include /** @@ -58,23 +59,96 @@ void ConfigurationManager::readConfiguration(boost::program_options::variables_m { bool silent = mSilent; mSilent = quiet; + + // User config has the highest priority. + auto composingVariables = separateComposingVariables(variables, description); + loadConfig(mFixedPath.getUserConfigPath(), variables, description); + mergeComposingVariables(variables, composingVariables, description); + boost::program_options::notify(variables); // read either local or global config depending on type of installation + composingVariables = separateComposingVariables(variables, description); bool loaded = loadConfig(mFixedPath.getLocalPath(), variables, description); + mergeComposingVariables(variables, composingVariables, description); boost::program_options::notify(variables); if (!loaded) { + composingVariables = separateComposingVariables(variables, description); loadConfig(mFixedPath.getGlobalConfigPath(), variables, description); + mergeComposingVariables(variables, composingVariables, description); boost::program_options::notify(variables); } - // User config has the highest priority. - loadConfig(mFixedPath.getUserConfigPath(), variables, description); - boost::program_options::notify(variables); - mSilent = silent; } +boost::program_options::variables_map ConfigurationManager::separateComposingVariables(boost::program_options::variables_map & variables, boost::program_options::options_description& description) +{ + boost::program_options::variables_map composingVariables; + for (auto itr = variables.begin(); itr != variables.end();) + { + if (description.find(itr->first, false).semantic()->is_composing()) + { + composingVariables.emplace(*itr); + itr = variables.erase(itr); + } + else + ++itr; + } + return composingVariables; +} + +void ConfigurationManager::mergeComposingVariables(boost::program_options::variables_map & first, boost::program_options::variables_map & second, boost::program_options::options_description& description) +{ + for (const auto& option : description.options()) + { + if (option->semantic()->is_composing()) + { + std::string name = option->canonical_display_name(); + + auto firstPosition = first.find(name); + if (firstPosition == first.end()) + { + first.emplace(name, second[name]); + continue; + } + + if (second[name].defaulted() || second[name].empty()) + continue; + + boost::any& firstValue = firstPosition->second.value(); + const boost::any& secondValue = second[name].value(); + + if (firstValue.type() == typeid(Files::EscapePathContainer)) + { + auto& firstPathContainer = boost::any_cast(firstValue); + const auto& secondPathContainer = boost::any_cast(secondValue); + + firstPathContainer.insert(firstPathContainer.end(), secondPathContainer.begin(), secondPathContainer.end()); + } + else if (firstValue.type() == typeid(Files::EscapeStringVector)) + { + auto& firstVector = boost::any_cast(firstValue); + const auto& secondVector = boost::any_cast(secondValue); + + firstVector.mVector.insert(firstVector.mVector.end(), secondVector.mVector.begin(), secondVector.mVector.end()); + } + else if (firstValue.type() == typeid(Fallback::FallbackMap)) + { + auto& firstMap = boost::any_cast(firstValue); + const auto& secondMap = boost::any_cast(secondValue); + + std::map tempMap(secondMap.mMap); + tempMap.merge(firstMap.mMap); + firstMap.mMap.swap(tempMap); + } + else + Log(Debug::Error) << "Unexpected composing variable type. Curse boost and their blasted arcane templates."; + } + } + +} + void ConfigurationManager::processPaths(Files::PathContainer& dataDirs, bool create) { std::string path; diff --git a/components/files/configurationmanager.hpp b/components/files/configurationmanager.hpp index 446abd4dc..0ec0a1f67 100644 --- a/components/files/configurationmanager.hpp +++ b/components/files/configurationmanager.hpp @@ -25,6 +25,10 @@ struct ConfigurationManager void readConfiguration(boost::program_options::variables_map& variables, boost::program_options::options_description& description, bool quiet=false); + boost::program_options::variables_map separateComposingVariables(boost::program_options::variables_map& variables, boost::program_options::options_description& description); + + void mergeComposingVariables(boost::program_options::variables_map& first, boost::program_options::variables_map& second, boost::program_options::options_description& description); + void processPaths(Files::PathContainer& dataDirs, bool create = false); ///< \param create Try creating the directory, if it does not exist. diff --git a/components/files/escape.cpp b/components/files/escape.cpp index 93ae9b885..8b11504d3 100644 --- a/components/files/escape.cpp +++ b/components/files/escape.cpp @@ -121,7 +121,7 @@ namespace Files EscapeStringVector * eSV = boost::any_cast(&v); for (std::vector::const_iterator it = tokens.begin(); it != tokens.end(); ++it) - eSV->mVector.push_back(EscapeHashString(*it)); + eSV->mVector.emplace_back(*it); } PathContainer EscapePath::toPathContainer(const EscapePathContainer & escapePathContainer) diff --git a/components/misc/barrier.hpp b/components/misc/barrier.hpp index 7259b8452..a5af9f565 100644 --- a/components/misc/barrier.hpp +++ b/components/misc/barrier.hpp @@ -21,7 +21,7 @@ namespace Misc /// @brief stop execution of threads until count distinct threads reach this point void wait() { - std::unique_lock lock(mMutex); + std::unique_lock lock(mMutex); ++mRendezvousCount; const int currentGeneration = mGeneration; diff --git a/components/myguiplatform/additivelayer.hpp b/components/myguiplatform/additivelayer.hpp index f0cfc1b41..f89e1d007 100644 --- a/components/myguiplatform/additivelayer.hpp +++ b/components/myguiplatform/additivelayer.hpp @@ -20,9 +20,9 @@ namespace osgMyGUI MYGUI_RTTI_DERIVED( AdditiveLayer ) AdditiveLayer(); - ~AdditiveLayer(); + ~AdditiveLayer() override; - void renderToTarget(MyGUI::IRenderTarget* _target, bool _update) final; + void renderToTarget(MyGUI::IRenderTarget* _target, bool _update) override; private: osg::ref_ptr mStateSet; diff --git a/components/myguiplatform/scalinglayer.hpp b/components/myguiplatform/scalinglayer.hpp index 7ce5f84f7..f9fd92a78 100644 --- a/components/myguiplatform/scalinglayer.hpp +++ b/components/myguiplatform/scalinglayer.hpp @@ -13,13 +13,13 @@ namespace osgMyGUI public: MYGUI_RTTI_DERIVED(ScalingLayer) - void deserialization(MyGUI::xml::ElementPtr _node, MyGUI::Version _version) final; + void deserialization(MyGUI::xml::ElementPtr _node, MyGUI::Version _version) override; - MyGUI::ILayerItem* getLayerItemByPoint(int _left, int _top) const final; - MyGUI::IntPoint getPosition(int _left, int _top) const final; - void renderToTarget(MyGUI::IRenderTarget* _target, bool _update) final; + MyGUI::ILayerItem* getLayerItemByPoint(int _left, int _top) const override; + MyGUI::IntPoint getPosition(int _left, int _top) const override; + void renderToTarget(MyGUI::IRenderTarget* _target, bool _update) override; - void resizeView(const MyGUI::IntSize& _viewSize) final; + void resizeView(const MyGUI::IntSize& _viewSize) override; private: void screenToLayerCoords(int& _left, int& _top) const; diff --git a/components/nif/controller.cpp b/components/nif/controller.cpp index 07699239e..1e909120e 100644 --- a/components/nif/controller.cpp +++ b/components/nif/controller.cpp @@ -92,6 +92,8 @@ namespace Nif void NiMaterialColorController::read(NIFStream *nif) { Controller::read(nif); + if (nif->getVersion() > NIFStream::generateVersion(10,1,0,103)) + interpolator.read(nif); // Two bits that correspond to the controlled material color. // 00: Ambient // 01: Diffuse @@ -101,12 +103,14 @@ namespace Nif targetColor = nif->getUShort() & 3; else targetColor = (flags >> 4) & 3; - data.read(nif); + if (nif->getVersion() <= NIFStream::generateVersion(10,1,0,103)) + data.read(nif); } void NiMaterialColorController::post(NIFFile *nif) { Controller::post(nif); + interpolator.post(nif); data.post(nif); } @@ -161,25 +165,33 @@ namespace Nif void NiKeyframeController::read(NIFStream *nif) { Controller::read(nif); - data.read(nif); + if (nif->getVersion() <= NIFStream::generateVersion(10,1,0,103)) + data.read(nif); + else + interpolator.read(nif); } void NiKeyframeController::post(NIFFile *nif) { Controller::post(nif); data.post(nif); + interpolator.post(nif); } void NiFloatInterpController::read(NIFStream *nif) { Controller::read(nif); - data.read(nif); + if (nif->getVersion() <= NIFStream::generateVersion(10,1,0,103)) + data.read(nif); + else + interpolator.read(nif); } void NiFloatInterpController::post(NIFFile *nif) { Controller::post(nif); data.post(nif); + interpolator.post(nif); } void NiGeomMorpherController::read(NIFStream *nif) @@ -189,13 +201,34 @@ namespace Nif /*bool updateNormals = !!*/nif->getUShort(); data.read(nif); if (nif->getVersion() >= NIFFile::NIFVersion::VER_MW) + { /*bool alwaysActive = */nif->getChar(); // Always 0 + if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,106)) + { + if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB) + { + interpolators.read(nif); + if (nif->getVersion() >= NIFStream::generateVersion(10,2,0,0) && nif->getBethVersion() > 9) + { + unsigned int numUnknown = nif->getUInt(); + nif->skip(4 * numUnknown); + } + } + else + { + // TODO: handle weighted interpolators + unsigned int numInterps = nif->getUInt(); + nif->skip(8 * numInterps); + } + } + } } void NiGeomMorpherController::post(NIFFile *nif) { Controller::post(nif); data.post(nif); + interpolators.post(nif); } void NiVisController::read(NIFStream *nif) @@ -213,6 +246,8 @@ namespace Nif void NiFlipController::read(NIFStream *nif) { Controller::read(nif); + if (nif->getVersion() >= NIFStream::generateVersion(10,2,0,0)) + mInterpolator.read(nif); mTexSlot = nif->getUInt(); if (nif->getVersion() <= NIFStream::generateVersion(10,1,0,103)) { @@ -225,7 +260,69 @@ namespace Nif void NiFlipController::post(NIFFile *nif) { Controller::post(nif); + mInterpolator.post(nif); mSources.post(nif); } + void bhkBlendController::read(NIFStream *nif) + { + Controller::read(nif); + nif->getUInt(); // Zero + } + + void NiPoint3Interpolator::read(NIFStream *nif) + { + defaultVal = nif->getVector3(); + data.read(nif); + } + + void NiPoint3Interpolator::post(NIFFile *nif) + { + data.post(nif); + } + + void NiBoolInterpolator::read(NIFStream *nif) + { + defaultVal = nif->getBoolean(); + data.read(nif); + } + + void NiBoolInterpolator::post(NIFFile *nif) + { + data.post(nif); + } + + void NiFloatInterpolator::read(NIFStream *nif) + { + defaultVal = nif->getFloat(); + data.read(nif); + } + + void NiFloatInterpolator::post(NIFFile *nif) + { + data.post(nif); + } + + void NiTransformInterpolator::read(NIFStream *nif) + { + defaultPos = nif->getVector3(); + defaultRot = nif->getQuaternion(); + defaultScale = nif->getFloat(); + if (nif->getVersion() <= NIFStream::generateVersion(10,1,0,109)) + { + if (!nif->getBoolean()) + defaultPos = osg::Vec3f(); + if (!nif->getBoolean()) + defaultRot = osg::Quat(); + if (!nif->getBoolean()) + defaultScale = 1.f; + } + data.read(nif); + } + + void NiTransformInterpolator::post(NIFFile *nif) + { + data.post(nif); + } + } diff --git a/components/nif/controller.hpp b/components/nif/controller.hpp index 54cf9bf8f..6b84d3749 100644 --- a/components/nif/controller.hpp +++ b/components/nif/controller.hpp @@ -83,6 +83,7 @@ using NiBSPArrayController = NiParticleSystemController; class NiMaterialColorController : public Controller { public: + NiPoint3InterpolatorPtr interpolator; NiPosDataPtr data; unsigned int targetColor; @@ -138,6 +139,7 @@ class NiKeyframeController : public Controller { public: NiKeyframeDataPtr data; + NiTransformInterpolatorPtr interpolator; void read(NIFStream *nif) override; void post(NIFFile *nif) override; @@ -146,6 +148,7 @@ public: struct NiFloatInterpController : public Controller { NiFloatDataPtr data; + NiFloatInterpolatorPtr interpolator; void read(NIFStream *nif) override; void post(NIFFile *nif) override; @@ -158,6 +161,7 @@ class NiGeomMorpherController : public Controller { public: NiMorphDataPtr data; + NiFloatInterpolatorList interpolators; void read(NIFStream *nif) override; void post(NIFFile *nif) override; @@ -175,6 +179,7 @@ public: class NiFlipController : public Controller { public: + NiFloatInterpolatorPtr mInterpolator; int mTexSlot; // NiTexturingProperty::TextureType float mDelta; // Time between two flips. delta = (start_time - stop_time) / num_sources NiSourceTextureList mSources; @@ -183,5 +188,47 @@ public: void post(NIFFile *nif) override; }; +struct bhkBlendController : public Controller +{ + void read(NIFStream *nif) override; +}; + +struct Interpolator : public Record { }; + +struct NiPoint3Interpolator : public Interpolator +{ + osg::Vec3f defaultVal; + NiPosDataPtr data; + void read(NIFStream *nif) override; + void post(NIFFile *nif) override; +}; + +struct NiBoolInterpolator : public Interpolator +{ + bool defaultVal; + NiBoolDataPtr data; + void read(NIFStream *nif) override; + void post(NIFFile *nif) override; +}; + +struct NiFloatInterpolator : public Interpolator +{ + float defaultVal; + NiFloatDataPtr data; + void read(NIFStream *nif) override; + void post(NIFFile *nif) override; +}; + +struct NiTransformInterpolator : public Interpolator +{ + osg::Vec3f defaultPos; + osg::Quat defaultRot; + float defaultScale; + NiKeyframeDataPtr data; + + void read(NIFStream *nif) override; + void post(NIFFile *nif) override; +}; + } // Namespace #endif diff --git a/components/nif/data.cpp b/components/nif/data.cpp index 91b3beba4..235825e4a 100644 --- a/components/nif/data.cpp +++ b/components/nif/data.cpp @@ -6,6 +6,8 @@ namespace Nif void NiSkinInstance::read(NIFStream *nif) { data.read(nif); + if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,101)) + partitions.read(nif); root.read(nif); bones.read(nif); } @@ -13,6 +15,7 @@ void NiSkinInstance::read(NIFStream *nif) void NiSkinInstance::post(NIFFile *nif) { data.post(nif); + partitions.post(nif); root.post(nif); bones.post(nif); @@ -67,19 +70,18 @@ void NiGeometryData::read(NIFStream *nif) if (nif->getBoolean()) nif->getVector4s(colors, verts); - // Only the first 6 bits are used as a count. I think the rest are - // flags of some sort. unsigned int numUVs = dataFlags; if (nif->getVersion() <= NIFStream::generateVersion(4,2,2,0)) - { numUVs = nif->getUShort(); - // In Morrowind this field only corresponds to the number of UV sets. - // NifTools research is inaccurate. - if (nif->getVersion() > NIFFile::NIFVersion::VER_MW) - numUVs &= 0x3f; + + // In Morrowind this field only corresponds to the number of UV sets. + // In later games only the first 6 bits are used as a count and the rest are flags. + if (nif->getVersion() > NIFFile::NIFVersion::VER_MW) + { + numUVs &= 0x3f; + if (nif->getVersion() == NIFFile::NIFVersion::VER_BGS && nif->getBethVersion() > 0) + numUVs &= 0x1; } - if (nif->getVersion() == NIFFile::NIFVersion::VER_BGS && nif->getBethVersion() > 0) - numUVs &= 0x1; bool hasUVs = true; if (nif->getVersion() <= NIFFile::NIFVersion::VER_MW) @@ -321,7 +323,7 @@ void NiSkinData::read(NIFStream *nif) int boneNum = nif->getInt(); if (nif->getVersion() >= NIFFile::NIFVersion::VER_MW && nif->getVersion() <= NIFStream::generateVersion(10,1,0,0)) - nif->skip(4); // NiSkinPartition link + partitions.read(nif); // Has vertex weights flag if (nif->getVersion() > NIFStream::generateVersion(4,2,1,0) && !nif->getBoolean()) @@ -346,6 +348,69 @@ void NiSkinData::read(NIFStream *nif) } } +void NiSkinData::post(NIFFile *nif) +{ + partitions.post(nif); +} + +void NiSkinPartition::read(NIFStream *nif) +{ + unsigned int num = nif->getUInt(); + data.resize(num); + for (auto& partition : data) + partition.read(nif); +} + +void NiSkinPartition::Partition::read(NIFStream *nif) +{ + unsigned short numVertices = nif->getUShort(); + unsigned short numTriangles = nif->getUShort(); + unsigned short numBones = nif->getUShort(); + unsigned short numStrips = nif->getUShort(); + unsigned short bonesPerVertex = nif->getUShort(); + if (numBones) + nif->getUShorts(bones, numBones); + + bool hasVertexMap = true; + if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,0)) + hasVertexMap = nif->getBoolean(); + if (hasVertexMap && numVertices) + nif->getUShorts(vertexMap, numVertices); + + bool hasVertexWeights = true; + if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,0)) + hasVertexWeights = nif->getBoolean(); + if (hasVertexWeights && numVertices && bonesPerVertex) + nif->getFloats(weights, numVertices * bonesPerVertex); + + std::vector stripLengths; + if (numStrips) + nif->getUShorts(stripLengths, numStrips); + + bool hasFaces = true; + if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,0)) + hasFaces = nif->getBoolean(); + if (hasFaces) + { + if (numStrips) + { + strips.resize(numStrips); + for (unsigned short i = 0; i < numStrips; i++) + nif->getUShorts(strips[i], stripLengths[i]); + } + else if (numTriangles) + nif->getUShorts(triangles, numTriangles * 3); + } + bool hasBoneIndices = nif->getChar() != 0; + if (hasBoneIndices && numVertices && bonesPerVertex) + nif->getChars(boneIndices, numVertices * bonesPerVertex); + if (nif->getBethVersion() > NIFFile::BethVersion::BETHVER_FO3) + { + nif->getChar(); // LOD level + nif->getBoolean(); // Global VB + } +} + void NiMorphData::read(NIFStream *nif) { int morphCount = nif->getInt(); @@ -356,7 +421,7 @@ void NiMorphData::read(NIFStream *nif) for(int i = 0;i < morphCount;i++) { mMorphs[i].mKeyFrames = std::make_shared(); - mMorphs[i].mKeyFrames->read(nif, true); + mMorphs[i].mKeyFrames->read(nif, true, /*morph*/true); nif->getVector3s(mMorphs[i].mVertices, vertCount); } } @@ -393,4 +458,17 @@ void NiPalette::read(NIFStream *nif) colors[i] = nif->getUInt() | alphaMask; } +void NiStringPalette::read(NIFStream *nif) +{ + palette = nif->getString(); + if (nif->getUInt() != palette.size()) + nif->file->warn("Failed size check in NiStringPalette"); +} + +void NiBoolData::read(NIFStream *nif) +{ + mKeyList = std::make_shared(); + mKeyList->read(nif); +} + } // Namespace diff --git a/components/nif/data.hpp b/components/nif/data.hpp index 0ba544645..35f329573 100644 --- a/components/nif/data.hpp +++ b/components/nif/data.hpp @@ -174,6 +174,7 @@ class NiSkinInstance : public Record { public: NiSkinDataPtr data; + NiSkinPartitionPtr partitions; NodePtr root; NodeList bones; @@ -200,6 +201,25 @@ public: Transformation trafo; std::vector bones; + NiSkinPartitionPtr partitions; + + void read(NIFStream *nif) override; + void post(NIFFile *nif) override; +}; + +struct NiSkinPartition : public Record +{ + struct Partition + { + std::vector bones; + std::vector vertexMap; + std::vector weights; + std::vector> strips; + std::vector triangles; + std::vector boneIndices; + void read(NIFStream *nif); + }; + std::vector data; void read(NIFStream *nif) override; }; @@ -240,5 +260,17 @@ public: void read(NIFStream *nif) override; }; +struct NiStringPalette : public Record +{ + std::string palette; + void read(NIFStream *nif) override; +}; + +struct NiBoolData : public Record +{ + ByteKeyMapPtr mKeyList; + void read(NIFStream *nif) override; +}; + } // Namespace #endif diff --git a/components/nif/extra.cpp b/components/nif/extra.cpp index d08e5d738..eeaf9d3ac 100644 --- a/components/nif/extra.cpp +++ b/components/nif/extra.cpp @@ -80,5 +80,11 @@ void NiFloatsExtraData::read(NIFStream *nif) nif->getFloats(data, num); } +void BSBound::read(NIFStream *nif) +{ + Extra::read(nif); + center = nif->getVector3(); + halfExtents = nif->getVector3(); +} } diff --git a/components/nif/extra.hpp b/components/nif/extra.hpp index 3be627004..5d7aa0c3b 100644 --- a/components/nif/extra.hpp +++ b/components/nif/extra.hpp @@ -109,5 +109,12 @@ struct NiFloatsExtraData : public Extra void read(NIFStream *nif) override; }; +struct BSBound : public Extra +{ + osg::Vec3f center, halfExtents; + + void read(NIFStream *nif) override; +}; + } // Namespace #endif diff --git a/components/nif/niffile.cpp b/components/nif/niffile.cpp index 8d65753d2..7915908d1 100644 --- a/components/nif/niffile.cpp +++ b/components/nif/niffile.cpp @@ -1,6 +1,7 @@ #include "niffile.hpp" #include "effect.hpp" +#include #include #include @@ -113,6 +114,19 @@ static std::map makeFactory() factory["NiColorExtraData"] = {&construct , RC_NiColorExtraData }; factory["NiFloatExtraData"] = {&construct , RC_NiFloatExtraData }; factory["NiFloatsExtraData"] = {&construct , RC_NiFloatsExtraData }; + factory["NiStringPalette"] = {&construct , RC_NiStringPalette }; + factory["NiBoolData"] = {&construct , RC_NiBoolData }; + factory["NiSkinPartition"] = {&construct , RC_NiSkinPartition }; + factory["BSXFlags"] = {&construct , RC_BSXFlags }; + factory["BSBound"] = {&construct , RC_BSBound }; + factory["NiTransformData"] = {&construct , RC_NiKeyframeData }; + factory["BSFadeNode"] = {&construct , RC_NiNode }; + factory["bhkBlendController"] = {&construct , RC_bhkBlendController }; + factory["NiFloatInterpolator"] = {&construct , RC_NiFloatInterpolator }; + factory["NiBoolInterpolator"] = {&construct , RC_NiBoolInterpolator }; + factory["NiPoint3Interpolator"] = {&construct , RC_NiPoint3Interpolator }; + factory["NiTransformController"] = {&construct , RC_NiKeyframeController }; + factory["NiTransformInterpolator"] = {&construct , RC_NiTransformInterpolator }; return factory; } @@ -137,15 +151,45 @@ void NIFFile::parse(Files::IStreamPtr stream) // Check the header string std::string head = nif.getVersionString(); - if(head.compare(0, 22, "NetImmerse File Format") != 0) + static const std::array verStrings = + { + "NetImmerse File Format", + "Gamebryo File Format" + }; + bool supported = false; + for (const std::string& verString : verStrings) + { + supported = (head.compare(0, verString.size(), verString) == 0); + if (supported) + break; + } + if (!supported) fail("Invalid NIF header: " + head); + supported = false; + // Get BCD version ver = nif.getUInt(); // 4.0.0.0 is an older, practically identical version of the format. // It's not used by Morrowind assets but Morrowind supports it. - if(ver != NIFStream::generateVersion(4,0,0,0) && ver != VER_MW) - fail("Unsupported NIF version: " + printVersion(ver)); + static const std::array supportedVers = + { + NIFStream::generateVersion(4,0,0,0), + VER_MW + }; + for (uint32_t supportedVer : supportedVers) + { + supported = (ver == supportedVer); + if (supported) + break; + } + if (!supported) + { + if (sLoadUnsupportedFiles) + warn("Unsupported NIF version: " + printVersion(ver) + ". Proceed with caution!"); + else + fail("Unsupported NIF version: " + printVersion(ver)); + } // NIF data endianness if (ver >= NIFStream::generateVersion(20,0,0,4)) @@ -160,7 +204,7 @@ void NIFFile::parse(Files::IStreamPtr stream) userVer = nif.getUInt(); // Number of records - size_t recNum = nif.getUInt(); + unsigned int recNum = nif.getUInt(); records.resize(recNum); // Bethesda stream header @@ -212,7 +256,7 @@ void NIFFile::parse(Files::IStreamPtr stream) } const bool hasRecordSeparators = ver >= NIFStream::generateVersion(10,0,0,0) && ver < NIFStream::generateVersion(10,2,0,0); - for(size_t i = 0;i < recNum;i++) + for (unsigned int i = 0; i < recNum; i++) { Record *r = nullptr; @@ -245,6 +289,9 @@ void NIFFile::parse(Files::IStreamPtr stream) else fail("Unknown record type " + rec); + if (!supported) + Log(Debug::Verbose) << "NIF Debug: Reading record of type " << rec << ", index " << i << " (" << filename << ")"; + assert(r != nullptr); assert(r->recType != RC_MISSING); r->recName = rec; @@ -253,11 +300,11 @@ void NIFFile::parse(Files::IStreamPtr stream) r->read(&nif); } - size_t rootNum = nif.getUInt(); + unsigned int rootNum = nif.getUInt(); roots.resize(rootNum); //Determine which records are roots - for(size_t i = 0;i < rootNum;i++) + for (unsigned int i = 0; i < rootNum; i++) { int idx = nif.getInt(); if (idx >= 0 && idx < int(records.size())) @@ -286,4 +333,11 @@ bool NIFFile::getUseSkinning() const return mUseSkinning; } +bool NIFFile::sLoadUnsupportedFiles = false; + +void NIFFile::setLoadUnsupportedFiles(bool load) +{ + sLoadUnsupportedFiles = load; +} + } diff --git a/components/nif/niffile.hpp b/components/nif/niffile.hpp index 9d8edac26..c6dd8af75 100644 --- a/components/nif/niffile.hpp +++ b/components/nif/niffile.hpp @@ -62,6 +62,8 @@ class NIFFile final : public File bool mUseSkinning = false; + static bool sLoadUnsupportedFiles; + /// Parse the file void parse(Files::IStreamPtr stream); @@ -149,6 +151,8 @@ public: /// Get the Bethesda version of the NIF format used unsigned int getBethVersion() const override { return bethVer; } + + static void setLoadUnsupportedFiles(bool load); }; using NIFFilePtr = std::shared_ptr; diff --git a/components/nif/nifkey.hpp b/components/nif/nifkey.hpp index 4c10327e1..de2fa31c8 100644 --- a/components/nif/nifkey.hpp +++ b/components/nif/nifkey.hpp @@ -52,16 +52,27 @@ struct KeyMapT { MapType mKeys; //Read in a KeyGroup (see http://niftools.sourceforge.net/doc/nif/NiKeyframeData.html) - void read(NIFStream *nif, bool force=false) + void read(NIFStream *nif, bool force = false, bool morph = false) { assert(nif); mInterpolationType = InterpolationType_Unknown; + if (morph && nif->getVersion() >= NIFStream::generateVersion(10,1,0,106)) + nif->getString(); // Frame name + size_t count = nif->getUInt(); - if(count == 0 && !force) + if (count == 0 && !force && !morph) return; + if (morph && nif->getVersion() > NIFStream::generateVersion(10,1,0,0)) + { + if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,104) && + nif->getVersion() <= NIFStream::generateVersion(20,1,0,2) && nif->getBethVersion() < 10) + nif->getFloat(); // Legacy weight + return; + } + mKeys.clear(); mInterpolationType = nif->getUInt(); diff --git a/components/nif/node.hpp b/components/nif/node.hpp index 72adfe06c..0b958d2c2 100644 --- a/components/nif/node.hpp +++ b/components/nif/node.hpp @@ -16,6 +16,117 @@ namespace Nif struct NiNode; +struct NiBoundingVolume +{ + enum Type + { + SPHERE_BV = 0, + BOX_BV = 1, + CAPSULE_BV = 2, + LOZENGE_BV = 3, + UNION_BV = 4, + HALFSPACE_BV = 5 + }; + + struct NiSphereBV + { + osg::Vec3f center; + float radius{0.f}; + }; + + struct NiBoxBV + { + osg::Vec3f center; + Matrix3 axis; + osg::Vec3f extents; + }; + + struct NiCapsuleBV + { + osg::Vec3f center, axis; + float extent{0.f}, radius{0.f}; + }; + + struct NiLozengeBV + { + float radius{0.f}, extent0{0.f}, extent1{0.f}; + osg::Vec3f center, axis0, axis1; + }; + + struct NiHalfSpaceBV + { + osg::Vec3f center, normal; + }; + + unsigned int type; + NiSphereBV sphere; + NiBoxBV box; + NiCapsuleBV capsule; + NiLozengeBV lozenge; + std::vector children; + NiHalfSpaceBV plane; + void read(NIFStream* nif) + { + type = nif->getUInt(); + switch (type) + { + case SPHERE_BV: + { + sphere.center = nif->getVector3(); + sphere.radius = nif->getFloat(); + break; + } + case BOX_BV: + { + box.center = nif->getVector3(); + box.axis = nif->getMatrix3(); + box.extents = nif->getVector3(); + break; + } + case CAPSULE_BV: + { + capsule.center = nif->getVector3(); + capsule.axis = nif->getVector3(); + capsule.extent = nif->getFloat(); + capsule.radius = nif->getFloat(); + break; + } + case LOZENGE_BV: + { + lozenge.radius = nif->getFloat(); + lozenge.extent0 = nif->getFloat(); + lozenge.extent1 = nif->getFloat(); + lozenge.center = nif->getVector3(); + lozenge.axis0 = nif->getVector3(); + lozenge.axis1 = nif->getVector3(); + break; + } + case UNION_BV: + { + unsigned int numChildren = nif->getUInt(); + if (numChildren == 0) + break; + children.resize(numChildren); + for (NiBoundingVolume& child : children) + child.read(nif); + break; + } + case HALFSPACE_BV: + { + plane.center = nif->getVector3(); + plane.normal = nif->getVector3(); + break; + } + default: + { + std::stringstream error; + error << "Unhandled NiBoundingVolume type: " << type; + nif->file->fail(error.str()); + } + } + } +}; + /** A Node is an object that's part of the main NIF tree. It has parent node (unless it's the root), and transformation (location and rotation) relative to it's parent. @@ -31,9 +142,7 @@ public: // Bounding box info bool hasBounds{false}; - osg::Vec3f boundPos; - Matrix3 boundRot; - osg::Vec3f boundXYZ; // Box size + NiBoundingVolume bounds; void read(NIFStream *nif) override { @@ -48,13 +157,8 @@ public: if (nif->getVersion() <= NIFStream::generateVersion(4,2,2,0)) hasBounds = nif->getBoolean(); - if(hasBounds) - { - nif->getInt(); // always 1 - boundPos = nif->getVector3(); - boundRot = nif->getMatrix3(); - boundXYZ = nif->getVector3(); - } + if (hasBounds) + bounds.read(nif); // Reference to the collision object in Gamebryo files. if (nif->getVersion() >= NIFStream::generateVersion(10,0,1,0)) nif->skip(4); diff --git a/components/nif/record.hpp b/components/nif/record.hpp index f9bb613a0..c5773643a 100644 --- a/components/nif/record.hpp +++ b/components/nif/record.hpp @@ -109,18 +109,28 @@ enum RecordType RC_NiVectorExtraData, RC_NiColorExtraData, RC_NiFloatExtraData, - RC_NiFloatsExtraData + RC_NiFloatsExtraData, + RC_NiStringPalette, + RC_NiBoolData, + RC_NiSkinPartition, + RC_BSXFlags, + RC_BSBound, + RC_bhkBlendController, + RC_NiFloatInterpolator, + RC_NiPoint3Interpolator, + RC_NiBoolInterpolator, + RC_NiTransformInterpolator, }; /// Base class for all records struct Record { // Record type and type name - int recType; + int recType{RC_MISSING}; std::string recName; - size_t recIndex; + unsigned int recIndex{~0u}; - Record() : recType(RC_MISSING), recIndex(~(size_t)0) {} + Record() = default; /// Parses the record from file virtual void read(NIFStream *nif) = 0; diff --git a/components/nif/recordptr.hpp b/components/nif/recordptr.hpp index 57607cb6a..b40a17583 100644 --- a/components/nif/recordptr.hpp +++ b/components/nif/recordptr.hpp @@ -143,6 +143,11 @@ class NiAutoNormalParticlesData; class NiPalette; struct NiParticleModifier; struct NiLinesData; +struct NiBoolData; +struct NiSkinPartition; +struct NiFloatInterpolator; +struct NiPoint3Interpolator; +struct NiTransformInterpolator; using NodePtr = RecordPtrT; using ExtraPtr = RecordPtrT; @@ -166,11 +171,17 @@ using NiRotatingParticlesDataPtr = RecordPtrT; using NiAutoNormalParticlesDataPtr = RecordPtrT; using NiPalettePtr = RecordPtrT; using NiParticleModifierPtr = RecordPtrT; +using NiBoolDataPtr = RecordPtrT; +using NiSkinPartitionPtr = RecordPtrT; +using NiFloatInterpolatorPtr = RecordPtrT; +using NiPoint3InterpolatorPtr = RecordPtrT; +using NiTransformInterpolatorPtr = RecordPtrT; using NodeList = RecordListT; using PropertyList = RecordListT; using ExtraList = RecordListT; using NiSourceTextureList = RecordListT; +using NiFloatInterpolatorList = RecordListT; } // Namespace #endif diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index 15834ffad..b1461e536 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -8,6 +8,7 @@ #include +#include #include #include @@ -24,11 +25,6 @@ osg::Matrixf getWorldTransform(const Nif::Node *node) return node->trafo.toMatrix(); } -btVector3 getbtVector(const osg::Vec3f &v) -{ - return btVector3(v.x(), v.y(), v.z()); -} - bool pathFileNameStartsWithX(const std::string& path) { const std::size_t slashpos = path.find_last_of("/\\"); @@ -36,7 +32,7 @@ bool pathFileNameStartsWithX(const std::string& path) return letterPos < path.size() && (path[letterPos] == 'x' || path[letterPos] == 'X'); } -void fillTriangleMeshWithTransform(btTriangleMesh& mesh, const Nif::NiTriShapeData& data, const osg::Matrixf &transform) +void fillTriangleMesh(btTriangleMesh& mesh, const Nif::NiTriShapeData& data, const osg::Matrixf &transform) { mesh.preallocateVertices(static_cast(data.vertices.size())); mesh.preallocateIndices(static_cast(data.triangles.size())); @@ -47,20 +43,20 @@ void fillTriangleMeshWithTransform(btTriangleMesh& mesh, const Nif::NiTriShapeDa for (std::size_t i = 0; i < triangles.size(); i += 3) { mesh.addTriangle( - getbtVector(vertices[triangles[i + 0]] * transform), - getbtVector(vertices[triangles[i + 1]] * transform), - getbtVector(vertices[triangles[i + 2]] * transform) + Misc::Convert::toBullet(vertices[triangles[i + 0]] * transform), + Misc::Convert::toBullet(vertices[triangles[i + 1]] * transform), + Misc::Convert::toBullet(vertices[triangles[i + 2]] * transform) ); } } -void fillTriangleMeshWithTransform(btTriangleMesh& mesh, const Nif::NiTriStripsData& data, const osg::Matrixf &transform) +void fillTriangleMesh(btTriangleMesh& mesh, const Nif::NiTriStripsData& data, const osg::Matrixf &transform) { const std::vector &vertices = data.vertices; const std::vector> &strips = data.strips; if (vertices.empty() || strips.empty()) return; - mesh.preallocateVertices(static_cast(data.vertices.size())); + mesh.preallocateVertices(static_cast(vertices.size())); int numTriangles = 0; for (const std::vector& strip : strips) { @@ -88,17 +84,17 @@ void fillTriangleMeshWithTransform(btTriangleMesh& mesh, const Nif::NiTriStripsD if (i%2==0) { mesh.addTriangle( - getbtVector(vertices[a] * transform), - getbtVector(vertices[b] * transform), - getbtVector(vertices[c] * transform) + Misc::Convert::toBullet(vertices[a] * transform), + Misc::Convert::toBullet(vertices[b] * transform), + Misc::Convert::toBullet(vertices[c] * transform) ); } else { mesh.addTriangle( - getbtVector(vertices[a] * transform), - getbtVector(vertices[c] * transform), - getbtVector(vertices[b] * transform) + Misc::Convert::toBullet(vertices[a] * transform), + Misc::Convert::toBullet(vertices[c] * transform), + Misc::Convert::toBullet(vertices[b] * transform) ); } } @@ -106,17 +102,12 @@ void fillTriangleMeshWithTransform(btTriangleMesh& mesh, const Nif::NiTriStripsD } } -void fillTriangleMeshWithTransform(btTriangleMesh& mesh, const Nif::Node* nifNode, const osg::Matrixf &transform) +void fillTriangleMesh(btTriangleMesh& mesh, const Nif::Node* nifNode, const osg::Matrixf &transform = osg::Matrixf()) { if (nifNode->recType == Nif::RC_NiTriShape) - fillTriangleMeshWithTransform(mesh, static_cast(nifNode)->data.get(), transform); - else // if (nifNode->recType == Nif::RC_NiTriStrips) - fillTriangleMeshWithTransform(mesh, static_cast(nifNode)->data.get(), transform); -} - -void fillTriangleMesh(btTriangleMesh& mesh, const Nif::Node* node) -{ - fillTriangleMeshWithTransform(mesh, node, osg::Matrixf()); + fillTriangleMesh(mesh, static_cast(nifNode)->data.get(), transform); + else if (nifNode->recType == Nif::RC_NiTriStrips) + fillTriangleMesh(mesh, static_cast(nifNode)->data.get(), transform); } } @@ -141,18 +132,21 @@ osg::ref_ptr BulletNifLoader::load(const Nif::File& nif) if ((node = dynamic_cast(r))) break; } + const std::string filename = nif.getFilename(); if (!node) { - warn("Found no root nodes in NIF."); + warn("Found no root nodes in NIF file " + filename); return mShape; } - if (findBoundingBox(node)) + if (findBoundingBox(node, filename)) { + const btVector3 halfExtents = Misc::Convert::toBullet(mShape->mCollisionBoxHalfExtents); + const btVector3 origin = Misc::Convert::toBullet(mShape->mCollisionBoxTranslate); std::unique_ptr compound (new btCompoundShape); - std::unique_ptr boxShape(new btBoxShape(getbtVector(mShape->mCollisionBoxHalfExtents))); + std::unique_ptr boxShape(new btBoxShape(halfExtents)); btTransform transform = btTransform::getIdentity(); - transform.setOrigin(getbtVector(mShape->mCollisionBoxTranslate)); + transform.setOrigin(origin); compound->addChildShape(transform, boxShape.get()); boxShape.release(); @@ -165,7 +159,6 @@ osg::ref_ptr BulletNifLoader::load(const Nif::File& nif) // files with the name convention xmodel.nif usually have keyframes stored in a separate file xmodel.kf (see Animation::addAnimSource). // assume all nodes in the file will be animated - const auto filename = nif.getFilename(); const bool isAnimated = pathFileNameStartsWithX(filename); handleNode(filename, node, 0, autogenerated, isAnimated, autogenerated); @@ -201,12 +194,25 @@ osg::ref_ptr BulletNifLoader::load(const Nif::File& nif) // Find a boundingBox in the node hierarchy. // Return: use bounding box for collision? -bool BulletNifLoader::findBoundingBox(const Nif::Node* node) +bool BulletNifLoader::findBoundingBox(const Nif::Node* node, const std::string& filename) { if (node->hasBounds) { - mShape->mCollisionBoxHalfExtents = node->boundXYZ; - mShape->mCollisionBoxTranslate = node->boundPos; + unsigned int type = node->bounds.type; + switch (type) + { + case Nif::NiBoundingVolume::Type::BOX_BV: + mShape->mCollisionBoxHalfExtents = node->bounds.box.extents; + mShape->mCollisionBoxTranslate = node->bounds.box.center; + break; + default: + { + std::stringstream warning; + warning << "Unsupported NiBoundingVolume type " << type << " in node " << node->recIndex; + warning << " in file " << filename; + warn(warning.str()); + } + } if (node->flags & Nif::NiNode::Flag_BBoxCollision) { @@ -222,7 +228,7 @@ bool BulletNifLoader::findBoundingBox(const Nif::Node* node) { if(!list[i].empty()) { - bool found = findBoundingBox (list[i].getPtr()); + bool found = findBoundingBox (list[i].getPtr(), filename); if (found) return true; } @@ -383,7 +389,7 @@ void BulletNifLoader::handleNiTriShape(const Nif::Node *nifNode, int flags, cons if (!mAvoidStaticMesh) mAvoidStaticMesh.reset(new btTriangleMesh(false)); - fillTriangleMeshWithTransform(*mAvoidStaticMesh, nifNode, transform); + fillTriangleMesh(*mAvoidStaticMesh, nifNode, transform); } else { @@ -391,7 +397,7 @@ void BulletNifLoader::handleNiTriShape(const Nif::Node *nifNode, int flags, cons mStaticMesh.reset(new btTriangleMesh(false)); // Static shape, just transform all vertices into position - fillTriangleMeshWithTransform(*mStaticMesh, nifNode, transform); + fillTriangleMesh(*mStaticMesh, nifNode, transform); } } diff --git a/components/nifbullet/bulletnifloader.hpp b/components/nifbullet/bulletnifloader.hpp index e423e5149..054b33fed 100644 --- a/components/nifbullet/bulletnifloader.hpp +++ b/components/nifbullet/bulletnifloader.hpp @@ -40,7 +40,7 @@ class BulletNifLoader public: void warn(const std::string &msg) { - Log(Debug::Warning) << "NIFLoader: Warn:" << msg; + Log(Debug::Warning) << "NIFLoader: Warn: " << msg; } void fail(const std::string &msg) @@ -52,7 +52,7 @@ public: osg::ref_ptr load(const Nif::File& file); private: - bool findBoundingBox(const Nif::Node* node); + bool findBoundingBox(const Nif::Node* node, const std::string& filename); void handleNode(const std::string& fileName, Nif::Node const *node, int flags, bool isCollisionNode, bool isAnimated=false, bool autogenerated=false, bool avoid=false); diff --git a/components/nifosg/controller.cpp b/components/nifosg/controller.cpp index 0f4c4a5bd..64e9f7de6 100644 --- a/components/nifosg/controller.cpp +++ b/components/nifosg/controller.cpp @@ -92,6 +92,26 @@ KeyframeController::KeyframeController(const Nif::NiKeyframeData *data) { } +KeyframeController::KeyframeController(const Nif::NiTransformInterpolator* interpolator) + : mRotations(interpolator->data->mRotations, interpolator->defaultRot) + , mXRotations(interpolator->data->mXRotations, 0.f) + , mYRotations(interpolator->data->mYRotations, 0.f) + , mZRotations(interpolator->data->mZRotations, 0.f) + , mTranslations(interpolator->data->mTranslations, interpolator->defaultPos) + , mScales(interpolator->data->mScales, interpolator->defaultScale) +{ +} + +KeyframeController::KeyframeController(const float scale, const osg::Vec3f& pos, const osg::Quat& rot) + : mRotations(Nif::QuaternionKeyMapPtr(), rot) + , mXRotations(Nif::FloatKeyMapPtr(), 0.f) + , mYRotations(Nif::FloatKeyMapPtr(), 0.f) + , mZRotations(Nif::FloatKeyMapPtr(), 0.f) + , mTranslations(Nif::Vector3KeyMapPtr(), pos) + , mScales(Nif::FloatKeyMapPtr(), scale) +{ +} + osg::Quat KeyframeController::getXYZRotation(float time) const { float xrot = 0, yrot = 0, zrot = 0; @@ -177,10 +197,25 @@ GeomMorpherController::GeomMorpherController(const GeomMorpherController ©, { } -GeomMorpherController::GeomMorpherController(const Nif::NiMorphData *data) +GeomMorpherController::GeomMorpherController(const Nif::NiGeomMorpherController* ctrl) { - for (unsigned int i=0; imMorphs.size(); ++i) - mKeyFrames.push_back(FloatInterpolator(data->mMorphs[i].mKeyFrames)); + if (ctrl->interpolators.length() == 0) + { + if (ctrl->data.empty()) + return; + for (const auto& morph : ctrl->data->mMorphs) + mKeyFrames.emplace_back(morph.mKeyFrames); + } + else + { + for (size_t i = 0; i < ctrl->interpolators.length(); ++i) + { + if (!ctrl->interpolators[i].empty()) + mKeyFrames.emplace_back(ctrl->interpolators[i].getPtr()); + else + mKeyFrames.emplace_back(); + } + } } void GeomMorpherController::update(osg::NodeVisitor *nv, osg::Drawable *drawable) @@ -313,6 +348,11 @@ RollController::RollController(const Nif::NiFloatData *data) { } +RollController::RollController(const Nif::NiFloatInterpolator* interpolator) + : mData(interpolator) +{ +} + RollController::RollController(const RollController ©, const osg::CopyOp ©op) : osg::NodeCallback(copy, copyop) , Controller(copy) @@ -344,6 +384,10 @@ void RollController::operator() (osg::Node* node, osg::NodeVisitor* nv) } } +AlphaController::AlphaController() +{ +} + AlphaController::AlphaController(const Nif::NiFloatData *data, const osg::Material* baseMaterial) : mData(data->mKeyList, 1.f) , mBaseMaterial(baseMaterial) @@ -351,7 +395,9 @@ AlphaController::AlphaController(const Nif::NiFloatData *data, const osg::Materi } -AlphaController::AlphaController() +AlphaController::AlphaController(const Nif::NiFloatInterpolator* interpolator, const osg::Material* baseMaterial) + : mData(interpolator) + , mBaseMaterial(baseMaterial) { } @@ -379,6 +425,10 @@ void AlphaController::apply(osg::StateSet *stateset, osg::NodeVisitor *nv) } } +MaterialColorController::MaterialColorController() +{ +} + MaterialColorController::MaterialColorController(const Nif::NiPosData *data, TargetColor color, const osg::Material* baseMaterial) : mData(data->mKeyList, osg::Vec3f(1,1,1)) , mTargetColor(color) @@ -386,7 +436,10 @@ MaterialColorController::MaterialColorController(const Nif::NiPosData *data, Tar { } -MaterialColorController::MaterialColorController() +MaterialColorController::MaterialColorController(const Nif::NiPoint3Interpolator* interpolator, TargetColor color, const osg::Material* baseMaterial) + : mData(interpolator) + , mTargetColor(color) + , mBaseMaterial(baseMaterial) { } @@ -448,6 +501,8 @@ FlipController::FlipController(const Nif::NiFlipController *ctrl, const std::vec , mDelta(ctrl->mDelta) , mTextures(textures) { + if (!ctrl->mInterpolator.empty()) + mData = ctrl->mInterpolator.getPtr(); } FlipController::FlipController(int texSlot, float delta, const std::vector >& textures) @@ -463,14 +518,19 @@ FlipController::FlipController(const FlipController ©, const osg::CopyOp &co , mTexSlot(copy.mTexSlot) , mDelta(copy.mDelta) , mTextures(copy.mTextures) + , mData(copy.mData) { } void FlipController::apply(osg::StateSet* stateset, osg::NodeVisitor* nv) { - if (hasInput() && mDelta != 0 && !mTextures.empty()) + if (hasInput() && !mTextures.empty()) { - int curTexture = int(getInputValue(nv) / mDelta) % mTextures.size(); + int curTexture = 0; + if (mDelta != 0) + curTexture = int(getInputValue(nv) / mDelta) % mTextures.size(); + else + curTexture = int(mData.interpKey(getInputValue(nv))) % mTextures.size(); stateset->setTextureAttribute(mTexSlot, mTextures[curTexture]); } } diff --git a/components/nifosg/controller.hpp b/components/nifosg/controller.hpp index c087e635f..996e4ef97 100644 --- a/components/nifosg/controller.hpp +++ b/components/nifosg/controller.hpp @@ -10,6 +10,7 @@ #include #include +#include #include @@ -59,6 +60,31 @@ namespace NifOsg ValueInterpolator() = default; + template< + class T, + typename = std::enable_if_t< + std::conjunction_v< + std::disjunction< + std::is_same, + std::is_same + >, + std::is_same + >, + T + > + > + ValueInterpolator(const T* interpolator) : mDefaultVal(interpolator->defaultVal) + { + if (interpolator->data.empty()) + return; + mKeys = interpolator->data->mKeyList; + if (mKeys) + { + mLastLowKey = mKeys->mKeys.end(); + mLastHighKey = mKeys->mKeys.end(); + } + } + ValueInterpolator(std::shared_ptr keys, ValueT defaultVal = ValueT()) : mKeys(keys) , mDefaultVal(defaultVal) @@ -188,7 +214,7 @@ namespace NifOsg class GeomMorpherController : public osg::Drawable::UpdateCallback, public SceneUtil::Controller { public: - GeomMorpherController(const Nif::NiMorphData* data); + GeomMorpherController(const Nif::NiGeomMorpherController* ctrl); GeomMorpherController(); GeomMorpherController(const GeomMorpherController& copy, const osg::CopyOp& copyop); @@ -203,7 +229,14 @@ namespace NifOsg class KeyframeController : public osg::NodeCallback, public SceneUtil::Controller { public: + // This is used if there's no interpolator but there is data (Morrowind meshes). KeyframeController(const Nif::NiKeyframeData *data); + // This is used if the interpolator has data. + KeyframeController(const Nif::NiTransformInterpolator* interpolator); + // This is used if there are default values available (e.g. from a data-less interpolator). + // If there's neither keyframe data nor an interpolator a KeyframeController must not be created. + KeyframeController(const float scale, const osg::Vec3f& pos, const osg::Quat& rot); + KeyframeController(); KeyframeController(const KeyframeController& copy, const osg::CopyOp& copyop); @@ -272,6 +305,7 @@ namespace NifOsg public: RollController(const Nif::NiFloatData *data); + RollController(const Nif::NiFloatInterpolator* interpolator); RollController() = default; RollController(const RollController& copy, const osg::CopyOp& copyop); @@ -287,6 +321,7 @@ namespace NifOsg osg::ref_ptr mBaseMaterial; public: AlphaController(const Nif::NiFloatData *data, const osg::Material* baseMaterial); + AlphaController(const Nif::NiFloatInterpolator* interpolator, const osg::Material* baseMaterial); AlphaController(); AlphaController(const AlphaController& copy, const osg::CopyOp& copyop); @@ -308,6 +343,7 @@ namespace NifOsg Emissive = 3 }; MaterialColorController(const Nif::NiPosData *data, TargetColor color, const osg::Material* baseMaterial); + MaterialColorController(const Nif::NiPoint3Interpolator* interpolator, TargetColor color, const osg::Material* baseMaterial); MaterialColorController(); MaterialColorController(const MaterialColorController& copy, const osg::CopyOp& copyop); @@ -329,6 +365,7 @@ namespace NifOsg int mTexSlot{0}; float mDelta{0.f}; std::vector > mTextures; + FloatInterpolator mData; public: FlipController(const Nif::NiFlipController* ctrl, const std::vector >& textures); diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 65f62346d..a5a61b317 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -43,7 +43,6 @@ #include #include "matrixtransform.hpp" -#include "nodeindexholder.hpp" #include "particle.hpp" namespace @@ -61,6 +60,18 @@ namespace } } + bool isTypeGeometry(int type) + { + switch (type) + { + case Nif::RC_NiTriShape: + case Nif::RC_NiTriStrips: + case Nif::RC_NiLines: + return true; + } + return false; + } + // Collect all properties affecting the given drawable that should be handled on drawable basis rather than on the node hierarchy above it. void collectDrawableProperties(const Nif::Node* nifNode, std::vector& out) { @@ -270,10 +281,10 @@ namespace NifOsg const Nif::NiStringExtraData *strdata = static_cast(extra.getPtr()); const Nif::NiKeyframeController *key = static_cast(ctrl.getPtr()); - if(key->data.empty()) + if (key->data.empty() && key->interpolator.empty()) continue; - osg::ref_ptr callback(new NifOsg::KeyframeController(key->data.getPtr())); + osg::ref_ptr callback(handleKeyframeController(key)); callback->setFunction(std::shared_ptr(new NifOsg::ControllerFunction(key))); if (!target.mKeyframeControllers.emplace(strdata->string, callback).second) @@ -527,9 +538,21 @@ namespace NifOsg // - finding the correct emitter node for a particle system // - establishing connections to the animated collision shapes, which are handled in a separate loader // - finding a random child NiNode in NiBspArrayController - node->getOrCreateUserDataContainer()->addUserObject(new NodeIndexHolder(nifNode->recIndex)); + node->setUserValue("recIndex", nifNode->recIndex); + + std::vector extraCollection; for (Nif::ExtraPtr e = nifNode->extra; !e.empty(); e = e->next) + extraCollection.emplace_back(e); + + for (size_t i = 0; i < nifNode->extralist.length(); ++i) + { + Nif::ExtraPtr e = nifNode->extralist[i]; + if (!e.empty()) + extraCollection.emplace_back(e); + } + + for (const auto& e : extraCollection) { if(e->recType == Nif::RC_NiTextKeyExtraData && textKeys) { @@ -585,7 +608,7 @@ namespace NifOsg applyNodeProperties(nifNode, node, composite, imageManager, boundTextures, animflags); - const bool isGeometry = nifNode->recType == Nif::RC_NiTriShape || nifNode->recType == Nif::RC_NiTriStrips || nifNode->recType == Nif::RC_NiLines; + const bool isGeometry = isTypeGeometry(nifNode->recType); if (isGeometry && !skipMeshes) { @@ -702,6 +725,24 @@ namespace NifOsg } } + static osg::ref_ptr handleKeyframeController(const Nif::NiKeyframeController* keyctrl) + { + osg::ref_ptr ctrl; + if (!keyctrl->interpolator.empty()) + { + const Nif::NiTransformInterpolator* interp = keyctrl->interpolator.getPtr(); + if (!interp->data.empty()) + ctrl = new NifOsg::KeyframeController(interp); + else + ctrl = new NifOsg::KeyframeController(interp->defaultScale, interp->defaultPos, interp->defaultRot); + } + else if (!keyctrl->data.empty()) + { + ctrl = new NifOsg::KeyframeController(keyctrl->data.getPtr()); + } + return ctrl; + } + void handleNodeControllers(const Nif::Node* nifNode, osg::Node* node, int animflags, bool& isAnimated) { for (Nif::ControllerPtr ctrl = nifNode->controller; !ctrl.empty(); ctrl = ctrl->next) @@ -711,9 +752,9 @@ namespace NifOsg if (ctrl->recType == Nif::RC_NiKeyframeController) { const Nif::NiKeyframeController *key = static_cast(ctrl.getPtr()); - if (key->data.empty()) + if (key->data.empty() && key->interpolator.empty()) continue; - osg::ref_ptr callback(new KeyframeController(key->data.getPtr())); + osg::ref_ptr callback(handleKeyframeController(key)); setupController(key, callback, animflags); node->addUpdateCallback(callback); isAnimated = true; @@ -740,9 +781,13 @@ namespace NifOsg else if (ctrl->recType == Nif::RC_NiRollController) { const Nif::NiRollController *rollctrl = static_cast(ctrl.getPtr()); - if (rollctrl->data.empty()) + if (rollctrl->data.empty() && rollctrl->interpolator.empty()) continue; - osg::ref_ptr callback(new RollController(rollctrl->data.getPtr())); + osg::ref_ptr callback; + if (!rollctrl->interpolator.empty()) + callback = new RollController(rollctrl->interpolator.getPtr()); + else // if (!rollctrl->data.empty()) + callback = new RollController(rollctrl->data.getPtr()); setupController(rollctrl, callback, animflags); node->addUpdateCallback(callback); isAnimated = true; @@ -768,19 +813,27 @@ namespace NifOsg if (ctrl->recType == Nif::RC_NiAlphaController) { const Nif::NiAlphaController* alphactrl = static_cast(ctrl.getPtr()); - if (alphactrl->data.empty()) + if (alphactrl->data.empty() && alphactrl->interpolator.empty()) continue; - osg::ref_ptr osgctrl(new AlphaController(alphactrl->data.getPtr(), baseMaterial)); + osg::ref_ptr osgctrl; + if (!alphactrl->interpolator.empty()) + osgctrl = new AlphaController(alphactrl->interpolator.getPtr(), baseMaterial); + else // if (!alphactrl->data.empty()) + osgctrl = new AlphaController(alphactrl->data.getPtr(), baseMaterial); setupController(alphactrl, osgctrl, animflags); composite->addController(osgctrl); } else if (ctrl->recType == Nif::RC_NiMaterialColorController) { const Nif::NiMaterialColorController* matctrl = static_cast(ctrl.getPtr()); - if (matctrl->data.empty()) + if (matctrl->data.empty() && matctrl->interpolator.empty()) continue; + osg::ref_ptr osgctrl; auto targetColor = static_cast(matctrl->targetColor); - osg::ref_ptr osgctrl(new MaterialColorController(matctrl->data.getPtr(), targetColor, baseMaterial)); + if (!matctrl->interpolator.empty()) + osgctrl = new MaterialColorController(matctrl->interpolator.getPtr(), targetColor, baseMaterial); + else // if (!matctrl->data.empty()) + osgctrl = new MaterialColorController(matctrl->data.getPtr(), targetColor, baseMaterial); setupController(matctrl, osgctrl, animflags); composite->addController(osgctrl); } @@ -1176,7 +1229,7 @@ namespace NifOsg void handleGeometry(const Nif::Node* nifNode, osg::Group* parentNode, SceneUtil::CompositeStateSetUpdater* composite, const std::vector& boundTextures, int animflags) { - assert(nifNode->recType == Nif::RC_NiTriShape || nifNode->recType == Nif::RC_NiTriStrips || nifNode->recType == Nif::RC_NiLines); + assert(isTypeGeometry(nifNode->recType)); osg::ref_ptr drawable; osg::ref_ptr geom (new osg::Geometry); handleNiGeometry(nifNode, geom, parentNode, composite, boundTextures, animflags); @@ -1191,7 +1244,7 @@ namespace NifOsg continue; drawable = handleMorphGeometry(nimorphctrl, geom, parentNode, composite, boundTextures, animflags); - osg::ref_ptr morphctrl = new GeomMorpherController(nimorphctrl->data.getPtr()); + osg::ref_ptr morphctrl = new GeomMorpherController(nimorphctrl); setupController(ctrl.getPtr(), morphctrl, animflags); drawable->setUpdateCallback(morphctrl); break; @@ -1221,7 +1274,7 @@ namespace NifOsg void handleSkinnedGeometry(const Nif::Node *nifNode, osg::Group *parentNode, SceneUtil::CompositeStateSetUpdater* composite, const std::vector& boundTextures, int animflags) { - assert(nifNode->recType == Nif::RC_NiTriShape || nifNode->recType == Nif::RC_NiTriStrips || nifNode->recType == Nif::RC_NiLines); + assert(isTypeGeometry(nifNode->recType)); osg::ref_ptr geometry (new osg::Geometry); handleNiGeometry(nifNode, geometry, parentNode, composite, boundTextures, animflags); osg::ref_ptr rig(new SceneUtil::RigGeometry); @@ -1401,32 +1454,16 @@ namespace NifOsg } // We're going to convert the indices that pixel data contains // into real colors using the palette. - const std::vector& palette = pixelData->palette->colors; - if (pixelData->fmt == Nif::NiPixelData::NIPXFMT_PAL8) - { - unsigned char* data = new unsigned char[pixels.size() * 3]; - for (size_t i = 0; i < pixels.size(); i++) - { - unsigned int color = palette[pixels[i]]; - data[i * 3 + 0] = (color >> 0) & 0xFF; - data[i * 3 + 1] = (color >> 8) & 0xFF; - data[i * 3 + 2] = (color >> 16) & 0xFF; - } - image->setImage(width, height, 1, pixelformat, pixelformat, GL_UNSIGNED_BYTE, data, osg::Image::USE_NEW_DELETE); - } - else // if (fmt = NIPXFMT_PALA8) + const auto& palette = pixelData->palette->colors; + const int numChannels = pixelformat == GL_RGBA ? 4 : 3; + unsigned char* data = new unsigned char[pixels.size() * numChannels]; + unsigned char* pixel = data; + for (unsigned char index : pixels) { - unsigned char* data = new unsigned char[pixels.size() * 4]; - for (size_t i = 0; i < pixels.size(); i++) - { - unsigned int color = palette[pixels[i]]; - data[i * 4 + 0] = (color >> 0) & 0xFF; - data[i * 4 + 1] = (color >> 8) & 0xFF; - data[i * 4 + 2] = (color >> 16) & 0xFF; - data[i * 4 + 3] = (color >> 24) & 0xFF; - } - image->setImage(width, height, 1, pixelformat, pixelformat, GL_UNSIGNED_BYTE, data, osg::Image::USE_NEW_DELETE); + memcpy(pixel, &palette[index], sizeof(unsigned char) * numChannels); + pixel += numChannels; } + image->setImage(width, height, 1, pixelformat, pixelformat, GL_UNSIGNED_BYTE, data, osg::Image::USE_NEW_DELETE); break; } default: diff --git a/components/nifosg/nodeindexholder.hpp b/components/nifosg/nodeindexholder.hpp deleted file mode 100644 index e7d4f0db3..000000000 --- a/components/nifosg/nodeindexholder.hpp +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef OPENMW_COMPONENTS_NIFOSG_NODEINDEXHOLDER_H -#define OPENMW_COMPONENTS_NIFOSG_NODEINDEXHOLDER_H - -#include - -namespace NifOsg -{ - - class NodeIndexHolder : public osg::Object - { - public: - NodeIndexHolder() = default; - NodeIndexHolder(int index) - : mIndex(index) - { - } - NodeIndexHolder(const NodeIndexHolder& copy, const osg::CopyOp& copyop) - : Object(copy, copyop) - , mIndex(copy.mIndex) - { - } - - META_Object(NifOsg, NodeIndexHolder) - - int getIndex() const { return mIndex; } - - private: - - // NIF record index - int mIndex{0}; - }; - -} - -#endif diff --git a/components/nifosg/particle.cpp b/components/nifosg/particle.cpp index 0cbc3f22b..2cb0ffc62 100644 --- a/components/nifosg/particle.cpp +++ b/components/nifosg/particle.cpp @@ -5,14 +5,13 @@ #include #include #include +#include #include #include #include #include -#include "nodeindexholder.hpp" - namespace NifOsg { @@ -357,7 +356,7 @@ void Emitter::emitParticles(double dt) } } -FindGroupByRecIndex::FindGroupByRecIndex(int recIndex) +FindGroupByRecIndex::FindGroupByRecIndex(unsigned int recIndex) : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) , mFound(nullptr) , mRecIndex(recIndex) @@ -381,19 +380,16 @@ void FindGroupByRecIndex::apply(osg::Geometry &node) void FindGroupByRecIndex::applyNode(osg::Node &searchNode) { - if (searchNode.getUserDataContainer() && searchNode.getUserDataContainer()->getNumUserObjects()) + unsigned int recIndex; + if (searchNode.getUserValue("recIndex", recIndex) && mRecIndex == recIndex) { - NodeIndexHolder* holder = dynamic_cast(searchNode.getUserDataContainer()->getUserObject(0)); - if (holder && holder->getIndex() == mRecIndex) - { - osg::Group* group = searchNode.asGroup(); - if (!group) - group = searchNode.getParent(0); + osg::Group* group = searchNode.asGroup(); + if (!group) + group = searchNode.getParent(0); - mFound = group; - mFoundPath = getNodePath(); - return; - } + mFound = group; + mFoundPath = getNodePath(); + return; } traverse(searchNode); } diff --git a/components/nifosg/particle.hpp b/components/nifosg/particle.hpp index 724a8a721..dd89f4501 100644 --- a/components/nifosg/particle.hpp +++ b/components/nifosg/particle.hpp @@ -204,7 +204,7 @@ namespace NifOsg class FindGroupByRecIndex : public osg::NodeVisitor { public: - FindGroupByRecIndex(int recIndex); + FindGroupByRecIndex(unsigned int recIndex); void apply(osg::Node &node) override; @@ -218,7 +218,7 @@ namespace NifOsg osg::Group* mFound; osg::NodePath mFoundPath; private: - int mRecIndex; + unsigned int mRecIndex; }; // Subclass emitter to support randomly choosing one of the child node's transforms for the emit position of new particles. diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index b87446351..44ba7e687 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -220,6 +220,7 @@ namespace Resource , mClampLighting(true) , mAutoUseNormalMaps(false) , mAutoUseSpecularMaps(false) + , mApplyLightingToEnvMaps(false) , mInstanceCache(new MultiObjectCache) , mSharedStateManager(new SharedStateManager) , mImageManager(imageManager) @@ -242,9 +243,9 @@ namespace Resource return mForceShaders; } - void SceneManager::recreateShaders(osg::ref_ptr node) + void SceneManager::recreateShaders(osg::ref_ptr node, const std::string& shaderPrefix) { - osg::ref_ptr shaderVisitor(createShaderVisitor()); + osg::ref_ptr shaderVisitor(createShaderVisitor(shaderPrefix)); shaderVisitor->setAllowedToModifyStateSets(false); node->accept(*shaderVisitor); } @@ -284,6 +285,11 @@ namespace Resource mSpecularMapPattern = pattern; } + void SceneManager::setApplyLightingToEnvMaps(bool apply) + { + mApplyLightingToEnvMaps = apply; + } + SceneManager::~SceneManager() { // this has to be defined in the .cpp file as we can't delete incomplete types @@ -761,15 +767,16 @@ namespace Resource stats->setAttribute(frameNumber, "Node Instance", mInstanceCache->getCacheSize()); } - Shader::ShaderVisitor *SceneManager::createShaderVisitor() + Shader::ShaderVisitor *SceneManager::createShaderVisitor(const std::string& shaderPrefix) { - Shader::ShaderVisitor* shaderVisitor = new Shader::ShaderVisitor(*mShaderManager.get(), *mImageManager, "objects_vertex.glsl", "objects_fragment.glsl"); + Shader::ShaderVisitor* shaderVisitor = new Shader::ShaderVisitor(*mShaderManager.get(), *mImageManager, shaderPrefix+"_vertex.glsl", shaderPrefix+"_fragment.glsl"); shaderVisitor->setForceShaders(mForceShaders); shaderVisitor->setAutoUseNormalMaps(mAutoUseNormalMaps); shaderVisitor->setNormalMapPattern(mNormalMapPattern); shaderVisitor->setNormalHeightMapPattern(mNormalHeightMapPattern); shaderVisitor->setAutoUseSpecularMaps(mAutoUseSpecularMaps); shaderVisitor->setSpecularMapPattern(mSpecularMapPattern); + shaderVisitor->setApplyLightingToEnvMaps(mApplyLightingToEnvMaps); return shaderVisitor; } diff --git a/components/resource/scenemanager.hpp b/components/resource/scenemanager.hpp index 3a72caf6a..8df556158 100644 --- a/components/resource/scenemanager.hpp +++ b/components/resource/scenemanager.hpp @@ -51,7 +51,7 @@ namespace Resource Shader::ShaderManager& getShaderManager(); /// Re-create shaders for this node, need to call this if texture stages or vertex color mode have changed. - void recreateShaders(osg::ref_ptr node); + void recreateShaders(osg::ref_ptr node, const std::string& shaderPrefix = "objects"); /// @see ShaderVisitor::setForceShaders void setForceShaders(bool force); @@ -73,6 +73,8 @@ namespace Resource void setSpecularMapPattern(const std::string& pattern); + void setApplyLightingToEnvMaps(bool apply); + void setShaderPath(const std::string& path); /// Check if a given scene is loaded and if so, update its usage timestamp to prevent it from being unloaded @@ -146,7 +148,7 @@ namespace Resource private: - Shader::ShaderVisitor* createShaderVisitor(); + Shader::ShaderVisitor* createShaderVisitor(const std::string& shaderPrefix = "objects"); std::unique_ptr mShaderManager; bool mForceShaders; @@ -156,6 +158,7 @@ namespace Resource std::string mNormalHeightMapPattern; bool mAutoUseSpecularMaps; std::string mSpecularMapPattern; + bool mApplyLightingToEnvMaps; osg::ref_ptr mInstanceCache; diff --git a/components/sceneutil/lightmanager.cpp b/components/sceneutil/lightmanager.cpp index 7f1be2eea..673e01f33 100644 --- a/components/sceneutil/lightmanager.cpp +++ b/components/sceneutil/lightmanager.cpp @@ -237,7 +237,7 @@ namespace SceneUtil std::vector > lights; lights.reserve(lightList.size()); for (unsigned int i=0; imLightSource->getLight(frameNum)); + lights.emplace_back(lightList[i]->mLightSource->getLight(frameNum)); // the first light state attribute handles the actual state setting for all lights // it's best to batch these up so that we don't need to touch the modelView matrix more than necessary diff --git a/components/sceneutil/mwshadowtechnique.cpp b/components/sceneutil/mwshadowtechnique.cpp index f1fca5dfc..f32b2da4d 100644 --- a/components/sceneutil/mwshadowtechnique.cpp +++ b/components/sceneutil/mwshadowtechnique.cpp @@ -25,6 +25,7 @@ #include #include +#include "shadowsbin.hpp" namespace { @@ -273,10 +274,20 @@ void VDSMCameraCullCallback::operator()(osg::Node* node, osg::NodeVisitor* nv) cv->pushCullingSet(); } #endif + // bin has to go inside camera cull or the rendertexture stage will override it + static osg::ref_ptr ss; + if (!ss) + { + ShadowsBinAdder adder("ShadowsBin"); + ss = new osg::StateSet; + ss->setRenderBinDetails(osg::StateSet::OPAQUE_BIN, "ShadowsBin", osg::StateSet::OVERRIDE_PROTECTED_RENDERBIN_DETAILS); + } + cv->pushStateSet(ss); if (_vdsm->getShadowedScene()) { _vdsm->getShadowedScene()->osg::Group::traverse(*nv); } + cv->popStateSet(); #if 1 if (!_polytope.empty()) { @@ -555,6 +566,7 @@ MWShadowTechnique::ShadowData::ShadowData(MWShadowTechnique::ViewDependentData* _camera = new osg::Camera; _camera->setName("ShadowCamera"); _camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF_INHERIT_VIEWPOINT); + _camera->setImplicitBufferAttachmentMask(0, 0); //_camera->setClearColor(osg::Vec4(1.0f,1.0f,1.0f,1.0f)); _camera->setClearColor(osg::Vec4(0.0f,0.0f,0.0f,0.0f)); @@ -808,9 +820,10 @@ void MWShadowTechnique::enableShadows() _enableShadows = true; } -void MWShadowTechnique::disableShadows() +void MWShadowTechnique::disableShadows(bool setDummyState) { _enableShadows = false; + mSetDummyStateWhenDisabled = setDummyState; } void SceneUtil::MWShadowTechnique::enableDebugHUD() @@ -874,15 +887,6 @@ void SceneUtil::MWShadowTechnique::setupCastingShader(Shader::ShaderManager & sh _castingProgram->addShader(shaderManager.getShader("shadowcasting_vertex.glsl", Shader::ShaderManager::DefineMap(), osg::Shader::VERTEX)); _castingProgram->addShader(shaderManager.getShader("shadowcasting_fragment.glsl", Shader::ShaderManager::DefineMap(), osg::Shader::FRAGMENT)); - - _shadowMapAlphaTestDisableUniform = shaderManager.getShadowMapAlphaTestDisableUniform(); - _shadowMapAlphaTestDisableUniform->setName("alphaTestShadows"); - _shadowMapAlphaTestDisableUniform->setType(osg::Uniform::BOOL); - _shadowMapAlphaTestDisableUniform->set(false); - - shaderManager.getShadowMapAlphaTestEnableUniform()->setName("alphaTestShadows"); - shaderManager.getShadowMapAlphaTestEnableUniform()->setType(osg::Uniform::BOOL); - shaderManager.getShadowMapAlphaTestEnableUniform()->set(true); } MWShadowTechnique::ViewDependentData* MWShadowTechnique::createViewDependentData(osgUtil::CullVisitor* /*cv*/) @@ -1473,7 +1477,28 @@ void MWShadowTechnique::cull(osgUtil::CullVisitor& cv) { if (!_enableShadows) { + if (mSetDummyStateWhenDisabled) + { + osg::ref_ptr dummyState = new osg::StateSet(); + + ShadowSettings* settings = getShadowedScene()->getShadowSettings(); + int baseUnit = settings->getBaseShadowTextureUnit(); + int endUnit = baseUnit + settings->getNumShadowMapsPerLight(); + for (int i = baseUnit; i < endUnit; ++i) + { + dummyState->setTextureAttributeAndModes(i, _fallbackShadowMapTexture, osg::StateAttribute::ON); + dummyState->addUniform(new osg::Uniform(("shadowTexture" + std::to_string(i - baseUnit)).c_str(), i)); + dummyState->addUniform(new osg::Uniform(("shadowTextureUnit" + std::to_string(i - baseUnit)).c_str(), i)); + } + + cv.pushStateSet(dummyState); + } + _shadowedScene->osg::Group::traverse(cv); + + if (mSetDummyStateWhenDisabled) + cv.popStateSet(); + return; } @@ -1645,7 +1670,7 @@ void MWShadowTechnique::createShaders() { perFrameUniformList.clear(); perFrameUniformList.push_back(baseTextureSampler); - perFrameUniformList.push_back(baseTextureUnit.get()); + perFrameUniformList.emplace_back(baseTextureUnit.get()); perFrameUniformList.push_back(maxDistance); perFrameUniformList.push_back(fadeStart); } @@ -1657,7 +1682,7 @@ void MWShadowTechnique::createShaders() sstr<<"shadowTexture"< shadowTextureSampler = new osg::Uniform(sstr.str().c_str(),(int)(settings->getBaseShadowTextureUnit()+sm_i)); for (auto& perFrameUniformList : _uniforms) - perFrameUniformList.push_back(shadowTextureSampler.get()); + perFrameUniformList.emplace_back(shadowTextureSampler.get()); } { @@ -1665,7 +1690,7 @@ void MWShadowTechnique::createShaders() sstr<<"shadowTextureUnit"< shadowTextureUnit = new osg::Uniform(sstr.str().c_str(),(int)(settings->getBaseShadowTextureUnit()+sm_i)); for (auto& perFrameUniformList : _uniforms) - perFrameUniformList.push_back(shadowTextureUnit.get()); + perFrameUniformList.emplace_back(shadowTextureUnit.get()); } } @@ -1711,6 +1736,8 @@ void MWShadowTechnique::createShaders() _fallbackShadowMapTexture->setWrap(osg::Texture2D::WRAP_T,osg::Texture2D::REPEAT); _fallbackShadowMapTexture->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::NEAREST); _fallbackShadowMapTexture->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::NEAREST); + _fallbackShadowMapTexture->setShadowComparison(true); + _fallbackShadowMapTexture->setShadowCompareFunc(osg::Texture::ShadowCompareFunc::ALWAYS); } @@ -1720,17 +1747,14 @@ void MWShadowTechnique::createShaders() _shadowCastingStateSet->setAttributeAndModes(_castingProgram, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); // The casting program uses a sampler, so to avoid undefined behaviour, we must bind a dummy texture in case no other is supplied _shadowCastingStateSet->setTextureAttributeAndModes(0, _fallbackBaseTexture.get(), osg::StateAttribute::ON); - _shadowCastingStateSet->addUniform(new osg::Uniform("useDiffuseMapForShadowAlpha", false)); - _shadowCastingStateSet->addUniform(_shadowMapAlphaTestDisableUniform); + _shadowCastingStateSet->addUniform(new osg::Uniform("useDiffuseMapForShadowAlpha", true)); + _shadowCastingStateSet->addUniform(new osg::Uniform("alphaTestShadows", false)); osg::ref_ptr depth = new osg::Depth; depth->setWriteMask(true); _shadowCastingStateSet->setAttribute(depth, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); _shadowCastingStateSet->setMode(GL_DEPTH_CLAMP, osg::StateAttribute::ON); - _shadowCastingStateSet->setRenderBinDetails(osg::StateSet::OPAQUE_BIN, "RenderBin", osg::StateSet::OVERRIDE_PROTECTED_RENDERBIN_DETAILS); - // TODO: compare performance when alpha testing is handled here versus using a discard in the fragment shader - // TODO: compare performance when we set a bunch of GL state to the default here with OVERRIDE set so that there are fewer pointless state switches } osg::Polytope MWShadowTechnique::computeLightViewFrustumPolytope(Frustum& frustum, LightData& positionedLight) @@ -2695,12 +2719,12 @@ bool MWShadowTechnique::cropShadowCameraToMainFrustum(Frustum& frustum, osg::Cam yMax = convexHull.max(1); zMin = convexHull.min(2); - planeList.push_back(osg::Plane(0.0, -1.0, 0.0, yMax)); - planeList.push_back(osg::Plane(0.0, 1.0, 0.0, -yMin)); - planeList.push_back(osg::Plane(-1.0, 0.0, 0.0, xMax)); - planeList.push_back(osg::Plane(1.0, 0.0, 0.0, -xMin)); + planeList.emplace_back(0.0, -1.0, 0.0, yMax); + planeList.emplace_back(0.0, 1.0, 0.0, -yMin); + planeList.emplace_back(-1.0, 0.0, 0.0, xMax); + planeList.emplace_back(1.0, 0.0, 0.0, -xMin); // In view space, the light is at the most positive value, and we want to cull stuff beyond the minimum value. - planeList.push_back(osg::Plane(0.0, 0.0, 1.0, -zMin)); + planeList.emplace_back(0.0, 0.0, 1.0, -zMin); // Don't add a zMax culling plane - we still want those objects, but don't care about their depth buffer value. } @@ -3333,7 +3357,7 @@ void SceneUtil::MWShadowTechnique::DebugHUD::addAnotherShadowMap() mDebugCameras[shadowMapNumber]->setClearColor(osg::Vec4(1.0, 1.0, 0.0, 1.0)); mDebugCameras[shadowMapNumber]->getOrCreateStateSet()->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF); - mDebugGeometry.push_back(osg::createTexturedQuadGeometry(osg::Vec3(-1, -1, 0), osg::Vec3(2, 0, 0), osg::Vec3(0, 2, 0))); + mDebugGeometry.emplace_back(osg::createTexturedQuadGeometry(osg::Vec3(-1, -1, 0), osg::Vec3(2, 0, 0), osg::Vec3(0, 2, 0))); mDebugGeometry[shadowMapNumber]->setCullingActive(false); mDebugCameras[shadowMapNumber]->addChild(mDebugGeometry[shadowMapNumber]); osg::ref_ptr stateSet = mDebugGeometry[shadowMapNumber]->getOrCreateStateSet(); diff --git a/components/sceneutil/mwshadowtechnique.hpp b/components/sceneutil/mwshadowtechnique.hpp index 7e0d74d2e..d28ff7fb4 100644 --- a/components/sceneutil/mwshadowtechnique.hpp +++ b/components/sceneutil/mwshadowtechnique.hpp @@ -67,7 +67,7 @@ namespace SceneUtil { virtual void enableShadows(); - virtual void disableShadows(); + virtual void disableShadows(bool setDummyState = false); virtual void enableDebugHUD(); @@ -302,6 +302,7 @@ namespace SceneUtil { osg::ref_ptr _program; bool _enableShadows; + bool mSetDummyStateWhenDisabled; double _splitPointUniformLogRatio = 0.5; double _splitPointDeltaBias = 0.0; @@ -338,7 +339,6 @@ namespace SceneUtil { osg::ref_ptr _debugHud; osg::ref_ptr _castingProgram; - osg::ref_ptr _shadowMapAlphaTestDisableUniform; }; } diff --git a/components/sceneutil/serialize.cpp b/components/sceneutil/serialize.cpp index 9e7aa83f6..d03612fc1 100644 --- a/components/sceneutil/serialize.cpp +++ b/components/sceneutil/serialize.cpp @@ -144,7 +144,6 @@ void registerSerializers() "NifOsg::UpdateMorphGeometry", "NifOsg::UVController", "NifOsg::VisController", - "NifOsg::NodeIndexHolder", "osgMyGUI::Drawable", "osg::DrawCallback", "osgOQ::ClearQueriesCallback", diff --git a/components/sceneutil/shadow.cpp b/components/sceneutil/shadow.cpp index d1490fee4..761998020 100644 --- a/components/sceneutil/shadow.cpp +++ b/components/sceneutil/shadow.cpp @@ -72,6 +72,9 @@ namespace SceneUtil void ShadowManager::disableShadowsForStateSet(osg::ref_ptr stateset) { + if (!Settings::Manager::getBool("enable shadows", "Shadows")) + return; + int numberOfShadowMapsPerLight = Settings::Manager::getInt("number of shadow maps", "Shadows"); numberOfShadowMapsPerLight = std::max(1, std::min(numberOfShadowMapsPerLight, 8)); @@ -169,7 +172,7 @@ namespace SceneUtil if (Settings::Manager::getBool("enable indoor shadows", "Shadows")) mShadowSettings->setCastsShadowTraversalMask(mIndoorShadowCastingMask); else - mShadowTechnique->disableShadows(); + mShadowTechnique->disableShadows(true); } void ShadowManager::enableOutdoorMode() diff --git a/components/sceneutil/shadowsbin.cpp b/components/sceneutil/shadowsbin.cpp new file mode 100644 index 000000000..520ad0362 --- /dev/null +++ b/components/sceneutil/shadowsbin.cpp @@ -0,0 +1,166 @@ +#include "shadowsbin.hpp" +#include +#include +#include +#include + +using namespace osgUtil; + +namespace +{ + template + inline void accumulateState(T& currentValue, T newValue, bool& isOverride, unsigned int overrideFlags) + { + if (isOverride && !(overrideFlags & osg::StateAttribute::PROTECTED)) return; + + if (overrideFlags & osg::StateAttribute::OVERRIDE) + isOverride = true; + + currentValue = newValue; + } + + inline void accumulateModeState(const osg::StateSet* ss, bool& currentValue, bool& isOverride, int mode) + { + const osg::StateSet::ModeList& l = ss->getModeList(); + osg::StateSet::ModeList::const_iterator mf = l.find(mode); + if (mf == l.end()) + return; + int flags = mf->second; + bool newValue = flags & osg::StateAttribute::ON; + accumulateState(currentValue, newValue, isOverride, ss->getMode(mode)); + } + + inline bool materialNeedShadows(osg::Material* m) + { + // I'm pretty sure this needs to check the colour mode - vertex colours might override this value. + return m->getDiffuse(osg::Material::FRONT).a() > 0.5; + } +} + +namespace SceneUtil +{ + +ShadowsBin::ShadowsBin() +{ + mNoTestStateSet = new osg::StateSet; + mNoTestStateSet->addUniform(new osg::Uniform("useDiffuseMapForShadowAlpha", false)); + mNoTestStateSet->addUniform(new osg::Uniform("alphaTestShadows", false)); + + mShaderAlphaTestStateSet = new osg::StateSet; + mShaderAlphaTestStateSet->addUniform(new osg::Uniform("alphaTestShadows", true)); + mShaderAlphaTestStateSet->setMode(GL_BLEND, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE); +} + +StateGraph* ShadowsBin::cullStateGraph(StateGraph* sg, StateGraph* root, std::unordered_set& uninterestingCache) +{ + std::vector return_path; + State state; + StateGraph* sg_new = sg; + do + { + if (uninterestingCache.find(sg_new) != uninterestingCache.end()) + break; + return_path.push_back(sg_new); + sg_new = sg_new->_parent; + } while (sg_new && sg_new != root); + + for(auto itr=return_path.rbegin(); itr!=return_path.rend(); ++itr) + { + const osg::StateSet* ss = (*itr)->getStateSet(); + if (!ss) + continue; + + accumulateModeState(ss, state.mAlphaBlend, state.mAlphaBlendOverride, GL_BLEND); + accumulateModeState(ss, state.mAlphaTest, state.mAlphaTestOverride, GL_ALPHA_TEST); + + const osg::StateSet::AttributeList& attributes = ss->getAttributeList(); + osg::StateSet::AttributeList::const_iterator found = attributes.find(std::make_pair(osg::StateAttribute::MATERIAL, 0)); + if (found != attributes.end()) + { + const osg::StateSet::RefAttributePair& rap = found->second; + accumulateState(state.mMaterial, static_cast(rap.first.get()), state.mMaterialOverride, rap.second); + if (state.mMaterial && !materialNeedShadows(state.mMaterial)) + state.mMaterial = nullptr; + } + + // osg::FrontFace specifies triangle winding, not front-face culling. We can't safely reparent anything under it. + found = attributes.find(std::make_pair(osg::StateAttribute::FRONTFACE, 0)); + if (found != attributes.end()) + state.mImportantState = true; + + if ((*itr) != sg && !state.interesting()) + uninterestingCache.insert(*itr); + } + + if (!state.needShadows()) + return nullptr; + + if (!state.needTexture() && !state.mImportantState) + { + for (RenderLeaf* leaf : sg->_leaves) + { + leaf->_parent = root; + root->_leaves.push_back(leaf); + } + return nullptr; + } + + if (state.mAlphaBlend) + { + sg_new = sg->find_or_insert(mShaderAlphaTestStateSet); + for (RenderLeaf* leaf : sg->_leaves) + { + leaf->_parent = sg_new; + sg_new->_leaves.push_back(leaf); + } + return sg_new; + } + return sg; +} + +bool ShadowsBin::State::needShadows() const +{ + if (!mMaterial) + return true; + return materialNeedShadows(mMaterial); +} + +void ShadowsBin::sortImplementation() +{ + // The cull visitor contains a stategraph. + // When a stateset is pushed, it's added/found as a child of the current stategraph node, then that node becomes the new current stategraph node. + // When a drawable is added, the current stategraph node is added to the current renderbin (if it's not there already) and the drawable is added as a renderleaf to the stategraph + // This means our list only contains stategraph nodes with directly-attached renderleaves, but they might have parents with more state set that needs to be considered. + if (!_stateGraphList.size()) + return; + StateGraph* root = _stateGraphList[0]; + while (root->_parent) + { + root = root->_parent; + const osg::StateSet* ss = root->getStateSet(); + if (ss->getMode(GL_NORMALIZE) & osg::StateAttribute::ON // that is root stategraph of renderingmanager cpp + || ss->getAttribute(osg::StateAttribute::VIEWPORT)) // fallback to rendertargets sg just in case + break; + if (!root->_parent) + return; + } + StateGraph* noTestRoot = root->find_or_insert(mNoTestStateSet.get()); + // root is now a stategraph with useDiffuseMapForShadowAlpha disabled but minimal other state + noTestRoot->_leaves.reserve(_stateGraphList.size()); + StateGraphList newList; + std::unordered_set uninterestingCache; + for (StateGraph* graph : _stateGraphList) + { + // Render leaves which shouldn't use the diffuse map for shadow alpha but do cast shadows become children of root, so graph is now empty. Don't add to newList. + // Graphs containing just render leaves which don't cast shadows are discarded. Don't add to newList. + // Graphs containing other leaves need to be in newList. + StateGraph* graphToAdd = cullStateGraph(graph, noTestRoot, uninterestingCache); + if (graphToAdd) + newList.push_back(graphToAdd); + } + if (!noTestRoot->_leaves.empty()) + newList.push_back(noTestRoot); + _stateGraphList = newList; +} + +} diff --git a/components/sceneutil/shadowsbin.hpp b/components/sceneutil/shadowsbin.hpp new file mode 100644 index 000000000..cc6fd3525 --- /dev/null +++ b/components/sceneutil/shadowsbin.hpp @@ -0,0 +1,76 @@ +#ifndef OPENMW_COMPONENTS_SCENEUTIL_SHADOWBIN_H +#define OPENMW_COMPONENTS_SCENEUTIL_SHADOWBIN_H +#include +#include + +namespace osg +{ + class Material; +} + +namespace SceneUtil +{ + + /// renderbin which culls redundant state for shadow map rendering + class ShadowsBin : public osgUtil::RenderBin + { + private: + osg::ref_ptr mNoTestStateSet; + osg::ref_ptr mShaderAlphaTestStateSet; + public: + META_Object(SceneUtil, ShadowsBin) + ShadowsBin(); + ShadowsBin(const ShadowsBin& rhs, const osg::CopyOp& copyop) + : osgUtil::RenderBin(rhs, copyop) + , mNoTestStateSet(rhs.mNoTestStateSet) + , mShaderAlphaTestStateSet(rhs.mShaderAlphaTestStateSet) + {} + + void sortImplementation() override; + + struct State + { + State() + : mAlphaBlend(false) + , mAlphaBlendOverride(false) + , mAlphaTest(false) + , mAlphaTestOverride(false) + , mMaterial(nullptr) + , mMaterialOverride(false) + , mImportantState(false) + {} + + bool mAlphaBlend; + bool mAlphaBlendOverride; + bool mAlphaTest; + bool mAlphaTestOverride; + osg::Material* mMaterial; + bool mMaterialOverride; + bool mImportantState; + bool needTexture() const { return mAlphaBlend || mAlphaTest; } + bool needShadows() const; + // A state is interesting if there's anything about it that might affect whether we can optimise child state + bool interesting() const + { + return !needShadows() || needTexture() || mAlphaBlendOverride || mAlphaTestOverride || mMaterialOverride || mImportantState; + } + }; + + osgUtil::StateGraph* cullStateGraph(osgUtil::StateGraph* sg, osgUtil::StateGraph* root, std::unordered_set& uninteresting); + + static void addPrototype(const std::string& name) + { + osg::ref_ptr bin (new ShadowsBin); + osgUtil::RenderBin::addRenderBinPrototype(name, bin); + } + }; + + class ShadowsBinAdder + { + public: + ShadowsBinAdder(const std::string& name){ ShadowsBin::addPrototype(name); } + }; + +} + +#endif diff --git a/components/sceneutil/statesetupdater.cpp b/components/sceneutil/statesetupdater.cpp index 9fb9ba496..5d7dbd755 100644 --- a/components/sceneutil/statesetupdater.cpp +++ b/components/sceneutil/statesetupdater.cpp @@ -73,7 +73,7 @@ namespace SceneUtil : StateSetUpdater(copy, copyop) { for (unsigned int i=0; imObjects.push_back(obj); + mWorkItem->mObjects.emplace_back(obj); } void UnrefQueue::flush(SceneUtil::WorkQueue *workQueue) diff --git a/components/sceneutil/visitor.cpp b/components/sceneutil/visitor.cpp index 876ea920f..00d18ffcb 100644 --- a/components/sceneutil/visitor.cpp +++ b/components/sceneutil/visitor.cpp @@ -101,7 +101,7 @@ namespace SceneUtil node.setStateSet(nullptr); if (node.getNodeMask() == 0x1 && node.getNumParents() == 1) - mToRemove.push_back(std::make_pair(&node, node.getParent(0))); + mToRemove.emplace_back(&node, node.getParent(0)); else traverse(node); } @@ -120,12 +120,12 @@ namespace SceneUtil osg::Group* parentParent = static_cast(*(parent - 1)); if (parentGroup->getNumChildren() == 1 && parentGroup->getDataVariance() == osg::Object::STATIC) { - mToRemove.push_back(std::make_pair(parentGroup, parentParent)); + mToRemove.emplace_back(parentGroup, parentParent); return; } } - mToRemove.push_back(std::make_pair(&node, parentGroup)); + mToRemove.emplace_back(&node, parentGroup); } void RemoveTriBipVisitor::apply(osg::Drawable& drw) @@ -150,7 +150,7 @@ namespace SceneUtil { osg::Group* parent = static_cast(*(getNodePath().end()-2)); // Not safe to remove in apply(), since the visitor is still iterating the child list - mToRemove.push_back(std::make_pair(&node, parent)); + mToRemove.emplace_back(&node, parent); } } } diff --git a/components/shader/shadermanager.cpp b/components/shader/shadermanager.cpp index bfaa11282..788a8720b 100644 --- a/components/shader/shadermanager.cpp +++ b/components/shader/shadermanager.cpp @@ -384,14 +384,4 @@ namespace Shader program.second->releaseGLObjects(state); } - const osg::ref_ptr ShaderManager::getShadowMapAlphaTestEnableUniform() - { - return mShadowMapAlphaTestEnableUniform; - } - - const osg::ref_ptr ShaderManager::getShadowMapAlphaTestDisableUniform() - { - return mShadowMapAlphaTestDisableUniform; - } - } diff --git a/components/shader/shadermanager.hpp b/components/shader/shadermanager.hpp index ed5bbc907..13db30b01 100644 --- a/components/shader/shadermanager.hpp +++ b/components/shader/shadermanager.hpp @@ -43,9 +43,6 @@ namespace Shader void releaseGLObjects(osg::State* state); - const osg::ref_ptr getShadowMapAlphaTestEnableUniform(); - const osg::ref_ptr getShadowMapAlphaTestDisableUniform(); - private: std::string mPath; @@ -63,9 +60,6 @@ namespace Shader ProgramMap mPrograms; std::mutex mMutex; - - const osg::ref_ptr mShadowMapAlphaTestEnableUniform = new osg::Uniform(); - const osg::ref_ptr mShadowMapAlphaTestDisableUniform = new osg::Uniform(); }; bool parseFors(std::string& source, const std::string& templateName); diff --git a/components/shader/shadervisitor.cpp b/components/shader/shadervisitor.cpp index c30307f29..10e9e606e 100644 --- a/components/shader/shadervisitor.cpp +++ b/components/shader/shadervisitor.cpp @@ -1,7 +1,5 @@ #include "shadervisitor.hpp" -#include -#include #include #include #include @@ -14,7 +12,6 @@ #include #include #include -#include #include "shadermanager.hpp" @@ -25,7 +22,6 @@ namespace Shader : mShaderRequired(false) , mColorMode(0) , mMaterialOverridden(false) - , mBlendFuncOverridden(false) , mNormalHeight(false) , mTexStageRequiringTangents(-1) , mNode(nullptr) @@ -43,12 +39,13 @@ namespace Shader , mAllowedToModifyStateSets(true) , mAutoUseNormalMaps(false) , mAutoUseSpecularMaps(false) + , mApplyLightingToEnvMaps(false) , mShaderManager(shaderManager) , mImageManager(imageManager) , mDefaultVsTemplate(defaultVsTemplate) , mDefaultFsTemplate(defaultFsTemplate) { - mRequirements.push_back(ShaderRequirements()); + mRequirements.emplace_back(); } void ShaderVisitor::setForceShaders(bool force) @@ -144,11 +141,9 @@ namespace Shader // Bump maps are off by default as well writableStateSet->setTextureMode(unit, GL_TEXTURE_2D, osg::StateAttribute::ON); } - else if (texName == "envMap") + else if (texName == "envMap" && mApplyLightingToEnvMaps) { - static const bool preLightEnv = Settings::Manager::getBool("apply lighting to environment maps", "Shaders"); - if (preLightEnv) - mRequirements.back().mShaderRequired = true; + mRequirements.back().mShaderRequired = true; } } else @@ -233,14 +228,11 @@ namespace Shader if (!writableStateSet) writableStateSet = getWritableStateSet(node); // We probably shouldn't construct a new version of this each time as Uniforms use pointer comparison for early-out. - // Also it should probably belong to the shader manager + // Also it should probably belong to the shader manager or be applied by the shadows bin writableStateSet->addUniform(new osg::Uniform("useDiffuseMapForShadowAlpha", true)); } } - bool alphaSettingsChanged = false; - bool alphaTestShadows = false; - const osg::StateSet::AttributeList& attributes = stateset->getAttributeList(); for (osg::StateSet::AttributeList::const_iterator it = attributes.begin(); it != attributes.end(); ++it) { @@ -284,28 +276,8 @@ namespace Shader mRequirements.back().mColorMode = colorMode; } } - else if (it->first.first == osg::StateAttribute::BLENDFUNC) - { - if (!mRequirements.back().mBlendFuncOverridden || it->second.second & osg::StateAttribute::PROTECTED) - { - if (it->second.second & osg::StateAttribute::OVERRIDE) - mRequirements.back().mBlendFuncOverridden = true; - - const osg::BlendFunc* blend = static_cast(it->second.first.get()); - if (blend->getSource() == osg::BlendFunc::SRC_ALPHA || blend->getSource() == osg::BlendFunc::SRC_COLOR) - alphaTestShadows = true; - alphaSettingsChanged = true; - } - } // Eventually, move alpha testing to discard in shader adn remove deprecated state here } - // we don't need to check for glEnable/glDisable of blending as we always set it at the same time - if (alphaSettingsChanged) - { - if (!writableStateSet) - writableStateSet = getWritableStateSet(node); - writableStateSet->addUniform(alphaTestShadows ? mShaderManager.getShadowMapAlphaTestEnableUniform() : mShaderManager.getShadowMapAlphaTestDisableUniform()); - } } void ShaderVisitor::pushRequirements(osg::Node& node) @@ -477,4 +449,9 @@ namespace Shader mSpecularMapPattern = pattern; } + void ShaderVisitor::setApplyLightingToEnvMaps(bool apply) + { + mApplyLightingToEnvMaps = apply; + } + } diff --git a/components/shader/shadervisitor.hpp b/components/shader/shadervisitor.hpp index bf1022180..6b2353b66 100644 --- a/components/shader/shadervisitor.hpp +++ b/components/shader/shadervisitor.hpp @@ -38,6 +38,8 @@ namespace Shader void setSpecularMapPattern(const std::string& pattern); + void setApplyLightingToEnvMaps(bool apply); + void apply(osg::Node& node) override; void apply(osg::Drawable& drawable) override; @@ -59,6 +61,8 @@ namespace Shader bool mAutoUseSpecularMaps; std::string mSpecularMapPattern; + bool mApplyLightingToEnvMaps; + ShaderManager& mShaderManager; Resource::ImageManager& mImageManager; @@ -75,7 +79,6 @@ namespace Shader int mColorMode; bool mMaterialOverridden; - bool mBlendFuncOverridden; bool mNormalHeight; // true if normal map has height info in alpha channel diff --git a/components/terrain/chunkmanager.cpp b/components/terrain/chunkmanager.cpp index 87966b47c..041414a87 100644 --- a/components/terrain/chunkmanager.cpp +++ b/components/terrain/chunkmanager.cpp @@ -112,7 +112,7 @@ void ChunkManager::createCompositeMapGeometry(float chunkSize, const osg::Vec2f& geom->setStateSet(*it); - compositeMap.mDrawables.push_back(geom); + compositeMap.mDrawables.emplace_back(geom); } } } diff --git a/components/terrain/viewdata.cpp b/components/terrain/viewdata.cpp index c24252b7d..e4d043ffc 100644 --- a/components/terrain/viewdata.cpp +++ b/components/terrain/viewdata.cpp @@ -192,7 +192,7 @@ ViewData *ViewDataMap::createOrReuseView() } else { - mViewVector.push_back(ViewData()); + mViewVector.emplace_back(); vd = &mViewVector.back(); } mUsedViews.push_back(vd); diff --git a/components/vfs/bsaarchive.cpp b/components/vfs/bsaarchive.cpp index 9646b62af..ac65c58a1 100644 --- a/components/vfs/bsaarchive.cpp +++ b/components/vfs/bsaarchive.cpp @@ -21,7 +21,7 @@ BsaArchive::BsaArchive(const std::string &filename) const Bsa::BSAFile::FileList &filelist = mFile->getList(); for(Bsa::BSAFile::FileList::const_iterator it = filelist.begin();it != filelist.end();++it) { - mResources.push_back(BsaArchiveFile(&*it, mFile.get())); + mResources.emplace_back(&*it, mFile.get()); } } diff --git a/components/widgets/box.hpp b/components/widgets/box.hpp index 5fa263b0c..a01bdb37c 100644 --- a/components/widgets/box.hpp +++ b/components/widgets/box.hpp @@ -56,11 +56,11 @@ namespace Gui MYGUI_RTTI_DERIVED( AutoSizedTextBox ) public: - MyGUI::IntSize getRequestedSize() final; - void setCaption(const MyGUI::UString& _value) final; + MyGUI::IntSize getRequestedSize() override; + void setCaption(const MyGUI::UString& _value) override; protected: - void setPropertyOverride(const std::string& _key, const std::string& _value) final; + void setPropertyOverride(const std::string& _key, const std::string& _value) override; std::string mFontSize; }; @@ -70,13 +70,13 @@ namespace Gui public: - MyGUI::IntSize getRequestedSize() final; - void setCaption(const MyGUI::UString& _value) final; + MyGUI::IntSize getRequestedSize() override; + void setCaption(const MyGUI::UString& _value) override; - void initialiseOverride() final; + void initialiseOverride() override; protected: - void setPropertyOverride(const std::string& _key, const std::string& _value) final; + void setPropertyOverride(const std::string& _key, const std::string& _value) override; int getWidth(); std::string mFontSize; bool mShrink = false; @@ -89,11 +89,11 @@ namespace Gui MYGUI_RTTI_DERIVED( AutoSizedButton ) public: - MyGUI::IntSize getRequestedSize() final; - void setCaption(const MyGUI::UString& _value) final; + MyGUI::IntSize getRequestedSize() override; + void setCaption(const MyGUI::UString& _value) override; protected: - void setPropertyOverride(const std::string& _key, const std::string& _value) final; + void setPropertyOverride(const std::string& _key, const std::string& _value) override; std::string mFontSize; }; @@ -128,7 +128,7 @@ namespace Gui public: Spacer(); - MyGUI::IntSize getRequestedSize() final { return MyGUI::IntSize(0,0); } + MyGUI::IntSize getRequestedSize() override { return MyGUI::IntSize(0,0); } }; class HBox : public Box, public MyGUI::Widget @@ -136,18 +136,18 @@ namespace Gui MYGUI_RTTI_DERIVED( HBox ) public: - void setSize (const MyGUI::IntSize &_value) final; - void setCoord (const MyGUI::IntCoord &_value) final; + void setSize (const MyGUI::IntSize &_value) override; + void setCoord (const MyGUI::IntCoord &_value) override; protected: - void initialiseOverride() final; + void initialiseOverride() override; - void align() final; - MyGUI::IntSize getRequestedSize() final; + void align() override; + MyGUI::IntSize getRequestedSize() override; - void setPropertyOverride(const std::string& _key, const std::string& _value) final; + void setPropertyOverride(const std::string& _key, const std::string& _value) override; - void onWidgetCreated(MyGUI::Widget* _widget) final; + void onWidgetCreated(MyGUI::Widget* _widget) override; }; class VBox : public Box, public MyGUI::Widget @@ -155,18 +155,18 @@ namespace Gui MYGUI_RTTI_DERIVED( VBox) public: - void setSize (const MyGUI::IntSize &_value) final; - void setCoord (const MyGUI::IntCoord &_value) final; + void setSize (const MyGUI::IntSize &_value) override; + void setCoord (const MyGUI::IntCoord &_value) override; protected: - void initialiseOverride() final; + void initialiseOverride() override; - void align() final; - MyGUI::IntSize getRequestedSize() final; + void align() override; + MyGUI::IntSize getRequestedSize() override; - void setPropertyOverride(const std::string& _key, const std::string& _value) final; + void setPropertyOverride(const std::string& _key, const std::string& _value) override; - void onWidgetCreated(MyGUI::Widget* _widget) final; + void onWidgetCreated(MyGUI::Widget* _widget) override; }; } diff --git a/components/widgets/imagebutton.hpp b/components/widgets/imagebutton.hpp index 160f24e99..6e3fc1733 100644 --- a/components/widgets/imagebutton.hpp +++ b/components/widgets/imagebutton.hpp @@ -31,13 +31,13 @@ namespace Gui static bool sDefaultNeedKeyFocus; protected: - void setPropertyOverride(const std::string& _key, const std::string& _value) final; - void onMouseLostFocus(MyGUI::Widget* _new) final; - void onMouseSetFocus(MyGUI::Widget* _old) final; - void onMouseButtonPressed(int _left, int _top, MyGUI::MouseButton _id) final; - void onMouseButtonReleased(int _left, int _top, MyGUI::MouseButton _id) final; - void onKeySetFocus(MyGUI::Widget* _old) final; - void onKeyLostFocus(MyGUI::Widget* _new) final; + void setPropertyOverride(const std::string& _key, const std::string& _value) override; + void onMouseLostFocus(MyGUI::Widget* _new) override; + void onMouseSetFocus(MyGUI::Widget* _old) override; + void onMouseButtonPressed(int _left, int _top, MyGUI::MouseButton _id) override; + void onMouseButtonReleased(int _left, int _top, MyGUI::MouseButton _id) override; + void onKeySetFocus(MyGUI::Widget* _old) override; + void onKeyLostFocus(MyGUI::Widget* _new) override; std::string mImageHighlighted; std::string mImageNormal; diff --git a/components/widgets/list.cpp b/components/widgets/list.cpp index 73e01675a..bf24acf7e 100644 --- a/components/widgets/list.cpp +++ b/components/widgets/list.cpp @@ -34,7 +34,7 @@ namespace Gui void MWList::addSeparator() { - mItems.push_back(""); + mItems.emplace_back(""); } void MWList::adjustSize() diff --git a/components/widgets/list.hpp b/components/widgets/list.hpp index 604d5dada..be0339f33 100644 --- a/components/widgets/list.hpp +++ b/components/widgets/list.hpp @@ -48,10 +48,10 @@ namespace Gui void scrollToTop(); - void setPropertyOverride(const std::string& _key, const std::string& _value) final; + void setPropertyOverride(const std::string& _key, const std::string& _value) override; protected: - void initialiseOverride() final; + void initialiseOverride() override; void redraw(bool scrollbarShown = false); diff --git a/components/widgets/numericeditbox.hpp b/components/widgets/numericeditbox.hpp index 2d7632f07..90805c04a 100644 --- a/components/widgets/numericeditbox.hpp +++ b/components/widgets/numericeditbox.hpp @@ -22,8 +22,8 @@ namespace Gui { } - void initialiseOverride() final; - void shutdownOverride() final; + void initialiseOverride() override; + void shutdownOverride() override; typedef MyGUI::delegates::CMultiDelegate1 EventHandle_ValueChanged; EventHandle_ValueChanged eventValueChanged; @@ -36,8 +36,8 @@ namespace Gui void setMaxValue(int maxValue); private: void onEditTextChange(MyGUI::EditBox* sender); - void onKeyLostFocus(MyGUI::Widget* _new) final; - void onKeyButtonPressed(MyGUI::KeyCode key, MyGUI::Char character) final; + void onKeyLostFocus(MyGUI::Widget* _new) override; + void onKeyButtonPressed(MyGUI::KeyCode key, MyGUI::Char character) override; int mValue; diff --git a/components/widgets/sharedstatebutton.hpp b/components/widgets/sharedstatebutton.hpp index 859543d20..42c6424b2 100644 --- a/components/widgets/sharedstatebutton.hpp +++ b/components/widgets/sharedstatebutton.hpp @@ -23,13 +23,13 @@ namespace Gui protected: void updateButtonState(); - void onMouseButtonPressed(int _left, int _top, MyGUI::MouseButton _id) final; - void onMouseButtonReleased(int _left, int _top, MyGUI::MouseButton _id) final; - void onMouseSetFocus(MyGUI::Widget* _old) final; - void onMouseLostFocus(MyGUI::Widget* _new) final; - void baseUpdateEnable() final; + void onMouseButtonPressed(int _left, int _top, MyGUI::MouseButton _id) override; + void onMouseButtonReleased(int _left, int _top, MyGUI::MouseButton _id) override; + void onMouseSetFocus(MyGUI::Widget* _old) override; + void onMouseLostFocus(MyGUI::Widget* _new) override; + void baseUpdateEnable() override; - void shutdownOverride() final; + void shutdownOverride() override; bool _setState(const std::string &_value); diff --git a/components/widgets/windowcaption.hpp b/components/widgets/windowcaption.hpp index f8c1a310c..256c82831 100644 --- a/components/widgets/windowcaption.hpp +++ b/components/widgets/windowcaption.hpp @@ -14,11 +14,11 @@ namespace Gui public: WindowCaption(); - void setCaption(const MyGUI::UString &_value) final; - void initialiseOverride() final; + void setCaption(const MyGUI::UString &_value) override; + void initialiseOverride() override; - void setSize(const MyGUI::IntSize& _value) final; - void setCoord(const MyGUI::IntCoord& _value) final; + void setSize(const MyGUI::IntSize& _value) override; + void setCoord(const MyGUI::IntCoord& _value) override; private: MyGUI::Widget* mLeft; diff --git a/docs/source/reference/modding/settings/GUI.rst b/docs/source/reference/modding/settings/GUI.rst index 349a98697..cad04ab5c 100644 --- a/docs/source/reference/modding/settings/GUI.rst +++ b/docs/source/reference/modding/settings/GUI.rst @@ -72,7 +72,7 @@ The Bethesda provided assets have a 4:3 aspect ratio, but other assets are permi If this setting is false, the assets will be centered in their correct aspect ratio, with black bars filling the remainder of the screen. -This setting can only be configured by editing the settings configuration file. +This setting can be configured in the Interface section of Advanced tab of the launcher. subtitles --------- diff --git a/docs/source/reference/modding/settings/index.rst b/docs/source/reference/modding/settings/index.rst index 2261fe8e1..586a99dbb 100644 --- a/docs/source/reference/modding/settings/index.rst +++ b/docs/source/reference/modding/settings/index.rst @@ -58,3 +58,4 @@ The ranges included with each setting are the physically possible ranges, not re windows navigator physics + models diff --git a/docs/source/reference/modding/settings/models.rst b/docs/source/reference/modding/settings/models.rst new file mode 100644 index 000000000..b0da374d6 --- /dev/null +++ b/docs/source/reference/modding/settings/models.rst @@ -0,0 +1,31 @@ +Models Settings +############### + +load unsupported nif files +-------------------------- + +:Type: boolean +:Range: True/False +:Default: False + +Allow the engine to load arbitrary NIF files as long as they appear to be valid. + +OpenMW has limited and **experimental** support for NIF files +that Morrowind itself cannot load, which normally goes unused. + +If enabled, this setting allows the NIF loader to make use of that functionality. + +.. warning:: + You must keep in mind that since the mentioned support is experimental, + loading unsupported NIF files may fail, and the degree of this failure may vary. + + In milder cases, OpenMW will reject the file anyway because + it lacks a definition for a certain record type that the file may use. + + In more severe cases OpenMW's incomplete understanding of a record type + can lead to memory corruption, freezes or even crashes. + + **Do not enable** this if you're not so sure that you know what you're doing. + +To help debug possible issues OpenMW will log its progress in loading +every file that uses an unsupported NIF version. diff --git a/docs/source/reference/modding/settings/terrain.rst b/docs/source/reference/modding/settings/terrain.rst index 824c27913..149ef979e 100644 --- a/docs/source/reference/modding/settings/terrain.rst +++ b/docs/source/reference/modding/settings/terrain.rst @@ -8,21 +8,19 @@ distant terrain :Range: True/False :Default: False -Controls whether the engine will use paging and LOD algorithms to load the terrain of the entire world at all times. +Controls whether the engine will use paging (chunking) and LOD algorithms to load the terrain of the entire world at all times. Otherwise, only the terrain of the surrounding cells is loaded. .. note:: When enabling distant terrain, make sure the 'viewing distance' in the camera section is set to a larger value so - that you can actually see the additional terrain. + that you can actually see the additional terrain and objects. To avoid frame drops as the player moves around, nearby terrain pages are always preloaded in the background, regardless of the preloading settings in the 'Cells' section, but the preloading of terrain behind a door or a travel destination, for example, will still be controlled by cell preloading settings. -The distant terrain engine is currently considered experimental -and may receive updates and/or further configuration options in the future. -The glaring omission of non-terrain objects in the distance somewhat limits this setting's usefulness. +The distant terrain engine is currently considered experimental and may receive updates in the future. vertex lod mod -------------- @@ -101,3 +99,107 @@ max composite geometry size Controls the maximum size of simple composite geometry chunk in cell units. With small values there will more draw calls and small textures, but higher values create more overdraw (not every texture layer is used everywhere). + +object paging +------------- + +:Type: boolean +:Range: True/False +:Default: True + +Controls whether the engine will use paging (chunking) algorithms to load non-terrain objects +outside of the active cell grid. + +Depending on the settings below every object in the game world has a chance +to be batched and be visible in the game world, effectively allowing +the engine to render distant objects with a relatively low performance impact automatically. + +In general, an object is more likely to be batched if the number of the object's vertices +and the corresponding memory cost of merging the object is low compared to +the expected number of the draw calls that are going to be optimized out. +This memory cost and the saved number of draw calls shall be called +the "merging cost" and the "merging benefit" in the following documentation. + +Objects that are scripted to disappear from the game world +will be handled properly as long as their scripts have a chance to actually disable them. + +This setting has no effect if distant terrain is disabled. + +object paging active grid +------------------------- +:Type: boolean +:Range: True/False +:Default: False + +Controls whether the objects in the active cells use the mentioned paging algorithms. +Active grid paging significantly improves the framerate when your setup is CPU-limited. + +.. note:: + Given that only 8 light sources may affect an object at a time at the moment, + lighting issues arising due to merged objects being considered a single object + may disrupt your gameplay experience. + +object paging merge factor +-------------------------- +:Type: float +:Range: >0 +:Default: 250.0 + +Affects the likelyhood of objects being merged. +Higher values improve the framerate at the cost of memory. + +Technically this is implemented as a multiplier to the merging benefit, and since +an object has a lot of vertices, sometimes in terms of hundreds and thousands, +and doesn't often need as much draw calls to be rendered (typically that number is in 1 or 2 digits) +this value needs to be large enough, as this is what makes +the merging cost and the merging benefit actually comparable for the sake of paging. + +object paging min size +---------------------- +:Type: float +:Range: >0 +:Default: 0.01 + +Controls how large an object must be to be visible in the scene. +The object's size is divided by its distance to the camera +and the result of the division is compared with this value. +The smaller this value is, the more objects you will see in the scene. + +object paging min size merge factor +----------------------------------- +:Type: float +:Range: >0 +:Default: 0.3 + +This setting gives inexpensive objects a chance to be rendered from a greater distance +even if the engine would rather discard them according to the previous setting. + +It controls the factor that the minimum size is multiplied by +roughly according to the following formula: + + factor = merge cost * min size cost multiplier / merge benefit + + factor = factor + (1 - factor) * min size merge factor + +Since the larger this factor is, the smaller chance a large object has to be rendered, +decreasing this value makes more objects visible in the scene +without impacting the performance as dramatically as the minimum size setting. + +object paging min size cost multiplier +-------------------------------------- +:Type: float +:Range: >0 +:Default: 25.0 + +This setting adjusts the calculated cost of merging an object used in the mentioned functionality. +The larger this value is, the less expensive objects can be before they are discarded. +See the formula above to figure out the math. + +object paging debug batches +--------------------------- +:Type: boolean +:Range: True/False +:Default: False + +This debug setting allows you to see what objects have been merged in the scene +by making them colored randomly. diff --git a/extern/recastnavigation/.id b/extern/recastnavigation/.id index 81e564671..b53727263 100644 --- a/extern/recastnavigation/.id +++ b/extern/recastnavigation/.id @@ -1 +1 @@ -57610fa6ef31b39020231906f8c5d40eaa8294ae +6624e7aef5e15df11cb2f5673574df8e4c96af6a diff --git a/extern/recastnavigation/CMakeLists.txt b/extern/recastnavigation/CMakeLists.txt index 0d31c2e36..cf35af1e8 100644 --- a/extern/recastnavigation/CMakeLists.txt +++ b/extern/recastnavigation/CMakeLists.txt @@ -13,6 +13,12 @@ SET(VERSION 1.0.0) option(RECASTNAVIGATION_STATIC "Build static libraries" ON) +if(MSVC AND BUILD_SHARED_LIBS) + set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) +endif() + +include(GNUInstallDirs) + add_subdirectory(DebugUtils) add_subdirectory(Detour) add_subdirectory(DetourCrowd) diff --git a/extern/recastnavigation/DebugUtils/CMakeLists.txt b/extern/recastnavigation/DebugUtils/CMakeLists.txt index 8b6a3fcf6..21d8f8f9d 100644 --- a/extern/recastnavigation/DebugUtils/CMakeLists.txt +++ b/extern/recastnavigation/DebugUtils/CMakeLists.txt @@ -1,12 +1,8 @@ file(GLOB SOURCES Source/*.cpp) - -if (RECASTNAVIGATION_STATIC) - add_library(DebugUtils STATIC ${SOURCES}) -else() - add_library(DebugUtils SHARED ${SOURCES}) -endif() +add_library(DebugUtils ${SOURCES}) add_library(RecastNavigation::DebugUtils ALIAS DebugUtils) +set_target_properties(DebugUtils PROPERTIES DEBUG_POSTFIX -d) set(DebugUtils_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Include") @@ -23,13 +19,18 @@ target_link_libraries(DebugUtils set_target_properties(DebugUtils PROPERTIES SOVERSION ${SOVERSION} VERSION ${VERSION} + COMPILE_PDB_OUTPUT_DIRECTORY . + COMPILE_PDB_NAME "DebugUtils-d" ) install(TARGETS DebugUtils - ARCHIVE DESTINATION lib - LIBRARY DESTINATION lib + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT library ) file(GLOB INCLUDES Include/*.h) -install(FILES ${INCLUDES} DESTINATION include) +install(FILES ${INCLUDES} DESTINATION + ${CMAKE_INSTALL_INCLUDEDIR}/recastnavigation) +install(FILES "$/DebugUtils-d.pdb" CONFIGURATIONS "Debug" DESTINATION "lib") diff --git a/extern/recastnavigation/Detour/CMakeLists.txt b/extern/recastnavigation/Detour/CMakeLists.txt index de88111d5..5cb47ec0e 100644 --- a/extern/recastnavigation/Detour/CMakeLists.txt +++ b/extern/recastnavigation/Detour/CMakeLists.txt @@ -1,12 +1,8 @@ file(GLOB SOURCES Source/*.cpp) - -if(RECASTNAVIGATION_STATIC) - add_library(Detour STATIC ${SOURCES}) -else() - add_library(Detour SHARED ${SOURCES}) -endif() +add_library(Detour ${SOURCES}) add_library(RecastNavigation::Detour ALIAS Detour) +set_target_properties(Detour PROPERTIES DEBUG_POSTFIX -d) set(Detour_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Include") @@ -17,13 +13,18 @@ target_include_directories(Detour PUBLIC set_target_properties(Detour PROPERTIES SOVERSION ${SOVERSION} VERSION ${VERSION} + COMPILE_PDB_OUTPUT_DIRECTORY . + COMPILE_PDB_NAME "Detour-d" ) install(TARGETS Detour - ARCHIVE DESTINATION lib - LIBRARY DESTINATION lib + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT library ) file(GLOB INCLUDES Include/*.h) -install(FILES ${INCLUDES} DESTINATION include) +install(FILES ${INCLUDES} DESTINATION + ${CMAKE_INSTALL_INCLUDEDIR}/recastnavigation) +install(FILES "$/Detour-d.pdb" CONFIGURATIONS "Debug" DESTINATION "lib") diff --git a/extern/recastnavigation/DetourCrowd/CMakeLists.txt b/extern/recastnavigation/DetourCrowd/CMakeLists.txt index 73cdf7ce8..d0e186be0 100644 --- a/extern/recastnavigation/DetourCrowd/CMakeLists.txt +++ b/extern/recastnavigation/DetourCrowd/CMakeLists.txt @@ -1,12 +1,8 @@ file(GLOB SOURCES Source/*.cpp) - -if (RECASTNAVIGATION_STATIC) - add_library(DetourCrowd STATIC ${SOURCES}) -else () - add_library(DetourCrowd SHARED ${SOURCES}) -endif () +add_library(DetourCrowd ${SOURCES}) add_library(RecastNavigation::DetourCrowd ALIAS DetourCrowd) +set_target_properties(DetourCrowd PROPERTIES DEBUG_POSTFIX -d) set(DetourCrowd_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Include") @@ -21,13 +17,18 @@ target_link_libraries(DetourCrowd set_target_properties(DetourCrowd PROPERTIES SOVERSION ${SOVERSION} VERSION ${VERSION} + COMPILE_PDB_OUTPUT_DIRECTORY . + COMPILE_PDB_NAME "DetourCrowd-d" ) install(TARGETS DetourCrowd - ARCHIVE DESTINATION lib - LIBRARY DESTINATION lib + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT library ) file(GLOB INCLUDES Include/*.h) -install(FILES ${INCLUDES} DESTINATION include) +install(FILES ${INCLUDES} DESTINATION + ${CMAKE_INSTALL_INCLUDEDIR}/recastnavigation) +install(FILES "$/DetourCrowd-d.pdb" CONFIGURATIONS "Debug" DESTINATION "lib") diff --git a/extern/recastnavigation/DetourCrowd/Source/DetourCrowd.cpp b/extern/recastnavigation/DetourCrowd/Source/DetourCrowd.cpp index 1e76e40ce..3f0311f7f 100644 --- a/extern/recastnavigation/DetourCrowd/Source/DetourCrowd.cpp +++ b/extern/recastnavigation/DetourCrowd/Source/DetourCrowd.cpp @@ -1409,12 +1409,14 @@ void dtCrowd::update(const float dt, dtCrowdAgentDebugInfo* debug) } // Update agents using off-mesh connection. - for (int i = 0; i < m_maxAgents; ++i) + for (int i = 0; i < nagents; ++i) { - dtCrowdAgentAnimation* anim = &m_agentAnims[i]; + dtCrowdAgent* ag = agents[i]; + const int idx = (int)(ag - m_agents); + dtCrowdAgentAnimation* anim = &m_agentAnims[idx]; if (!anim->active) continue; - dtCrowdAgent* ag = agents[i]; + anim->t += dt; if (anim->t > anim->tmax) diff --git a/extern/recastnavigation/DetourTileCache/CMakeLists.txt b/extern/recastnavigation/DetourTileCache/CMakeLists.txt index 121b8edcc..3703ebb92 100644 --- a/extern/recastnavigation/DetourTileCache/CMakeLists.txt +++ b/extern/recastnavigation/DetourTileCache/CMakeLists.txt @@ -1,12 +1,8 @@ file(GLOB SOURCES Source/*.cpp) - -if (RECASTNAVIGATION_STATIC) - add_library(DetourTileCache STATIC ${SOURCES}) -else () - add_library(DetourTileCache SHARED ${SOURCES}) -endif () +add_library(DetourTileCache ${SOURCES}) add_library(RecastNavigation::DetourTileCache ALIAS DetourTileCache) +set_target_properties(DetourTileCache PROPERTIES DEBUG_POSTFIX -d) set(DetourTileCache_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Include") @@ -21,14 +17,19 @@ target_link_libraries(DetourTileCache set_target_properties(DetourTileCache PROPERTIES SOVERSION ${SOVERSION} VERSION ${VERSION} + COMPILE_PDB_OUTPUT_DIRECTORY . + COMPILE_PDB_NAME "DetourTileCache-d" ) install(TARGETS DetourTileCache - ARCHIVE DESTINATION lib - LIBRARY DESTINATION lib + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT library ) file(GLOB INCLUDES Include/*.h) -install(FILES ${INCLUDES} DESTINATION include) +install(FILES ${INCLUDES} DESTINATION + ${CMAKE_INSTALL_INCLUDEDIR}/recastnavigation) +install(FILES "$/DetourTileCache-d.pdb" CONFIGURATIONS "Debug" DESTINATION "lib") diff --git a/extern/recastnavigation/Recast/CMakeLists.txt b/extern/recastnavigation/Recast/CMakeLists.txt index 5e843762e..360654464 100644 --- a/extern/recastnavigation/Recast/CMakeLists.txt +++ b/extern/recastnavigation/Recast/CMakeLists.txt @@ -1,12 +1,8 @@ file(GLOB SOURCES Source/*.cpp) - -if (RECASTNAVIGATION_STATIC) - add_library(Recast STATIC ${SOURCES}) -else () - add_library(Recast SHARED ${SOURCES}) -endif () +add_library(Recast ${SOURCES}) add_library(RecastNavigation::Recast ALIAS Recast) +set_target_properties(Recast PROPERTIES DEBUG_POSTFIX -d) set(Recast_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Include") @@ -17,13 +13,18 @@ target_include_directories(Recast PUBLIC set_target_properties(Recast PROPERTIES SOVERSION ${SOVERSION} VERSION ${VERSION} + COMPILE_PDB_OUTPUT_DIRECTORY . + COMPILE_PDB_NAME "Recast-d" ) install(TARGETS Recast - ARCHIVE DESTINATION lib - LIBRARY DESTINATION lib + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT library ) file(GLOB INCLUDES Include/*.h) -install(FILES ${INCLUDES} DESTINATION include) +install(FILES ${INCLUDES} DESTINATION + ${CMAKE_INSTALL_INCLUDEDIR}/recastnavigation) +install(FILES "$/Recast-d.pdb" CONFIGURATIONS "Debug" DESTINATION "lib") diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 9c2071096..8bf69ef9e 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -933,17 +933,24 @@ object shadows = false enable indoor shadows = true [Physics] -# how much background thread to use in the physics solver. 0 to disable (i.e solver run in the main thread) +# Set the number of background threads used for physics. +# If no background threads are used, physics calculations are processed in the main thread +# and the settings below have no effect. async num threads = 0 -# maintain a cache of lineofsight request in the bacground physics thread -# determines for how much frames an inactive lineofsight request should be kept updated in the cache -# -1 to disable (i.e the LOS will be calculated only on request) +# Set the number of frames an inactive line-of-sight request will be kept +# refreshed in the background physics thread cache. +# If this is set to -1, line-of-sight requests are never cached. lineofsight keep inactive cache = 0 -# wether to defer aabb update till before collision detection +# Defer bounding boxes update until collision detection. defer aabb update = true +[Models] +# Attempt to load any valid NIF file regardless of its version and track the progress. +# Loading arbitrary meshes is not advised and may cause instability. +load unsupported nif files = false + [VR] # Should match your real height in the format meters.centimeters. This is used to scale your position within the vr stage to better match your character. real height = 1.85 diff --git a/files/ui/advancedpage.ui b/files/ui/advancedpage.ui index c678ffbba..3f53180da 100644 --- a/files/ui/advancedpage.ui +++ b/files/ui/advancedpage.ui @@ -714,6 +714,16 @@ True: In non-combat mode camera is positioned behind the character's shoulder. C + + + + <html><head/><body><p>Stretch menus, load screens, etc. to the window aspect ratio.</p></body></html> + + + Stretch menu background + + +