diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c7a1a403f..20b233c66 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -147,6 +147,7 @@ Debian_Clang_tests: macOS11_Xcode12: extends: .MacOS image: macos-11-xcode-12 + allow_failure: true cache: key: macOS11_Xcode12.v1 variables: @@ -155,7 +156,6 @@ macOS11_Xcode12: macOS10.15_Xcode11: extends: .MacOS image: macos-10.15-xcode-11 - allow_failure: true cache: key: macOS10.15_Xcode11.v1 variables: diff --git a/CHANGELOG.md b/CHANGELOG.md index 87009f58d..ba8081dae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -100,6 +100,7 @@ Bug #5739: Saving and loading the save a second or two before hitting the ground doesn't count fall damage Bug #5758: Paralyzed actors behavior is inconsistent with vanilla Bug #5762: Movement solver is insufficiently robust + BUG #5800: Equipping a CE enchanted ring deselects an already equipped and selected enchanted ring from the spell menu Bug #5807: Video decoding crash on ARM Bug #5821: NPCs from mods getting removed if mod order was changed Bug #5835: OpenMW doesn't accept negative values for NPC's hello, alarm, fight, and flee @@ -118,6 +119,7 @@ Bug #5923: Clicking on empty spaces between journal entries might show random topics Bug #5934: AddItem command doesn't accept negative values Bug #5975: NIF controllers from sheath meshes are used + Bug #5991: Activate should always be allowed for inventory items Bug #5995: NiUVController doesn't calculate the UV offset properly Feature #390: 3rd person look "over the shoulder" Feature #832: OpenMW-CS: Handle deleted references diff --git a/CI/before_install.osx.sh b/CI/before_install.osx.sh index 51bcb9a59..ec19c7321 100755 --- a/CI/before_install.osx.sh +++ b/CI/before_install.osx.sh @@ -17,3 +17,6 @@ qmake --version curl -fSL -R -J https://downloads.openmw.org/osx/dependencies/openmw-deps-8f5ef6e.zip -o ~/openmw-deps.zip unzip -o ~/openmw-deps.zip -d /private/tmp/openmw-deps > /dev/null + +# additional libraries +[ -z "${TRAVIS}" ] && HOMEBREW_NO_AUTO_UPDATE=1 brew install fontconfig \ No newline at end of file diff --git a/CI/before_script.msvc.sh b/CI/before_script.msvc.sh index e1afdb059..d98c28038 100644 --- a/CI/before_script.msvc.sh +++ b/CI/before_script.msvc.sh @@ -73,9 +73,6 @@ CONFIGURATIONS=() TEST_FRAMEWORK="" GOOGLE_INSTALL_ROOT="" INSTALL_PREFIX="." -BULLET_DOUBLE=true -BULLET_DBL="" -BULLET_DBL_DISPLAY="Single precision" ACTIVATE_MSVC="" SINGLE_CONFIG="" @@ -99,9 +96,6 @@ while [ $# -gt 0 ]; do d ) SKIP_DOWNLOAD=true ;; - D ) - BULLET_DOUBLE=true ;; - e ) SKIP_EXTRACT=true ;; @@ -149,8 +143,6 @@ Options: For single-config generators, several configurations can be set up at once by specifying -c multiple times. -d Skip checking the downloads. - -D - Use double-precision Bullet -e Skip extracting dependencies. -h @@ -433,9 +425,6 @@ if [ -n "$SINGLE_CONFIG" ]; then if [ -n "$SKIP_DOWNLOAD" ]; then RECURSIVE_OPTIONS+=("-d") fi - if [ -n "$BULLET_DOUBLE" ]; then - RECURSIVE_OPTIONS+=("-D") - fi if [ -n "$SKIP_EXTRACT" ]; then RECURSIVE_OPTIONS+=("-e") fi @@ -508,12 +497,6 @@ if ! [ -z $UNITY_BUILD ]; then add_cmake_opts "-DOPENMW_UNITY_BUILD=True" fi -if [ -n "$BULLET_DOUBLE" ]; then - BULLET_DBL="-double" - BULLET_DBL_DISPLAY="Double precision" - add_cmake_opts "-DBULLET_USE_DOUBLES=True" -fi - echo echo "===================================" echo "Starting prebuild on MSVC${MSVC_DISPLAY_YEAR} WIN${BITS}" @@ -538,9 +521,9 @@ if [ -z $SKIP_DOWNLOAD ]; then fi # Bullet - download "Bullet 2.89 (${BULLET_DBL_DISPLAY})" \ - "https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/Bullet-2.89-msvc${MSVC_YEAR}-win${BITS}${BULLET_DBL}.7z" \ - "Bullet-2.89-msvc${MSVC_YEAR}-win${BITS}${BULLET_DBL}.7z" + download "Bullet 2.89" \ + "https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/Bullet-2.89-msvc${MSVC_YEAR}-win${BITS}-double.7z" \ + "Bullet-2.89-msvc${MSVC_YEAR}-win${BITS}-double.7z" # FFmpeg download "FFmpeg 4.2.2" \ @@ -680,15 +663,15 @@ fi cd $DEPS echo # Bullet -printf "Bullet 2.89 (${BULLET_DBL_DISPLAY})... " +printf "Bullet 2.89... " { cd $DEPS_INSTALL if [ -d Bullet ]; then printf -- "Exists. (No version checking) " elif [ -z $SKIP_EXTRACT ]; then rm -rf Bullet - eval 7z x -y "${DEPS}/Bullet-2.89-msvc${MSVC_YEAR}-win${BITS}${BULLET_DBL}.7z" $STRIP - mv "Bullet-2.89-msvc${MSVC_YEAR}-win${BITS}${BULLET_DBL}" Bullet + eval 7z x -y "${DEPS}/Bullet-2.89-msvc${MSVC_YEAR}-win${BITS}-double.7z" $STRIP + mv "Bullet-2.89-msvc${MSVC_YEAR}-win${BITS}-double" Bullet fi add_cmake_opts -DBULLET_ROOT="$(real_pwd)/Bullet" echo Done. diff --git a/CI/before_script.osx.sh b/CI/before_script.osx.sh index f9191eb89..265e05b8e 100755 --- a/CI/before_script.osx.sh +++ b/CI/before_script.osx.sh @@ -25,6 +25,5 @@ cmake \ -D BUILD_BSATOOL=TRUE \ -D BUILD_ESSIMPORTER=TRUE \ -D BUILD_NIFTEST=TRUE \ --D BULLET_USE_DOUBLES=TRUE \ -G"Unix Makefiles" \ .. diff --git a/CI/install_debian_deps.sh b/CI/install_debian_deps.sh index 490fad0da..2f905314b 100755 --- a/CI/install_debian_deps.sh +++ b/CI/install_debian_deps.sh @@ -22,12 +22,12 @@ declare -rA GROUPED_DEPS=( libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev libsdl2-dev libqt5opengl5-dev libopenal-dev libunshield-dev libtinyxml-dev libbullet-dev liblz4-dev libpng-dev libjpeg-dev + ca-certificates " # TODO: add librecastnavigation-dev when debian is ready # These dependencies can alternatively be built and linked statically. [openmw-deps-dynamic]="libmygui-dev libopenscenegraph-dev" - [coverity]="curl" # Pre-requisites for building MyGUI and OSG for static linking. @@ -64,4 +64,4 @@ export APT_CACHE_DIR="${PWD}/apt-cache" set -x mkdir -pv "$APT_CACHE_DIR" apt-get update -yq -apt-get -q -o dir::cache::archives="$APT_CACHE_DIR" install -y "${deps[@]}" +apt-get -q -o dir::cache::archives="$APT_CACHE_DIR" install -y --no-install-recommends "${deps[@]}" diff --git a/CMakeLists.txt b/CMakeLists.txt index 37d579d72..607a5d56b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,9 +13,6 @@ if(POLICY CMP0083) cmake_policy(SET CMP0083 NEW) endif() -# Detect OS -include(cmake/OSIdentity.cmake) - option(OPENMW_GL4ES_MANUAL_INIT "Manually initialize gl4es. This is more reliable on platforms without a windowing system. Requires gl4es to be configured with -DNOEGL=ON -DNO_LOADER=ON -DNO_INIT_CONSTRUCTOR=ON." OFF) if(OPENMW_GL4ES_MANUAL_INIT) add_definitions(-DOPENMW_GL4ES_MANUAL_INIT) @@ -35,7 +32,6 @@ option(BUILD_DOCS "Build documentation." OFF ) option(BUILD_WITH_CODE_COVERAGE "Enable code coverage with gconv" OFF) option(BUILD_UNITTESTS "Enable Unittests with Google C++ Unittest" OFF) option(BUILD_BENCHMARKS "Build benchmarks with Google Benchmark" OFF) -option(BULLET_USE_DOUBLES "Use double precision for Bullet" ON) option(BUILD_OPENMW_MP "Build OpenMW-MP" ON) option(BUILD_BROWSER "Build tes3mp Server Browser" ON) option(BUILD_MASTER "Build tes3mp Master Server" OFF) @@ -286,16 +282,16 @@ if(FFmpeg_FOUND) set(FFVER_OK FALSE) endif() endif() + + if(NOT FFVER_OK AND NOT APPLE) # unable to detect on version on MacOS < 11.0 + message(FATAL_ERROR "FFmpeg version is too old, 3.2 is required" ) + endif() endif() if(NOT FFmpeg_FOUND) message(FATAL_ERROR "FFmpeg was not found" ) endif() -if(NOT FFVER_OK) - message(FATAL_ERROR "FFmpeg version is too old, 3.2 is required" ) -endif() - if(WIN32) message("Can not detect FFmpeg version, at least the 3.2 is required" ) endif() @@ -339,7 +335,31 @@ if(OPENMW_USE_SYSTEM_BULLET) set(REQUIRED_BULLET_VERSION 283) # but for build testing, 283 is fine endif() - find_package(Bullet ${REQUIRED_BULLET_VERSION} REQUIRED COMPONENTS BulletCollision LinearMath) + # First, try BulletConfig-float64.cmake which comes with Debian derivatives. + # This file does not define the Bullet version in a CMake-friendly way. + find_package(Bullet CONFIGS BulletConfig-float64.cmake QUIET COMPONENTS BulletCollision LinearMath) + if (BULLET_FOUND) + string(REPLACE "." "" _bullet_version_num ${BULLET_VERSION_STRING}) + if (_bullet_version_num VERSION_LESS REQUIRED_BULLET_VERSION) + message(FATAL_ERROR "System bullet version too old, OpenMW requires at least ${REQUIRED_BULLET_VERSION}, got ${_bullet_version_num}") + endif() + # Fix the relative include: + set(BULLET_INCLUDE_DIRS "${BULLET_ROOT_DIR}/${BULLET_INCLUDE_DIRS}") + include(FindPackageMessage) + find_package_message(Bullet "Found Bullet: ${BULLET_LIBRARIES} ${BULLET_VERSION_STRING}" "${BULLET_VERSION_STRING}-float64") + else() + find_package(Bullet ${REQUIRED_BULLET_VERSION} REQUIRED COMPONENTS BulletCollision LinearMath) + endif() + + # Only link the Bullet libraries that we need: + string(REGEX MATCHALL "((optimized|debug);)?[^;]*(BulletCollision|LinearMath)[^;]*" BULLET_LIBRARIES "${BULLET_LIBRARIES}") + + include(cmake/CheckBulletPrecision.cmake) + if (HAS_DOUBLE_PRECISION_BULLET) + message(STATUS "Bullet uses double precision") + else() + message(FATAL_ERROR "Bullet does not uses double precision") + endif() endif() if (NOT WIN32 AND BUILD_WIZARD) # windows users can just run the morrowind installer diff --git a/apps/benchmarks/detournavigator/navmeshtilescache.cpp b/apps/benchmarks/detournavigator/navmeshtilescache.cpp index 10aa0672a..2c7a981ad 100644 --- a/apps/benchmarks/detournavigator/navmeshtilescache.cpp +++ b/apps/benchmarks/detournavigator/navmeshtilescache.cpp @@ -114,9 +114,8 @@ namespace generateAreaTypes(std::back_inserter(areaTypes), indices.size() / 3, random); std::vector water; generateWater(std::back_inserter(water), 2, random); - const std::size_t trianglesPerChunk = 256; RecastMesh recastMesh(generation, revision, std::move(indices), std::move(vertices), - std::move(areaTypes), std::move(water), trianglesPerChunk); + std::move(areaTypes), std::move(water)); std::vector offMeshConnections; generateOffMeshConnection(std::back_inserter(offMeshConnections), 300, random); return Key {agentHalfExtents, tilePosition, std::move(recastMesh), std::move(offMeshConnections)}; diff --git a/apps/launcher/advancedpage.cpp b/apps/launcher/advancedpage.cpp index 91e3842fa..4ba732234 100644 --- a/apps/launcher/advancedpage.cpp +++ b/apps/launcher/advancedpage.cpp @@ -3,10 +3,8 @@ #include #include -#include #include #include -#include #include #include #include diff --git a/apps/launcher/advancedpage.hpp b/apps/launcher/advancedpage.hpp index a373fae43..93b818dbb 100644 --- a/apps/launcher/advancedpage.hpp +++ b/apps/launcher/advancedpage.hpp @@ -1,7 +1,6 @@ #ifndef ADVANCEDPAGE_H #define ADVANCEDPAGE_H -#include #include #include diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index 81544b094..956483a3f 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -4,7 +4,6 @@ #include #include -#include #include #include #include @@ -14,7 +13,6 @@ #include #include -#include #include #include diff --git a/apps/launcher/datafilespage.hpp b/apps/launcher/datafilespage.hpp index 3d200e6d2..5a7a6dc6e 100644 --- a/apps/launcher/datafilespage.hpp +++ b/apps/launcher/datafilespage.hpp @@ -6,7 +6,6 @@ #include -#include #include class QSortFilterProxyModel; diff --git a/apps/launcher/graphicspage.hpp b/apps/launcher/graphicspage.hpp index 35f711500..7801e6475 100644 --- a/apps/launcher/graphicspage.hpp +++ b/apps/launcher/graphicspage.hpp @@ -1,8 +1,6 @@ #ifndef GRAPHICSPAGE_H #define GRAPHICSPAGE_H -#include - #include "ui_graphicspage.h" #include diff --git a/apps/launcher/main.cpp b/apps/launcher/main.cpp index f15abafce..9c9acb4a1 100644 --- a/apps/launcher/main.cpp +++ b/apps/launcher/main.cpp @@ -1,10 +1,8 @@ #include -#include #include #include #include -#include #ifdef MAC_OS_X_VERSION_MIN_REQUIRED #undef MAC_OS_X_VERSION_MIN_REQUIRED diff --git a/apps/launcher/maindialog.cpp b/apps/launcher/maindialog.cpp index 82c185e56..2592af817 100644 --- a/apps/launcher/maindialog.cpp +++ b/apps/launcher/maindialog.cpp @@ -5,15 +5,12 @@ #include #include -#include #include #include #include #include #include -#include - #include "playpage.hpp" #include "graphicspage.hpp" #include "datafilespage.hpp" diff --git a/apps/launcher/maindialog.hpp b/apps/launcher/maindialog.hpp index de7423330..80e014e28 100644 --- a/apps/launcher/maindialog.hpp +++ b/apps/launcher/maindialog.hpp @@ -1,8 +1,6 @@ #ifndef MAINDIALOG_H #define MAINDIALOG_H -#include -#include #ifndef Q_MOC_RUN #include diff --git a/apps/launcher/playpage.hpp b/apps/launcher/playpage.hpp index b0bd3ee27..8f414dc6a 100644 --- a/apps/launcher/playpage.hpp +++ b/apps/launcher/playpage.hpp @@ -1,8 +1,6 @@ #ifndef PLAYPAGE_H #define PLAYPAGE_H -#include - #include "ui_playpage.h" class QComboBox; diff --git a/apps/launcher/sdlinit.cpp b/apps/launcher/sdlinit.cpp index 1fe1fd4c2..4717fb281 100644 --- a/apps/launcher/sdlinit.cpp +++ b/apps/launcher/sdlinit.cpp @@ -1,7 +1,6 @@ #include #include -#include bool initSDL() { diff --git a/apps/launcher/settingspage.cpp b/apps/launcher/settingspage.cpp index 59d7cfd25..ca7fd028a 100644 --- a/apps/launcher/settingspage.cpp +++ b/apps/launcher/settingspage.cpp @@ -2,7 +2,6 @@ #include #include -#include #include #include diff --git a/apps/launcher/settingspage.hpp b/apps/launcher/settingspage.hpp index 97b099dd6..df7c0e8eb 100644 --- a/apps/launcher/settingspage.hpp +++ b/apps/launcher/settingspage.hpp @@ -1,9 +1,6 @@ #ifndef SETTINGSPAGE_HPP #define SETTINGSPAGE_HPP -#include -#include - #include #include "ui_settingspage.h" diff --git a/apps/launcher/utils/cellnameloader.hpp b/apps/launcher/utils/cellnameloader.hpp index c58d09226..899ff75ad 100644 --- a/apps/launcher/utils/cellnameloader.hpp +++ b/apps/launcher/utils/cellnameloader.hpp @@ -1,7 +1,6 @@ #ifndef OPENMW_CELLNAMELOADER_H #define OPENMW_CELLNAMELOADER_H -#include #include #include diff --git a/apps/launcher/utils/lineedit.hpp b/apps/launcher/utils/lineedit.hpp index da28e858c..89de39588 100644 --- a/apps/launcher/utils/lineedit.hpp +++ b/apps/launcher/utils/lineedit.hpp @@ -11,7 +11,6 @@ #define LINEEDIT_H #include -#include #include #include diff --git a/apps/launcher/utils/profilescombobox.cpp b/apps/launcher/utils/profilescombobox.cpp index 462c2ebc2..af349ddff 100644 --- a/apps/launcher/utils/profilescombobox.cpp +++ b/apps/launcher/utils/profilescombobox.cpp @@ -1,5 +1,4 @@ #include -#include #include #include #include diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index f61266286..d94a5f0f3 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -407,7 +407,7 @@ namespace MWBase virtual MWWorld::Ptr moveObject(const MWWorld::Ptr &ptr, MWWorld::CellStore* newCell, float x, float y, float z, bool movePhysics=true) = 0; ///< @return an updated Ptr - virtual MWWorld::Ptr moveObjectBy(const MWWorld::Ptr &ptr, osg::Vec3f vec, bool moveToActive) = 0; + virtual MWWorld::Ptr moveObjectBy(const MWWorld::Ptr &ptr, osg::Vec3f vec, bool moveToActive, bool ignoreCollisions) = 0; ///< @return an updated Ptr virtual void scaleObject (const MWWorld::Ptr& ptr, float scale) = 0; diff --git a/apps/openmw/mwphysics/actor.cpp b/apps/openmw/mwphysics/actor.cpp index 6dde0c16f..c9eac2e1c 100644 --- a/apps/openmw/mwphysics/actor.cpp +++ b/apps/openmw/mwphysics/actor.cpp @@ -158,7 +158,7 @@ void Actor::updatePosition() mSimulationPosition = mWorldPosition; mPositionOffset = osg::Vec3f(); mStandingOnPtr = nullptr; - mSkipSimulation = true; + mSkipCollisions = true; } void Actor::updateWorldPosition() @@ -175,9 +175,7 @@ osg::Vec3f Actor::getWorldPosition() const void Actor::setSimulationPosition(const osg::Vec3f& position) { - if (!mSkipSimulation) - mSimulationPosition = position; - mSkipSimulation = false; + mSimulationPosition = position; } osg::Vec3f Actor::getSimulationPosition() const @@ -211,21 +209,19 @@ osg::Vec3f Actor::getCollisionObjectPosition() const bool Actor::setPosition(const osg::Vec3f& position) { std::scoped_lock lock(mPositionMutex); - // position is being forced, ignore simulation results until we sync up - if (mSkipSimulation) - return false; - bool hasChanged = mPosition != position || mPositionOffset.length() != 0 || mWorldPositionChanged; updateWorldPosition(); applyOffsetChange(); + bool hasChanged = mPosition != position || mWorldPositionChanged; mPreviousPosition = mPosition; mPosition = position; return hasChanged; } -void Actor::adjustPosition(const osg::Vec3f& offset) +void Actor::adjustPosition(const osg::Vec3f& offset, bool ignoreCollisions) { std::scoped_lock lock(mPositionMutex); mPositionOffset += offset; + mSkipCollisions = mSkipCollisions || ignoreCollisions; } void Actor::applyOffsetChange() @@ -337,4 +333,9 @@ void Actor::setStandingOnPtr(const MWWorld::Ptr& ptr) mStandingOnPtr = ptr; } +bool Actor::skipCollisions() +{ + return std::exchange(mSkipCollisions, false); +} + } diff --git a/apps/openmw/mwphysics/actor.hpp b/apps/openmw/mwphysics/actor.hpp index 472a79bff..54f9009bf 100644 --- a/apps/openmw/mwphysics/actor.hpp +++ b/apps/openmw/mwphysics/actor.hpp @@ -101,7 +101,7 @@ namespace MWPhysics void updatePosition(); // register a position offset that will be applied during simulation. - void adjustPosition(const osg::Vec3f& offset); + void adjustPosition(const osg::Vec3f& offset, bool ignoreCollisions); // apply position offset. Can't be called during simulation void applyOffsetChange(); @@ -177,6 +177,8 @@ namespace MWPhysics mLastStuckPosition = position; } + bool skipCollisions(); + private: MWWorld::Ptr mStandingOnPtr; /// Removes then re-adds the collision object to the dynamics world @@ -206,7 +208,7 @@ namespace MWPhysics osg::Vec3f mPreviousPosition; osg::Vec3f mPositionOffset; bool mWorldPositionChanged; - bool mSkipSimulation; + bool mSkipCollisions; btTransform mLocalTransform; mutable std::mutex mPositionMutex; diff --git a/apps/openmw/mwphysics/movementsolver.cpp b/apps/openmw/mwphysics/movementsolver.cpp index 1c04ee65a..fd0e090fc 100644 --- a/apps/openmw/mwphysics/movementsolver.cpp +++ b/apps/openmw/mwphysics/movementsolver.cpp @@ -131,7 +131,7 @@ namespace MWPhysics // Reset per-frame data physicActor->setWalkingOnWater(false); // Anything to collide with? - if(!physicActor->getCollisionMode()) + if(!physicActor->getCollisionMode() || actor.mSkipCollisionDetection) { actor.mPosition += (osg::Quat(refpos.rot[0], osg::Vec3f(-1, 0, 0)) * osg::Quat(refpos.rot[2], osg::Vec3f(0, 0, -1)) @@ -437,7 +437,7 @@ namespace MWPhysics return; auto* physicActor = actor.mActorRaw; - if(!physicActor->getCollisionMode()) // noclipping/tcl + if(!physicActor->getCollisionMode() || actor.mSkipCollisionDetection) // noclipping/tcl return; auto* collisionObject = physicActor->getCollisionObject(); diff --git a/apps/openmw/mwphysics/mtphysics.cpp b/apps/openmw/mwphysics/mtphysics.cpp index 4957ef422..95a741825 100644 --- a/apps/openmw/mwphysics/mtphysics.cpp +++ b/apps/openmw/mwphysics/mtphysics.cpp @@ -361,7 +361,6 @@ namespace MWPhysics for (const auto& [_, actor] : actors) { actor->updatePosition(); - actor->setSimulationPosition(actor->getWorldPosition()); // updatePosition skip next simulation, now we need to "consume" it actor->updateCollisionObjectPosition(); mMovedActors.emplace_back(actor->getPtr()); } diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 37ee0e056..2aa8e6d05 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -978,9 +978,7 @@ namespace MWPhysics void ActorFrameData::updatePosition(btCollisionWorld* world) { mActorRaw->updateWorldPosition(); - // If physics runs "fast enough", position are interpolated without simulation - // By calling this here, we are sure that offsets are applied at least once per frame, - // regardless of simulation speed. + mSkipCollisionDetection = mActorRaw->skipCollisions(); mActorRaw->applyOffsetChange(); mPosition = mActorRaw->getPosition(); if (mWaterCollision && mPosition.z() < mWaterlevel && canMoveToWaterSurface(mActorRaw, mWaterlevel, world)) diff --git a/apps/openmw/mwphysics/physicssystem.hpp b/apps/openmw/mwphysics/physicssystem.hpp index 978eb4d0d..9250f188e 100644 --- a/apps/openmw/mwphysics/physicssystem.hpp +++ b/apps/openmw/mwphysics/physicssystem.hpp @@ -91,6 +91,7 @@ namespace MWPhysics bool mFloatToSurface; bool mNeedLand; bool mWaterCollision; + bool mSkipCollisionDetection; float mWaterlevel; float mSlowFall; float mOldHeight; diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 8c937cf5c..199ff4ca6 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -394,7 +394,7 @@ namespace MWScript MWWorld::Ptr ptr = R()(runtime); - if (ptr.getRefData().activateByScript()) + if (ptr.getRefData().activateByScript() || ptr.getContainerStore()) context.executeActivation(ptr, MWMechanics::getPlayer()); } }; diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 8f4094521..a03b662aa 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -49,7 +49,7 @@ namespace MWScript std::vector actors; MWBase::Environment::get().getWorld()->getActorsStandingOn (ptr, actors); for (auto& actor : actors) - MWBase::Environment::get().getWorld()->moveObjectBy(actor, diff, false); + MWBase::Environment::get().getWorld()->moveObjectBy(actor, diff, false, false); } template @@ -363,7 +363,7 @@ namespace MWScript } dynamic_cast(runtime.getContext()).updatePtr(ptr, - MWBase::Environment::get().getWorld()->moveObjectBy(ptr, newPos - curPos, true)); + MWBase::Environment::get().getWorld()->moveObjectBy(ptr, newPos - curPos, true, true)); } }; @@ -902,7 +902,7 @@ namespace MWScript // This approach can be used to create elevators. moveStandingActors(ptr, diff); dynamic_cast(runtime.getContext()).updatePtr(ptr, - MWBase::Environment::get().getWorld()->moveObjectBy(ptr, diff, false)); + MWBase::Environment::get().getWorld()->moveObjectBy(ptr, diff, false, true)); } }; @@ -938,7 +938,7 @@ namespace MWScript // This approach can be used to create elevators. moveStandingActors(ptr, diff); dynamic_cast(runtime.getContext()).updatePtr(ptr, - MWBase::Environment::get().getWorld()->moveObjectBy(ptr, diff, false)); + MWBase::Environment::get().getWorld()->moveObjectBy(ptr, diff, false, true)); } }; diff --git a/apps/openmw/mwworld/actionequip.cpp b/apps/openmw/mwworld/actionequip.cpp index 4cb0dbe51..92cd04438 100644 --- a/apps/openmw/mwworld/actionequip.cpp +++ b/apps/openmw/mwworld/actionequip.cpp @@ -91,13 +91,29 @@ namespace MWWorld // move all slots one towards begin(), then equip the item in the slot that is now free if (slot == slots_.first.end()) { - for (slot=slots_.first.begin();slot!=slots_.first.end(); ++slot) + ContainerStoreIterator enchItem = invStore.getSelectedEnchantItem(); + bool reEquip = false; + for (slot = slots_.first.begin(); slot != slots_.first.end(); ++slot) { invStore.unequipSlot(*slot, actor, false); - if (slot+1 != slots_.first.end()) - invStore.equip(*slot, invStore.getSlot(*(slot+1)), actor); + if (slot + 1 != slots_.first.end()) + { + invStore.equip(*slot, invStore.getSlot(*(slot + 1)), actor); + } else + { invStore.equip(*slot, it, actor); + } + + //Fix for issue of selected enchated item getting remmoved on cycle + if (invStore.getSlot(*slot) == enchItem) + { + reEquip = true; + } + } + if (reEquip) + { + invStore.setSelectedEnchantItem(enchItem); } } } diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index 6bc60968c..dfd69b2e5 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -448,7 +448,7 @@ namespace MWWorld void CellPreloader::abortTerrainPreloadExcept(const CellPreloader::PositionCellGrid *exceptPos) { const float resetThreshold = ESM::Land::REAL_SIZE; - for (auto pos : mTerrainPreloadPositions) + for (const auto& pos : mTerrainPreloadPositions) if (exceptPos && (pos.first-exceptPos->first).length2() < resetThreshold*resetThreshold && pos.second == exceptPos->second) return; if (mTerrainPreloadItem && !mTerrainPreloadItem->isDone()) @@ -461,10 +461,10 @@ namespace MWWorld bool contains(const std::vector& container, const std::vector& contained) { - for (auto pos : contained) + for (const auto& pos : contained) { bool found = false; - for (auto pos2 : container) + for (const auto& pos2 : container) { if ((pos.first-pos2.first).length2() < 1 && pos.second == pos2.second) { diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index 3451da611..0eca2804f 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -8,28 +8,11 @@ #include #include +#include #include namespace { - template - class GetRecords - { - const std::string mFind; - std::vector *mRecords; - - public: - GetRecords(const std::string &str, std::vector *records) - : mFind(Misc::StringUtils::lowerCase(str)), mRecords(records) - { } - - void operator()(const T *item) - { - if(Misc::StringUtils::ciCompareLen(mFind, item->mId, mFind.size()) == 0) - mRecords->push_back(item); - } - }; - struct Compare { bool operator()(const ESM::Land *x, const ESM::Land *y) { @@ -169,7 +152,11 @@ namespace MWWorld const T *Store::searchRandom(const std::string &id) const { std::vector results; - std::for_each(mShared.begin(), mShared.end(), GetRecords(id, &results)); + std::copy_if(mShared.begin(), mShared.end(), std::back_inserter(results), + [&id](const T* item) + { + return Misc::StringUtils::ciCompareLen(id, item->mId, id.size()) == 0; + }); if(!results.empty()) return results[Misc::Rng::rollDice(results.size())]; return nullptr; @@ -186,17 +173,6 @@ namespace MWWorld return ptr; } template - const T *Store::findRandom(const std::string &id) const - { - const T *ptr = searchRandom(id); - if(ptr == nullptr) - { - const std::string msg = T::getRecordType() + " starting with '" + id + "' not found"; - throw std::runtime_error(msg); - } - return ptr; - } - template RecordId Store::load(ESM::ESMReader &esm) { T record; diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index 4cc817dd6..8a964f982 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -179,10 +179,6 @@ namespace MWWorld const T *find(const std::string &id) const; - /** Returns a random record that starts with the named ID. An exception is thrown if none - * are found. */ - const T *findRandom(const std::string &id) const; - iterator begin() const; iterator end() const; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index ec7ad0d3f..4d138e023 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1441,12 +1441,12 @@ namespace MWWorld return moveObject(ptr, cell, x, y, z, movePhysics); } - MWWorld::Ptr World::moveObjectBy(const Ptr& ptr, osg::Vec3f vec, bool moveToActive) + MWWorld::Ptr World::moveObjectBy(const Ptr& ptr, osg::Vec3f vec, bool moveToActive, bool ignoreCollisions) { auto* actor = mPhysics->getActor(ptr); osg::Vec3f newpos = ptr.getRefData().getPosition().asVec3() + vec; if (actor) - actor->adjustPosition(vec); + actor->adjustPosition(vec, ignoreCollisions); if (ptr.getClass().isActor()) return moveObject(ptr, newpos.x(), newpos.y(), newpos.z(), false, moveToActive && ptr != getPlayerPtr()); return moveObject(ptr, newpos.x(), newpos.y(), newpos.z()); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 8fd36a5d7..ab803246a 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -476,7 +476,7 @@ namespace MWWorld MWWorld::Ptr moveObject (const Ptr& ptr, CellStore* newCell, float x, float y, float z, bool movePhysics=true) override; ///< @return an updated Ptr - MWWorld::Ptr moveObjectBy(const Ptr& ptr, osg::Vec3f vec, bool moveToActive) override; + MWWorld::Ptr moveObjectBy(const Ptr& ptr, osg::Vec3f vec, bool moveToActive, bool ignoreCollisions) override; ///< @return an updated Ptr void scaleObject (const Ptr& ptr, float scale) override; diff --git a/apps/openmw_test_suite/detournavigator/navigator.cpp b/apps/openmw_test_suite/detournavigator/navigator.cpp index 3b7dc4915..619276588 100644 --- a/apps/openmw_test_suite/detournavigator/navigator.cpp +++ b/apps/openmw_test_suite/detournavigator/navigator.cpp @@ -68,7 +68,6 @@ namespace mSettings.mMaxNavMeshTilesCacheSize = 1024 * 1024; mSettings.mMaxPolygonPathSize = 1024; mSettings.mMaxSmoothPathSize = 1024; - mSettings.mTrianglesPerChunk = 256; mSettings.mMaxPolys = 4096; mSettings.mMaxTilesNumber = 512; mSettings.mMinUpdateInterval = std::chrono::milliseconds(50); diff --git a/apps/openmw_test_suite/detournavigator/navmeshtilescache.cpp b/apps/openmw_test_suite/detournavigator/navmeshtilescache.cpp index 5d4b1602f..a16c3af1d 100644 --- a/apps/openmw_test_suite/detournavigator/navmeshtilescache.cpp +++ b/apps/openmw_test_suite/detournavigator/navmeshtilescache.cpp @@ -31,9 +31,7 @@ namespace const std::vector mVertices {{0, 0, 0, 1, 0, 0, 1, 1, 0}}; const std::vector mAreaTypes {1, AreaType_ground}; const std::vector mWater {}; - const std::size_t mTrianglesPerChunk {1}; - const RecastMesh mRecastMesh {mGeneration, mRevision, mIndices, mVertices, - mAreaTypes, mWater, mTrianglesPerChunk}; + const RecastMesh mRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, mWater}; const std::vector mOffMeshConnections {}; unsigned char* const mData = reinterpret_cast(dtAlloc(1, DT_ALLOC_PERM)); NavMeshData mNavMeshData {mData, 1}; @@ -130,8 +128,7 @@ namespace const std::size_t maxSize = 1; NavMeshTilesCache cache(maxSize); const std::vector water {1, RecastMesh::Water {1, btTransform::getIdentity()}}; - const RecastMesh unexistentRecastMesh {mGeneration, mRevision, mIndices, mVertices, - mAreaTypes, water, mTrianglesPerChunk}; + const RecastMesh unexistentRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, water}; cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, std::move(mNavMeshData)); EXPECT_FALSE(cache.get(mAgentHalfExtents, mTilePosition, unexistentRecastMesh, mOffMeshConnections)); @@ -145,8 +142,7 @@ namespace NavMeshTilesCache cache(maxSize); const std::vector water {1, RecastMesh::Water {1, btTransform::getIdentity()}}; - const RecastMesh anotherRecastMesh {mGeneration, mRevision, mIndices, mVertices, - mAreaTypes, water, mTrianglesPerChunk}; + const RecastMesh anotherRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, water}; const auto anotherData = reinterpret_cast(dtAlloc(1, DT_ALLOC_PERM)); NavMeshData anotherNavMeshData {anotherData, 1}; @@ -166,8 +162,7 @@ namespace NavMeshTilesCache cache(maxSize); const std::vector water {1, RecastMesh::Water {1, btTransform::getIdentity()}}; - const RecastMesh anotherRecastMesh {mGeneration, mRevision, mIndices, mVertices, - mAreaTypes, water, mTrianglesPerChunk}; + const RecastMesh anotherRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, water}; const auto anotherData = reinterpret_cast(dtAlloc(1, DT_ALLOC_PERM)); NavMeshData anotherNavMeshData {anotherData, 1}; @@ -186,13 +181,13 @@ namespace const std::vector leastRecentlySetWater {1, RecastMesh::Water {1, btTransform::getIdentity()}}; const RecastMesh leastRecentlySetRecastMesh {mGeneration, mRevision, mIndices, mVertices, - mAreaTypes, leastRecentlySetWater, mTrianglesPerChunk}; + mAreaTypes, leastRecentlySetWater}; const auto leastRecentlySetData = reinterpret_cast(dtAlloc(1, DT_ALLOC_PERM)); NavMeshData leastRecentlySetNavMeshData {leastRecentlySetData, 1}; const std::vector mostRecentlySetWater {1, RecastMesh::Water {2, btTransform::getIdentity()}}; const RecastMesh mostRecentlySetRecastMesh {mGeneration, mRevision, mIndices, mVertices, - mAreaTypes, mostRecentlySetWater, mTrianglesPerChunk}; + mAreaTypes, mostRecentlySetWater}; const auto mostRecentlySetData = reinterpret_cast(dtAlloc(1, DT_ALLOC_PERM)); NavMeshData mostRecentlySetNavMeshData {mostRecentlySetData, 1}; @@ -218,13 +213,13 @@ namespace const std::vector leastRecentlyUsedWater {1, RecastMesh::Water {1, btTransform::getIdentity()}}; const RecastMesh leastRecentlyUsedRecastMesh {mGeneration, mRevision, mIndices, mVertices, - mAreaTypes, leastRecentlyUsedWater, mTrianglesPerChunk}; + mAreaTypes, leastRecentlyUsedWater}; const auto leastRecentlyUsedData = reinterpret_cast(dtAlloc(1, DT_ALLOC_PERM)); NavMeshData leastRecentlyUsedNavMeshData {leastRecentlyUsedData, 1}; const std::vector mostRecentlyUsedWater {1, RecastMesh::Water {2, btTransform::getIdentity()}}; const RecastMesh mostRecentlyUsedRecastMesh {mGeneration, mRevision, mIndices, mVertices, - mAreaTypes, mostRecentlyUsedWater, mTrianglesPerChunk}; + mAreaTypes, mostRecentlyUsedWater}; const auto mostRecentlyUsedData = reinterpret_cast(dtAlloc(1, DT_ALLOC_PERM)); NavMeshData mostRecentlyUsedNavMeshData {mostRecentlyUsedData, 1}; @@ -261,7 +256,7 @@ namespace NavMeshTilesCache cache(maxSize); const std::vector water {1, RecastMesh::Water {1, btTransform::getIdentity()}}; - const RecastMesh tooLargeRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, water, mTrianglesPerChunk}; + const RecastMesh tooLargeRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, water}; const auto tooLargeData = reinterpret_cast(dtAlloc(2, DT_ALLOC_PERM)); NavMeshData tooLargeNavMeshData {tooLargeData, 2}; @@ -280,13 +275,13 @@ namespace NavMeshTilesCache cache(maxSize); const std::vector anotherWater {1, RecastMesh::Water {1, btTransform::getIdentity()}}; - const RecastMesh anotherRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, anotherWater, mTrianglesPerChunk}; + const RecastMesh anotherRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, anotherWater}; const auto anotherData = reinterpret_cast(dtAlloc(1, DT_ALLOC_PERM)); NavMeshData anotherNavMeshData {anotherData, 1}; const std::vector tooLargeWater {1, RecastMesh::Water {2, btTransform::getIdentity()}}; const RecastMesh tooLargeRecastMesh {mGeneration, mRevision, mIndices, mVertices, - mAreaTypes, tooLargeWater, mTrianglesPerChunk}; + mAreaTypes, tooLargeWater}; const auto tooLargeData = reinterpret_cast(dtAlloc(2, DT_ALLOC_PERM)); NavMeshData tooLargeNavMeshData {tooLargeData, 2}; @@ -310,7 +305,7 @@ namespace const std::vector water {1, RecastMesh::Water {1, btTransform::getIdentity()}}; const RecastMesh anotherRecastMesh {mGeneration, mRevision, mIndices, mVertices, - mAreaTypes, water, mTrianglesPerChunk}; + mAreaTypes, water}; const auto anotherData = reinterpret_cast(dtAlloc(1, DT_ALLOC_PERM)); NavMeshData anotherNavMeshData {anotherData, 1}; @@ -333,7 +328,7 @@ namespace NavMeshTilesCache cache(maxSize); const std::vector water {1, RecastMesh::Water {1, btTransform::getIdentity()}}; - const RecastMesh anotherRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, water, mTrianglesPerChunk}; + const RecastMesh anotherRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, water}; const auto anotherData = reinterpret_cast(dtAlloc(1, DT_ALLOC_PERM)); NavMeshData anotherNavMeshData {anotherData, 1}; diff --git a/apps/openmw_test_suite/detournavigator/recastmeshbuilder.cpp b/apps/openmw_test_suite/detournavigator/recastmeshbuilder.cpp index bcbf448ac..5f43f4800 100644 --- a/apps/openmw_test_suite/detournavigator/recastmeshbuilder.cpp +++ b/apps/openmw_test_suite/detournavigator/recastmeshbuilder.cpp @@ -37,7 +37,6 @@ namespace DetourNavigatorRecastMeshBuilderTest() { mSettings.mRecastScaleFactor = 1.0f; - mSettings.mTrianglesPerChunk = 256; mBounds.mMin = osg::Vec2f(-std::numeric_limits::max() * std::numeric_limits::epsilon(), -std::numeric_limits::max() * std::numeric_limits::epsilon()); mBounds.mMax = osg::Vec2f(std::numeric_limits::max() * std::numeric_limits::epsilon(), diff --git a/apps/openmw_test_suite/detournavigator/tilecachedrecastmeshmanager.cpp b/apps/openmw_test_suite/detournavigator/tilecachedrecastmeshmanager.cpp index be5209001..2d6d9f66b 100644 --- a/apps/openmw_test_suite/detournavigator/tilecachedrecastmeshmanager.cpp +++ b/apps/openmw_test_suite/detournavigator/tilecachedrecastmeshmanager.cpp @@ -24,7 +24,6 @@ namespace mSettings.mCellSize = 0.2f; mSettings.mRecastScaleFactor = 0.017647058823529415f; mSettings.mTileSize = 64; - mSettings.mTrianglesPerChunk = 256; } void onChangedTile(const TilePosition& tilePosition) diff --git a/apps/wizard/componentselectionpage.hpp b/apps/wizard/componentselectionpage.hpp index 2509b9f5e..961669ab5 100644 --- a/apps/wizard/componentselectionpage.hpp +++ b/apps/wizard/componentselectionpage.hpp @@ -1,8 +1,6 @@ #ifndef COMPONENTSELECTIONPAGE_HPP #define COMPONENTSELECTIONPAGE_HPP -#include - #include "ui_componentselectionpage.h" namespace Wizard diff --git a/apps/wizard/conclusionpage.hpp b/apps/wizard/conclusionpage.hpp index f5f27dfca..72af2cceb 100644 --- a/apps/wizard/conclusionpage.hpp +++ b/apps/wizard/conclusionpage.hpp @@ -1,8 +1,6 @@ #ifndef CONCLUSIONPAGE_HPP #define CONCLUSIONPAGE_HPP -#include - #include "ui_conclusionpage.h" namespace Wizard diff --git a/apps/wizard/existinginstallationpage.hpp b/apps/wizard/existinginstallationpage.hpp index bb229e249..fbbbe432e 100644 --- a/apps/wizard/existinginstallationpage.hpp +++ b/apps/wizard/existinginstallationpage.hpp @@ -1,8 +1,6 @@ #ifndef EXISTINGINSTALLATIONPAGE_HPP #define EXISTINGINSTALLATIONPAGE_HPP -#include - #include "ui_existinginstallationpage.h" namespace Wizard diff --git a/apps/wizard/importpage.hpp b/apps/wizard/importpage.hpp index 412d39ac1..b1f26409a 100644 --- a/apps/wizard/importpage.hpp +++ b/apps/wizard/importpage.hpp @@ -1,8 +1,6 @@ #ifndef IMPORTPAGE_HPP #define IMPORTPAGE_HPP -#include - #include "ui_importpage.h" namespace Wizard diff --git a/apps/wizard/inisettings.cpp b/apps/wizard/inisettings.cpp index d4a63c676..e9cec12f5 100644 --- a/apps/wizard/inisettings.cpp +++ b/apps/wizard/inisettings.cpp @@ -1,7 +1,5 @@ #include "inisettings.hpp" -#include - #include #include #include diff --git a/apps/wizard/installationtargetpage.hpp b/apps/wizard/installationtargetpage.hpp index 7cba29573..1f1e95228 100644 --- a/apps/wizard/installationtargetpage.hpp +++ b/apps/wizard/installationtargetpage.hpp @@ -1,8 +1,6 @@ #ifndef INSTALLATIONTARGETPAGE_HPP #define INSTALLATIONTARGETPAGE_HPP -#include - #include "ui_installationtargetpage.h" namespace Files diff --git a/apps/wizard/intropage.hpp b/apps/wizard/intropage.hpp index c8cd69016..3fae6a747 100644 --- a/apps/wizard/intropage.hpp +++ b/apps/wizard/intropage.hpp @@ -1,8 +1,6 @@ #ifndef INTROPAGE_HPP #define INTROPAGE_HPP -#include - #include "ui_intropage.h" namespace Wizard diff --git a/apps/wizard/languageselectionpage.hpp b/apps/wizard/languageselectionpage.hpp index cc86ba9b3..81f8faa48 100644 --- a/apps/wizard/languageselectionpage.hpp +++ b/apps/wizard/languageselectionpage.hpp @@ -1,8 +1,6 @@ #ifndef LANGUAGESELECTIONPAGE_HPP #define LANGUAGESELECTIONPAGE_HPP -#include - #include "ui_languageselectionpage.h" namespace Wizard diff --git a/apps/wizard/main.cpp b/apps/wizard/main.cpp index e3624742a..67410df14 100644 --- a/apps/wizard/main.cpp +++ b/apps/wizard/main.cpp @@ -1,7 +1,5 @@ #include -#include #include -#include #include "mainwizard.hpp" diff --git a/apps/wizard/mainwizard.hpp b/apps/wizard/mainwizard.hpp index 4c221635e..249ae1cf9 100644 --- a/apps/wizard/mainwizard.hpp +++ b/apps/wizard/mainwizard.hpp @@ -1,9 +1,7 @@ #ifndef MAINWIZARD_HPP #define MAINWIZARD_HPP -#include #include -#include #include diff --git a/apps/wizard/methodselectionpage.hpp b/apps/wizard/methodselectionpage.hpp index 57d551d27..e844024b4 100644 --- a/apps/wizard/methodselectionpage.hpp +++ b/apps/wizard/methodselectionpage.hpp @@ -1,8 +1,6 @@ #ifndef METHODSELECTIONPAGE_HPP #define METHODSELECTIONPAGE_HPP -#include - #include "ui_methodselectionpage.h" namespace Wizard diff --git a/apps/wizard/utils/componentlistwidget.cpp b/apps/wizard/utils/componentlistwidget.cpp index 6a5d019b5..ff37e5d8c 100644 --- a/apps/wizard/utils/componentlistwidget.cpp +++ b/apps/wizard/utils/componentlistwidget.cpp @@ -1,7 +1,6 @@ #include "componentlistwidget.hpp" #include -#include ComponentListWidget::ComponentListWidget(QWidget *parent) : QListWidget(parent) diff --git a/cmake/CheckBulletPrecision.cmake b/cmake/CheckBulletPrecision.cmake new file mode 100644 index 000000000..77409f932 --- /dev/null +++ b/cmake/CheckBulletPrecision.cmake @@ -0,0 +1,26 @@ +set(TMP_ROOT ${CMAKE_BINARY_DIR}/try-compile) +file(MAKE_DIRECTORY ${TMP_ROOT}) + +file(WRITE ${TMP_ROOT}/checkbullet.cpp +" +#include +int main(int argc, char** argv) +{ + btSphereShape shape(1.0); + btScalar mass(1.0); + btVector3 inertia; + shape.calculateLocalInertia(mass, inertia); + return 0; +} +") + +message(STATUS "Checking if Bullet uses double precision") + +try_compile(RESULT_VAR + ${TMP_ROOT}/temp + ${TMP_ROOT}/checkbullet.cpp + COMPILE_DEFINITIONS "-DBT_USE_DOUBLE_PRECISION" + LINK_LIBRARIES ${BULLET_LIBRARIES} + CMAKE_FLAGS "-DINCLUDE_DIRECTORIES=${BULLET_INCLUDE_DIRS}" + ) +set(HAS_DOUBLE_PRECISION_BULLET ${RESULT_VAR}) diff --git a/cmake/OSIdentity.cmake b/cmake/OSIdentity.cmake deleted file mode 100644 index 40b7b2089..000000000 --- a/cmake/OSIdentity.cmake +++ /dev/null @@ -1,67 +0,0 @@ -if (UNIX) - - if (APPLE) - - set(CMAKE_OS_NAME "OSX" CACHE STRING "Operating system name" FORCE) - - else (APPLE) - - ## Check for Debian GNU/Linux ________________ - - find_file(DEBIAN_FOUND debian_version debconf.conf - PATHS /etc - ) - if (DEBIAN_FOUND) - set(CMAKE_OS_NAME "Debian" CACHE STRING "Operating system name" FORCE) - endif (DEBIAN_FOUND) - - ## Check for Fedora _________________________ - - find_file(FEDORA_FOUND fedora-release - PATHS /etc - ) - if (FEDORA_FOUND) - set(CMAKE_OS_NAME "Fedora" CACHE STRING "Operating system name" FORCE) - endif (FEDORA_FOUND) - - ## Check for RedHat _________________________ - - find_file(REDHAT_FOUND redhat-release inittab.RH - PATHS /etc - ) - if (REDHAT_FOUND) - set(CMAKE_OS_NAME "RedHat" CACHE STRING "Operating system name" FORCE) - endif (REDHAT_FOUND) - - ## Extra check for Ubuntu ____________________ - - if (DEBIAN_FOUND) - - ## At its core Ubuntu is a Debian system, with - ## a slightly altered configuration; hence from - ## a first superficial inspection a system will - ## be considered as Debian, which signifies an - ## extra check is required. - - find_file(UBUNTU_EXTRA legal issue - PATHS /etc - ) - - if (UBUNTU_EXTRA) - ## Scan contents of file - file(STRINGS ${UBUNTU_EXTRA} UBUNTU_FOUND - REGEX Ubuntu - ) - ## Check result of string search - if (UBUNTU_FOUND) - set(CMAKE_OS_NAME "Ubuntu" CACHE STRING "Operating system name" FORCE) - set(DEBIAN_FOUND FALSE) - endif (UBUNTU_FOUND) - - endif (UBUNTU_EXTRA) - - endif (DEBIAN_FOUND) - - endif (APPLE) - -endif (UNIX) diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 6fe5e6f74..b244985d5 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -349,11 +349,7 @@ if (BUILD_OPENMW OR BUILD_OPENCS) endif () # End of tes3mp change (major) -if (BULLET_USE_DOUBLES AND (UBUNTU_FOUND OR DEBIAN_FOUND) AND OPENMW_USE_SYSTEM_BULLET) - target_link_libraries(components BulletCollision-float64 LinearMath-float64) -else() - target_link_libraries(components ${BULLET_LIBRARIES}) -endif() +target_link_libraries(components ${BULLET_LIBRARIES}) if (WIN32) target_link_libraries(components @@ -387,6 +383,4 @@ endif() # Make the variable accessible for other subdirectories set(COMPONENT_FILES ${COMPONENT_FILES} PARENT_SCOPE) -if (BULLET_USE_DOUBLES) - target_compile_definitions(components PUBLIC BT_USE_DOUBLE_PRECISION) -endif() +target_compile_definitions(components PUBLIC BT_USE_DOUBLE_PRECISION) diff --git a/components/detournavigator/chunkytrimesh.cpp b/components/detournavigator/chunkytrimesh.cpp deleted file mode 100644 index ffd39d0a9..000000000 --- a/components/detournavigator/chunkytrimesh.cpp +++ /dev/null @@ -1,179 +0,0 @@ -#include "chunkytrimesh.hpp" -#include "exceptions.hpp" - -#include - -#include - -namespace DetourNavigator -{ - namespace - { - struct BoundsItem - { - Rect mBounds; - std::ptrdiff_t mOffset; - unsigned char mAreaTypes; - }; - - template - struct LessBoundsItem - { - bool operator ()(const BoundsItem& lhs, const BoundsItem& rhs) const - { - return lhs.mBounds.mMinBound[axis] < rhs.mBounds.mMinBound[axis]; - } - }; - - void calcExtends(const std::vector& items, const std::size_t imin, const std::size_t imax, - Rect& bounds) - { - bounds = items[imin].mBounds; - - std::for_each( - items.begin() + static_cast(imin) + 1, - items.begin() + static_cast(imax), - [&] (const BoundsItem& item) - { - for (int i = 0; i < 2; ++i) - { - bounds.mMinBound[i] = std::min(bounds.mMinBound[i], item.mBounds.mMinBound[i]); - bounds.mMaxBound[i] = std::max(bounds.mMaxBound[i], item.mBounds.mMaxBound[i]); - } - }); - } - - void subdivide(std::vector& items, const std::size_t imin, const std::size_t imax, - const std::size_t trisPerChunk, const std::vector& inIndices, const std::vector& inAreaTypes, - std::size_t& curNode, std::vector& nodes, std::size_t& curTri, - std::vector& outIndices, std::vector& outAreaTypes) - { - const auto inum = imax - imin; - const auto icur = curNode; - - if (curNode >= nodes.size()) - return; - - ChunkyTriMeshNode& node = nodes[curNode++]; - - if (inum <= trisPerChunk) - { - // Leaf - calcExtends(items, imin, imax, node.mBounds); - - // Copy triangles. - node.mOffset = static_cast(curTri); - node.mSize = inum; - - for (std::size_t i = imin; i < imax; ++i) - { - std::copy( - inIndices.begin() + items[i].mOffset * 3, - inIndices.begin() + items[i].mOffset * 3 + 3, - outIndices.begin() + static_cast(curTri) * 3 - ); - outAreaTypes[curTri] = inAreaTypes[static_cast(items[i].mOffset)]; - curTri++; - } - } - else - { - // Split - calcExtends(items, imin, imax, node.mBounds); - - if (node.mBounds.mMaxBound.x() - node.mBounds.mMinBound.x() - >= node.mBounds.mMaxBound.y() - node.mBounds.mMinBound.y()) - { - // Sort along x-axis - std::sort( - items.begin() + static_cast(imin), - items.begin() + static_cast(imax), - LessBoundsItem<0> {} - ); - } - else - { - // Sort along y-axis - std::sort( - items.begin() + static_cast(imin), - items.begin() + static_cast(imax), - LessBoundsItem<1> {} - ); - } - - const auto isplit = imin + inum / 2; - - // Left - subdivide(items, imin, isplit, trisPerChunk, inIndices, inAreaTypes, curNode, nodes, curTri, outIndices, outAreaTypes); - // Right - subdivide(items, isplit, imax, trisPerChunk, inIndices, inAreaTypes, curNode, nodes, curTri, outIndices, outAreaTypes); - - const auto iescape = static_cast(curNode) - static_cast(icur); - // Negative index means escape. - node.mOffset = -iescape; - } - } - } - - ChunkyTriMesh::ChunkyTriMesh(const std::vector& verts, const std::vector& indices, - const std::vector& flags, const std::size_t trisPerChunk) - : mMaxTrisPerChunk(0) - { - const auto trianglesCount = indices.size() / 3; - - if (trianglesCount == 0) - return; - - const auto nchunks = (trianglesCount + trisPerChunk - 1) / trisPerChunk; - - mNodes.resize(nchunks * 4); - mIndices.resize(trianglesCount * 3); - mAreaTypes.resize(trianglesCount); - - // Build tree - std::vector items(trianglesCount); - - for (std::size_t i = 0; i < trianglesCount; i++) - { - auto& item = items[i]; - - item.mOffset = static_cast(i); - item.mAreaTypes = flags[i]; - - // Calc triangle XZ bounds. - const auto baseIndex = static_cast(indices[i * 3]) * 3; - - item.mBounds.mMinBound.x() = item.mBounds.mMaxBound.x() = verts[baseIndex + 0]; - item.mBounds.mMinBound.y() = item.mBounds.mMaxBound.y() = verts[baseIndex + 2]; - - for (std::size_t j = 1; j < 3; ++j) - { - const auto index = static_cast(indices[i * 3 + j]) * 3; - - item.mBounds.mMinBound.x() = std::min(item.mBounds.mMinBound.x(), verts[index + 0]); - item.mBounds.mMinBound.y() = std::min(item.mBounds.mMinBound.y(), verts[index + 2]); - - item.mBounds.mMaxBound.x() = std::max(item.mBounds.mMaxBound.x(), verts[index + 0]); - item.mBounds.mMaxBound.y() = std::max(item.mBounds.mMaxBound.y(), verts[index + 2]); - } - } - - std::size_t curTri = 0; - std::size_t curNode = 0; - subdivide(items, 0, trianglesCount, trisPerChunk, indices, flags, curNode, mNodes, curTri, mIndices, mAreaTypes); - - items.clear(); - - mNodes.resize(curNode); - - // Calc max tris per node. - for (auto& node : mNodes) - { - const bool isLeaf = node.mOffset >= 0; - if (!isLeaf) - continue; - if (node.mSize > mMaxTrisPerChunk) - mMaxTrisPerChunk = node.mSize; - } - } -} diff --git a/components/detournavigator/chunkytrimesh.hpp b/components/detournavigator/chunkytrimesh.hpp deleted file mode 100644 index 7f98f5d22..000000000 --- a/components/detournavigator/chunkytrimesh.hpp +++ /dev/null @@ -1,102 +0,0 @@ -#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_CHUNKYTRIMESH_H -#define OPENMW_COMPONENTS_DETOURNAVIGATOR_CHUNKYTRIMESH_H - -#include "areatype.hpp" - -#include - -#include -#include - -namespace DetourNavigator -{ - struct Rect - { - osg::Vec2f mMinBound; - osg::Vec2f mMaxBound; - }; - - struct ChunkyTriMeshNode - { - Rect mBounds; - std::ptrdiff_t mOffset; - std::size_t mSize; - }; - - struct Chunk - { - const int* const mIndices; - const AreaType* const mAreaTypes; - const std::size_t mSize; - }; - - inline bool checkOverlapRect(const Rect& lhs, const Rect& rhs) - { - bool overlap = true; - overlap = (lhs.mMinBound.x() > rhs.mMaxBound.x() || lhs.mMaxBound.x() < rhs.mMinBound.x()) ? false : overlap; - overlap = (lhs.mMinBound.y() > rhs.mMaxBound.y() || lhs.mMaxBound.y() < rhs.mMinBound.y()) ? false : overlap; - return overlap; - } - - class ChunkyTriMesh - { - public: - /// Creates partitioned triangle mesh (AABB tree), - /// where each node contains at max trisPerChunk triangles. - ChunkyTriMesh(const std::vector& verts, const std::vector& tris, - const std::vector& flags, const std::size_t trisPerChunk); - - ChunkyTriMesh(ChunkyTriMesh&&) = default; - ChunkyTriMesh& operator=(ChunkyTriMesh&&) = default; - - ChunkyTriMesh(const ChunkyTriMesh&) = delete; - ChunkyTriMesh& operator=(const ChunkyTriMesh&) = delete; - - /// Returns the chunk indices which overlap the input rectable. - template - void forEachChunksOverlappingRect(const Rect& rect, Function&& function) const - { - // Traverse tree - for (std::size_t i = 0; i < mNodes.size(); ) - { - const ChunkyTriMeshNode* node = &mNodes[i]; - const bool overlap = checkOverlapRect(rect, node->mBounds); - const bool isLeafNode = node->mOffset >= 0; - - if (isLeafNode && overlap) - function(i); - - if (overlap || isLeafNode) - i++; - else - { - const auto escapeIndex = -node->mOffset; - i += static_cast(escapeIndex); - } - } - } - - Chunk getChunk(const std::size_t chunkId) const - { - const auto& node = mNodes[chunkId]; - return Chunk { - mIndices.data() + node.mOffset * 3, - mAreaTypes.data() + node.mOffset, - node.mSize - }; - } - - std::size_t getMaxTrisPerChunk() const - { - return mMaxTrisPerChunk; - } - - private: - std::vector mNodes; - std::vector mIndices; - std::vector mAreaTypes; - std::size_t mMaxTrisPerChunk; - }; -} - -#endif diff --git a/components/detournavigator/makenavmesh.cpp b/components/detournavigator/makenavmesh.cpp index 9701b4950..70fd44c6c 100644 --- a/components/detournavigator/makenavmesh.cpp +++ b/components/detournavigator/makenavmesh.cpp @@ -1,5 +1,4 @@ #include "makenavmesh.hpp" -#include "chunkytrimesh.hpp" #include "debug.hpp" #include "dtstatus.hpp" #include "exceptions.hpp" @@ -22,6 +21,7 @@ #include #include #include +#include namespace { @@ -178,65 +178,30 @@ namespace bool rasterizeSolidObjectsTriangles(rcContext& context, const RecastMesh& recastMesh, const rcConfig& config, rcHeightfield& solid) { - const auto& chunkyMesh = recastMesh.getChunkyTriMesh(); - std::vector areas(chunkyMesh.getMaxTrisPerChunk(), AreaType_null); const osg::Vec2f tileBoundsMin(config.bmin[0], config.bmin[2]); const osg::Vec2f tileBoundsMax(config.bmax[0], config.bmax[2]); - bool result = false; + std::vector areas(recastMesh.getAreaTypes().begin(), recastMesh.getAreaTypes().end()); - chunkyMesh.forEachChunksOverlappingRect(Rect {tileBoundsMin, tileBoundsMax}, - [&] (const std::size_t cid) - { - const auto chunk = chunkyMesh.getChunk(cid); + rcClearUnwalkableTriangles( + &context, + config.walkableSlopeAngle, + recastMesh.getVertices().data(), + static_cast(recastMesh.getVerticesCount()), + recastMesh.getIndices().data(), + static_cast(areas.size()), + areas.data() + ); - std::fill( - areas.begin(), - std::min(areas.begin() + static_cast(chunk.mSize), - areas.end()), - AreaType_null - ); - - rcMarkWalkableTriangles( - &context, - config.walkableSlopeAngle, - recastMesh.getVertices().data(), - static_cast(recastMesh.getVerticesCount()), - chunk.mIndices, - static_cast(chunk.mSize), - areas.data() - ); - - for (std::size_t i = 0; i < chunk.mSize; ++i) - areas[i] = chunk.mAreaTypes[i]; - - rcClearUnwalkableTriangles( - &context, - config.walkableSlopeAngle, - recastMesh.getVertices().data(), - static_cast(recastMesh.getVerticesCount()), - chunk.mIndices, - static_cast(chunk.mSize), - areas.data() - ); - - const auto trianglesRasterized = rcRasterizeTriangles( - &context, - recastMesh.getVertices().data(), - static_cast(recastMesh.getVerticesCount()), - chunk.mIndices, - areas.data(), - static_cast(chunk.mSize), - solid, - config.walkableClimb - ); - - if (!trianglesRasterized) - throw NavigatorException("Failed to create rasterize triangles from recast mesh for navmesh"); - - result = true; - }); - - return result; + return rcRasterizeTriangles( + &context, + recastMesh.getVertices().data(), + static_cast(recastMesh.getVerticesCount()), + recastMesh.getIndices().data(), + areas.data(), + static_cast(areas.size()), + solid, + config.walkableClimb + ); } void rasterizeWaterTriangles(rcContext& context, const osg::Vec3f& agentHalfExtents, const RecastMesh& recastMesh, diff --git a/components/detournavigator/navigator.hpp b/components/detournavigator/navigator.hpp index d08bfa640..edf597348 100644 --- a/components/detournavigator/navigator.hpp +++ b/components/detournavigator/navigator.hpp @@ -6,7 +6,6 @@ #include "settings.hpp" #include "objectid.hpp" #include "navmeshcacheitem.hpp" -#include "recastmesh.hpp" #include "recastmeshtiles.hpp" namespace ESM diff --git a/components/detournavigator/recastmesh.cpp b/components/detournavigator/recastmesh.cpp index dc56f7b93..1a9982402 100644 --- a/components/detournavigator/recastmesh.cpp +++ b/components/detournavigator/recastmesh.cpp @@ -6,14 +6,13 @@ namespace DetourNavigator { RecastMesh::RecastMesh(std::size_t generation, std::size_t revision, std::vector indices, std::vector vertices, - std::vector areaTypes, std::vector water, const std::size_t trianglesPerChunk) + std::vector areaTypes, std::vector water) : mGeneration(generation) , mRevision(revision) , mIndices(std::move(indices)) , mVertices(std::move(vertices)) , mAreaTypes(std::move(areaTypes)) , mWater(std::move(water)) - , mChunkyTriMesh(mVertices, mIndices, mAreaTypes, trianglesPerChunk) { if (getTrianglesCount() != mAreaTypes.size()) throw InvalidArgument("Number of flags doesn't match number of triangles: triangles=" diff --git a/components/detournavigator/recastmesh.hpp b/components/detournavigator/recastmesh.hpp index 746422ac8..1961d518d 100644 --- a/components/detournavigator/recastmesh.hpp +++ b/components/detournavigator/recastmesh.hpp @@ -2,7 +2,6 @@ #define OPENMW_COMPONENTS_DETOURNAVIGATOR_RECASTMESH_H #include "areatype.hpp" -#include "chunkytrimesh.hpp" #include "bounds.hpp" #include @@ -28,7 +27,7 @@ namespace DetourNavigator }; RecastMesh(std::size_t generation, std::size_t revision, std::vector indices, std::vector vertices, - std::vector areaTypes, std::vector water, const std::size_t trianglesPerChunk); + std::vector areaTypes, std::vector water); std::size_t getGeneration() const { @@ -70,11 +69,6 @@ namespace DetourNavigator return mIndices.size() / 3; } - const ChunkyTriMesh& getChunkyTriMesh() const - { - return mChunkyTriMesh; - } - const Bounds& getBounds() const { return mBounds; @@ -87,7 +81,6 @@ namespace DetourNavigator std::vector mVertices; std::vector mAreaTypes; std::vector mWater; - ChunkyTriMesh mChunkyTriMesh; Bounds mBounds; }; diff --git a/components/detournavigator/recastmeshbuilder.cpp b/components/detournavigator/recastmeshbuilder.cpp index f8456acf0..7e06c9229 100644 --- a/components/detournavigator/recastmeshbuilder.cpp +++ b/components/detournavigator/recastmeshbuilder.cpp @@ -1,5 +1,4 @@ #include "recastmeshbuilder.hpp" -#include "chunkytrimesh.hpp" #include "debug.hpp" #include "settings.hpp" #include "settingsutils.hpp" @@ -19,6 +18,7 @@ #include #include #include +#include namespace DetourNavigator { @@ -156,8 +156,7 @@ namespace DetourNavigator { optimizeRecastMesh(mIndices, mVertices); std::sort(mWater.begin(), mWater.end()); - return std::make_shared(generation, revision, mIndices, mVertices, mAreaTypes, - mWater, mSettings.get().mTrianglesPerChunk); + return std::make_shared(generation, revision, mIndices, mVertices, mAreaTypes, mWater); } void RecastMeshBuilder::reset() diff --git a/components/detournavigator/recastmeshtiles.hpp b/components/detournavigator/recastmeshtiles.hpp index 68e30ba63..03059c744 100644 --- a/components/detournavigator/recastmeshtiles.hpp +++ b/components/detournavigator/recastmeshtiles.hpp @@ -2,13 +2,14 @@ #define OPENMW_COMPONENTS_DETOURNAVIGATOR_RECASTMESHTILE_H #include "tileposition.hpp" -#include "recastmesh.hpp" #include #include namespace DetourNavigator { + class RecastMesh; + using RecastMeshTiles = std::map>; } diff --git a/components/detournavigator/settings.cpp b/components/detournavigator/settings.cpp index 977b80cf5..0c4d0f838 100644 --- a/components/detournavigator/settings.cpp +++ b/components/detournavigator/settings.cpp @@ -33,7 +33,6 @@ namespace DetourNavigator navigatorSettings.mMaxNavMeshTilesCacheSize = static_cast(::Settings::Manager::getInt("max nav mesh tiles cache size", "Navigator")); navigatorSettings.mMaxPolygonPathSize = static_cast(::Settings::Manager::getInt("max polygon path size", "Navigator")); navigatorSettings.mMaxSmoothPathSize = static_cast(::Settings::Manager::getInt("max smooth path size", "Navigator")); - navigatorSettings.mTrianglesPerChunk = static_cast(::Settings::Manager::getInt("triangles per chunk", "Navigator")); navigatorSettings.mEnableWriteRecastMeshToFile = ::Settings::Manager::getBool("enable write recast mesh to file", "Navigator"); navigatorSettings.mEnableWriteNavMeshToFile = ::Settings::Manager::getBool("enable write nav mesh to file", "Navigator"); navigatorSettings.mRecastMeshPathPrefix = ::Settings::Manager::getString("recast mesh path prefix", "Navigator"); diff --git a/components/detournavigator/settings.hpp b/components/detournavigator/settings.hpp index d73087b21..ece16e35a 100644 --- a/components/detournavigator/settings.hpp +++ b/components/detournavigator/settings.hpp @@ -35,7 +35,6 @@ namespace DetourNavigator std::size_t mMaxNavMeshTilesCacheSize = 0; std::size_t mMaxPolygonPathSize = 0; std::size_t mMaxSmoothPathSize = 0; - std::size_t mTrianglesPerChunk = 0; std::string mRecastMeshPathPrefix; std::string mNavMeshPathPrefix; std::chrono::milliseconds mMinUpdateInterval; diff --git a/components/esm/loadland.cpp b/components/esm/loadland.cpp index 2aa8f21db..38596628f 100644 --- a/components/esm/loadland.cpp +++ b/components/esm/loadland.cpp @@ -161,9 +161,9 @@ namespace ESM { // Generate WNAM record signed char wnam[LAND_GLOBAL_MAP_LOD_SIZE]; - float max = std::numeric_limits::max(); - float min = std::numeric_limits::min(); - float vertMult = static_cast(ESM::Land::LAND_SIZE - 1) / LAND_GLOBAL_MAP_LOD_SIZE_SQRT; + constexpr float max = std::numeric_limits::max(); + constexpr float min = std::numeric_limits::min(); + constexpr float vertMult = static_cast(ESM::Land::LAND_SIZE - 1) / LAND_GLOBAL_MAP_LOD_SIZE_SQRT; for (int row = 0; row < LAND_GLOBAL_MAP_LOD_SIZE_SQRT; ++row) { for (int col = 0; col < LAND_GLOBAL_MAP_LOD_SIZE_SQRT; ++col) diff --git a/components/nif/niffile.cpp b/components/nif/niffile.cpp index 3e226b35e..08301ce47 100644 --- a/components/nif/niffile.cpp +++ b/components/nif/niffile.cpp @@ -212,7 +212,7 @@ void NIFFile::parse(Files::IStreamPtr stream) userVer = nif.getUInt(); // Number of records - unsigned int recNum = nif.getUInt(); + const std::size_t recNum = nif.getUInt(); records.resize(recNum); // Bethesda stream header @@ -251,7 +251,7 @@ void NIFFile::parse(Files::IStreamPtr stream) std::vector recSizes; // Currently unused nif.getUInts(recSizes, recNum); } - unsigned int stringNum = nif.getUInt(); + const std::size_t stringNum = nif.getUInt(); nif.getUInt(); // Max string length if (stringNum) nif.getSizedStrings(strings, stringNum); @@ -264,7 +264,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 (unsigned int i = 0; i < recNum; i++) + for (std::size_t i = 0; i < recNum; i++) { Record *r = nullptr; @@ -308,21 +308,21 @@ void NIFFile::parse(Files::IStreamPtr stream) r->read(&nif); } - unsigned int rootNum = nif.getUInt(); + const std::size_t rootNum = nif.getUInt(); roots.resize(rootNum); //Determine which records are roots - for (unsigned int i = 0; i < rootNum; i++) + for (std::size_t i = 0; i < rootNum; i++) { int idx = nif.getInt(); - if (idx >= 0 && idx < int(records.size())) + if (idx >= 0 && static_cast(idx) < records.size()) { roots[i] = records[idx]; } else { roots[i] = nullptr; - warn("Null Root found"); + warn("Root " + std::to_string(i + 1) + " does not point to a record: index " + std::to_string(idx)); } } diff --git a/components/nif/nifstream.cpp b/components/nif/nifstream.cpp index 07c9c917c..6129a1739 100644 --- a/components/nif/nifstream.cpp +++ b/components/nif/nifstream.cpp @@ -7,7 +7,7 @@ namespace Nif osg::Quat NIFStream::getQuaternion() { float f[4]; - readLittleEndianBufferOfType<4, float>(inp, (float*)&f); + readLittleEndianBufferOfType<4, float>(inp, f); osg::Quat quat; quat.w() = f[0]; quat.x() = f[1]; diff --git a/components/nif/nifstream.hpp b/components/nif/nifstream.hpp index 4d221b867..b6bf01ce5 100644 --- a/components/nif/nifstream.hpp +++ b/components/nif/nifstream.hpp @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include #include @@ -22,31 +24,32 @@ namespace Nif class NIFFile; -/* - readLittleEndianBufferOfType: This template should only be used with arithmetic types -*/ -template inline void readLittleEndianBufferOfType(Files::IStreamPtr &pIStream, T* dest) +template inline void readLittleEndianBufferOfType(Files::IStreamPtr &pIStream, T* dest) { + static_assert(std::is_arithmetic_v, "Buffer element type is not arithmetic"); pIStream->read((char*)dest, numInstances * sizeof(T)); + if (pIStream->bad()) + throw std::runtime_error("Failed to read little endian typed (" + std::string(typeid(T).name()) + ") buffer of " + + std::to_string(numInstances) + " instances"); if constexpr (Misc::IS_BIG_ENDIAN) - for (uint32_t i = 0; i < numInstances; i++) + for (std::size_t i = 0; i < numInstances; i++) Misc::swapEndiannessInplace(dest[i]); } -/* - readLittleEndianDynamicBufferOfType: This template should only be used with arithmetic types -*/ -template inline void readLittleEndianDynamicBufferOfType(Files::IStreamPtr &pIStream, T* dest, uint32_t numInstances) +template inline void readLittleEndianDynamicBufferOfType(Files::IStreamPtr &pIStream, T* dest, std::size_t numInstances) { + static_assert(std::is_arithmetic_v, "Buffer element type is not arithmetic"); pIStream->read((char*)dest, numInstances * sizeof(T)); + if (pIStream->bad()) + throw std::runtime_error("Failed to read little endian dynamic buffer of " + std::to_string(numInstances) + " instances"); if constexpr (Misc::IS_BIG_ENDIAN) - for (uint32_t i = 0; i < numInstances; i++) + for (std::size_t i = 0; i < numInstances; i++) Misc::swapEndiannessInplace(dest[i]); } template type inline readLittleEndianType(Files::IStreamPtr &pIStream) { type val; - readLittleEndianBufferOfType<1, type>(pIStream, (type*)&val); + readLittleEndianBufferOfType<1, type>(pIStream, &val); return val; } @@ -96,21 +99,21 @@ public: osg::Vec2f getVector2() { osg::Vec2f vec; - readLittleEndianBufferOfType<2,float>(inp, (float*)&vec._v[0]); + readLittleEndianBufferOfType<2,float>(inp, vec._v); return vec; } osg::Vec3f getVector3() { osg::Vec3f vec; - readLittleEndianBufferOfType<3, float>(inp, (float*)&vec._v[0]); + readLittleEndianBufferOfType<3, float>(inp, vec._v); return vec; } osg::Vec4f getVector4() { osg::Vec4f vec; - readLittleEndianBufferOfType<4, float>(inp, (float*)&vec._v[0]); + readLittleEndianBufferOfType<4, float>(inp, vec._v); return vec; } @@ -142,11 +145,11 @@ public: ///Read in a string of the given length std::string getSizedString(size_t length) { - std::vector str(length + 1, 0); - + std::string str(length, '\0'); inp->read(str.data(), length); - - return str.data(); + if (inp->bad()) + throw std::runtime_error("Failed to read sized string of " + std::to_string(length) + " chars"); + return str; } ///Read in a string of the length specified in the file std::string getSizedString() @@ -167,6 +170,8 @@ public: { std::string result; std::getline(*inp, result); + if (inp->bad()) + throw std::runtime_error("Failed to read version string"); return result; } diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index 72c740af3..6ae175939 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -121,12 +121,14 @@ osg::ref_ptr BulletNifLoader::load(const Nif::File& nif) mAvoidStaticMesh.reset(); const size_t numRoots = nif.numRoots(); - std::vector roots; + std::vector roots; for (size_t i = 0; i < numRoots; ++i) { - Nif::Record* r = nif.getRoot(i); - assert(r != nullptr); - if (Nif::Node* node = dynamic_cast(r)) + const Nif::Record* r = nif.getRoot(i); + if (!r) + continue; + const Nif::Node* node = dynamic_cast(r); + if (node) roots.emplace_back(node); } const std::string filename = nif.getFilename(); diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 49d52074a..f7d6fd3ed 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -254,8 +254,7 @@ namespace NifOsg for (size_t i = 0; i < numRoots; ++i) { const Nif::Record *r = nif->getRoot(i); - assert(r != nullptr); - if (r->recType == Nif::RC_NiSequenceStreamHelper) + if (r && r->recType == Nif::RC_NiSequenceStreamHelper) { seq = static_cast(r); break; @@ -312,7 +311,10 @@ namespace NifOsg for (size_t i = 0; i < numRoots; ++i) { const Nif::Record* r = nif->getRoot(i); - if (const Nif::Node* nifNode = dynamic_cast(r)) + if (!r) + continue; + const Nif::Node* nifNode = dynamic_cast(r); + if (nifNode) roots.emplace_back(nifNode); } if (roots.empty()) diff --git a/components/sceneutil/mwshadowtechnique.cpp b/components/sceneutil/mwshadowtechnique.cpp index bf6581ae6..e9eb9b2b2 100644 --- a/components/sceneutil/mwshadowtechnique.cpp +++ b/components/sceneutil/mwshadowtechnique.cpp @@ -1361,7 +1361,7 @@ void MWShadowTechnique::cull(osgUtil::CullVisitor& cv) if (settings->getMultipleShadowMapHint() == ShadowSettings::CASCADED) { cropShadowCameraToMainFrustum(frustum, camera, cascaseNear, cascadeFar, extraPlanes); - for (auto plane : extraPlanes) + for (const auto& plane : extraPlanes) local_polytope.getPlaneList().push_back(plane); local_polytope.setupMask(); } @@ -1998,19 +1998,19 @@ struct ConvexHull Vertices findInternalEdges(osg::Vec3d mainVertex, Vertices connectedVertices) { Vertices internalEdgeVertices; - for (auto vertex : connectedVertices) + for (const auto& vertex : connectedVertices) { osg::Matrixd matrix; osg::Vec3d dir = vertex - mainVertex; matrix.makeLookAt(mainVertex, vertex, dir.z() == 0 ? osg::Vec3d(0, 0, 1) : osg::Vec3d(1, 0, 0)); Vertices testVertices; - for (auto testVertex : connectedVertices) + for (const auto& testVertex : connectedVertices) { if (vertex != testVertex) testVertices.push_back(testVertex); } std::vector bearings; - for (auto testVertex : testVertices) + for (const auto& testVertex : testVertices) { osg::Vec3d transformedVertex = testVertex * matrix; bearings.push_back(atan2(transformedVertex.y(), transformedVertex.x())); @@ -2039,7 +2039,7 @@ struct ConvexHull // Collect the set of vertices VertexSet vertices; - for (Edge edge : _edges) + for (const Edge& edge : _edges) { vertices.insert(edge.first); vertices.insert(edge.second); @@ -2069,7 +2069,7 @@ struct ConvexHull for (auto vertex : extremeVertices) { Vertices connectedVertices; - for (Edge edge : _edges) + for (const Edge& edge : _edges) { if (edge.first == vertex) connectedVertices.push_back(edge.second); @@ -2107,7 +2107,7 @@ struct ConvexHull osg::Vec3d vertex = *unprocessedConnectedVertices.begin(); unprocessedConnectedVertices.erase(unprocessedConnectedVertices.begin()); connectedVertices.insert(vertex); - for (Edge edge : _edges) + for (const Edge& edge : _edges) { osg::Vec3d otherEnd; if (edge.first == vertex) @@ -2124,7 +2124,7 @@ struct ConvexHull } } - for (Edge edge : _edges) + for (const Edge& edge : _edges) { if (connectedVertices.count(edge.first) || connectedVertices.count(edge.second)) finalEdges.push_back(edge); diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 70b44935c..e9f580556 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -212,8 +212,8 @@ public: { // We arrived at a leaf. // Since the tree is used for LOD level selection instead of culling, we do not need to load the actual height data here. - float minZ = -std::numeric_limits::max(); - float maxZ = std::numeric_limits::max(); + constexpr float minZ = -std::numeric_limits::max(); + constexpr float maxZ = std::numeric_limits::max(); float cellWorldSize = mStorage->getCellWorldSize(); osg::BoundingBox boundingBox(osg::Vec3f((center.x()-halfSize)*cellWorldSize, (center.y()-halfSize)*cellWorldSize, minZ), osg::Vec3f((center.x()+halfSize)*cellWorldSize, (center.y()+halfSize)*cellWorldSize, maxZ)); diff --git a/components/vfs/filesystemarchive.cpp b/components/vfs/filesystemarchive.cpp index 17f3891ec..6eef4b93a 100644 --- a/components/vfs/filesystemarchive.cpp +++ b/components/vfs/filesystemarchive.cpp @@ -55,12 +55,7 @@ namespace VFS bool FileSystemArchive::contains(const std::string& file, char (*normalize_function)(char)) const { - for (const auto& it : mIndex) - { - if(it.first == file) - return true; - } - return false; + return mIndex.find(file) != mIndex.end(); } std::string FileSystemArchive::getDescription() const diff --git a/docs/source/reference/modding/custom-models/index.rst b/docs/source/reference/modding/custom-models/index.rst index 8f575bf3c..b204dffd9 100644 --- a/docs/source/reference/modding/custom-models/index.rst +++ b/docs/source/reference/modding/custom-models/index.rst @@ -2,7 +2,11 @@ Custom Models ############# -Custom models can be imported into OpenMW using a variety of formats. Below is a quick overview of supported formats, followed by separate articles with further look at the pipelines. +Custom models can be imported into OpenMW using a variety of formats. Models for the majority of in-game objects are assigned through openMW-CS :doc:`/manuals/openmw-cs/index`. + +Some models, however, are essential for OpenMW to even run. These include player and NPC animations, and meshes for the sky. They are assigned in the ``settings.cfg`` file, with more information available in :doc:`/reference/modding/settings/models` . + +Below is a quick overview of supported formats, followed by separate articles with further look at the pipelines. * **COLLADA** has no license restrictions and is suitable for modding as well as standalone games based on the OpenMW engine. It supports static and animated models. While it doesn't yet work in all parts of the engine, work is being done to resolve the remaining limitations. diff --git a/docs/source/reference/modding/settings/models.rst b/docs/source/reference/modding/settings/models.rst index b0da374d6..c0192067a 100644 --- a/docs/source/reference/modding/settings/models.rst +++ b/docs/source/reference/modding/settings/models.rst @@ -29,3 +29,213 @@ If enabled, this setting allows the NIF loader to make use of that functionality To help debug possible issues OpenMW will log its progress in loading every file that uses an unsupported NIF version. + +xbaseanim +--------- + +:Type: string +:Range: +:Default: meshes/xbase_anim.nif + +Path to the file used for 3rd person base animation model that looks also for the corresponding kf-file. + +.. note:: + If you are using the COLLADA format, you don't need to separate the files as they are separated between .nif and .kf files. It works if you plug the same COLLADA file into all animation-related entries, just make sure there is a corresponding textkeys file. You can read more about the textkeys in :doc:`../../modding/custom-models/pipeline-blender-collada`. + +baseanim +-------- + +:Type: string +:Range: +:Default: meshes/base_anim.nif + +Path to the file used for 3rd person base model with textkeys-data. + +xbaseanim1st +------------ + +:Type: string +:Range: +:Default: meshes/xbase_anim.1st.nif + +Path to the file used for 1st person base animation model that looks also for corresponding kf-file. + +baseanimkna +----------- + +:Type: string +:Range: +:Default: meshes/base_animkna.nif + +Path to the file used for 3rd person beast race base model with textkeys-data. + +baseanimkna1st +-------------- + +:Type: string +:Range: +:Default: meshes/base_animkna.1st.nif + +Path to the file used for 1st person beast race base animation model. + +xbaseanimfemale +--------------- + +:Type: string +:Range: +:Default: meshes/xbase_anim_female.nif + +Path to the file used for 3rd person female base animation model. + +baseanimfemale +-------------- + +:Type: string +:Range: +:Default: meshes/base_anim_female.nif + +Path to the file used for 3rd person female base model with textkeys-data. + +baseanimfemale1st +----------------- + +:Type: string +:Range: +:Default: meshes/base_anim_female.1st.nif + +Path to the file used for 1st person female base model with textkeys-data. + +wolfskin +-------- + +:Type: string +:Range: +:Default: meshes/wolf/skin.nif + +Path to the file used for 3rd person werewolf skin. + +wolfskin1st +----------- + +:Type: string +:Range: +:Default: meshes/wolf/skin.1st.nif + +Path to the file used for 1st person werewolf skin. + +xargonianswimkna +---------------- + +:Type: string +:Range: +:Default: meshes/xargonian_swimkna.nif + +Path to the file used for Argonian swimkna. + +xbaseanimkf +----------- + +:Type: string +:Range: +:Default: meshes/xbase_anim.kf + +File to load xbaseanim 3rd person animations. + +xbaseanim1stkf +-------------- + +:Type: string +:Range: +:Default: meshes/xbase_anim.1st.kf + +File to load xbaseanim 3rd person animations. + +xbaseanimfemalekf +----------------- + +:Type: string +:Range: +:Default: meshes/xbase_anim_female.kf + +File to load xbaseanim animations from. + +xargonianswimknakf +------------------ + +:Type: string +:Range: +:Default: meshes/xargonian_swimkna.kf + +File to load xargonianswimkna animations from. + +skyatmosphere +------------- + +:Type: string +:Range: +:Default: meshes/sky_atmosphere.nif + +Path to the file used for the sky atmosphere mesh, which is one of the three meshes needed to render the sky. It's used to make the top half of the sky blue and renders in front of the background color. + +skyclouds +--------- + +:Type: string +:Range: +:Default: meshes/sky_clouds_01.nif. + +Path to the file used for the sky clouds mesh, which is one of the three meshes needed to render the sky. It displays a scrolling texture of clouds in front of the atmosphere mesh and background color + +skynight01 +---------- + +:Type: string +:Range: +:Default: meshes/sky_night_01.nif + +Path to the file used for the sky stars mesh, which is one of the three meshes needed to render the sky. During night, it displays a texture with stars in front of the atmosphere and behind the clouds. If skynight02 is present, skynight01 will not be used. + +skynight02 +---------- + +:Type: string +:Range: +:Default: meshes/sky_night_02.nif + +Path to the file used for the sky stars mesh, which is one of the three meshes needed to render the sky. During night, it displays a texture with stars in front of the atmosphere and behind the clouds. If it's present it will be used instead of skynight01. + +weatherashcloud +--------------- + +:Type: string +:Range: +:Default: meshes/ashcloud.nif + +Path to the file used for the ash clouds weather effect in Morrowind. OpenMW doesn't use this file, instead it renders a similar looking particle effect. Changing this won't have any effect. + +weatherblightcloud +------------------ + +:Type: string +:Range: +:Default: meshes/blightcloud.nif + +Path to the file used for the blight clouds weather effect in Morrowind. OpenMW doesn't use this file, instead it renders a similar looking particle effect. Changing this won't have any effect. + +weathersnow +----------- + +:Type: string +:Range: +:Default: meshes/snow.nif + +Path to the file used for the snow falling weather effect in Morrowind. OpenMW doesn't use this file, instead it renders a similar looking particle effect. Changing this won't have any effect. + +weatherblizzard +--------------- + +:Type: string +:Range: +:Default: meshes/blizzard.nif + +Path to the file used for the blizzard clouds weather effect in Morrowind. OpenMW doesn't use this file, instead it renders a similar looking particle effect. Changing this won't have any effect. diff --git a/docs/source/reference/modding/settings/navigator.rst b/docs/source/reference/modding/settings/navigator.rst index fcef549d0..b9485c3e9 100644 --- a/docs/source/reference/modding/settings/navigator.rst +++ b/docs/source/reference/modding/settings/navigator.rst @@ -231,15 +231,6 @@ max smooth path size Maximum size of smoothed path. -triangles per chunk -------------------- - -:Type: integer -:Range: > 0 -:Default: 256 - -Maximum number of triangles in each node of mesh AABB tree. - Expert Recastnavigation related settings **************************************** diff --git a/extern/CMakeLists.txt b/extern/CMakeLists.txt index 3842b8357..8257b6b23 100644 --- a/extern/CMakeLists.txt +++ b/extern/CMakeLists.txt @@ -28,7 +28,7 @@ if(NOT OPENMW_USE_SYSTEM_BULLET) set(BUILD_CPU_DEMOS OFF CACHE BOOL "") set(BUILD_EGL OFF CACHE BOOL "") - set(USE_DOUBLE_PRECISION ${BULLET_USE_DOUBLES} CACHE BOOL "") + set(USE_DOUBLE_PRECISION ON CACHE BOOL "") set(BULLET2_MULTITHREADING ON CACHE BOOL "") if(BULLET_STATIC) diff --git a/files/settings-default.cfg b/files/settings-default.cfg index c20820e68..ea678c70f 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -875,9 +875,6 @@ max polygon path size = 1024 # Maximum size of smoothed path (value > 0) max smooth path size = 1024 -# Maximum number of triangles in each node of mesh AABB tree (value > 0) -triangles per chunk = 256 - # Write recast mesh to file in .obj format for each use to update nav mesh (true, false) enable write recast mesh to file = false diff --git a/files/shaders/nv_default_fragment.glsl b/files/shaders/nv_default_fragment.glsl index eadadba39..1bd0f4e31 100644 --- a/files/shaders/nv_default_fragment.glsl +++ b/files/shaders/nv_default_fragment.glsl @@ -56,8 +56,6 @@ void main() #if @normalMap vec4 normalTex = texture2D(normalMap, normalMapUV); - // Must flip Y for DirectX format normal maps - normalTex.y = 1.0 - normalTex.y; vec3 normalizedNormal = normalize(passNormal); vec3 normalizedTangent = normalize(passTangent.xyz); diff --git a/files/ui/advancedpage.ui b/files/ui/advancedpage.ui index b6ec20634..8c3dea4b3 100644 --- a/files/ui/advancedpage.ui +++ b/files/ui/advancedpage.ui @@ -6,7 +6,7 @@ 0 0 - 617 + 732 487 @@ -273,6 +273,30 @@ + + + + <html><head/><body><p>By default, the fog becomes thicker proportionally to your distance from the clipping plane set at the clipping distance, which causes distortion at the edges of the screen. +This setting makes the fog use the actual eye point distance (or so called Euclidean distance) to calculate the fog, which makes the fog look less artificial, especially if you have a wide FOV.</p></body></html> + + + Radial fog + + + + + + + <html><head/><body><p>Normally environment map reflections aren't affected by lighting, which makes environment-mapped (and thus bump-mapped objects) glow in the dark. +Morrowind Code Patch includes an option to remedy that by doing environment-mapping before applying lighting, this is the equivalent of that option. +Affected objects will use shaders. +</p></body></html> + + + Bump/reflect map local lighting + + + @@ -283,6 +307,16 @@ + + + + <html><head/><body><p>Load per-group KF-files and skeleton files from Animations folder</p></body></html> + + + Use additional animation sources + + + @@ -297,7 +331,7 @@ legacy - + @@ -313,16 +347,6 @@ - - - - <html><head/><body><p>Makes NPCs and player movement more smooth. Recommended to use with "turn to movement direction" enabled.</p></body></html> - - - Smooth movement - - - @@ -333,26 +357,6 @@ - - - - <html><head/><body><p>See 'auto use object normal maps'. Affects terrain.</p></body></html> - - - Auto use terrain normal maps - - - - - - - <html><head/><body><p>If a file with pattern 'terrain specular map pattern' exists, use that file as a 'diffuse specular' map. The texture must contain the layer colour in the RGB channel (as usual), and a specular multiplier in the alpha channel.</p></body></html> - - - Auto use terrain specular maps - - - @@ -363,51 +367,6 @@ - - - - <html><head/><body><p>Use casting animations for magic items, just as for spells.</p></body></html> - - - Use magic item animation - - - - - - - <html><head/><body><p>Load per-group KF-files and skeleton files from Animations folder</p></body></html> - - - Use additional animation sources - - - - - - - <html><head/><body><p>If this option is enabled, normal maps are automatically recognized and used if they are named appropriately -(see 'normal map pattern', e.g. for a base texture foo.dds, the normal map texture would have to be named foo_n.dds). -If this option is disabled, normal maps are only used if they are explicitly listed within the mesh file (.nif or .osg file). Affects objects.</p></body></html> - - - Auto use object normal maps - - - - - - - <html><head/><body><p>Normally environment map reflections aren't affected by lighting, which makes environment-mapped (and thus bump-mapped objects) glow in the dark. -Morrowind Code Patch includes an option to remedy that by doing environment-mapping before applying lighting, this is the equivalent of that option. -Affected objects will use shaders. -</p></body></html> - - - Bump/reflect map local lighting - - - @@ -432,6 +391,16 @@ Affected objects will use shaders. + + + + <html><head/><body><p>See 'auto use object normal maps'. Affects terrain.</p></body></html> + + + Auto use terrain normal maps + + + @@ -446,50 +415,81 @@ If this option is disabled, normal maps are only used if they are explicitly lis - - + + - <html><head/><body><p>By default, the fog becomes thicker proportionally to your distance from the clipping plane set at the clipping distance, which causes distortion at the edges of the screen. -This setting makes the fog use the actual eye point distance (or so called Euclidean distance) to calculate the fog, which makes the fog look less artificial, especially if you have a wide FOV.</p></body></html> + <html><head/><body><p>If a file with pattern 'terrain specular map pattern' exists, use that file as a 'diffuse specular' map. The texture must contain the layer colour in the RGB channel (as usual), and a specular multiplier in the alpha channel.</p></body></html> - Radial fog + Auto use terrain specular maps - - - - - - 20 - - - - - false - + + - <html><head/><body><p>Render holstered weapons (with quivers and scabbards), requires modded assets.</p></body></html> + <html><head/><body><p>Makes NPCs and player movement more smooth. Recommended to use with "turn to movement direction" enabled.</p></body></html> - Weapon sheathing + Smooth movement - - - - false - + + - <html><head/><body><p>Render holstered shield, requires modded assets.</p></body></html> + <html><head/><body><p>Use casting animations for magic items, just as for spells.</p></body></html> - Shield sheathing + Use magic item animation + + + + <html><head/><body><p>If this option is enabled, normal maps are automatically recognized and used if they are named appropriately +(see 'normal map pattern', e.g. for a base texture foo.dds, the normal map texture would have to be named foo_n.dds). +If this option is disabled, normal maps are only used if they are explicitly listed within the mesh file (.nif or .osg file). Affects objects.</p></body></html> + + + Auto use object normal maps + + + + + + + 20 + + + + + false + + + <html><head/><body><p>Render holstered weapons (with quivers and scabbards), requires modded assets.</p></body></html> + + + Weapon sheathing + + + + + + + false + + + <html><head/><body><p>Render holstered shield, requires modded assets.</p></body></html> + + + Shield sheathing + + + + + @@ -516,12 +516,12 @@ This setting makes the fog use the actual eye point distance (or so called Eucli - - Audio Device - Select your preferred audio device. + + Audio Device + @@ -554,12 +554,12 @@ This setting makes the fog use the actual eye point distance (or so called Eucli - - HRTF - This setting controls HRTF, which simulates 3D sound on stereo systems. + + HRTF + @@ -602,12 +602,12 @@ This setting makes the fog use the actual eye point distance (or so called Eucli - - HRTF Profile - Select your preferred HRTF profile. + + HRTF Profile +