From b3a260459205c4edf005ad3f5ab9b544b57ef361 Mon Sep 17 00:00:00 2001 From: scrawl <720642+scrawl@users.noreply.github.com> Date: Sun, 15 Oct 2017 09:47:48 +0000 Subject: [PATCH 01/53] Disable tools build to try and stay under the time limit --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6606b0290..ae9c57dad 100644 --- a/.travis.yml +++ b/.travis.yml @@ -41,8 +41,8 @@ addons: project: name: "OpenMW/openmw" description: "" - notification_email: scrawl@baseoftrash.de - build_command_prepend: "cmake . -DBUILD_UNITTESTS=FALSE" + notification_email: 720642+scrawl@users.noreply.github.com + build_command_prepend: "cmake . -DBUILD_UNITTESTS=FALSE -DBUILD_OPENCS=FALSE -DBUILD_BSATOOL=FALSE -DBUILD_ESMTOOL=FALSE -DBUILD_LAUNCHER=FALSE -DBUILD_MWINIIMPORTER=FALSE -DBUILD_WIZARD=FALSE" build_command: "make -j3" branch_pattern: coverity_scan matrix: From 829faf7b2c614c7e51ccd19be6acd44f6f65e8e1 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 3 Nov 2018 10:42:14 +0400 Subject: [PATCH 02/53] Improve toggleactorspaths console command --- apps/openmw/mwbase/world.hpp | 2 ++ apps/openmw/mwmechanics/actors.cpp | 1 + apps/openmw/mwmechanics/aipackage.cpp | 9 +++++---- apps/openmw/mwworld/worldimp.cpp | 5 +++++ apps/openmw/mwworld/worldimp.hpp | 2 ++ components/compiler/extensions0.cpp | 1 + 6 files changed, 16 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 0167cbfee..ffacb267c 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -607,6 +607,8 @@ namespace MWBase virtual void updateActorPath(const MWWorld::ConstPtr& actor, const std::deque& path, const osg::Vec3f& halfExtents, const osg::Vec3f& start, const osg::Vec3f& end) const = 0; + virtual void removeActorPath(const MWWorld::ConstPtr& actor) const = 0; + virtual void setNavMeshNumberToRender(const std::size_t value) = 0; }; } diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index d4350e07c..283b5d2b2 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1603,6 +1603,7 @@ namespace MWMechanics continue; } + MWBase::Environment::get().getWorld()->removeActorPath(iter->first); CharacterController::KillResult killResult = iter->second->getCharacterController()->kill(); if (killResult == CharacterController::Result_DeathAnimStarted) { diff --git a/apps/openmw/mwmechanics/aipackage.cpp b/apps/openmw/mwmechanics/aipackage.cpp index bb1650532..c2e5c9afb 100644 --- a/apps/openmw/mwmechanics/aipackage.cpp +++ b/apps/openmw/mwmechanics/aipackage.cpp @@ -104,10 +104,7 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f& const osg::Vec3f position = actor.getRefData().getPosition().asVec3(); //position of the actor MWBase::World* world = MWBase::Environment::get().getWorld(); - { - const osg::Vec3f halfExtents = world->getHalfExtents(actor); - world->updateActorPath(actor, mPathFinder.getPath(), halfExtents, position, dest); - } + const osg::Vec3f halfExtents = world->getHalfExtents(actor); /// Stops the actor when it gets too close to a unloaded cell //... At current time, this test is unnecessary. AI shuts down when actor is more than "actors processing range" setting value @@ -116,6 +113,7 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f& if (isNearInactiveCell(position)) { actor.getClass().getMovementSettings(actor).mPosition[1] = 0; + world->updateActorPath(actor, mPathFinder.getPath(), halfExtents, position, dest); return false; } @@ -180,9 +178,12 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f& // turn to destination point zTurn(actor, getZAngleToPoint(position, dest)); smoothTurn(actor, getXAngleToPoint(position, dest), 0); + world->removeActorPath(actor); return true; } + world->updateActorPath(actor, mPathFinder.getPath(), halfExtents, position, dest); + if (mRotateOnTheRunChecks == 0 || isReachableRotatingOnTheRun(actor, *mPathFinder.getPath().begin())) // to prevent circling around a path point { diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 30c1292a7..fd6f4ff6f 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -3790,6 +3790,11 @@ namespace MWWorld mRendering->updateActorPath(actor, path, halfExtents, start, end); } + void World::removeActorPath(const MWWorld::ConstPtr& actor) const + { + mRendering->removeActorPath(actor); + } + void World::setNavMeshNumberToRender(const std::size_t value) { mRendering->setNavMeshNumber(value); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 9e288bd6b..8b3be2013 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -709,6 +709,8 @@ namespace MWWorld void updateActorPath(const MWWorld::ConstPtr& actor, const std::deque& path, const osg::Vec3f& halfExtents, const osg::Vec3f& start, const osg::Vec3f& end) const override; + void removeActorPath(const MWWorld::ConstPtr& actor) const override; + void setNavMeshNumberToRender(const std::size_t value) override; }; } diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index a640d76d8..1b5558a3a 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -321,6 +321,7 @@ namespace Compiler extensions.registerInstruction ("tb", "", opcodeToggleBorders); extensions.registerInstruction ("toggleborders", "", opcodeToggleBorders); extensions.registerInstruction ("togglenavmesh", "", opcodeToggleNavMesh); + extensions.registerInstruction ("tap", "", opcodeToggleActorsPaths); extensions.registerInstruction ("toggleactorspaths", "", opcodeToggleActorsPaths); extensions.registerInstruction ("setnavmeshnumber", "l", opcodeSetNavMeshNumberToRender); } From 2ba026e2b2fc35b7cffa471db2d014a7f0621975 Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 3 Nov 2018 16:05:14 +0300 Subject: [PATCH 03/53] Limit navmesh cache size by data size + key size --- .../detournavigator/navmeshtilescache.cpp | 46 ++++++++++++++----- .../detournavigator/navmeshtilescache.cpp | 21 ++++++--- 2 files changed, 49 insertions(+), 18 deletions(-) diff --git a/apps/openmw_test_suite/detournavigator/navmeshtilescache.cpp b/apps/openmw_test_suite/detournavigator/navmeshtilescache.cpp index 93fc5fbaf..8b1ee230d 100644 --- a/apps/openmw_test_suite/detournavigator/navmeshtilescache.cpp +++ b/apps/openmw_test_suite/detournavigator/navmeshtilescache.cpp @@ -55,17 +55,22 @@ namespace TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_should_return_cached_value) { - const std::size_t maxSize = 1; + const std::size_t navMeshDataSize = 1; + const std::size_t navMeshKeySize = 49; + const std::size_t maxSize = navMeshDataSize + navMeshKeySize; NavMeshTilesCache cache(maxSize); const auto result = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, std::move(mNavMeshData)); + ASSERT_TRUE(result); EXPECT_EQ(result.get(), (NavMeshDataRef {mData, 1})); } TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_existing_element_should_throw_exception) { - const std::size_t maxSize = 2; + const std::size_t navMeshDataSize = 1; + const std::size_t navMeshKeySize = 49; + const std::size_t maxSize = 2 * (navMeshDataSize + navMeshKeySize); NavMeshTilesCache cache(maxSize); const auto anotherData = reinterpret_cast(dtAlloc(1, DT_ALLOC_PERM)); NavMeshData anotherNavMeshData {anotherData, 1}; @@ -79,7 +84,9 @@ namespace TEST_F(DetourNavigatorNavMeshTilesCacheTest, get_should_return_cached_value) { - const std::size_t maxSize = 1; + const std::size_t navMeshDataSize = 1; + const std::size_t navMeshKeySize = 49; + const std::size_t maxSize = navMeshDataSize + navMeshKeySize; NavMeshTilesCache cache(maxSize); cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, std::move(mNavMeshData)); @@ -121,7 +128,9 @@ namespace TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_should_replace_unused_value) { - const std::size_t maxSize = 1; + const std::size_t navMeshDataSize = 1; + const std::size_t navMeshKeySize = 117; + const std::size_t maxSize = navMeshDataSize + navMeshKeySize; NavMeshTilesCache cache(maxSize); const std::vector water {1, RecastMesh::Water {1, btTransform::getIdentity()}}; @@ -139,7 +148,9 @@ namespace TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_should_not_replace_used_value) { - const std::size_t maxSize = 1; + const std::size_t navMeshDataSize = 1; + const std::size_t navMeshKeySize = 49; + const std::size_t maxSize = navMeshDataSize + navMeshKeySize; NavMeshTilesCache cache(maxSize); const std::vector water {1, RecastMesh::Water {1, btTransform::getIdentity()}}; @@ -155,7 +166,9 @@ namespace TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_should_replace_unused_least_recently_set_value) { - const std::size_t maxSize = 2; + const std::size_t navMeshDataSize = 1; + const std::size_t navMeshKeySize = 117; + const std::size_t maxSize = 2 * (navMeshDataSize + navMeshKeySize); NavMeshTilesCache cache(maxSize); const std::vector leastRecentlySetWater {1, RecastMesh::Water {1, btTransform::getIdentity()}}; @@ -185,7 +198,9 @@ namespace TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_should_replace_unused_least_recently_used_value) { - const std::size_t maxSize = 2; + const std::size_t navMeshDataSize = 1; + const std::size_t navMeshKeySize = 117; + const std::size_t maxSize = 2 * (navMeshDataSize + navMeshKeySize); NavMeshTilesCache cache(maxSize); const std::vector leastRecentlyUsedWater {1, RecastMesh::Water {1, btTransform::getIdentity()}}; @@ -227,7 +242,9 @@ namespace TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_should_not_replace_unused_least_recently_used_value_when_item_does_not_not_fit_cache_max_size) { - const std::size_t maxSize = 1; + const std::size_t navMeshDataSize = 1; + const std::size_t navMeshKeySize = 49; + const std::size_t maxSize = 2 * (navMeshDataSize + navMeshKeySize); NavMeshTilesCache cache(maxSize); const std::vector water {1, RecastMesh::Water {1, btTransform::getIdentity()}}; @@ -243,7 +260,10 @@ namespace TEST_F(DetourNavigatorNavMeshTilesCacheTest, set_should_not_replace_unused_least_recently_used_value_when_item_does_not_not_fit_size_of_unused_items) { - const std::size_t maxSize = 2; + const std::size_t navMeshDataSize = 1; + const std::size_t navMeshKeySize1 = 49; + const std::size_t navMeshKeySize2 = 117; + const std::size_t maxSize = 2 * navMeshDataSize + navMeshKeySize1 + navMeshKeySize2; NavMeshTilesCache cache(maxSize); const std::vector anotherWater {1, RecastMesh::Water {1, btTransform::getIdentity()}}; @@ -269,7 +289,9 @@ namespace TEST_F(DetourNavigatorNavMeshTilesCacheTest, release_used_after_set_then_used_by_get_item_should_left_this_item_available) { - const std::size_t maxSize = 1; + const std::size_t navMeshDataSize = 1; + const std::size_t navMeshKeySize = 49; + const std::size_t maxSize = navMeshDataSize + navMeshKeySize; NavMeshTilesCache cache(maxSize); const std::vector water {1, RecastMesh::Water {1, btTransform::getIdentity()}}; @@ -290,7 +312,9 @@ namespace TEST_F(DetourNavigatorNavMeshTilesCacheTest, release_twice_used_item_should_left_this_item_available) { - const std::size_t maxSize = 1; + const std::size_t navMeshDataSize = 1; + const std::size_t navMeshKeySize = 49; + const std::size_t maxSize = navMeshDataSize + navMeshKeySize; NavMeshTilesCache cache(maxSize); const std::vector water {1, RecastMesh::Water {1, btTransform::getIdentity()}}; diff --git a/components/detournavigator/navmeshtilescache.cpp b/components/detournavigator/navmeshtilescache.cpp index 8c5fc6381..fa0af2221 100644 --- a/components/detournavigator/navmeshtilescache.cpp +++ b/components/detournavigator/navmeshtilescache.cpp @@ -1,6 +1,8 @@ #include "navmeshtilescache.hpp" #include "exceptions.hpp" +#include + namespace DetourNavigator { namespace @@ -84,10 +86,15 @@ namespace DetourNavigator if (navMeshSize > mFreeNavMeshDataSize + (mMaxNavMeshDataSize - mUsedNavMeshDataSize)) return Value(); - while (!mFreeItems.empty() && mUsedNavMeshDataSize + navMeshSize > mMaxNavMeshDataSize) + const auto navMeshKey = makeNavMeshKey(recastMesh, offMeshConnections); + const auto itemSize = navMeshSize + navMeshKey.size(); + + if (itemSize > mFreeNavMeshDataSize + (mMaxNavMeshDataSize - mUsedNavMeshDataSize)) + return Value(); + + while (!mFreeItems.empty() && mUsedNavMeshDataSize + itemSize > mMaxNavMeshDataSize) removeLeastRecentlyUsed(); - const auto navMeshKey = makeNavMeshKey(recastMesh, offMeshConnections); const auto iterator = mFreeItems.emplace(mFreeItems.end(), agentHalfExtents, changedTile, navMeshKey); const auto emplaced = mValues[agentHalfExtents][changedTile].emplace(navMeshKey, iterator); @@ -98,8 +105,8 @@ namespace DetourNavigator } iterator->mNavMeshData = std::move(value); - mUsedNavMeshDataSize += navMeshSize; - mFreeNavMeshDataSize += navMeshSize; + mUsedNavMeshDataSize += itemSize; + mFreeNavMeshDataSize += itemSize; acquireItemUnsafe(iterator); @@ -122,7 +129,7 @@ namespace DetourNavigator if (value == tileValues->second.end()) return; - mUsedNavMeshDataSize -= static_cast(item.mNavMeshData.mSize); + mUsedNavMeshDataSize -= static_cast(item.mNavMeshData.mSize) + item.mNavMeshKey.size(); mFreeItems.pop_back(); tileValues->second.erase(value); @@ -142,7 +149,7 @@ namespace DetourNavigator return; mBusyItems.splice(mBusyItems.end(), mFreeItems, iterator); - mFreeNavMeshDataSize -= static_cast(iterator->mNavMeshData.mSize); + mFreeNavMeshDataSize -= static_cast(iterator->mNavMeshData.mSize) + iterator->mNavMeshKey.size(); } void NavMeshTilesCache::releaseItem(ItemIterator iterator) @@ -153,6 +160,6 @@ namespace DetourNavigator const std::lock_guard lock(mMutex); mFreeItems.splice(mFreeItems.begin(), mBusyItems, iterator); - mFreeNavMeshDataSize += static_cast(iterator->mNavMeshData.mSize); + mFreeNavMeshDataSize += static_cast(iterator->mNavMeshData.mSize) + iterator->mNavMeshKey.size(); } } From b77684a135be4c554636bfc8af7965bffe99ebc6 Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 3 Nov 2018 16:13:46 +0300 Subject: [PATCH 04/53] Add TODOs --- components/detournavigator/navmeshtilescache.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/detournavigator/navmeshtilescache.cpp b/components/detournavigator/navmeshtilescache.cpp index fa0af2221..484831493 100644 --- a/components/detournavigator/navmeshtilescache.cpp +++ b/components/detournavigator/navmeshtilescache.cpp @@ -63,6 +63,7 @@ namespace DetourNavigator if (tileValues == agentValues->second.end()) return Value(); + // TODO: use different function to make key to avoid unnecessary std::string allocation const auto tile = tileValues->second.find(makeNavMeshKey(recastMesh, offMeshConnections)); if (tile == tileValues->second.end()) return Value(); @@ -96,6 +97,7 @@ namespace DetourNavigator removeLeastRecentlyUsed(); const auto iterator = mFreeItems.emplace(mFreeItems.end(), agentHalfExtents, changedTile, navMeshKey); + // TODO: use std::string_view or some alternative to avoid navMeshKey copy into both mFreeItems and mValues const auto emplaced = mValues[agentHalfExtents][changedTile].emplace(navMeshKey, iterator); if (!emplaced.second) From 41319eb2bf37bc7b11da453f85797466e07816ea Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 3 Nov 2018 16:41:36 +0300 Subject: [PATCH 05/53] Use new change type for update object To perform jobs for updated animated objects and doors with lowest priority. --- components/detournavigator/asyncnavmeshupdater.hpp | 1 + components/detournavigator/navmeshmanager.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/components/detournavigator/asyncnavmeshupdater.hpp b/components/detournavigator/asyncnavmeshupdater.hpp index c9d7b1940..39898e48e 100644 --- a/components/detournavigator/asyncnavmeshupdater.hpp +++ b/components/detournavigator/asyncnavmeshupdater.hpp @@ -29,6 +29,7 @@ namespace DetourNavigator remove = 0, mixed = 1, add = 2, + update = 3, }; class AsyncNavMeshUpdater diff --git a/components/detournavigator/navmeshmanager.cpp b/components/detournavigator/navmeshmanager.cpp index 9afe105d9..66bf39aaf 100644 --- a/components/detournavigator/navmeshmanager.cpp +++ b/components/detournavigator/navmeshmanager.cpp @@ -46,7 +46,7 @@ namespace DetourNavigator { if (!mRecastMeshManager.updateObject(id, transform, areaType)) return false; - addChangedTiles(shape, transform, ChangeType::mixed); + addChangedTiles(shape, transform, ChangeType::update); return true; } From f88d5e808ca955332aa40d979bca887146cd5b72 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 20 Oct 2018 11:09:52 +0400 Subject: [PATCH 06/53] Rewrite media decoder to use FFMpeg 3.2+ API (task #4686) --- CHANGELOG.md | 1 + CI/before_install.osx.sh | 2 +- CMakeLists.txt | 45 +++++ apps/openmw/mwsound/ffmpeg_decoder.cpp | 108 ++++++----- apps/openmw/mwsound/ffmpeg_decoder.hpp | 14 +- apps/openmw/mwsound/movieaudiofactory.cpp | 4 +- .../osg-ffmpeg-videoplayer/audiodecoder.cpp | 91 ++++++--- .../osg-ffmpeg-videoplayer/audiodecoder.hpp | 10 +- extern/osg-ffmpeg-videoplayer/videostate.cpp | 177 +++++++++--------- extern/osg-ffmpeg-videoplayer/videostate.hpp | 15 ++ 10 files changed, 282 insertions(+), 185 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 524ce2cc2..d50c96ef4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ Feature #2229: Improve pathfinding AI Feature #3442: Default values for fallbacks from ini file + Task #4686: Upgrade media decoder to a more current FFmpeg API 0.45.0 diff --git a/CI/before_install.osx.sh b/CI/before_install.osx.sh index da72563a2..d75ee3be5 100755 --- a/CI/before_install.osx.sh +++ b/CI/before_install.osx.sh @@ -6,5 +6,5 @@ brew outdated cmake || brew upgrade cmake brew outdated pkgconfig || brew upgrade pkgconfig brew install qt -curl -fSL -R -J https://downloads.openmw.org/osx/dependencies/openmw-deps-4eec887.zip -o ~/openmw-deps.zip +curl -fSL -R -J https://downloads.openmw.org/osx/dependencies/openmw-deps-7cf2789.zip -o ~/openmw-deps.zip unzip -o ~/openmw-deps.zip -d /private/tmp/openmw-deps > /dev/null diff --git a/CMakeLists.txt b/CMakeLists.txt index 6fbc42fb7..606d27bfd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -153,7 +153,52 @@ if (USE_QT) endif() # Sound setup + +# Require at least ffmpeg 3.2 for now +SET(FFVER_OK FALSE) + find_package(FFmpeg REQUIRED COMPONENTS AVCODEC AVFORMAT AVUTIL SWSCALE SWRESAMPLE) + +if(FFmpeg_FOUND) + SET(FFVER_OK TRUE) + + # Can not detect FFmpeg version on Windows for now + if (NOT WIN32) + if(FFmpeg_AVFORMAT_VERSION VERSION_LESS "57.56.100") + message(STATUS "libavformat is too old! (${FFmpeg_AVFORMAT_VERSION}, wanted 57.56.100)") + set(FFVER_OK FALSE) + endif() + if(FFmpeg_AVCODEC_VERSION VERSION_LESS "57.64.100") + message(STATUS "libavcodec is too old! (${FFmpeg_AVCODEC_VERSION}, wanted 57.64.100)") + set(FFVER_OK FALSE) + endif() + if(FFmpeg_AVUTIL_VERSION VERSION_LESS "55.34.100") + message(STATUS "libavutil is too old! (${FFmpeg_AVUTIL_VERSION}, wanted 55.34.100)") + set(FFVER_OK FALSE) + endif() + if(FFmpeg_SWSCALE_VERSION VERSION_LESS "4.2.100") + message(STATUS "libswscale is too old! (${FFmpeg_SWSCALE_VERSION}, wanted 4.2.100)") + set(FFVER_OK FALSE) + endif() + if(FFmpeg_SWRESAMPLE_VERSION VERSION_LESS "2.3.100") + message(STATUS "libswresample is too old! (${FFmpeg_SWRESAMPLE_VERSION}, wanted 2.3.100)") + set(FFVER_OK FALSE) + endif() + 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() + # Required for building the FFmpeg headers add_definitions(-D__STDC_CONSTANT_MACROS) diff --git a/apps/openmw/mwsound/ffmpeg_decoder.cpp b/apps/openmw/mwsound/ffmpeg_decoder.cpp index 53452c305..447c386c9 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.cpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.cpp @@ -82,7 +82,7 @@ bool FFmpeg_Decoder::getNextPacket() } /* Free the packet and look for another */ - av_free_packet(&mPacket); + av_packet_unref(&mPacket); } return false; @@ -90,9 +90,9 @@ bool FFmpeg_Decoder::getNextPacket() bool FFmpeg_Decoder::getAVAudioData() { - int got_frame; + bool got_frame; - if((*mStream)->codec->codec_type != AVMEDIA_TYPE_AUDIO) + if(mCodecCtx->codec_type != AVMEDIA_TYPE_AUDIO) return false; do { @@ -100,19 +100,18 @@ bool FFmpeg_Decoder::getAVAudioData() return false; /* Decode some data, and check for errors */ - int len = 0; - if((len=avcodec_decode_audio4((*mStream)->codec, mFrame, &got_frame, &mPacket)) < 0) + int ret = 0; + ret = avcodec_receive_frame(mCodecCtx, mFrame); + if (ret == 0) + got_frame = true; + if (ret == AVERROR(EAGAIN)) + ret = 0; + if (ret == 0) + ret = avcodec_send_packet(mCodecCtx, &mPacket); + if (ret < 0 && ret != AVERROR(EAGAIN)) return false; - /* Move the unread data to the front and clear the end bits */ - int remaining = mPacket.size - len; - if(remaining <= 0) - av_free_packet(&mPacket); - else - { - memmove(mPacket.data, &mPacket.data[len], remaining); - av_shrink_packet(&mPacket, remaining); - } + av_packet_unref(&mPacket); if (!got_frame || mFrame->nb_samples == 0) continue; @@ -139,8 +138,8 @@ bool FFmpeg_Decoder::getAVAudioData() else mFrameData = &mFrame->data[0]; - } while(got_frame == 0 || mFrame->nb_samples == 0); - mNextPts += (double)mFrame->nb_samples / (double)(*mStream)->codec->sample_rate; + } while(!got_frame || mFrame->nb_samples == 0); + mNextPts += (double)mFrame->nb_samples / mCodecCtx->sample_rate; return true; } @@ -213,7 +212,7 @@ void FFmpeg_Decoder::open(const std::string &fname) for(size_t j = 0;j < mFormatCtx->nb_streams;j++) { - if(mFormatCtx->streams[j]->codec->codec_type == AVMEDIA_TYPE_AUDIO) + if(mFormatCtx->streams[j]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { mStream = &mFormatCtx->streams[j]; break; @@ -222,39 +221,48 @@ void FFmpeg_Decoder::open(const std::string &fname) if(!mStream) throw std::runtime_error("No audio streams in "+fname); - (*mStream)->codec->request_sample_fmt = (*mStream)->codec->sample_fmt; - - AVCodec *codec = avcodec_find_decoder((*mStream)->codec->codec_id); + AVCodec *codec = avcodec_find_decoder((*mStream)->codecpar->codec_id); if(!codec) { std::string ss = "No codec found for id " + - std::to_string((*mStream)->codec->codec_id); + std::to_string((*mStream)->codecpar->codec_id); throw std::runtime_error(ss); } - if(avcodec_open2((*mStream)->codec, codec, nullptr) < 0) - throw std::runtime_error(std::string("Failed to open audio codec ") + - codec->long_name); + + AVCodecContext *avctx = avcodec_alloc_context3(codec); + avcodec_parameters_to_context(avctx, (*mStream)->codecpar); + +// This is not needed anymore above FFMpeg version 4.0 +#if LIBAVCODEC_VERSION_INT < 3805796 + av_codec_set_pkt_timebase(avctx, (*mStream)->time_base); +#endif + + mCodecCtx = avctx; + + if(avcodec_open2(mCodecCtx, codec, nullptr) < 0) + throw std::runtime_error(std::string("Failed to open audio codec ") + codec->long_name); mFrame = av_frame_alloc(); - if((*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_FLT || - (*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_FLTP) + if(mCodecCtx->sample_fmt == AV_SAMPLE_FMT_FLT || mCodecCtx->sample_fmt == AV_SAMPLE_FMT_FLTP) mOutputSampleFormat = AV_SAMPLE_FMT_S16; // FIXME: Check for AL_EXT_FLOAT32 support - else if((*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_U8P) + else if(mCodecCtx->sample_fmt == AV_SAMPLE_FMT_U8P) mOutputSampleFormat = AV_SAMPLE_FMT_U8; - else if((*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_S16P) + else if(mCodecCtx->sample_fmt == AV_SAMPLE_FMT_S16P) mOutputSampleFormat = AV_SAMPLE_FMT_S16; else mOutputSampleFormat = AV_SAMPLE_FMT_S16; - mOutputChannelLayout = (*mStream)->codec->channel_layout; + mOutputChannelLayout = (*mStream)->codecpar->channel_layout; if(mOutputChannelLayout == 0) - mOutputChannelLayout = av_get_default_channel_layout((*mStream)->codec->channels); + mOutputChannelLayout = av_get_default_channel_layout(mCodecCtx->channels); + + mCodecCtx->channel_layout = mOutputChannelLayout; } catch(...) { if(mStream) - avcodec_close((*mStream)->codec); + avcodec_free_context(&mCodecCtx); mStream = nullptr; if (mFormatCtx != nullptr) @@ -275,10 +283,10 @@ void FFmpeg_Decoder::open(const std::string &fname) void FFmpeg_Decoder::close() { if(mStream) - avcodec_close((*mStream)->codec); + avcodec_free_context(&mCodecCtx); mStream = nullptr; - av_free_packet(&mPacket); + av_packet_unref(&mPacket); av_freep(&mFrame); swr_free(&mSwr); av_freep(&mDataBuf); @@ -308,7 +316,12 @@ void FFmpeg_Decoder::close() std::string FFmpeg_Decoder::getName() { +// In the FFMpeg 4.0 a "filename" field was replaced by "url" +#if LIBAVCODEC_VERSION_INT < 3805796 return mFormatCtx->filename; +#else + return mFormatCtx->url; +#endif } void FFmpeg_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType *type) @@ -341,11 +354,10 @@ void FFmpeg_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType * else { char str[1024]; - av_get_channel_layout_string(str, sizeof(str), (*mStream)->codec->channels, - (*mStream)->codec->channel_layout); + av_get_channel_layout_string(str, sizeof(str), mCodecCtx->channels, mCodecCtx->channel_layout); Log(Debug::Error) << "Unsupported channel layout: "<< str; - if((*mStream)->codec->channels == 1) + if(mCodecCtx->channels == 1) { mOutputChannelLayout = AV_CH_LAYOUT_MONO; *chans = ChannelConfig_Mono; @@ -357,27 +369,28 @@ void FFmpeg_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType * } } - *samplerate = (*mStream)->codec->sample_rate; - int64_t ch_layout = (*mStream)->codec->channel_layout; + *samplerate = mCodecCtx->sample_rate; + int64_t ch_layout = mCodecCtx->channel_layout; if(ch_layout == 0) - ch_layout = av_get_default_channel_layout((*mStream)->codec->channels); + ch_layout = av_get_default_channel_layout(mCodecCtx->channels); - if(mOutputSampleFormat != (*mStream)->codec->sample_fmt || + if(mOutputSampleFormat != mCodecCtx->sample_fmt || mOutputChannelLayout != ch_layout) { mSwr = swr_alloc_set_opts(mSwr, // SwrContext mOutputChannelLayout, // output ch layout mOutputSampleFormat, // output sample format - (*mStream)->codec->sample_rate, // output sample rate + mCodecCtx->sample_rate, // output sample rate ch_layout, // input ch layout - (*mStream)->codec->sample_fmt, // input sample format - (*mStream)->codec->sample_rate, // input sample rate + mCodecCtx->sample_fmt, // input sample format + mCodecCtx->sample_rate, // input sample rate 0, // logging level offset nullptr); // log context if(!mSwr) throw std::runtime_error("Couldn't allocate SwrContext"); - if(swr_init(mSwr) < 0) - throw std::runtime_error("Couldn't initialize SwrContext"); + int init=swr_init(mSwr); + if(init < 0) + throw std::runtime_error("Couldn't initialize SwrContext: "+std::to_string(init)); } } @@ -412,7 +425,7 @@ size_t FFmpeg_Decoder::getSampleOffset() { int delay = (mFrameSize-mFramePos) / av_get_channel_layout_nb_channels(mOutputChannelLayout) / av_get_bytes_per_sample(mOutputSampleFormat); - return (int)(mNextPts*(*mStream)->codec->sample_rate) - delay; + return (int)(mNextPts*mCodecCtx->sample_rate) - delay; } FFmpeg_Decoder::FFmpeg_Decoder(const VFS::Manager* vfs) @@ -437,7 +450,10 @@ FFmpeg_Decoder::FFmpeg_Decoder(const VFS::Manager* vfs) static bool done_init = false; if(!done_init) { +// This is not needed anymore above FFMpeg version 4.0 +#if LIBAVCODEC_VERSION_INT < 3805796 av_register_all(); +#endif av_log_set_level(AV_LOG_ERROR); done_init = true; } diff --git a/apps/openmw/mwsound/ffmpeg_decoder.hpp b/apps/openmw/mwsound/ffmpeg_decoder.hpp index 2b76e0d7d..92d046d85 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.hpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.hpp @@ -6,18 +6,7 @@ extern "C" { #include #include - -// From libavutil version 52.2.0 and onward the declaration of -// AV_CH_LAYOUT_* is removed from libavcodec/avcodec.h and moved to -// libavutil/channel_layout.h -#if AV_VERSION_INT(52, 2, 0) <= AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \ - LIBAVUTIL_VERSION_MINOR, LIBAVUTIL_VERSION_MICRO) - #include -#endif - -#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55,28,1) -#define av_frame_alloc avcodec_alloc_frame -#endif +#include // From version 54.56 binkaudio encoding format changed from S16 to FLTP. See: // https://gitorious.org/ffmpeg/ffmpeg/commit/7bfd1766d1c18f07b0a2dd042418a874d49ea60d @@ -38,6 +27,7 @@ namespace MWSound class FFmpeg_Decoder final : public Sound_Decoder { AVFormatContext *mFormatCtx; + AVCodecContext *mCodecCtx; AVStream **mStream; AVPacket mPacket; diff --git a/apps/openmw/mwsound/movieaudiofactory.cpp b/apps/openmw/mwsound/movieaudiofactory.cpp index faa8d94ca..146023bc1 100644 --- a/apps/openmw/mwsound/movieaudiofactory.cpp +++ b/apps/openmw/mwsound/movieaudiofactory.cpp @@ -48,7 +48,7 @@ namespace MWSound { ssize_t clock_delay = (mFrameSize-mFramePos) / av_get_channel_layout_nb_channels(mOutputChannelLayout) / av_get_bytes_per_sample(mOutputSampleFormat); - return (size_t)(mAudioClock*mAVStream->codec->sample_rate) - clock_delay; + return (size_t)(mAudioClock*mAudioContext->sample_rate) - clock_delay; } std::string getStreamName() @@ -61,7 +61,7 @@ namespace MWSound virtual double getAudioClock() { - return (double)getSampleOffset()/(double)mAVStream->codec->sample_rate - + return (double)getSampleOffset()/(double)mAudioContext->sample_rate - MWBase::Environment::get().getSoundManager()->getTrackTimeDelay(mAudioTrack); } diff --git a/extern/osg-ffmpeg-videoplayer/audiodecoder.cpp b/extern/osg-ffmpeg-videoplayer/audiodecoder.cpp index ee6400177..0c314ec7d 100644 --- a/extern/osg-ffmpeg-videoplayer/audiodecoder.cpp +++ b/extern/osg-ffmpeg-videoplayer/audiodecoder.cpp @@ -6,15 +6,9 @@ extern "C" { - #include #include - - #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55,28,1) - #define av_frame_alloc avcodec_alloc_frame - #endif - } #include "videostate.hpp" @@ -61,6 +55,7 @@ MovieAudioDecoder::MovieAudioDecoder(VideoState* videoState) , mFrameData(NULL) , mDataBufLen(0) , mFrame(av_frame_alloc()) + , mGetNextPacket(true) , mAudioDiffAccum(0.0) , mAudioDiffAvgCoef(exp(log(0.01 / AUDIO_DIFF_AVG_NB))) /* Correct audio only if larger error than this */ @@ -68,10 +63,34 @@ MovieAudioDecoder::MovieAudioDecoder(VideoState* videoState) , mAudioDiffAvgCount(0) { mAudioResampler.reset(new AudioResampler()); + + AVCodec *codec = avcodec_find_decoder(mAVStream->codecpar->codec_id); + if(!codec) + { + std::string ss = "No codec found for id " + + std::to_string(mAVStream->codecpar->codec_id); + throw std::runtime_error(ss); + } + + AVCodecContext *avctx = avcodec_alloc_context3(codec); + avcodec_parameters_to_context(avctx, mAVStream->codecpar); + +// This is not needed anymore above FFMpeg version 4.0 +#if LIBAVCODEC_VERSION_INT < 3805796 + av_codec_set_pkt_timebase(avctx, mAVStream->time_base); +#endif + + mAudioContext = avctx; + + if(avcodec_open2(mAudioContext, codec, nullptr) < 0) + throw std::runtime_error(std::string("Failed to open audio codec ") + codec->long_name); } MovieAudioDecoder::~MovieAudioDecoder() { + if(mAudioContext) + avcodec_free_context(&mAudioContext); + av_freep(&mFrame); av_freep(&mDataBuf); } @@ -81,13 +100,13 @@ void MovieAudioDecoder::setupFormat() if (mAudioResampler->mSwr) return; // already set up - AVSampleFormat inputSampleFormat = mAVStream->codec->sample_fmt; + AVSampleFormat inputSampleFormat = mAudioContext->sample_fmt; - uint64_t inputChannelLayout = mAVStream->codec->channel_layout; + uint64_t inputChannelLayout = mAudioContext->channel_layout; if (inputChannelLayout == 0) - inputChannelLayout = av_get_default_channel_layout(mAVStream->codec->channels); + inputChannelLayout = av_get_default_channel_layout(mAudioContext->channels); - int inputSampleRate = mAVStream->codec->sample_rate; + int inputSampleRate = mAudioContext->sample_rate; mOutputSampleRate = inputSampleRate; mOutputSampleFormat = inputSampleFormat; @@ -133,7 +152,7 @@ int MovieAudioDecoder::synchronize_audio() { int n = av_get_bytes_per_sample(mOutputSampleFormat) * av_get_channel_layout_nb_channels(mOutputChannelLayout); - sample_skip = ((int)(diff * mAVStream->codec->sample_rate) * n); + sample_skip = ((int)(diff * mAudioContext->sample_rate) * n); } } @@ -146,23 +165,31 @@ int MovieAudioDecoder::audio_decode_frame(AVFrame *frame, int &sample_skip) for(;;) { - while(pkt->size > 0) + /* send the packet with the compressed data to the decoder */ + int ret = 0; + if (mGetNextPacket) + ret = avcodec_send_packet(mAudioContext, pkt); + + /* read all the output frames (in general there may be any number of them */ + while (ret >= 0) { - int len1, got_frame; - - len1 = avcodec_decode_audio4(mAVStream->codec, frame, &got_frame, pkt); - if(len1 < 0) break; - - if(len1 <= pkt->size) + ret = avcodec_receive_frame(mAudioContext, frame); + if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN)) { - /* Move the unread data to the front and clear the end bits */ - int remaining = pkt->size - len1; - memmove(pkt->data, &pkt->data[len1], remaining); - av_shrink_packet(pkt, remaining); + // EAGAIN means that we need additional packages to decode this frame. + // AVERROR_EOF means the end of package. + mGetNextPacket = true; + break; + } + else if (ret < 0) + { + // Error encountered. Stop to decode audio stream. + av_packet_unref(&mPacket); + mGetNextPacket = true; + return -1; } - /* No data yet? Look for more frames */ - if(!got_frame || frame->nb_samples <= 0) + if(frame->nb_samples <= 0) continue; if(mAudioResampler->mSwr) @@ -170,7 +197,7 @@ int MovieAudioDecoder::audio_decode_frame(AVFrame *frame, int &sample_skip) if(!mDataBuf || mDataBufLen < frame->nb_samples) { av_freep(&mDataBuf); - if(av_samples_alloc(&mDataBuf, NULL, av_get_channel_layout_nb_channels(mOutputChannelLayout), + if(av_samples_alloc(&mDataBuf, nullptr, av_get_channel_layout_nb_channels(mOutputChannelLayout), frame->nb_samples, mOutputSampleFormat, 0) < 0) break; else @@ -187,14 +214,16 @@ int MovieAudioDecoder::audio_decode_frame(AVFrame *frame, int &sample_skip) else mFrameData = &frame->data[0]; - mAudioClock += (double)frame->nb_samples / - (double)mAVStream->codec->sample_rate; + int result = frame->nb_samples * av_get_channel_layout_nb_channels(mOutputChannelLayout) * + av_get_bytes_per_sample(mOutputSampleFormat); /* We have data, return it and come back for more later */ - return frame->nb_samples * av_get_channel_layout_nb_channels(mOutputChannelLayout) * - av_get_bytes_per_sample(mOutputSampleFormat); + mGetNextPacket = false; + return result; } - av_free_packet(pkt); + + av_packet_unref(&mPacket); + mGetNextPacket = true; /* next packet */ if(mVideoState->audioq.get(pkt, mVideoState) < 0) @@ -202,7 +231,7 @@ int MovieAudioDecoder::audio_decode_frame(AVFrame *frame, int &sample_skip) if(pkt->data == mVideoState->mFlushPktData) { - avcodec_flush_buffers(mAVStream->codec); + avcodec_flush_buffers(mAudioContext); mAudioDiffAccum = 0.0; mAudioDiffAvgCount = 0; mAudioClock = av_q2d(mAVStream->time_base)*pkt->pts; diff --git a/extern/osg-ffmpeg-videoplayer/audiodecoder.hpp b/extern/osg-ffmpeg-videoplayer/audiodecoder.hpp index 95644ea69..afb088431 100644 --- a/extern/osg-ffmpeg-videoplayer/audiodecoder.hpp +++ b/extern/osg-ffmpeg-videoplayer/audiodecoder.hpp @@ -11,11 +11,7 @@ extern "C" #include #include #include - - #if AV_VERSION_INT(52, 2, 0) <= AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \ - LIBAVUTIL_VERSION_MINOR, LIBAVUTIL_VERSION_MICRO) - #include - #endif + #include } #if defined(_WIN32) && !defined(__MINGW32__) @@ -35,6 +31,7 @@ class MovieAudioDecoder { protected: VideoState *mVideoState; + AVCodecContext* mAudioContext; AVStream *mAVStream; enum AVSampleFormat mOutputSampleFormat; uint64_t mOutputChannelLayout; @@ -51,7 +48,7 @@ private: throw std::bad_alloc(); } ~AutoAVPacket() - { av_free_packet(this); } + { av_packet_unref(this); } }; @@ -63,6 +60,7 @@ private: AutoAVPacket mPacket; AVFrame *mFrame; + bool mGetNextPacket; /* averaging filter for audio sync */ double mAudioDiffAccum; diff --git a/extern/osg-ffmpeg-videoplayer/videostate.cpp b/extern/osg-ffmpeg-videoplayer/videostate.cpp index 35bfca4aa..014c5c4f5 100644 --- a/extern/osg-ffmpeg-videoplayer/videostate.cpp +++ b/extern/osg-ffmpeg-videoplayer/videostate.cpp @@ -11,19 +11,7 @@ extern "C" #include #include #include - - // From libavformat version 55.0.100 and onward the declaration of av_gettime() is - // removed from libavformat/avformat.h and moved to libavutil/time.h - // https://github.com/FFmpeg/FFmpeg/commit/06a83505992d5f49846c18507a6c3eb8a47c650e - #if AV_VERSION_INT(55, 0, 100) <= AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ - LIBAVFORMAT_VERSION_MINOR, LIBAVFORMAT_VERSION_MICRO) - #include - #endif - - #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55,28,1) - #define av_frame_alloc avcodec_alloc_frame - #endif - + #include } static const char* flushString = "FLUSH"; @@ -54,6 +42,8 @@ namespace Video VideoState::VideoState() : mAudioFactory(NULL) , format_ctx(NULL) + , video_ctx(NULL) + , audio_ctx(NULL) , av_sync_type(AV_SYNC_DEFAULT) , audio_st(NULL) , video_st(NULL), frame_last_pts(0.0) @@ -67,8 +57,10 @@ VideoState::VideoState() { mFlushPktData = flush_pkt.data; - // Register all formats and codecs +// This is not needed anymore above FFMpeg version 4.0 +#if LIBAVCODEC_VERSION_INT < 3805796 av_register_all(); +#endif } VideoState::~VideoState() @@ -85,11 +77,12 @@ void VideoState::setAudioFactory(MovieAudioFactory *factory) void PacketQueue::put(AVPacket *pkt) { AVPacketList *pkt1; - if(pkt != &flush_pkt && !pkt->buf && av_dup_packet(pkt) < 0) - throw std::runtime_error("Failed to duplicate packet"); - pkt1 = (AVPacketList*)av_malloc(sizeof(AVPacketList)); if(!pkt1) throw std::bad_alloc(); + + if(pkt != &flush_pkt && !pkt->buf && av_packet_ref(&pkt1->pkt, pkt) < 0) + throw std::runtime_error("Failed to duplicate packet"); + pkt1->pkt = *pkt; pkt1->next = NULL; @@ -150,7 +143,7 @@ void PacketQueue::clear() { pkt1 = pkt->next; if (pkt->pkt.data != flush_pkt.data) - av_free_packet(&pkt->pkt); + av_packet_unref(&pkt->pkt); av_freep(&pkt); } this->last_pkt = NULL; @@ -211,7 +204,7 @@ int64_t VideoState::istream_seek(void *user_data, int64_t offset, int whence) void VideoState::video_display(VideoPicture *vp) { - if((*this->video_st)->codec->width != 0 && (*this->video_st)->codec->height != 0) + if(this->video_ctx->width != 0 && this->video_ctx->height != 0) { if (!mTexture.get()) { @@ -224,7 +217,7 @@ void VideoState::video_display(VideoPicture *vp) osg::ref_ptr image = new osg::Image; - image->setImage((*this->video_st)->codec->width, (*this->video_st)->codec->height, + image->setImage(this->video_ctx->width, this->video_ctx->height, 1, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, &vp->data[0], osg::Image::NO_DELETE); mTexture->setImage(image); @@ -303,9 +296,9 @@ int VideoState::queue_picture(AVFrame *pFrame, double pts) // matches a commonly used format (ie YUV420P) if(this->sws_context == NULL) { - int w = (*this->video_st)->codec->width; - int h = (*this->video_st)->codec->height; - this->sws_context = sws_getContext(w, h, (*this->video_st)->codec->pix_fmt, + int w = this->video_ctx->width; + int h = this->video_ctx->height; + this->sws_context = sws_getContext(w, h, this->video_ctx->pix_fmt, w, h, AV_PIX_FMT_RGBA, SWS_BICUBIC, NULL, NULL, NULL); if(this->sws_context == NULL) @@ -313,11 +306,11 @@ int VideoState::queue_picture(AVFrame *pFrame, double pts) } vp->pts = pts; - vp->data.resize((*this->video_st)->codec->width * (*this->video_st)->codec->height * 4); + vp->data.resize(this->video_ctx->width * this->video_ctx->height * 4); uint8_t *dst[4] = { &vp->data[0], nullptr, nullptr, nullptr }; sws_scale(this->sws_context, pFrame->data, pFrame->linesize, - 0, (*this->video_st)->codec->height, dst, this->rgbaFrame->linesize); + 0, this->video_ctx->height, dst, this->rgbaFrame->linesize); // now we inform our display thread that we have a pic ready this->pictq_windex = (this->pictq_windex+1) % VIDEO_PICTURE_ARRAY_SIZE; @@ -338,7 +331,7 @@ double VideoState::synchronize_video(AVFrame *src_frame, double pts) pts = this->video_clock; /* update the video clock */ - frame_delay = av_q2d((*this->video_st)->codec->time_base); + frame_delay = av_q2d(this->video_ctx->pkt_timebase); /* if we are repeating a frame, adjust clock accordingly */ frame_delay += src_frame->repeat_pict * (frame_delay * 0.5); @@ -347,30 +340,6 @@ double VideoState::synchronize_video(AVFrame *src_frame, double pts) return pts; } -static void our_free_buffer(void *opaque, uint8_t *data); -/* These are called whenever we allocate a frame - * buffer. We use this to store the global_pts in - * a frame at the time it is allocated. - */ -static int64_t global_video_pkt_pts = AV_NOPTS_VALUE; -static int our_get_buffer(struct AVCodecContext *c, AVFrame *pic, int flags) -{ - AVBufferRef *ref; - int ret = avcodec_default_get_buffer2(c, pic, flags); - int64_t *pts = (int64_t*)av_malloc(sizeof(int64_t)); - *pts = global_video_pkt_pts; - pic->opaque = pts; - ref = av_buffer_create((uint8_t *)pic->opaque, sizeof(int64_t), our_free_buffer, pic->buf[0], flags); - pic->buf[0] = ref; - return ret; -} -static void our_free_buffer(void *opaque, uint8_t *data) -{ - AVBufferRef *ref = (AVBufferRef *)opaque; - av_buffer_unref(&ref); - av_free(data); -} - class VideoThread : public OpenThreads::Thread { public: @@ -384,19 +353,18 @@ public: { VideoState* self = mVideoState; AVPacket pkt1, *packet = &pkt1; - int frameFinished; AVFrame *pFrame; pFrame = av_frame_alloc(); self->rgbaFrame = av_frame_alloc(); - avpicture_alloc((AVPicture*)self->rgbaFrame, AV_PIX_FMT_RGBA, (*self->video_st)->codec->width, (*self->video_st)->codec->height); + av_image_alloc(self->rgbaFrame->data, self->rgbaFrame->linesize, self->video_ctx->width, self->video_ctx->height, AV_PIX_FMT_RGBA, 1); while(self->videoq.get(packet, self) >= 0) { if(packet->data == flush_pkt.data) { - avcodec_flush_buffers((*self->video_st)->codec); + avcodec_flush_buffers(self->video_ctx); self->pictq_mutex.lock(); self->pictq_size = 0; @@ -405,37 +373,36 @@ public: self->pictq_mutex.unlock(); self->frame_last_pts = packet->pts * av_q2d((*self->video_st)->time_base); - global_video_pkt_pts = static_cast(self->frame_last_pts); continue; } - // Save global pts to be stored in pFrame - global_video_pkt_pts = packet->pts; // Decode video frame - if(avcodec_decode_video2((*self->video_st)->codec, pFrame, &frameFinished, packet) < 0) + int ret = avcodec_send_packet(self->video_ctx, packet); + // EAGAIN is not expected + if (ret < 0) throw std::runtime_error("Error decoding video frame"); - double pts = 0; - if(packet->dts != AV_NOPTS_VALUE) - pts = static_cast(packet->dts); - else if(pFrame->opaque && *(int64_t*)pFrame->opaque != AV_NOPTS_VALUE) - pts = static_cast(*(int64_t*)pFrame->opaque); - pts *= av_q2d((*self->video_st)->time_base); - - av_free_packet(packet); - - // Did we get a video frame? - if(frameFinished) + while (!ret) { - pts = self->synchronize_video(pFrame, pts); - if(self->queue_picture(pFrame, pts) < 0) - break; + ret = avcodec_receive_frame(self->video_ctx, pFrame); + if (!ret) + { + double pts = pFrame->best_effort_timestamp; + pts *= av_q2d((*self->video_st)->time_base); + + pts = self->synchronize_video(pFrame, pts); + + if(self->queue_picture(pFrame, pts) < 0) + break; + } } } + av_packet_unref(packet); + av_free(pFrame); - avpicture_free((AVPicture*)self->rgbaFrame); + av_freep(&self->rgbaFrame->data[0]); av_free(self->rgbaFrame); } @@ -497,7 +464,14 @@ public: // AVSEEK_FLAG_BACKWARD appears to be needed, otherwise ffmpeg may seek to a keyframe *after* the given time // we want to seek to any keyframe *before* the given time, so we can continue decoding as normal from there on if(av_seek_frame(self->format_ctx, streamIndex, timestamp, AVSEEK_FLAG_BACKWARD) < 0) + { +// In the FFMpeg 4.0 a "filename" field was replaced by "url" +#if LIBAVCODEC_VERSION_INT < 3805796 std::cerr << "Error seeking " << self->format_ctx->filename << std::endl; +#else + std::cerr << "Error seeking " << self->format_ctx->url << std::endl; +#endif + } else { // Clear the packet queues and put a special packet with the new clock time @@ -548,7 +522,7 @@ public: else if(self->audio_st && packet->stream_index == self->audio_st-pFormatCtx->streams) self->audioq.put(packet); else - av_free_packet(packet); + av_packet_unref(packet); } } catch(std::exception& e) { @@ -572,30 +546,43 @@ bool VideoState::update() int VideoState::stream_open(int stream_index, AVFormatContext *pFormatCtx) { - AVCodecContext *codecCtx; AVCodec *codec; if(stream_index < 0 || stream_index >= static_cast(pFormatCtx->nb_streams)) return -1; // Get a pointer to the codec context for the video stream - codecCtx = pFormatCtx->streams[stream_index]->codec; - codec = avcodec_find_decoder(codecCtx->codec_id); - if(!codec || (avcodec_open2(codecCtx, codec, NULL) < 0)) + codec = avcodec_find_decoder(pFormatCtx->streams[stream_index]->codecpar->codec_id); + if(!codec) { fprintf(stderr, "Unsupported codec!\n"); return -1; } - switch(codecCtx->codec_type) + switch(pFormatCtx->streams[stream_index]->codecpar->codec_type) { case AVMEDIA_TYPE_AUDIO: this->audio_st = pFormatCtx->streams + stream_index; + // Get a pointer to the codec context for the video stream + this->audio_ctx = avcodec_alloc_context3(codec); + avcodec_parameters_to_context(this->audio_ctx, pFormatCtx->streams[stream_index]->codecpar); + +// This is not needed anymore above FFMpeg version 4.0 +#if LIBAVCODEC_VERSION_INT < 3805796 + av_codec_set_pkt_timebase(this->audio_ctx, pFormatCtx->streams[stream_index]->time_base); +#endif + + if (avcodec_open2(this->audio_ctx, codec, NULL) < 0) + { + fprintf(stderr, "Unsupported codec!\n"); + return -1; + } + if (!mAudioFactory) { std::cerr << "No audio factory registered, can not play audio stream" << std::endl; - avcodec_close((*this->audio_st)->codec); + avcodec_free_context(&this->audio_ctx); this->audio_st = NULL; return -1; } @@ -604,7 +591,7 @@ int VideoState::stream_open(int stream_index, AVFormatContext *pFormatCtx) if (!mAudioDecoder.get()) { std::cerr << "Failed to create audio decoder, can not play audio stream" << std::endl; - avcodec_close((*this->audio_st)->codec); + avcodec_free_context(&this->audio_ctx); this->audio_st = NULL; return -1; } @@ -614,7 +601,21 @@ int VideoState::stream_open(int stream_index, AVFormatContext *pFormatCtx) case AVMEDIA_TYPE_VIDEO: this->video_st = pFormatCtx->streams + stream_index; - codecCtx->get_buffer2 = our_get_buffer; + // Get a pointer to the codec context for the video stream + this->video_ctx = avcodec_alloc_context3(codec); + avcodec_parameters_to_context(this->video_ctx, pFormatCtx->streams[stream_index]->codecpar); + +// This is not needed anymore above FFMpeg version 4.0 +#if LIBAVCODEC_VERSION_INT < 3805796 + av_codec_set_pkt_timebase(this->video_ctx, pFormatCtx->streams[stream_index]->time_base); +#endif + + if (avcodec_open2(this->video_ctx, codec, NULL) < 0) + { + fprintf(stderr, "Unsupported codec!\n"); + return -1; + } + this->video_thread.reset(new VideoThread(this)); break; @@ -680,9 +681,9 @@ void VideoState::init(std::shared_ptr inputstream, const std::stri for(i = 0;i < this->format_ctx->nb_streams;i++) { - if(this->format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO && video_index < 0) + if(this->format_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && video_index < 0) video_index = i; - if(this->format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO && audio_index < 0) + if(this->format_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && audio_index < 0) audio_index = i; } @@ -720,12 +721,14 @@ void VideoState::deinit() this->video_thread.reset(); } - if(this->audio_st) - avcodec_close((*this->audio_st)->codec); + if(this->audio_ctx) + avcodec_free_context(&this->audio_ctx); this->audio_st = NULL; - if(this->video_st) - avcodec_close((*this->video_st)->codec); + this->audio_ctx = NULL; + if(this->video_ctx) + avcodec_free_context(&this->video_ctx); this->video_st = NULL; + this->video_ctx = NULL; if(this->sws_context) sws_freeContext(this->sws_context); diff --git a/extern/osg-ffmpeg-videoplayer/videostate.hpp b/extern/osg-ffmpeg-videoplayer/videostate.hpp index e61f4feb7..2fdb98da5 100644 --- a/extern/osg-ffmpeg-videoplayer/videostate.hpp +++ b/extern/osg-ffmpeg-videoplayer/videostate.hpp @@ -15,6 +15,19 @@ namespace osg class Texture2D; } +extern "C" +{ +#include +#include +#include +#include + +// From version 54.56 binkaudio encoding format changed from S16 to FLTP. See: +// https://gitorious.org/ffmpeg/ffmpeg/commit/7bfd1766d1c18f07b0a2dd042418a874d49ea60d +// https://ffmpeg.zeranoe.com/forum/viewtopic.php?f=15&t=872 +#include +} + #include "videodefs.hpp" #define VIDEO_PICTURE_QUEUE_SIZE 50 @@ -131,6 +144,8 @@ struct VideoState { std::shared_ptr stream; AVFormatContext* format_ctx; + AVCodecContext* video_ctx; + AVCodecContext* audio_ctx; int av_sync_type; From b099981c91c726303fda4b7da69bad153f35afae Mon Sep 17 00:00:00 2001 From: Stanislaw Halik Date: Sat, 3 Nov 2018 20:19:44 +0100 Subject: [PATCH 07/53] fix CI msvc build outside git bash It was only by accident that git bash worked for CI basing on the "real_pwd()" expression. Replace the dubious replacement pattern with "cygpath", which is present on git bash, Cygwin, and msys2 alike. In particular git bash uses msys2 internally. I was able to confirm a working build under msys2 with a wrapper for MSVC invocation via the Ninja generator. --- CI/before_script.msvc.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CI/before_script.msvc.sh b/CI/before_script.msvc.sh index c328dd882..55dda74af 100644 --- a/CI/before_script.msvc.sh +++ b/CI/before_script.msvc.sh @@ -194,7 +194,11 @@ download() { } real_pwd() { - pwd | sed "s,/\(.\),\1:," + if type cygpath >/dev/null 2>&1; then + cygpath -am "$PWD" + else + pwd # not git bash, Cygwin or the like + fi } CMAKE_OPTS="" From a387c6b91017e2b9ff18d536a04461be4cb9388c Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sun, 4 Nov 2018 11:11:55 +0400 Subject: [PATCH 08/53] Fix MSVC2015 C4503 warning --- components/detournavigator/navmeshtilescache.cpp | 14 +++++++------- components/detournavigator/navmeshtilescache.hpp | 8 +++++++- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/components/detournavigator/navmeshtilescache.cpp b/components/detournavigator/navmeshtilescache.cpp index 484831493..8bbe8ed48 100644 --- a/components/detournavigator/navmeshtilescache.cpp +++ b/components/detournavigator/navmeshtilescache.cpp @@ -64,8 +64,8 @@ namespace DetourNavigator return Value(); // TODO: use different function to make key to avoid unnecessary std::string allocation - const auto tile = tileValues->second.find(makeNavMeshKey(recastMesh, offMeshConnections)); - if (tile == tileValues->second.end()) + const auto tile = tileValues->second.Map.find(makeNavMeshKey(recastMesh, offMeshConnections)); + if (tile == tileValues->second.Map.end()) return Value(); acquireItemUnsafe(tile->second); @@ -98,7 +98,7 @@ namespace DetourNavigator const auto iterator = mFreeItems.emplace(mFreeItems.end(), agentHalfExtents, changedTile, navMeshKey); // TODO: use std::string_view or some alternative to avoid navMeshKey copy into both mFreeItems and mValues - const auto emplaced = mValues[agentHalfExtents][changedTile].emplace(navMeshKey, iterator); + const auto emplaced = mValues[agentHalfExtents][changedTile].Map.emplace(navMeshKey, iterator); if (!emplaced.second) { @@ -127,15 +127,15 @@ namespace DetourNavigator if (tileValues == agentValues->second.end()) return; - const auto value = tileValues->second.find(item.mNavMeshKey); - if (value == tileValues->second.end()) + const auto value = tileValues->second.Map.find(item.mNavMeshKey); + if (value == tileValues->second.Map.end()) return; mUsedNavMeshDataSize -= static_cast(item.mNavMeshData.mSize) + item.mNavMeshKey.size(); mFreeItems.pop_back(); - tileValues->second.erase(value); - if (!tileValues->second.empty()) + tileValues->second.Map.erase(value); + if (!tileValues->second.Map.empty()) return; agentValues->second.erase(tileValues); diff --git a/components/detournavigator/navmeshtilescache.hpp b/components/detournavigator/navmeshtilescache.hpp index 1d0ecb5dc..27ac499e8 100644 --- a/components/detournavigator/navmeshtilescache.hpp +++ b/components/detournavigator/navmeshtilescache.hpp @@ -108,13 +108,19 @@ namespace DetourNavigator NavMeshData&& value); private: + + struct TileMap + { + std::map Map; + }; + std::mutex mMutex; std::size_t mMaxNavMeshDataSize; std::size_t mUsedNavMeshDataSize; std::size_t mFreeNavMeshDataSize; std::list mBusyItems; std::list mFreeItems; - std::map>> mValues; + std::map> mValues; void removeLeastRecentlyUsed(); From db5638bf6d0a9690ca2a9a5a9fce3eb902d0cf3b Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 4 Nov 2018 18:11:15 +0300 Subject: [PATCH 09/53] Add global recast allocator to allocate temp buffers on stack --- apps/openmw/mwworld/worldimp.cpp | 3 +- .../detournavigator/recastallocutils.hpp | 90 +++++++++++++++++++ .../detournavigator/recastglobalallocator.hpp | 68 ++++++++++++++ .../detournavigator/recasttempallocator.hpp | 65 ++++++++++++++ 4 files changed, 225 insertions(+), 1 deletion(-) create mode 100644 components/detournavigator/recastallocutils.hpp create mode 100644 components/detournavigator/recastglobalallocator.hpp create mode 100644 components/detournavigator/recasttempallocator.hpp diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index fd6f4ff6f..733b961a7 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -25,7 +25,7 @@ #include #include -#include +#include #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" @@ -230,6 +230,7 @@ namespace MWWorld if (Settings::Manager::getBool("enable log", "Navigator")) DetourNavigator::Log::instance().setSink(std::unique_ptr( new DetourNavigator::FileSink(Settings::Manager::getString("log path", "Navigator")))); + DetourNavigator::RecastGlobalAllocator::init(); mNavigator.reset(new DetourNavigator::Navigator(navigatorSettings)); mRendering.reset(new MWRender::RenderingManager(viewer, rootNode, resourceSystem, workQueue, &mFallback, resourcePath, *mNavigator)); diff --git a/components/detournavigator/recastallocutils.hpp b/components/detournavigator/recastallocutils.hpp new file mode 100644 index 000000000..436d9c49a --- /dev/null +++ b/components/detournavigator/recastallocutils.hpp @@ -0,0 +1,90 @@ +#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_RECASTALLOCUTILS_H +#define OPENMW_COMPONENTS_DETOURNAVIGATOR_RECASTALLOCUTILS_H + +#include + +#include + +namespace DetourNavigator +{ + static_assert(sizeof(std::size_t) == sizeof(void*), ""); + + enum BufferType : std::size_t + { + BufferType_perm, + BufferType_temp, + BufferType_unused, + }; + + inline BufferType* tempPtrBufferType(void* ptr) + { + return reinterpret_cast(static_cast(ptr) + 1); + } + + inline BufferType getTempPtrBufferType(void* ptr) + { + return *tempPtrBufferType(ptr); + } + + inline void setTempPtrBufferType(void* ptr, BufferType value) + { + *tempPtrBufferType(ptr) = value; + } + + inline void** tempPtrPrev(void* ptr) + { + return static_cast(ptr); + } + + inline void* getTempPtrPrev(void* ptr) + { + return *tempPtrPrev(ptr); + } + + inline void setTempPtrPrev(void* ptr, void* value) + { + *tempPtrPrev(ptr) = value; + } + + inline void* getTempPtrDataPtr(void* ptr) + { + return reinterpret_cast(static_cast(ptr) + 2); + } + + inline BufferType* dataPtrBufferType(void* dataPtr) + { + return reinterpret_cast(static_cast(dataPtr) - 1); + } + + inline BufferType getDataPtrBufferType(void* dataPtr) + { + return *dataPtrBufferType(dataPtr); + } + + inline void setDataPtrBufferType(void* dataPtr, BufferType value) + { + *dataPtrBufferType(dataPtr) = value; + } + + inline void* getTempDataPtrStackPtr(void* dataPtr) + { + return static_cast(dataPtr) - 2; + } + + inline void* getPermDataPtrHeapPtr(void* dataPtr) + { + return static_cast(dataPtr) - 1; + } + + inline void setPermPtrBufferType(void* ptr, BufferType value) + { + *static_cast(ptr) = value; + } + + inline void* getPermPtrDataPtr(void* ptr) + { + return static_cast(ptr) + 1; + } +} + +#endif diff --git a/components/detournavigator/recastglobalallocator.hpp b/components/detournavigator/recastglobalallocator.hpp new file mode 100644 index 000000000..7c4b2c534 --- /dev/null +++ b/components/detournavigator/recastglobalallocator.hpp @@ -0,0 +1,68 @@ +#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_RECASTGLOBALALLOCATOR_H +#define OPENMW_COMPONENTS_DETOURNAVIGATOR_RECASTGLOBALALLOCATOR_H + +#include "recasttempallocator.hpp" + +namespace DetourNavigator +{ + class RecastGlobalAllocator + { + public: + static void init() + { + instance(); + } + + static void* alloc(size_t size, rcAllocHint hint) + { + void* result = nullptr; + if (rcLikely(hint == RC_ALLOC_TEMP)) + result = tempAllocator().alloc(size); + if (rcUnlikely(!result)) + result = allocPerm(size); + return result; + } + + static void free(void* ptr) + { + if (rcUnlikely(!ptr)) + return; + if (rcLikely(BufferType_temp == getDataPtrBufferType(ptr))) + tempAllocator().free(ptr); + else + { + assert(BufferType_perm == getDataPtrBufferType(ptr)); + ::free(getPermDataPtrHeapPtr(ptr)); + } + } + + private: + RecastGlobalAllocator() + { + rcAllocSetCustom(&RecastGlobalAllocator::alloc, &RecastGlobalAllocator::free); + } + + static RecastGlobalAllocator& instance() + { + static RecastGlobalAllocator value; + return value; + } + + static RecastTempAllocator& tempAllocator() + { + static thread_local RecastTempAllocator value(1024ul * 1024ul); + return value; + } + + static void* allocPerm(size_t size) + { + const auto ptr = ::malloc(size + sizeof(std::size_t)); + if (rcUnlikely(!ptr)) + return ptr; + setPermPtrBufferType(ptr, BufferType_perm); + return getPermPtrDataPtr(ptr); + } + }; +} + +#endif diff --git a/components/detournavigator/recasttempallocator.hpp b/components/detournavigator/recasttempallocator.hpp new file mode 100644 index 000000000..fbf9fd62c --- /dev/null +++ b/components/detournavigator/recasttempallocator.hpp @@ -0,0 +1,65 @@ +#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_RECASTTEMPALLOCATOR_H +#define OPENMW_COMPONENTS_DETOURNAVIGATOR_RECASTTEMPALLOCATOR_H + +#include "recastallocutils.hpp" + +#include +#include +#include + +namespace DetourNavigator +{ + class RecastTempAllocator + { + public: + RecastTempAllocator(std::size_t capacity) + : mStack(capacity), mTop(mStack.data()), mPrev(nullptr) + {} + + void* alloc(std::size_t size) + { + std::size_t space = mStack.size() - getUsedSize(); + void* top = mTop; + const auto itemSize = 2 * sizeof(std::size_t) + size; + if (rcUnlikely(!std::align(sizeof(std::size_t), itemSize, top, space))) + return nullptr; + setTempPtrBufferType(top, BufferType_temp); + setTempPtrPrev(top, mPrev); + mTop = static_cast(top) + itemSize; + mPrev = static_cast(top); + return getTempPtrDataPtr(top); + } + + void free(void* ptr) + { + if (rcUnlikely(!ptr)) + return; + assert(BufferType_temp == getDataPtrBufferType(ptr)); + if (!mPrev || getTempDataPtrStackPtr(ptr) != mPrev) + { + setDataPtrBufferType(ptr, BufferType_unused); + return; + } + mTop = getTempDataPtrStackPtr(ptr); + mPrev = getTempPtrPrev(mTop); + while (mPrev && BufferType_unused == getTempPtrBufferType(mPrev)) + { + mTop = mPrev; + mPrev = getTempPtrPrev(mTop); + } + return; + } + + private: + std::vector mStack; + void* mTop; + void* mPrev; + + std::size_t getUsedSize() const + { + return static_cast(static_cast(mTop) - mStack.data()); + } + }; +} + +#endif From b477775e1602893c6586b0525ed104b1b383636e Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 4 Nov 2018 18:34:31 +0300 Subject: [PATCH 10/53] Use callback to iterate over chunks --- components/detournavigator/chunkytrimesh.hpp | 6 +- components/detournavigator/makenavmesh.cpp | 93 ++++++++++---------- 2 files changed, 49 insertions(+), 50 deletions(-) diff --git a/components/detournavigator/chunkytrimesh.hpp b/components/detournavigator/chunkytrimesh.hpp index c9d35f086..9f6275ec8 100644 --- a/components/detournavigator/chunkytrimesh.hpp +++ b/components/detournavigator/chunkytrimesh.hpp @@ -50,8 +50,8 @@ namespace DetourNavigator ChunkyTriMesh& operator=(const ChunkyTriMesh&) = delete; /// Returns the chunk indices which overlap the input rectable. - template - void getChunksOverlappingRect(const Rect& rect, OutputIterator out) const + template + void forEachChunksOverlappingRect(const Rect& rect, Function&& function) const { // Traverse tree for (std::size_t i = 0; i < mNodes.size(); ) @@ -61,7 +61,7 @@ namespace DetourNavigator const bool isLeafNode = node->mOffset >= 0; if (isLeafNode && overlap) - *out++ = i; + function(i); if (overlap || isLeafNode) i++; diff --git a/components/detournavigator/makenavmesh.cpp b/components/detournavigator/makenavmesh.cpp index 7072f2a23..3ee730386 100644 --- a/components/detournavigator/makenavmesh.cpp +++ b/components/detournavigator/makenavmesh.cpp @@ -149,62 +149,61 @@ namespace 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]); - std::vector cids; - chunkyMesh.getChunksOverlappingRect(Rect {tileBoundsMin, tileBoundsMax}, std::back_inserter(cids)); + bool result = false; - if (cids.empty()) - return false; + chunkyMesh.forEachChunksOverlappingRect(Rect {tileBoundsMin, tileBoundsMax}, + [&] (const std::size_t cid) + { + const auto chunk = chunkyMesh.getChunk(cid); - for (const auto cid : cids) - { - const auto chunk = chunkyMesh.getChunk(cid); + std::fill( + areas.begin(), + std::min(areas.begin() + static_cast(chunk.mSize), + areas.end()), + AreaType_null + ); - 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() + ); - 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]; - 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() + ); - 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 + ); - 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"); - if (!trianglesRasterized) - throw NavigatorException("Failed to create rasterize triangles from recast mesh for navmesh"); - } + result = true; + }); - return true; + return result; } void rasterizeWaterTriangles(rcContext& context, const osg::Vec3f& agentHalfExtents, const RecastMesh& recastMesh, From d2c2bdadb5ab1f16a79120ffd91623d157e4cf37 Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 4 Nov 2018 21:23:30 +0300 Subject: [PATCH 11/53] Add align implementation --- components/detournavigator/recastallocutils.hpp | 12 ++++++++++++ components/detournavigator/recasttempallocator.hpp | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/components/detournavigator/recastallocutils.hpp b/components/detournavigator/recastallocutils.hpp index 436d9c49a..7b083d139 100644 --- a/components/detournavigator/recastallocutils.hpp +++ b/components/detournavigator/recastallocutils.hpp @@ -85,6 +85,18 @@ namespace DetourNavigator { return static_cast(ptr) + 1; } + + // TODO: use std::align + inline void* align(std::size_t align, std::size_t size, void*& ptr, std::size_t& space) noexcept + { + const auto intptr = reinterpret_cast(ptr); + const auto aligned = (intptr - 1u + align) & - align; + const auto diff = aligned - intptr; + if ((size + diff) > space) + return nullptr; + space -= diff; + return ptr = reinterpret_cast(aligned); + } } #endif diff --git a/components/detournavigator/recasttempallocator.hpp b/components/detournavigator/recasttempallocator.hpp index fbf9fd62c..e369b4224 100644 --- a/components/detournavigator/recasttempallocator.hpp +++ b/components/detournavigator/recasttempallocator.hpp @@ -21,7 +21,7 @@ namespace DetourNavigator std::size_t space = mStack.size() - getUsedSize(); void* top = mTop; const auto itemSize = 2 * sizeof(std::size_t) + size; - if (rcUnlikely(!std::align(sizeof(std::size_t), itemSize, top, space))) + if (rcUnlikely(!align(sizeof(std::size_t), itemSize, top, space))) return nullptr; setTempPtrBufferType(top, BufferType_temp); setTempPtrPrev(top, mPrev); From 696bb9adc897884f1c3960900403b1790428a5e3 Mon Sep 17 00:00:00 2001 From: elsid Date: Mon, 5 Nov 2018 02:17:54 +0300 Subject: [PATCH 12/53] Add missing decrease for free navmesh data size --- components/detournavigator/navmeshtilescache.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/components/detournavigator/navmeshtilescache.cpp b/components/detournavigator/navmeshtilescache.cpp index 8bbe8ed48..cd60fa7e8 100644 --- a/components/detournavigator/navmeshtilescache.cpp +++ b/components/detournavigator/navmeshtilescache.cpp @@ -132,6 +132,7 @@ namespace DetourNavigator return; mUsedNavMeshDataSize -= static_cast(item.mNavMeshData.mSize) + item.mNavMeshKey.size(); + mFreeNavMeshDataSize -= static_cast(item.mNavMeshData.mSize) + item.mNavMeshKey.size(); mFreeItems.pop_back(); tileValues->second.Map.erase(value); From 6e78fbb538ee4e9908dd4e9c4a659df6d62de26e Mon Sep 17 00:00:00 2001 From: elsid Date: Mon, 5 Nov 2018 01:59:33 +0300 Subject: [PATCH 13/53] Double key size for item cache size --- .../detournavigator/navmeshtilescache.cpp | 22 +++++++++---------- .../detournavigator/navmeshtilescache.cpp | 10 ++++----- .../detournavigator/navmeshtilescache.hpp | 5 +++++ 3 files changed, 21 insertions(+), 16 deletions(-) diff --git a/apps/openmw_test_suite/detournavigator/navmeshtilescache.cpp b/apps/openmw_test_suite/detournavigator/navmeshtilescache.cpp index 8b1ee230d..17b17b97c 100644 --- a/apps/openmw_test_suite/detournavigator/navmeshtilescache.cpp +++ b/apps/openmw_test_suite/detournavigator/navmeshtilescache.cpp @@ -57,7 +57,7 @@ namespace { const std::size_t navMeshDataSize = 1; const std::size_t navMeshKeySize = 49; - const std::size_t maxSize = navMeshDataSize + navMeshKeySize; + const std::size_t maxSize = navMeshDataSize + 2 * navMeshKeySize; NavMeshTilesCache cache(maxSize); const auto result = cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, @@ -70,7 +70,7 @@ namespace { const std::size_t navMeshDataSize = 1; const std::size_t navMeshKeySize = 49; - const std::size_t maxSize = 2 * (navMeshDataSize + navMeshKeySize); + const std::size_t maxSize = 2 * (navMeshDataSize + 2 * navMeshKeySize); NavMeshTilesCache cache(maxSize); const auto anotherData = reinterpret_cast(dtAlloc(1, DT_ALLOC_PERM)); NavMeshData anotherNavMeshData {anotherData, 1}; @@ -86,7 +86,7 @@ namespace { const std::size_t navMeshDataSize = 1; const std::size_t navMeshKeySize = 49; - const std::size_t maxSize = navMeshDataSize + navMeshKeySize; + const std::size_t maxSize = navMeshDataSize + 2 * navMeshKeySize; NavMeshTilesCache cache(maxSize); cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, std::move(mNavMeshData)); @@ -130,7 +130,7 @@ namespace { const std::size_t navMeshDataSize = 1; const std::size_t navMeshKeySize = 117; - const std::size_t maxSize = navMeshDataSize + navMeshKeySize; + const std::size_t maxSize = navMeshDataSize + 2 * navMeshKeySize; NavMeshTilesCache cache(maxSize); const std::vector water {1, RecastMesh::Water {1, btTransform::getIdentity()}}; @@ -150,7 +150,7 @@ namespace { const std::size_t navMeshDataSize = 1; const std::size_t navMeshKeySize = 49; - const std::size_t maxSize = navMeshDataSize + navMeshKeySize; + const std::size_t maxSize = navMeshDataSize + 2 * navMeshKeySize; NavMeshTilesCache cache(maxSize); const std::vector water {1, RecastMesh::Water {1, btTransform::getIdentity()}}; @@ -168,7 +168,7 @@ namespace { const std::size_t navMeshDataSize = 1; const std::size_t navMeshKeySize = 117; - const std::size_t maxSize = 2 * (navMeshDataSize + navMeshKeySize); + const std::size_t maxSize = 2 * (navMeshDataSize + 2 * navMeshKeySize); NavMeshTilesCache cache(maxSize); const std::vector leastRecentlySetWater {1, RecastMesh::Water {1, btTransform::getIdentity()}}; @@ -200,7 +200,7 @@ namespace { const std::size_t navMeshDataSize = 1; const std::size_t navMeshKeySize = 117; - const std::size_t maxSize = 2 * (navMeshDataSize + navMeshKeySize); + const std::size_t maxSize = 2 * (navMeshDataSize + 2 * navMeshKeySize); NavMeshTilesCache cache(maxSize); const std::vector leastRecentlyUsedWater {1, RecastMesh::Water {1, btTransform::getIdentity()}}; @@ -244,7 +244,7 @@ namespace { const std::size_t navMeshDataSize = 1; const std::size_t navMeshKeySize = 49; - const std::size_t maxSize = 2 * (navMeshDataSize + navMeshKeySize); + const std::size_t maxSize = 2 * (navMeshDataSize + 2 * navMeshKeySize); NavMeshTilesCache cache(maxSize); const std::vector water {1, RecastMesh::Water {1, btTransform::getIdentity()}}; @@ -263,7 +263,7 @@ namespace const std::size_t navMeshDataSize = 1; const std::size_t navMeshKeySize1 = 49; const std::size_t navMeshKeySize2 = 117; - const std::size_t maxSize = 2 * navMeshDataSize + navMeshKeySize1 + navMeshKeySize2; + const std::size_t maxSize = 2 * navMeshDataSize + 2 * navMeshKeySize1 + 2 * navMeshKeySize2; NavMeshTilesCache cache(maxSize); const std::vector anotherWater {1, RecastMesh::Water {1, btTransform::getIdentity()}}; @@ -291,7 +291,7 @@ namespace { const std::size_t navMeshDataSize = 1; const std::size_t navMeshKeySize = 49; - const std::size_t maxSize = navMeshDataSize + navMeshKeySize; + const std::size_t maxSize = navMeshDataSize + 2 * navMeshKeySize; NavMeshTilesCache cache(maxSize); const std::vector water {1, RecastMesh::Water {1, btTransform::getIdentity()}}; @@ -314,7 +314,7 @@ namespace { const std::size_t navMeshDataSize = 1; const std::size_t navMeshKeySize = 49; - const std::size_t maxSize = navMeshDataSize + navMeshKeySize; + const std::size_t maxSize = navMeshDataSize + 2 * navMeshKeySize; NavMeshTilesCache cache(maxSize); const std::vector water {1, RecastMesh::Water {1, btTransform::getIdentity()}}; diff --git a/components/detournavigator/navmeshtilescache.cpp b/components/detournavigator/navmeshtilescache.cpp index cd60fa7e8..6bfbfb395 100644 --- a/components/detournavigator/navmeshtilescache.cpp +++ b/components/detournavigator/navmeshtilescache.cpp @@ -88,7 +88,7 @@ namespace DetourNavigator return Value(); const auto navMeshKey = makeNavMeshKey(recastMesh, offMeshConnections); - const auto itemSize = navMeshSize + navMeshKey.size(); + const auto itemSize = navMeshSize + 2 * navMeshKey.size(); if (itemSize > mFreeNavMeshDataSize + (mMaxNavMeshDataSize - mUsedNavMeshDataSize)) return Value(); @@ -131,8 +131,8 @@ namespace DetourNavigator if (value == tileValues->second.Map.end()) return; - mUsedNavMeshDataSize -= static_cast(item.mNavMeshData.mSize) + item.mNavMeshKey.size(); - mFreeNavMeshDataSize -= static_cast(item.mNavMeshData.mSize) + item.mNavMeshKey.size(); + mUsedNavMeshDataSize -= getSize(item); + mFreeNavMeshDataSize -= getSize(item); mFreeItems.pop_back(); tileValues->second.Map.erase(value); @@ -152,7 +152,7 @@ namespace DetourNavigator return; mBusyItems.splice(mBusyItems.end(), mFreeItems, iterator); - mFreeNavMeshDataSize -= static_cast(iterator->mNavMeshData.mSize) + iterator->mNavMeshKey.size(); + mFreeNavMeshDataSize -= getSize(*iterator); } void NavMeshTilesCache::releaseItem(ItemIterator iterator) @@ -163,6 +163,6 @@ namespace DetourNavigator const std::lock_guard lock(mMutex); mFreeItems.splice(mFreeItems.begin(), mBusyItems, iterator); - mFreeNavMeshDataSize += static_cast(iterator->mNavMeshData.mSize) + iterator->mNavMeshKey.size(); + mFreeNavMeshDataSize += getSize(*iterator); } } diff --git a/components/detournavigator/navmeshtilescache.hpp b/components/detournavigator/navmeshtilescache.hpp index 27ac499e8..45c0a6bb3 100644 --- a/components/detournavigator/navmeshtilescache.hpp +++ b/components/detournavigator/navmeshtilescache.hpp @@ -127,6 +127,11 @@ namespace DetourNavigator void acquireItemUnsafe(ItemIterator iterator); void releaseItem(ItemIterator iterator); + + static std::size_t getSize(const Item& item) + { + return static_cast(item.mNavMeshData.mSize) + 2 * item.mNavMeshKey.size(); + } }; } From 92e45507d8bba9321163e3bfd3099cd92ac9de20 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 3 Mar 2018 14:16:21 +0400 Subject: [PATCH 14/53] Weapon sheathing support, including quivers and scabbards (feature #4673) --- CHANGELOG.md | 2 +- apps/openmw/mwclass/creature.cpp | 2 +- apps/openmw/mwrender/actoranimation.cpp | 346 +++++++++++++++- apps/openmw/mwrender/actoranimation.hpp | 19 + apps/openmw/mwrender/animation.cpp | 2 - apps/openmw/mwrender/creatureanimation.cpp | 15 + apps/openmw/mwrender/creatureanimation.hpp | 2 + apps/openmw/mwrender/npcanimation.cpp | 29 +- apps/openmw/mwrender/npcanimation.hpp | 1 + apps/openmw/mwworld/containerstore.cpp | 6 +- apps/openmw/mwworld/containerstore.hpp | 5 +- apps/openmw/mwworld/inventorystore.cpp | 384 +++++++++++------- apps/openmw/mwworld/inventorystore.hpp | 9 +- components/resource/scenemanager.cpp | 3 +- .../reference/modding/settings/game.rst | 13 +- files/settings-default.cfg | 3 + 16 files changed, 673 insertions(+), 168 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d50c96ef4..22c426d51 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,9 +3,9 @@ Feature #2229: Improve pathfinding AI Feature #3442: Default values for fallbacks from ini file + Feature #4673: Weapon sheathing Task #4686: Upgrade media decoder to a more current FFmpeg API - 0.45.0 ------ diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 8a6f7d23d..c89ea8b7f 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -166,7 +166,7 @@ namespace MWClass getContainerStore(ptr).fill(ref->mBase->mInventory, ptr.getCellRef().getRefId()); if (hasInventory) - getInventoryStore(ptr).autoEquipShield(ptr); + getInventoryStore(ptr).autoEquip(ptr); } } diff --git a/apps/openmw/mwrender/actoranimation.cpp b/apps/openmw/mwrender/actoranimation.cpp index e6aff8d19..e4cf28394 100644 --- a/apps/openmw/mwrender/actoranimation.cpp +++ b/apps/openmw/mwrender/actoranimation.cpp @@ -1,5 +1,4 @@ #include "actoranimation.hpp" - #include #include @@ -9,11 +8,22 @@ #include #include +#include +#include + +#include #include #include +#include #include +#include + +#include + +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwworld/ptr.hpp" @@ -43,6 +53,8 @@ ActorAnimation::ActorAnimation(const MWWorld::Ptr& ptr, osg::ref_ptr // Make sure we cleaned object from effects, just in cast if we re-use node removeEffects(); + + mWeaponSheathing = Settings::Manager::getBool("weapon sheathing", "Game"); } ActorAnimation::~ActorAnimation() @@ -51,6 +63,302 @@ ActorAnimation::~ActorAnimation() { mInsert->removeChild(iter->second); } + + mScabbard.reset(); +} + +PartHolderPtr ActorAnimation::getWeaponPart(const std::string& model, const std::string& bonename, bool enchantedGlow, osg::Vec4f* glowColor) +{ + osg::Group* parent = getBoneByName(bonename); + if (!parent) + return nullptr; + + osg::ref_ptr instance = mResourceSystem->getSceneManager()->getInstance(model, parent); + + const NodeMap& nodeMap = getNodeMap(); + NodeMap::const_iterator found = nodeMap.find(Misc::StringUtils::lowerCase(bonename)); + if (found == nodeMap.end()) + return PartHolderPtr(); + + if (enchantedGlow) + addGlow(instance, *glowColor); + + return PartHolderPtr(new PartHolder(instance)); +} + +osg::Group* ActorAnimation::getBoneByName(std::string boneName) +{ + if (!mObjectRoot) + return nullptr; + + SceneUtil::FindByNameVisitor findVisitor (boneName); + mObjectRoot->accept(findVisitor); + + return findVisitor.mFoundNode; +} + +std::string ActorAnimation::getHolsteredWeaponBoneName(const MWWorld::ConstPtr& weapon) +{ + std::string boneName; + if(weapon.isEmpty()) + return boneName; + + const std::string &type = weapon.getClass().getTypeName(); + if(type == typeid(ESM::Weapon).name()) + { + const MWWorld::LiveCellRef *ref = weapon.get(); + ESM::Weapon::Type weaponType = (ESM::Weapon::Type)ref->mBase->mData.mType; + return getHolsteredWeaponBoneName(weaponType); + } + + return boneName; +} + +std::string ActorAnimation::getHolsteredWeaponBoneName(const unsigned int weaponType) +{ + std::string boneName; + + switch(weaponType) + { + case ESM::Weapon::ShortBladeOneHand: + boneName = "Bip01 ShortBladeOneHand"; + break; + case ESM::Weapon::LongBladeOneHand: + boneName = "Bip01 LongBladeOneHand"; + break; + case ESM::Weapon::BluntOneHand: + boneName = "Bip01 BluntOneHand"; + break; + case ESM::Weapon::AxeOneHand: + boneName = "Bip01 LongBladeOneHand"; + break; + case ESM::Weapon::LongBladeTwoHand: + boneName = "Bip01 LongBladeTwoClose"; + break; + case ESM::Weapon::BluntTwoClose: + boneName = "Bip01 BluntTwoClose"; + break; + case ESM::Weapon::AxeTwoHand: + boneName = "Bip01 AxeTwoClose"; + break; + case ESM::Weapon::BluntTwoWide: + boneName = "Bip01 BluntTwoWide"; + break; + case ESM::Weapon::SpearTwoWide: + boneName = "Bip01 SpearTwoWide"; + break; + case ESM::Weapon::MarksmanBow: + boneName = "Bip01 MarksmanBow"; + break; + case ESM::Weapon::MarksmanCrossbow: + boneName = "Bip01 MarksmanCrossbow"; + break; + case ESM::Weapon::MarksmanThrown: + boneName = "Bip01 MarksmanThrown"; + break; + default: + break; + } + + return boneName; +} + +void ActorAnimation::injectWeaponBones() +{ + if (!mResourceSystem->getVFS()->exists("meshes\\xbase_anim_sh.nif")) + { + mWeaponSheathing = false; + return; + } + + osg::ref_ptr sheathSkeleton = mResourceSystem->getSceneManager()->getInstance("meshes\\xbase_anim_sh.nif"); + + for (unsigned int type=0; type<=ESM::Weapon::MarksmanThrown; ++type) + { + const std::string holsteredBoneName = getHolsteredWeaponBoneName(type); + + SceneUtil::FindByNameVisitor findVisitor (holsteredBoneName); + sheathSkeleton->accept(findVisitor); + osg::ref_ptr sheathNode = findVisitor.mFoundNode; + + if (sheathNode && sheathNode.get()->getNumParents()) + { + osg::Group* sheathParent = getBoneByName(sheathNode.get()->getParent(0)->getName()); + + if (sheathParent) + { + sheathNode.get()->getParent(0)->removeChild(sheathNode); + sheathParent->addChild(sheathNode); + } + } + } +} + +// To make sure we do not run morph controllers for weapons, i.e. bows +class EmptyCallback : public osg::NodeCallback +{ + public: + + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) + { + } +}; + +void ActorAnimation::updateHolsteredWeapon(bool showHolsteredWeapons) +{ + if (!mWeaponSheathing) + return; + + if (!mPtr.getClass().hasInventoryStore(mPtr)) + return; + + mScabbard.reset(); + + const MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr); + MWWorld::ConstContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); + if (weapon == inv.end() || weapon->getTypeName() != typeid(ESM::Weapon).name()) + return; + + // Since throwing weapons stack themselves, do not show such weapon itself + if (weapon->get()->mBase->mData.mType == ESM::Weapon::MarksmanThrown) + showHolsteredWeapons = false; + + std::string mesh = weapon->getClass().getModel(*weapon); + std::string scabbardName = mesh; + + std::string boneName = getHolsteredWeaponBoneName(*weapon); + if (mesh.empty() || boneName.empty()) + return; + + // If the scabbard is not found, use a weapon mesh as fallback + scabbardName = scabbardName.replace(scabbardName.size()-4, 4, "_sh.nif"); + bool isEnchanted = !weapon->getClass().getEnchantment(*weapon).empty(); + if(!mResourceSystem->getVFS()->exists(scabbardName)) + { + if (showHolsteredWeapons) + { + osg::Vec4f glowColor = getEnchantmentColor(*weapon); + mScabbard = getWeaponPart(mesh, boneName, isEnchanted, &glowColor); + if (mScabbard) + mScabbard->getNode()->setUpdateCallback(new EmptyCallback); + } + + return; + } + + mScabbard = getWeaponPart(scabbardName, boneName); + + osg::Group* weaponNode = getBoneByName("Bip01 Weapon"); + if (!weaponNode) + return; + + // When we draw weapon, hide the Weapon node from sheath model. + // Otherwise add the enchanted glow to it. + if (!showHolsteredWeapons) + { + weaponNode->setNodeMask(0); + } + else + { + // If mesh author declared empty weapon node, use transformation from this node, but use the common weapon mesh. + // This approach allows to tweak weapon position without need to store the whole weapon mesh in the _sh file. + if (!weaponNode->getNumChildren()) + { + osg::ref_ptr fallbackNode = mResourceSystem->getSceneManager()->getInstance(mesh, weaponNode); + fallbackNode->setUpdateCallback(new EmptyCallback); + } + + if (isEnchanted) + { + osg::Vec4f glowColor = getEnchantmentColor(*weapon); + addGlow(weaponNode, glowColor); + } + } +} + +void ActorAnimation::updateQuiver() +{ + if (!mWeaponSheathing) + return; + + if (!mPtr.getClass().hasInventoryStore(mPtr)) + return; + + const MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr); + MWWorld::ConstContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); + if(weapon == inv.end() || weapon->getTypeName() != typeid(ESM::Weapon).name()) + return; + + std::string mesh = weapon->getClass().getModel(*weapon); + std::string boneName = getHolsteredWeaponBoneName(*weapon); + if (mesh.empty() || boneName.empty()) + return; + + osg::Group* ammoNode = getBoneByName("Bip01 Ammo"); + if (!ammoNode) + return; + + // Special case for throwing weapons - they do not use ammo, but they stack themselves + bool suitableAmmo = false; + MWWorld::ConstContainerStoreIterator ammo = weapon; + unsigned int ammoCount = 0; + if (weapon->get()->mBase->mData.mType == ESM::Weapon::MarksmanThrown) + { + ammoCount = ammo->getRefData().getCount(); + osg::Group* throwingWeaponNode = getBoneByName("Weapon Bone"); + if (throwingWeaponNode && throwingWeaponNode->getNumChildren()) + ammoCount--; + + suitableAmmo = true; + } + else + { + ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition); + if (ammo == inv.end()) + return; + + ammoCount = ammo->getRefData().getCount(); + bool arrowAttached = isArrowAttached(); + if (arrowAttached) + ammoCount--; + + if (weapon->get()->mBase->mData.mType == ESM::Weapon::MarksmanCrossbow) + suitableAmmo = ammo->get()->mBase->mData.mType == ESM::Weapon::Bolt; + else if (weapon->get()->mBase->mData.mType == ESM::Weapon::MarksmanBow) + suitableAmmo = ammo->get()->mBase->mData.mType == ESM::Weapon::Arrow; + } + + if (ammoNode && suitableAmmo) + { + // We should not show more ammo than equipped and more than quiver mesh has + ammoCount = std::min(ammoCount, ammoNode->getNumChildren()); + + // Remove existing ammo nodes + for (unsigned int i=0; igetNumChildren(); ++i) + { + osg::ref_ptr arrowNode = ammoNode->getChild(i)->asGroup(); + if (!arrowNode->getNumChildren()) + continue; + + osg::ref_ptr arrowChildNode = arrowNode->getChild(0); + arrowNode->removeChild(arrowChildNode); + } + + // Add new ones + osg::Vec4f glowColor = getEnchantmentColor(*ammo); + std::string model = ammo->getClass().getModel(*ammo); + for (unsigned int i=0; i arrowNode = ammoNode->getChild(i)->asGroup(); + osg::ref_ptr arrow = mResourceSystem->getSceneManager()->getInstance(model, arrowNode); + if (!ammo->getClass().getEnchantment(*ammo).empty()) + addGlow(arrow, glowColor); + } + } + + // recreate shaders for invisible actors, otherwise new nodes will be visible + if (mAlpha != 1.f) + mResourceSystem->getSceneManager()->recreateShaders(mObjectRoot); } void ActorAnimation::itemAdded(const MWWorld::ConstPtr& item, int /*count*/) @@ -63,6 +371,24 @@ void ActorAnimation::itemAdded(const MWWorld::ConstPtr& item, int /*count*/) addHiddenItemLight(item, light); } } + + if (!mPtr.getClass().hasInventoryStore(mPtr)) + return; + + // If the count of equipped ammo or throwing weapon was changed, we should update quiver + const MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr); + MWWorld::ConstContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); + if(weapon == inv.end() || weapon->getTypeName() != typeid(ESM::Weapon).name()) + return; + + MWWorld::ConstContainerStoreIterator ammo = inv.end(); + if (weapon->get()->mBase->mData.mType == ESM::Weapon::MarksmanThrown) + ammo = weapon; + else + ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition); + + if(ammo != inv.end() && item.getCellRef().getRefId() == ammo->getCellRef().getRefId()) + updateQuiver(); } void ActorAnimation::itemRemoved(const MWWorld::ConstPtr& item, int /*count*/) @@ -78,6 +404,24 @@ void ActorAnimation::itemRemoved(const MWWorld::ConstPtr& item, int /*count*/) } } } + + if (!mPtr.getClass().hasInventoryStore(mPtr)) + return; + + // If the count of equipped ammo or throwing weapon was changed, we should update quiver + const MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr); + MWWorld::ConstContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); + if(weapon == inv.end() || weapon->getTypeName() != typeid(ESM::Weapon).name()) + return; + + MWWorld::ConstContainerStoreIterator ammo = inv.end(); + if (weapon->get()->mBase->mData.mType == ESM::Weapon::MarksmanThrown) + ammo = weapon; + else + ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition); + + if(ammo != inv.end() && item.getCellRef().getRefId() == ammo->getCellRef().getRefId()) + updateQuiver(); } void ActorAnimation::addHiddenItemLight(const MWWorld::ConstPtr& item, const ESM::Light* esmLight) diff --git a/apps/openmw/mwrender/actoranimation.hpp b/apps/openmw/mwrender/actoranimation.hpp index c00f05718..7a8acd3a8 100644 --- a/apps/openmw/mwrender/actoranimation.hpp +++ b/apps/openmw/mwrender/actoranimation.hpp @@ -6,6 +6,7 @@ #include #include "../mwworld/containerstore.hpp" +#include "../mwworld/inventorystore.hpp" #include "animation.hpp" @@ -36,6 +37,24 @@ class ActorAnimation : public Animation, public MWWorld::ContainerStoreListener virtual void itemAdded(const MWWorld::ConstPtr& item, int count); virtual void itemRemoved(const MWWorld::ConstPtr& item, int count); + virtual bool isArrowAttached() const { return false; } + + protected: + bool mWeaponSheathing; + osg::Group* getBoneByName(std::string boneName); + virtual void updateHolsteredWeapon(bool showHolsteredWeapons); + virtual void injectWeaponBones(); + virtual void updateQuiver(); + virtual std::string getHolsteredWeaponBoneName(const MWWorld::ConstPtr& weapon); + virtual std::string getHolsteredWeaponBoneName(const unsigned int weaponType); + virtual PartHolderPtr getWeaponPart(const std::string& model, const std::string& bonename, bool enchantedGlow, osg::Vec4f* glowColor); + virtual PartHolderPtr getWeaponPart(const std::string& model, const std::string& bonename) + { + osg::Vec4f stubColor = osg::Vec4f(0,0,0,0); + return getWeaponPart(model, bonename, false, &stubColor); + }; + + PartHolderPtr mScabbard; private: void addHiddenItemLight(const MWWorld::ConstPtr& item, const ESM::Light* esmLight); diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index bf293bd55..7730b9104 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -1748,8 +1748,6 @@ namespace MWRender } else { - osg::StateSet* stateset (new osg::StateSet); - osg::BlendFunc* blendfunc (new osg::BlendFunc); stateset->setAttributeAndModes(blendfunc, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index a262d0021..aeee69ab0 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -49,7 +49,12 @@ CreatureWeaponAnimation::CreatureWeaponAnimation(const MWWorld::Ptr &ptr, const setObjectRoot(model, true, false, true); if((ref->mBase->mFlags&ESM::Creature::Bipedal)) + { + if (mWeaponSheathing) + injectWeaponBones(); + addAnimSource("meshes\\xbase_anim.nif", model); + } addAnimSource(model, model); mPtr.getClass().getInventoryStore(mPtr).setInvListener(this, mPtr); @@ -84,6 +89,9 @@ void CreatureWeaponAnimation::updateParts() mWeapon.reset(); mShield.reset(); + updateHolsteredWeapon(!mShowWeapons); + updateQuiver(); + if (mShowWeapons) updatePart(mWeapon, MWWorld::InventoryStore::Slot_CarriedRight); if (mShowCarriedLeft) @@ -157,14 +165,21 @@ void CreatureWeaponAnimation::updatePart(PartHolderPtr& scene, int slot) } } +bool CreatureWeaponAnimation::isArrowAttached() const +{ + return mAmmunition != nullptr; +} + void CreatureWeaponAnimation::attachArrow() { WeaponAnimation::attachArrow(mPtr); + updateQuiver(); } void CreatureWeaponAnimation::releaseArrow(float attackStrength) { WeaponAnimation::releaseArrow(mPtr, attackStrength); + updateQuiver(); } osg::Group *CreatureWeaponAnimation::getArrowBone() diff --git a/apps/openmw/mwrender/creatureanimation.hpp b/apps/openmw/mwrender/creatureanimation.hpp index ff959a551..d0fd5bdb4 100644 --- a/apps/openmw/mwrender/creatureanimation.hpp +++ b/apps/openmw/mwrender/creatureanimation.hpp @@ -54,6 +54,8 @@ namespace MWRender /// to indicate the facing orientation of the character. virtual void setPitchFactor(float factor) { mPitchFactor = factor; } + protected: + virtual bool isArrowAttached() const; private: PartHolderPtr mWeapon; diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index c7669f5e9..f21f24884 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -21,6 +21,8 @@ #include #include +#include + #include // TextKeyMapHolder #include "../mwworld/esmstore.hpp" @@ -308,6 +310,12 @@ void NpcAnimation::setViewMode(NpcAnimation::ViewMode viewMode) if(mViewMode == viewMode) return; + // Disable weapon sheathing in the 1st-person mode + if (viewMode == VM_FirstPerson) + mWeaponSheathing = false; + else + mWeaponSheathing = Settings::Manager::getBool("weapon sheathing", "Game"); + mViewMode = viewMode; rebuild(); @@ -389,6 +397,7 @@ void NpcAnimation::setRenderBin() void NpcAnimation::rebuild() { + mScabbard.reset(); updateNpcBase(); MWBase::Environment::get().getMechanicsManager()->forceStateUpdate(mPtr); @@ -460,6 +469,11 @@ void NpcAnimation::updateNpcBase() setObjectRoot(smodel, true, true, false); + if (mWeaponSheathing) + injectWeaponBones(); + + updateParts(); + if(!is1stPerson) { const std::string base = "meshes\\xbase_anim.nif"; @@ -488,8 +502,6 @@ void NpcAnimation::updateNpcBase() mObjectRoot->addCullCallback(new OverrideFieldOfViewCallback(mFirstPersonFieldOfView)); } - updateParts(); - mWeaponAnimationTime->updateStartTime(); } @@ -899,7 +911,8 @@ void NpcAnimation::showWeapons(bool showWeapon) attachArrow(); } } - if (mAlpha != 1.f) + // Note: we will need to recreate shaders later if we use weapon sheathing anyway, so there is no point to update them here + if (mAlpha != 1.f && !mWeaponSheathing) mResourceSystem->getSceneManager()->recreateShaders(mObjectRoot); } else @@ -909,6 +922,9 @@ void NpcAnimation::showWeapons(bool showWeapon) if (mPtr == MWMechanics::getPlayer()) MWBase::Environment::get().getWorld()->getPlayer().setAttackingOrSpell(false); } + + updateHolsteredWeapon(!mShowWeapons); + updateQuiver(); } void NpcAnimation::showCarriedLeft(bool show) @@ -936,11 +952,13 @@ void NpcAnimation::showCarriedLeft(bool show) void NpcAnimation::attachArrow() { WeaponAnimation::attachArrow(mPtr); + updateQuiver(); } void NpcAnimation::releaseArrow(float attackStrength) { WeaponAnimation::releaseArrow(mPtr, attackStrength); + updateQuiver(); } osg::Group* NpcAnimation::getArrowBone() @@ -1185,4 +1203,9 @@ void NpcAnimation::setAccurateAiming(bool enabled) mAccurateAiming = enabled; } +bool NpcAnimation::isArrowAttached() const +{ + return mAmmunition != nullptr; +} + } diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index 335ca5d5a..1fbdd863c 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -95,6 +95,7 @@ private: protected: virtual void addControllers(); + virtual bool isArrowAttached() const; public: /** diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 6a24ae4f0..ee3089901 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -330,7 +330,8 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr item.getRefData().getLocals().setVarByInt(script, "onpcadd", 1); } - if (mListener) + // we should not fire event for InventoryStore yet - it has some custom logic + if (mListener && !actorPtr.getClass().hasInventoryStore(actorPtr)) mListener->itemAdded(item, count); return it; @@ -439,7 +440,8 @@ int MWWorld::ContainerStore::remove(const Ptr& item, int count, const Ptr& actor flagAsModified(); - if (mListener) + // we should not fire event for InventoryStore yet - it has some custom logic + if (mListener && !actor.getClass().hasInventoryStore(actor)) mListener->itemRemoved(item, count - toRemove); // number of removed items diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index b67eb6552..4564d2fa3 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -68,6 +68,9 @@ namespace MWWorld static const std::string sGoldId; + protected: + ContainerStoreListener* mListener; + private: MWWorld::CellRefList potions; @@ -87,8 +90,6 @@ namespace MWWorld ///< Stores result of levelled item spawns. <(refId, spawningGroup), count> /// This is used to restock levelled items(s) if the old item was sold. - ContainerStoreListener* mListener; - mutable float mCachedWeight; mutable bool mWeightUpToDate; ContainerStoreIterator addImp (const Ptr& ptr, int count); diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 647ae261b..6fe1d0c73 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -99,7 +99,8 @@ void MWWorld::InventoryStore::readEquipmentState(const MWWorld::ContainerStoreIt } MWWorld::InventoryStore::InventoryStore() - : mListener(nullptr) + : ContainerStore() + , mInventoryListener(nullptr) , mUpdatesEnabled (true) , mFirstAutoEquip(true) , mSelectedEnchantItem(end()) @@ -111,7 +112,7 @@ MWWorld::InventoryStore::InventoryStore() MWWorld::InventoryStore::InventoryStore (const InventoryStore& store) : ContainerStore (store) , mMagicEffects(store.mMagicEffects) - , mListener(store.mListener) + , mInventoryListener(store.mInventoryListener) , mUpdatesEnabled(store.mUpdatesEnabled) , mFirstAutoEquip(store.mFirstAutoEquip) , mPermanentMagicEffectMagnitudes(store.mPermanentMagicEffectMagnitudes) @@ -124,6 +125,7 @@ MWWorld::InventoryStore::InventoryStore (const InventoryStore& store) MWWorld::InventoryStore& MWWorld::InventoryStore::operator= (const InventoryStore& store) { mListener = store.mListener; + mInventoryListener = store.mInventoryListener; mMagicEffects = store.mMagicEffects; mFirstAutoEquip = store.mFirstAutoEquip; mPermanentMagicEffectMagnitudes = store.mPermanentMagicEffectMagnitudes; @@ -147,6 +149,9 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::add(const Ptr& itemPtr, autoEquip(actorPtr); } + if (mListener) + mListener->itemAdded(itemPtr, count); + return retVal; } @@ -246,25 +251,195 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::findSlot (int slot) con return mSlots[slot]; } -void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor) +void MWWorld::InventoryStore::autoEquipWeapon (const MWWorld::Ptr& actor, TSlots& slots_) { + if (!actor.getClass().isNpc()) + { + // In original game creatures do not autoequip weapon, but we need it for weapon sheathing. + // The only case when the difference is noticable - when this creature sells weapon. + // So just disable weapon autoequipping for creatures which sells weapon. + int services = actor.getClass().getServices(actor); + bool sellsWeapon = services & (ESM::NPC::Weapon|ESM::NPC::MagicItems); + if (sellsWeapon) + return; + } + + static const ESM::Skill::SkillEnum weaponSkills[] = + { + ESM::Skill::LongBlade, + ESM::Skill::Axe, + ESM::Skill::Spear, + ESM::Skill::ShortBlade, + ESM::Skill::Marksman, + ESM::Skill::BluntWeapon + }; + const size_t weaponSkillsLength = sizeof(weaponSkills) / sizeof(weaponSkills[0]); + + bool weaponSkillVisited[weaponSkillsLength] = { false }; + + // give arrows/bolt with max damage by default + int arrowMax = 0; + int boltMax = 0; + ContainerStoreIterator arrow(end()); + ContainerStoreIterator bolt(end()); + + // rate ammo + for (ContainerStoreIterator iter(begin(ContainerStore::Type_Weapon)); iter!=end(); ++iter) + { + const ESM::Weapon* esmWeapon = iter->get()->mBase; + + if (esmWeapon->mData.mType == ESM::Weapon::Arrow) + { + if (esmWeapon->mData.mChop[1] >= arrowMax) + { + arrowMax = esmWeapon->mData.mChop[1]; + arrow = iter; + } + } + else if (esmWeapon->mData.mType == ESM::Weapon::Bolt) + { + if (esmWeapon->mData.mChop[1] >= boltMax) + { + boltMax = esmWeapon->mData.mChop[1]; + bolt = iter; + } + } + } + + // rate weapon + for (int i = 0; i < static_cast(weaponSkillsLength); ++i) + { + int max = 0; + int maxWeaponSkill = -1; + + for (int j = 0; j < static_cast(weaponSkillsLength); ++j) + { + int skillValue = actor.getClass().getSkill(actor, static_cast(weaponSkills[j])); + if (skillValue > max && !weaponSkillVisited[j]) + { + max = skillValue; + maxWeaponSkill = j; + } + } + + if (maxWeaponSkill == -1) + break; + + max = 0; + ContainerStoreIterator weapon(end()); + + for (ContainerStoreIterator iter(begin(ContainerStore::Type_Weapon)); iter!=end(); ++iter) + { + if (!canActorAutoEquip(actor, *iter)) + continue; + + const ESM::Weapon* esmWeapon = iter->get()->mBase; + + if (esmWeapon->mData.mType == ESM::Weapon::Arrow || esmWeapon->mData.mType == ESM::Weapon::Bolt) + continue; + + if (iter->getClass().getEquipmentSkill(*iter) == weaponSkills[maxWeaponSkill]) + { + if (esmWeapon->mData.mChop[1] >= max) + { + max = esmWeapon->mData.mChop[1]; + weapon = iter; + } + + if (esmWeapon->mData.mSlash[1] >= max) + { + max = esmWeapon->mData.mSlash[1]; + weapon = iter; + } + + if (esmWeapon->mData.mThrust[1] >= max) + { + max = esmWeapon->mData.mThrust[1]; + weapon = iter; + } + } + } + + bool isBow = false; + bool isCrossbow = false; + if (weapon != end()) + { + const MWWorld::LiveCellRef *ref = weapon->get(); + ESM::Weapon::Type type = (ESM::Weapon::Type)ref->mBase->mData.mType; + + if (type == ESM::Weapon::MarksmanBow) + isBow = true; + else if (type == ESM::Weapon::MarksmanCrossbow) + isCrossbow = true; + } + + if (weapon != end() && weapon->getClass().canBeEquipped(*weapon, actor).first) + { + // Do not equip ranged weapons, if there is no suitable ammo + bool hasAmmo = true; + if (isBow == true) + { + if (arrow == end()) + hasAmmo = false; + else + slots_[Slot_Ammunition] = arrow; + } + if (isCrossbow == true) + { + if (bolt == end()) + hasAmmo = false; + else + slots_[Slot_Ammunition] = bolt; + } + + if (hasAmmo) + { + std::pair, bool> itemsSlots = weapon->getClass().getEquipmentSlots (*weapon); + + if (!itemsSlots.first.empty()) + { + if (!itemsSlots.second) + { + if (weapon->getRefData().getCount() > 1) + { + unstack(*weapon, actor); + } + } + + int slot = itemsSlots.first.front(); + slots_[slot] = weapon; + + if (!isBow && !isCrossbow) + slots_[Slot_Ammunition] = end(); + } + + break; + } + } + + weaponSkillVisited[maxWeaponSkill] = true; + } +} + +void MWWorld::InventoryStore::autoEquipArmor (const MWWorld::Ptr& actor, TSlots& slots_) +{ + // Only NPCs can wear armor for now. + // For creatures we equip only shields. + if (!actor.getClass().isNpc()) + { + autoEquipShield(actor, slots_); + return; + } + const MWBase::World *world = MWBase::Environment::get().getWorld(); const MWWorld::Store &store = world->getStore().get(); static float fUnarmoredBase1 = store.find("fUnarmoredBase1")->mValue.getFloat(); static float fUnarmoredBase2 = store.find("fUnarmoredBase2")->mValue.getFloat(); - int unarmoredSkill = actor.getClass().getSkill(actor, ESM::Skill::Unarmored); + int unarmoredSkill = actor.getClass().getSkill(actor, ESM::Skill::Unarmored); float unarmoredRating = (fUnarmoredBase1 * unarmoredSkill) * (fUnarmoredBase2 * unarmoredSkill); - TSlots slots_; - initSlots (slots_); - - // Disable model update during auto-equip - mUpdatesEnabled = false; - - // Autoequip clothing, armor and weapons. - // Equipping lights is handled in Actors::updateEquippedLight based on environment light. for (ContainerStoreIterator iter (begin(ContainerStore::Type_Clothing | ContainerStore::Type_Armor)); iter!=end(); ++iter) { Ptr test = *iter; @@ -289,12 +464,12 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor) std::pair, bool> itemsSlots = iter->getClass().getEquipmentSlots (*iter); - // checking if current item poited by iter can be equipped + // checking if current item pointed by iter can be equipped for (std::vector::const_iterator iter2 (itemsSlots.first.begin()); iter2!=itemsSlots.first.end(); ++iter2) { // if true then it means slot is equipped already - // check if slot may require swapping if current item is more valueable + // check if slot may require swapping if current item is more valuable if (slots_.at (*iter2)!=end()) { Ptr old = *slots_.at (*iter2); @@ -362,98 +537,48 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor) break; } } +} - static const ESM::Skill::SkillEnum weaponSkills[] = +void MWWorld::InventoryStore::autoEquipShield(const MWWorld::Ptr& actor, TSlots& slots_) +{ + for (ContainerStoreIterator iter(begin(ContainerStore::Type_Armor)); iter != end(); ++iter) { - ESM::Skill::LongBlade, - ESM::Skill::Axe, - ESM::Skill::Spear, - ESM::Skill::ShortBlade, - ESM::Skill::Marksman, - ESM::Skill::BluntWeapon - }; - const size_t weaponSkillsLength = sizeof(weaponSkills) / sizeof(weaponSkills[0]); - - bool weaponSkillVisited[weaponSkillsLength] = { false }; - - for (int i = 0; i < static_cast(weaponSkillsLength); ++i) - { - int max = 0; - int maxWeaponSkill = -1; - - for (int j = 0; j < static_cast(weaponSkillsLength); ++j) + if (iter->get()->mBase->mData.mType != ESM::Armor::Shield) + continue; + if (iter->getClass().canBeEquipped(*iter, actor).first != 1) + continue; + if (iter->getClass().getItemHealth(*iter) <= 0) + continue; + std::pair, bool> shieldSlots = + iter->getClass().getEquipmentSlots(*iter); + if (shieldSlots.first.empty()) + continue; + int slot = shieldSlots.first[0]; + const ContainerStoreIterator& shield = mSlots[slot]; + if (shield != end() + && shield.getType() == Type_Armor && shield->get()->mBase->mData.mType == ESM::Armor::Shield) { - int skillValue = actor.getClass().getSkill(actor, static_cast(weaponSkills[j])); - - if (skillValue > max && !weaponSkillVisited[j]) - { - max = skillValue; - maxWeaponSkill = j; - } - } - - if (maxWeaponSkill == -1) - break; - - max = 0; - ContainerStoreIterator weapon(end()); - - for (ContainerStoreIterator iter(begin(ContainerStore::Type_Weapon)); iter!=end(); ++iter) - { - if (!canActorAutoEquip(actor, *iter)) + if (shield->getClass().getItemHealth(*shield) >= iter->getClass().getItemHealth(*iter)) continue; - - const ESM::Weapon* esmWeapon = iter->get()->mBase; - - if (esmWeapon->mData.mType == ESM::Weapon::Arrow || esmWeapon->mData.mType == ESM::Weapon::Bolt) - continue; - - if (iter->getClass().getEquipmentSkill(*iter) == weaponSkills[maxWeaponSkill]) - { - if (esmWeapon->mData.mChop[1] >= max) - { - max = esmWeapon->mData.mChop[1]; - weapon = iter; - } - - if (esmWeapon->mData.mSlash[1] >= max) - { - max = esmWeapon->mData.mSlash[1]; - weapon = iter; - } - - if (esmWeapon->mData.mThrust[1] >= max) - { - max = esmWeapon->mData.mThrust[1]; - weapon = iter; - } - } } - - if (weapon != end() && weapon->getClass().canBeEquipped(*weapon, actor).first) - { - std::pair, bool> itemsSlots = - weapon->getClass().getEquipmentSlots (*weapon); - - if (!itemsSlots.first.empty()) - { - if (!itemsSlots.second) - { - if (weapon->getRefData().getCount() > 1) - { - unstack(*weapon, actor); - } - } - - int slot = itemsSlots.first.front(); - slots_[slot] = weapon; - } - - break; - } - - weaponSkillVisited[maxWeaponSkill] = true; + slots_[slot] = iter; } +} + +void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor) +{ + TSlots slots_; + initSlots (slots_); + + // Disable model update during auto-equip + mUpdatesEnabled = false; + + // Autoequip clothing, armor and weapons. + // Equipping lights is handled in Actors::updateEquippedLight based on environment light. + // Note: creatures do not use the armor mitigation and can equip only shields + // Use a custom logic for them - select shield based on its health instead of armor rating (since it useless for creatures) + autoEquipWeapon(actor, slots_); + autoEquipArmor(actor, slots_); bool changed = false; @@ -476,50 +601,6 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor) } } -void MWWorld::InventoryStore::autoEquipShield(const MWWorld::Ptr& actor) -{ - bool updated = false; - - mUpdatesEnabled = false; - for (ContainerStoreIterator iter(begin(ContainerStore::Type_Armor)); iter != end(); ++iter) - { - if (iter->get()->mBase->mData.mType != ESM::Armor::Shield) - continue; - - if (iter->getClass().canBeEquipped(*iter, actor).first != 1) - continue; - - if (iter->getClass().getItemHealth(*iter) <= 0) - continue; - - std::pair, bool> shieldSlots = - iter->getClass().getEquipmentSlots(*iter); - - if (shieldSlots.first.empty()) - continue; - - int slot = shieldSlots.first[0]; - const ContainerStoreIterator& shield = mSlots[slot]; - - if (shield != end() - && shield.getType() == Type_Armor && shield->get()->mBase->mData.mType == ESM::Armor::Shield) - { - if (shield->getClass().getItemHealth(*shield) >= iter->getClass().getItemHealth(*iter)) - continue; - } - - equip(slot, iter, actor); - updated = true; - } - mUpdatesEnabled = true; - - if (updated) - { - fireEquipmentChangedEvent(actor); - updateMagicEffects(actor); - } -} - const MWMechanics::MagicEffects& MWWorld::InventoryStore::getMagicEffects() const { return mMagicEffects; @@ -532,7 +613,7 @@ void MWWorld::InventoryStore::updateMagicEffects(const Ptr& actor) return; // Delay update until the listener is set up - if (!mListener) + if (!mInventoryListener) return; mMagicEffects = MWMechanics::MagicEffects(); @@ -603,7 +684,7 @@ void MWWorld::InventoryStore::updateMagicEffects(const Ptr& actor) // During first auto equip, we don't play any sounds. // Basically we don't want sounds when the actor is first loaded, // the items should appear as if they'd always been equipped. - mListener->permanentEffectAdded(magicEffect, !mFirstAutoEquip); + mInventoryListener->permanentEffectAdded(magicEffect, !mFirstAutoEquip); } if (magnitude) @@ -737,6 +818,9 @@ int MWWorld::InventoryStore::remove(const Ptr& item, int count, const Ptr& actor mSelectedEnchantItem = end(); } + if (mListener) + mListener->itemRemoved(item, retCount); + return retCount; } @@ -822,12 +906,12 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::unequipItemQuantity(con MWWorld::InventoryStoreListener* MWWorld::InventoryStore::getInvListener() { - return mListener; + return mInventoryListener; } void MWWorld::InventoryStore::setInvListener(InventoryStoreListener *listener, const Ptr& actor) { - mListener = listener; + mInventoryListener = listener; updateMagicEffects(actor); } @@ -835,8 +919,8 @@ void MWWorld::InventoryStore::fireEquipmentChangedEvent(const Ptr& actor) { if (!mUpdatesEnabled) return; - if (mListener) - mListener->equipmentChanged(); + if (mInventoryListener) + mInventoryListener->equipmentChanged(); // if player, update inventory window /* diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp index 49991c164..e99a99bfa 100644 --- a/apps/openmw/mwworld/inventorystore.hpp +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -69,7 +69,7 @@ namespace MWWorld MWMechanics::MagicEffects mMagicEffects; - InventoryStoreListener* mListener; + InventoryStoreListener* mInventoryListener; // Enables updates of magic effects and actor model whenever items are equipped or unequipped. // This is disabled during autoequip to avoid excessive updates @@ -94,6 +94,10 @@ namespace MWWorld TSlots mSlots; + void autoEquipWeapon(const MWWorld::Ptr& actor, TSlots& slots_); + void autoEquipArmor(const MWWorld::Ptr& actor, TSlots& slots_); + void autoEquipShield(const MWWorld::Ptr& actor, TSlots& slots_); + // selected magic item (for using enchantments of type "Cast once" or "Cast when used") ContainerStoreIterator mSelectedEnchantItem; @@ -164,9 +168,6 @@ namespace MWWorld void autoEquip (const MWWorld::Ptr& actor); ///< Auto equip items according to stats and item value. - void autoEquipShield(const MWWorld::Ptr& actor); - ///< Auto-equip the shield with most health. - const MWMechanics::MagicEffects& getMagicEffects() const; ///< Return magic effects from worn items. diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index 8edb3d765..328a10cd1 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -396,7 +396,8 @@ namespace Resource { const char* reserved[] = {"Head", "Neck", "Chest", "Groin", "Right Hand", "Left Hand", "Right Wrist", "Left Wrist", "Shield Bone", "Right Forearm", "Left Forearm", "Right Upper Arm", "Left Upper Arm", "Right Foot", "Left Foot", "Right Ankle", "Left Ankle", "Right Knee", "Left Knee", "Right Upper Leg", "Left Upper Leg", "Right Clavicle", - "Left Clavicle", "Weapon Bone", "Tail", "Bip01", "Root Bone", "BoneOffset", "AttachLight", "ArrowBone", "Camera"}; + "Left Clavicle", "Weapon Bone", "Tail", "Bip01", "Root Bone", "BoneOffset", "AttachLight", "Arrow", "Camera"}; + reservedNames = std::vector(reserved, reserved + sizeof(reserved)/sizeof(reserved[0])); for (unsigned int i=0; i Date: Mon, 5 Nov 2018 16:37:47 +0100 Subject: [PATCH 15/53] bump /extern/recastnavigation to commit 3087e805b02d5eb8fff7851234fa2b3f71290eba; fixes clang warning about missing delete: https://github.com/recastnavigation/recastnavigation/issues/359 --- extern/recastnavigation/.id | 2 +- extern/recastnavigation/Recast/Include/RecastAlloc.h | 1 + extern/recastnavigation/RecastDemo/premake5.lua | 5 ++--- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/extern/recastnavigation/.id b/extern/recastnavigation/.id index c4937ac23..4ff8c7be6 100644 --- a/extern/recastnavigation/.id +++ b/extern/recastnavigation/.id @@ -1 +1 @@ -7bfd9a1d4caccf61e0485b2b05b29966348a8b39 +3087e805b02d5eb8fff7851234fa2b3f71290eba diff --git a/extern/recastnavigation/Recast/Include/RecastAlloc.h b/extern/recastnavigation/Recast/Include/RecastAlloc.h index 3e78496d1..e436af9a0 100644 --- a/extern/recastnavigation/Recast/Include/RecastAlloc.h +++ b/extern/recastnavigation/Recast/Include/RecastAlloc.h @@ -66,6 +66,7 @@ void rcFree(void* ptr); /// and STL. struct rcNewTag {}; inline void* operator new(size_t, const rcNewTag&, void* p) { return p; } +inline void operator delete(void*, const rcNewTag&, void*) {} /// Signed to avoid warnnings when comparing to int loop indexes, and common error with comparing to zero. /// MSVC2010 has a bug where ssize_t is unsigned (!!!). diff --git a/extern/recastnavigation/RecastDemo/premake5.lua b/extern/recastnavigation/RecastDemo/premake5.lua index 8545c6d7c..d2a6343f1 100644 --- a/extern/recastnavigation/RecastDemo/premake5.lua +++ b/extern/recastnavigation/RecastDemo/premake5.lua @@ -41,8 +41,7 @@ solution "recastnavigation" -- warnings "Extra" uses /W4 which is too aggressive for us, so use W3 instead. -- Disable: -- * C4351: new behavior for array initialization - -- * C4291: no matching operator delete found; we don't use exceptions, so doesn't matter - buildoptions { "/W3", "/wd4351", "/wd4291" } + buildoptions { "/W3", "/wd4351" } filter "platforms:Win32" architecture "x32" @@ -170,7 +169,7 @@ project "RecastDemo" } postbuildcommands { -- Copy the SDL2 dll to the Bin folder. - '{COPY} "%{wks.location}../../Contrib/SDL/lib/%{cfg.architecture:gsub("x86_64", "x64")}/SDL2.dll" "%{cfg.targetdir}"' + '{COPY} "%{path.getabsolute("Contrib/SDL/lib/" .. cfg.architecture:gsub("x86_64", "x64") .. "/SDL2.dll")}" "%{cfg.targetdir}"' } -- mac includes and libs From 5e3e01cac08fafa9a5616db3e2d3518e7724ada5 Mon Sep 17 00:00:00 2001 From: Stanislaw Halik Date: Sat, 3 Nov 2018 18:21:22 +0000 Subject: [PATCH 16/53] fix resolution on HiDPI displays under Windows The same Windows functionality as scaling user interface elements, confuses fullscreen games unless they set a particular of metadata to indicate that they perform the scaling by themselves. What happened was treating 2160p as 1440p despite the former being chosen. The same occured with other game title prior to introducing the metadata bits. Fortunately with CMake there's no need to invoke the mt.exe "manifest tool" manually. Note that the setting of "per-monitor DPI aware" still leaves openmw confused, hence the choice of global-DPI-aware. --- CHANGELOG.md | 1 + apps/openmw/CMakeLists.txt | 1 + files/windows/openmw.exe.manifest | 18 ++++++++++++++++++ files/windows/openmw.rc | 1 + 4 files changed, 21 insertions(+) create mode 100644 files/windows/openmw.exe.manifest diff --git a/CHANGELOG.md b/CHANGELOG.md index d50c96ef4..aecf2fa4d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ 0.46.0 ------ + Bug #3623: Fix HiDPI on Windows Feature #2229: Improve pathfinding AI Feature #3442: Default values for fallbacks from ini file Task #4686: Upgrade media decoder to a more current FFmpeg API diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 04584a342..0bb6e900d 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -4,6 +4,7 @@ set(GAME engine.cpp ${CMAKE_SOURCE_DIR}/files/windows/openmw.rc + ${CMAKE_SOURCE_DIR}/files/windows/openmw.exe.manifest ) if (ANDROID) diff --git a/files/windows/openmw.exe.manifest b/files/windows/openmw.exe.manifest new file mode 100644 index 000000000..c0b358a4a --- /dev/null +++ b/files/windows/openmw.exe.manifest @@ -0,0 +1,18 @@ + + + + Q2PRO + + + + + + + + + + True + + + + diff --git a/files/windows/openmw.rc b/files/windows/openmw.rc index b879a7dd5..f41172a3d 100644 --- a/files/windows/openmw.rc +++ b/files/windows/openmw.rc @@ -1 +1,2 @@ IDI_ICON1 ICON DISCARDABLE "openmw.ico" +CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "openmw.exe.manifest" From 4efe1bc89266d789e16aebf0c65552172da0364f Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Wed, 31 Oct 2018 01:04:44 +0300 Subject: [PATCH 17/53] Add prison marker record fallback definition (bug #4701) --- CHANGELOG.md | 1 + apps/opencs/model/world/data.cpp | 29 ++++++++++++++++++++++------- apps/openmw/mwworld/esmstore.cpp | 1 + apps/openmw/mwworld/store.cpp | 32 +++++++++++++++++++++++++++----- components/esm/loaddoor.hpp | 15 +++++++++++++++ 5 files changed, 66 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d50c96ef4..9eaef6640 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ 0.46.0 ------ + Bug #4701: PrisonMarker record is not hardcoded like other markers Feature #2229: Improve pathfinding AI Feature #3442: Default values for fallbacks from ini file Task #4686: Upgrade media decoder to a more current FFmpeg API diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index f3f897a29..63c7a52a8 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -977,15 +977,19 @@ int CSMWorld::Data::startLoading (const boost::filesystem::path& path, bool base void CSMWorld::Data::loadFallbackEntries() { // Load default marker definitions, if game files do not have them for some reason - std::pair markers[] = { - std::make_pair("divinemarker", "marker_divine.nif"), - std::make_pair("doormarker", "marker_arrow.nif"), - std::make_pair("northmarker", "marker_north.nif"), - std::make_pair("templemarker", "marker_temple.nif"), - std::make_pair("travelmarker", "marker_travel.nif") + std::pair staticMarkers[] = { + std::make_pair("DivineMarker", "marker_divine.nif"), + std::make_pair("DoorMarker", "marker_arrow.nif"), + std::make_pair("NorthMarker", "marker_north.nif"), + std::make_pair("TempleMarker", "marker_temple.nif"), + std::make_pair("TravelMarker", "marker_travel.nif") }; - for (const std::pair marker : markers) + std::pair doorMarkers[] = { + std::make_pair("PrisonMarker", "marker_prison.nif") + }; + + for (const std::pair marker : staticMarkers) { if (mReferenceables.searchId (marker.first)==-1) { @@ -995,6 +999,17 @@ void CSMWorld::Data::loadFallbackEntries() mReferenceables.appendRecord (record, CSMWorld::UniversalId::Type_Static); } } + + for (const std::pair marker : doorMarkers) + { + if (mReferenceables.searchId (marker.first)==-1) + { + CSMWorld::Record record; + record.mBase = ESM::Door(marker.first, std::string(), marker.second, std::string(), std::string(), std::string()); + record.mState = CSMWorld::RecordBase::State_BaseOnly; + mReferenceables.appendRecord (record, CSMWorld::UniversalId::Type_Door); + } + } } bool CSMWorld::Data::continueLoading (CSMDoc::Messages& messages) diff --git a/apps/openmw/mwworld/esmstore.cpp b/apps/openmw/mwworld/esmstore.cpp index 01d8e4b82..c85e3d30e 100644 --- a/apps/openmw/mwworld/esmstore.cpp +++ b/apps/openmw/mwworld/esmstore.cpp @@ -141,6 +141,7 @@ void ESMStore::setUp(bool validateRecords) mAttributes.setUp(); mDialogs.setUp(); mStatics.setUp(); + mDoors.setUp(); if (validateRecords) validate(); diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index b6bf2b7eb..ec9965a6c 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -1059,11 +1059,11 @@ namespace MWWorld { // Load default marker definitions, if game files do not have them for some reason std::pair markers[] = { - std::make_pair("divinemarker", "marker_divine.nif"), - std::make_pair("doormarker", "marker_arrow.nif"), - std::make_pair("northmarker", "marker_north.nif"), - std::make_pair("templemarker", "marker_temple.nif"), - std::make_pair("travelmarker", "marker_travel.nif") + std::make_pair("DivineMarker", "marker_divine.nif"), + std::make_pair("DoorMarker", "marker_arrow.nif"), + std::make_pair("NorthMarker", "marker_north.nif"), + std::make_pair("TempleMarker", "marker_temple.nif"), + std::make_pair("TravelMarker", "marker_travel.nif") }; for (const std::pair marker : markers) @@ -1080,6 +1080,28 @@ namespace MWWorld } } + template<> + void Store::setUp() + { + // Load default Door type marker definitions + std::pair markers[] = { + std::make_pair("PrisonMarker", "marker_prison.nif") + }; + + for (const std::pair marker : markers) + { + if (search(marker.first) == 0) + { + ESM::Door newMarker = ESM::Door(marker.first, std::string(), marker.second, std::string(), std::string(), std::string()); + std::pair ret = mStatic.insert(std::make_pair(marker.first, newMarker)); + if (ret.first != mStatic.end()) + { + mShared.push_back(&ret.first->second); + } + } + } + } + template <> inline RecordId Store::load(ESM::ESMReader &esm) { // The original letter case of a dialogue ID is saved, because it's printed diff --git a/components/esm/loaddoor.hpp b/components/esm/loaddoor.hpp index 3afe5d5e4..955abec89 100644 --- a/components/esm/loaddoor.hpp +++ b/components/esm/loaddoor.hpp @@ -22,6 +22,21 @@ struct Door void blank(); ///< Set record to default state (does not touch the ID). + + Door(const std::string id, const std::string name, const std::string &model, + const std::string script, const std::string opensound, const std::string closesound) + : mId(id) + , mName(name) + , mModel(model) + , mScript(script) + , mOpenSound(opensound) + , mCloseSound(closesound) + { + } + + Door() + { + } }; } #endif From 39f8637e95a102acff9705f3b5d5701a536d0023 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Tue, 6 Nov 2018 01:35:20 +0300 Subject: [PATCH 18/53] Simplify some actor loops and avoid some redundant calculations --- apps/openmw/mwmechanics/actors.cpp | 160 +++++++++--------- .../mwmechanics/mechanicsmanagerimp.cpp | 107 +++++------- 2 files changed, 123 insertions(+), 144 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 283b5d2b2..4d87637a5 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -309,6 +309,12 @@ namespace MWMechanics void Actors::updateHeadTracking(const MWWorld::Ptr& actor, const MWWorld::Ptr& targetActor, MWWorld::Ptr& headTrackTarget, float& sqrHeadTrackDistance) { + if (!actor.getRefData().getBaseNode()) + return; + + if (targetActor.getClass().getCreatureStats(targetActor).isDead()) + return; + static const float fMaxHeadTrackDistance = MWBase::Environment::get().getWorld()->getStore().get() .find("fMaxHeadTrackDistance")->mValue.getFloat(); static const float fInteriorHeadTrackMult = MWBase::Environment::get().getWorld()->getStore().get() @@ -318,22 +324,16 @@ namespace MWMechanics if (!currentCell->isExterior() && !(currentCell->mData.mFlags & ESM::Cell::QuasiEx)) maxDistance *= fInteriorHeadTrackMult; - const ESM::Position& actor1Pos = actor.getRefData().getPosition(); - const ESM::Position& actor2Pos = targetActor.getRefData().getPosition(); - float sqrDist = (actor1Pos.asVec3() - actor2Pos.asVec3()).length2(); + const osg::Vec3f actor1Pos(actor.getRefData().getPosition().asVec3()); + const osg::Vec3f actor2Pos(targetActor.getRefData().getPosition().asVec3()); + float sqrDist = (actor1Pos - actor2Pos).length2(); if (sqrDist > maxDistance*maxDistance) return; - if (targetActor.getClass().getCreatureStats(targetActor).isDead()) - return; - - if (!actor.getRefData().getBaseNode()) - return; - // stop tracking when target is behind the actor osg::Vec3f actorDirection = actor.getRefData().getBaseNode()->getAttitude() * osg::Vec3f(0,1,0); - osg::Vec3f targetDirection (actor2Pos.asVec3() - actor1Pos.asVec3()); + osg::Vec3f targetDirection(actor2Pos - actor1Pos); actorDirection.z() = 0; targetDirection.z() = 0; actorDirection.normalize(); @@ -350,25 +350,25 @@ namespace MWMechanics void Actors::engageCombat (const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2, std::map >& cachedAllies, bool againstPlayer) { + // No combat for totally static creatures + if (!actor1.getClass().isMobile(actor1)) + return; + CreatureStats& creatureStats1 = actor1.getClass().getCreatureStats(actor1); - if (creatureStats1.getAiSequence().isInCombat(actor2)) + if (creatureStats1.isDead() || creatureStats1.getAiSequence().isInCombat(actor2)) return; const CreatureStats& creatureStats2 = actor2.getClass().getCreatureStats(actor2); - if (creatureStats1.isDead() || creatureStats2.isDead()) + if (creatureStats2.isDead()) return; - const ESM::Position& actor1Pos = actor1.getRefData().getPosition(); - const ESM::Position& actor2Pos = actor2.getRefData().getPosition(); - float sqrDist = (actor1Pos.asVec3() - actor2Pos.asVec3()).length2(); + const osg::Vec3f actor1Pos(actor1.getRefData().getPosition().asVec3()); + const osg::Vec3f actor2Pos(actor2.getRefData().getPosition().asVec3()); + float sqrDist = (actor1Pos - actor2Pos).length2(); if (sqrDist > mActorsProcessingRange*mActorsProcessingRange) return; - // No combat for totally static creatures - if (!actor1.getClass().isMobile(actor1)) - return; - // If this is set to true, actor1 will start combat with actor2 if the awareness check at the end of the method returns true bool aggressive = false; @@ -379,22 +379,22 @@ namespace MWMechanics getActorsSidingWith(actor1, allies1, cachedAllies); // If an ally of actor1 has been attacked by actor2 or has attacked actor2, start combat between actor1 and actor2 - for (std::set::const_iterator it = allies1.begin(); it != allies1.end(); ++it) + for (const MWWorld::Ptr &ally : allies1) { - if (creatureStats1.getAiSequence().isInCombat(*it)) + if (creatureStats1.getAiSequence().isInCombat(ally)) continue; - if (creatureStats2.matchesActorId(it->getClass().getCreatureStats(*it).getHitAttemptActorId())) + if (creatureStats2.matchesActorId(ally.getClass().getCreatureStats(ally).getHitAttemptActorId())) { MWBase::Environment::get().getMechanicsManager()->startCombat(actor1, actor2); // Also set the same hit attempt actor. Otherwise, if fighting the player, they may stop combat // if the player gets out of reach, while the ally would continue combat with the player - creatureStats1.setHitAttemptActorId(it->getClass().getCreatureStats(*it).getHitAttemptActorId()); - return; + creatureStats1.setHitAttemptActorId(ally.getClass().getCreatureStats(ally).getHitAttemptActorId()); + return; } // If there's been no attack attempt yet but an ally of actor1 is in combat with actor2, become aggressive to actor2 - if (it->getClass().getCreatureStats(*it).getAiSequence().isInCombat(actor2)) + if (ally.getClass().getCreatureStats(ally).getAiSequence().isInCombat(actor2)) aggressive = true; } @@ -415,20 +415,24 @@ namespace MWMechanics getActorsSidingWith(actor2, allies2, cachedAllies); // Check that an ally of actor2 is also in combat with actor1 - for (std::set::const_iterator it = allies2.begin(); it != allies2.end(); ++it) + for (const MWWorld::Ptr &ally2 : allies2) { - if ((it)->getClass().getCreatureStats(*it).getAiSequence().isInCombat(actor1)) + if (ally2.getClass().getCreatureStats(ally2).getAiSequence().isInCombat(actor1)) { MWBase::Environment::get().getMechanicsManager()->startCombat(actor1, actor2); // Also have actor1's allies start combat - for (std::set::const_iterator it2 = allies1.begin(); it2 != allies1.end(); ++it2) - MWBase::Environment::get().getMechanicsManager()->startCombat(*it2, actor2); + for (const MWWorld::Ptr ally1 : allies1) + MWBase::Environment::get().getMechanicsManager()->startCombat(ally1, actor2); return; } } } } + // Stop here if target is unreachable + if (!canFight(actor1, actor2)) + return; + // If set in the settings file, player followers and escorters will become aggressive toward enemies in combat with them or the player static const bool followersAttackOnSight = Settings::Manager::getBool("followers attack on sight", "Game"); if (!aggressive && isPlayerFollowerOrEscorter && followersAttackOnSight) @@ -437,9 +441,9 @@ namespace MWMechanics aggressive = true; else { - for (std::set::const_iterator it = allies1.begin(); it != allies1.end(); ++it) + for (const MWWorld::Ptr &ally : allies1) { - if (actor2.getClass().getCreatureStats(actor2).getAiSequence().isInCombat(*it)) + if (actor2.getClass().getCreatureStats(actor2).getAiSequence().isInCombat(ally)) { aggressive = true; break; @@ -448,10 +452,6 @@ namespace MWMechanics } } - // Stop here if target is unreachable - if (!MWMechanics::canFight(actor1, actor2)) - return; - // Do aggression check if actor2 is the player or a player follower or escorter if (!aggressive) { @@ -465,7 +465,7 @@ namespace MWMechanics } // Make guards go aggressive with creatures that are in combat, unless the creature is a follower or escorter - if (actor1.getClass().isClass(actor1, "Guard") && !actor2.getClass().isNpc()) + if (!aggressive && actor1.getClass().isClass(actor1, "Guard") && !actor2.getClass().isNpc() && creatureStats2.getAiSequence().isInCombat()) { // Check if the creature is too far static const float fAlarmRadius = MWBase::Environment::get().getWorld()->getStore().get().find("fAlarmRadius")->mValue.getFloat(); @@ -473,18 +473,18 @@ namespace MWMechanics return; bool followerOrEscorter = false; - for (std::list::const_iterator it = creatureStats2.getAiSequence().begin(); it != creatureStats2.getAiSequence().end(); ++it) + for (const AiPackage* package : creatureStats2.getAiSequence()) { // The follow package must be first or have nothing but combat before it - if ((*it)->sideWithTarget()) + if (package->sideWithTarget()) { followerOrEscorter = true; break; } - else if ((*it)->getTypeId() != MWMechanics::AiPackage::TypeIdCombat) + else if (package->getTypeId() != MWMechanics::AiPackage::TypeIdCombat) break; } - if (!followerOrEscorter && creatureStats2.getAiSequence().isInCombat()) + if (!followerOrEscorter) aggressive = true; } @@ -927,14 +927,11 @@ namespace MWMechanics if (stats.getTimeToStartDrowning() == -1.f) stats.setTimeToStartDrowning(fHoldBreathTime); - if (stats.getTimeToStartDrowning() < fHoldBreathTime / 2) + if (!isPlayer && stats.getTimeToStartDrowning() < fHoldBreathTime / 2) { - if(!isPlayer) - { - MWMechanics::AiSequence& seq = ptr.getClass().getCreatureStats(ptr).getAiSequence(); - if(seq.getTypeId() != MWMechanics::AiPackage::TypeIdBreathe) //Only add it once - seq.stack(MWMechanics::AiBreathe(), ptr); - } + AiSequence& seq = ptr.getClass().getCreatureStats(ptr).getAiSequence(); + if (seq.getTypeId() != AiPackage::TypeIdBreathe) //Only add it once + seq.stack(AiBreathe(), ptr); } MWBase::World *world = MWBase::Environment::get().getWorld(); @@ -1200,17 +1197,16 @@ namespace MWMechanics } // Otherwise check if any actor in AI processing range sees the target actor - std::vector actors; + std::vector neighbors; osg::Vec3f position (actor.getRefData().getPosition().asVec3()); - getObjectsInRange(position, mActorsProcessingRange, actors); - for(std::vector::iterator it = actors.begin(); it != actors.end(); ++it) + getObjectsInRange(position, mActorsProcessingRange, neighbors); + for (const MWWorld::Ptr &neighbor : neighbors) { - if (*it == actor) + if (neighbor == actor) continue; - bool result = - MWBase::Environment::get().getWorld()->getLOS(*it, actor) && - MWBase::Environment::get().getMechanicsManager()->awarenessCheck(actor, *it); + bool result = MWBase::Environment::get().getWorld()->getLOS(neighbor, actor); + result &= MWBase::Environment::get().getMechanicsManager()->awarenessCheck(actor, neighbor); if (result) return true; @@ -1830,21 +1826,21 @@ namespace MWMechanics // An actor counts as siding with this actor if Follow or Escort is the current AI package, or there are only Combat and Wander packages before the Follow/Escort package // Actors that are targeted by this actor's Follow or Escort packages also side with them - for (auto package = stats.getAiSequence().begin(); package != stats.getAiSequence().end(); ++package) + for (const AiPackage* package : stats.getAiSequence()) { - if ((*package)->sideWithTarget() && !(*package)->getTarget().isEmpty()) + if (package->sideWithTarget() && !package->getTarget().isEmpty()) { if (sameActor) { - list.push_back((*package)->getTarget()); + list.push_back(package->getTarget()); } - else if ((*package)->getTarget() == actor) + else if (package->getTarget() == actor) { list.push_back(iteratedActor); } break; } - else if ((*package)->getTypeId() != AiPackage::TypeIdCombat && (*package)->getTypeId() != AiPackage::TypeIdWander) + else if (package->getTypeId() != AiPackage::TypeIdCombat && package->getTypeId() != AiPackage::TypeIdWander) break; } } @@ -1866,11 +1862,11 @@ namespace MWMechanics // An actor counts as following if AiFollow is the current AiPackage, // or there are only Combat and Wander packages before the AiFollow package - for (auto package = stats.getAiSequence().begin(); package != stats.getAiSequence().end(); ++package) + for (const AiPackage* package : stats.getAiSequence()) { - if ((*package)->followTargetThroughDoors() && (*package)->getTarget() == actor) + if (package->followTargetThroughDoors() && package->getTarget() == actor) list.push_back(iteratedActor); - else if ((*package)->getTypeId() != AiPackage::TypeIdCombat && (*package)->getTypeId() != AiPackage::TypeIdWander) + else if (package->getTypeId() != AiPackage::TypeIdCombat && package->getTypeId() != AiPackage::TypeIdWander) break; } } @@ -1879,16 +1875,16 @@ namespace MWMechanics void Actors::getActorsFollowing(const MWWorld::Ptr &actor, std::set& out) { std::list followers = getActorsFollowing(actor); - for(std::list::iterator it = followers.begin();it != followers.end();++it) - if (out.insert(*it).second) - getActorsFollowing(*it, out); + for(const MWWorld::Ptr &follower : followers) + if (out.insert(follower).second) + getActorsFollowing(follower, out); } void Actors::getActorsSidingWith(const MWWorld::Ptr &actor, std::set& out) { std::list followers = getActorsSidingWith(actor); - for(std::list::iterator it = followers.begin();it != followers.end();++it) - if (out.insert(*it).second) - getActorsSidingWith(*it, out); + for(const MWWorld::Ptr &follower : followers) + if (out.insert(follower).second) + getActorsSidingWith(follower, out); } void Actors::getActorsSidingWith(const MWWorld::Ptr &actor, std::set& out, std::map >& cachedAllies) { @@ -1899,17 +1895,17 @@ namespace MWMechanics else { std::list followers = getActorsSidingWith(actor); - for (std::list::iterator it = followers.begin(); it != followers.end(); ++it) - if (out.insert(*it).second) - getActorsSidingWith(*it, out, cachedAllies); + for (const MWWorld::Ptr &follower : followers) + if (out.insert(follower).second) + getActorsSidingWith(follower, out, cachedAllies); // Cache ptrs and their sets of allies cachedAllies.insert(std::make_pair(actor, out)); - for (std::set::const_iterator it = out.begin(); it != out.end(); ++it) + for (const MWWorld::Ptr &iter : out) { - search = cachedAllies.find(*it); + search = cachedAllies.find(iter); if (search == cachedAllies.end()) - cachedAllies.insert(std::make_pair(*it, out)); + cachedAllies.insert(std::make_pair(iter, out)); } } } @@ -1929,14 +1925,14 @@ namespace MWMechanics // An actor counts as following if AiFollow is the current AiPackage, // or there are only Combat and Wander packages before the AiFollow package - for (auto package = stats.getAiSequence().begin(); package != stats.getAiSequence().end(); ++package) + for (AiPackage* package : stats.getAiSequence()) { - if ((*package)->followTargetThroughDoors() && (*package)->getTarget() == actor) + if (package->followTargetThroughDoors() && package->getTarget() == actor) { - list.push_back(static_cast(*package)->getFollowIndex()); + list.push_back(static_cast(package)->getFollowIndex()); break; } - else if ((*package)->getTypeId() != AiPackage::TypeIdCombat && (*package)->getTypeId() != AiPackage::TypeIdWander) + else if (package->getTypeId() != AiPackage::TypeIdCombat && package->getTypeId() != AiPackage::TypeIdWander) break; } } @@ -1948,17 +1944,17 @@ namespace MWMechanics std::vector neighbors; osg::Vec3f position (actor.getRefData().getPosition().asVec3()); getObjectsInRange(position, mActorsProcessingRange, neighbors); - for(auto neighbor = neighbors.begin(); neighbor != neighbors.end(); ++neighbor) + for(const MWWorld::Ptr neighbor : neighbors) { - if (*neighbor == actor) + if (neighbor == actor) continue; - const CreatureStats &stats = neighbor->getClass().getCreatureStats(*neighbor); + const CreatureStats &stats = neighbor.getClass().getCreatureStats(neighbor); if (stats.isDead()) continue; if (stats.getAiSequence().isInCombat(actor)) - list.push_front(*neighbor); + list.push_front(neighbor); } return list; } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 3c540a9d4..224321383 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -129,10 +129,9 @@ namespace MWMechanics npcStats.getSkill (i).setBase (5 + bonus); } - for (std::vector::const_iterator iter (race->mPowers.mList.begin()); - iter!=race->mPowers.mList.end(); ++iter) + for (const std::string power : race->mPowers.mList) { - creatureStats.getSpells().add (*iter); + creatureStats.getSpells().add(power); } } @@ -145,10 +144,9 @@ namespace MWMechanics const ESM::BirthSign *sign = esmStore.get().find(signId); - for (std::vector::const_iterator iter (sign->mPowers.mList.begin()); - iter!=sign->mPowers.mList.end(); ++iter) + for (const std::string power : sign->mPowers.mList) { - creatureStats.getSpells().add (*iter); + creatureStats.getSpells().add(power); } } @@ -218,8 +216,8 @@ namespace MWMechanics std::vector selectedSpells = autoCalcPlayerSpells(skills, attributes, race); - for (std::vector::iterator it = selectedSpells.begin(); it != selectedSpells.end(); ++it) - creatureStats.getSpells().add(*it); + for (const std::string spell : selectedSpells) + creatureStats.getSpells().add(spell); // forced update and current value adjustments mActors.updateActor (ptr, 0); @@ -887,9 +885,8 @@ namespace MWMechanics // Build a list of known bound item ID's const MWWorld::Store &gameSettings = MWBase::Environment::get().getWorld()->getStore().get(); - for (MWWorld::Store::iterator currentIteration = gameSettings.begin(); currentIteration != gameSettings.end(); ++currentIteration) + for (const ESM::GameSetting ¤tSetting : gameSettings) { - const ESM::GameSetting ¤tSetting = *currentIteration; std::string currentGMSTID = currentSetting.mId; Misc::StringUtils::lowerCaseInPlace(currentGMSTID); @@ -1209,36 +1206,20 @@ namespace MWMechanics // Did anyone see it? bool crimeSeen = false; - for (std::vector::iterator it = neighbors.begin(); it != neighbors.end(); ++it) + for (const MWWorld::Ptr &neighbor : neighbors) { - if (*it == player) - continue; // skip player - if (it->getClass().getCreatureStats(*it).isDead()) + if (!canReportCrime(neighbor, victim, playerFollowers)) continue; - // Unconsious actor can not report about crime - if (it->getClass().getCreatureStats(*it).getKnockedDown()) - continue; - - if ((*it == victim && victimAware) - || (MWBase::Environment::get().getWorld()->getLOS(player, *it) && awarenessCheck(player, *it) ) + if ((neighbor == victim && victimAware) // Murder crime can be reported even if no one saw it (hearing is enough, I guess). // TODO: Add mod support for stealth executions! - || (type == OT_Murder && *it != victim)) + || (type == OT_Murder && neighbor != victim) + || (MWBase::Environment::get().getWorld()->getLOS(player, neighbor) && awarenessCheck(player, neighbor))) { - // Crime reporting only applies to NPCs - if (!it->getClass().isNpc()) - continue; - - if (it->getClass().getCreatureStats(*it).getAiSequence().isInCombat(victim)) - continue; - - if (playerFollowers.find(*it) != playerFollowers.end()) - continue; - // NPC will complain about theft even if he will do nothing about it if (type == OT_Theft || type == OT_Pickpocket) - MWBase::Environment::get().getDialogueManager()->say(*it, "thief"); + MWBase::Environment::get().getDialogueManager()->say(neighbor, "thief"); crimeSeen = true; } @@ -1361,66 +1342,66 @@ namespace MWMechanics getActorsSidingWith(player, playerFollowers); // Tell everyone (including the original reporter) in alarm range - for (std::vector::iterator it = neighbors.begin(); it != neighbors.end(); ++it) + for (const MWWorld::Ptr &actor : neighbors) { - if (!canReportCrime(*it, victim, playerFollowers)) + if (!canReportCrime(actor, victim, playerFollowers)) continue; // Will the witness report the crime? - if (it->getClass().getCreatureStats(*it).getAiSetting(CreatureStats::AI_Alarm).getBase() >= 100) + if (actor.getClass().getCreatureStats(actor).getAiSetting(CreatureStats::AI_Alarm).getBase() >= 100) { reported = true; if (type == OT_Trespassing) - MWBase::Environment::get().getDialogueManager()->say(*it, "intruder"); + MWBase::Environment::get().getDialogueManager()->say(actor, "intruder"); } } - for (std::vector::iterator it = neighbors.begin(); it != neighbors.end(); ++it) + for (const MWWorld::Ptr &actor : neighbors) { - if (!canReportCrime(*it, victim, playerFollowers)) + if (!canReportCrime(actor, victim, playerFollowers)) continue; - if (it->getClass().isClass(*it, "guard") && reported) + if (reported && actor.getClass().isClass(actor, "guard")) { // Mark as Alarmed for dialogue - it->getClass().getCreatureStats(*it).setAlarmed(true); + actor.getClass().getCreatureStats(actor).setAlarmed(true); // Set the crime ID, which we will use to calm down participants // once the bounty has been paid. - it->getClass().getNpcStats(*it).setCrimeId(id); + actor.getClass().getNpcStats(actor).setCrimeId(id); - if (!it->getClass().getCreatureStats(*it).getAiSequence().hasPackage(AiPackage::TypeIdPursue)) + if (!actor.getClass().getCreatureStats(actor).getAiSequence().hasPackage(AiPackage::TypeIdPursue)) { - it->getClass().getCreatureStats(*it).getAiSequence().stack(AiPursue(player), *it); + actor.getClass().getCreatureStats(actor).getAiSequence().stack(AiPursue(player), actor); } } else { - float dispTerm = (*it == victim) ? dispVictim : disp; + float dispTerm = (actor == victim) ? dispVictim : disp; - float alarmTerm = 0.01f * it->getClass().getCreatureStats(*it).getAiSetting(CreatureStats::AI_Alarm).getBase(); + float alarmTerm = 0.01f * actor.getClass().getCreatureStats(actor).getAiSetting(CreatureStats::AI_Alarm).getBase(); if (type == OT_Pickpocket && alarmTerm <= 0) alarmTerm = 1.0; - if (*it != victim) + if (actor != victim) dispTerm *= alarmTerm; - float fightTerm = static_cast((*it == victim) ? fightVictim : fight); + float fightTerm = static_cast((actor == victim) ? fightVictim : fight); fightTerm += getFightDispositionBias(dispTerm); - fightTerm += getFightDistanceBias(*it, player); + fightTerm += getFightDistanceBias(actor, player); fightTerm *= alarmTerm; - int observerFightRating = it->getClass().getCreatureStats(*it).getAiSetting(CreatureStats::AI_Fight).getBase(); + int observerFightRating = actor.getClass().getCreatureStats(actor).getAiSetting(CreatureStats::AI_Fight).getBase(); if (observerFightRating + fightTerm > 100) fightTerm = static_cast(100 - observerFightRating); fightTerm = std::max(0.f, fightTerm); if (observerFightRating + fightTerm >= 100) { - startCombat(*it, player); + startCombat(actor, player); - NpcStats& observerStats = it->getClass().getNpcStats(*it); + NpcStats& observerStats = actor.getClass().getNpcStats(actor); // Apply aggression value to the base Fight rating, so that the actor can continue fighting // after a Calm spell wears off observerStats.setAiSetting(CreatureStats::AI_Fight, observerFightRating + static_cast(fightTerm)); @@ -1527,7 +1508,7 @@ namespace MWMechanics bool MechanicsManager::canCommitCrimeAgainst(const MWWorld::Ptr &target, const MWWorld::Ptr &attacker) { - MWMechanics::AiSequence seq = target.getClass().getCreatureStats(target).getAiSequence(); + const MWMechanics::AiSequence& seq = target.getClass().getCreatureStats(target).getAiSequence(); return target.getClass().isNpc() && !attacker.isEmpty() && !seq.isInCombat(attacker) && !isAggressive(target, attacker) && !seq.isEngagedWithActor() && !target.getClass().getCreatureStats(target).getAiSequence().hasPackage(AiPackage::TypeIdPursue); @@ -1871,23 +1852,25 @@ namespace MWMechanics windowManager->setWerewolfOverlay(werewolf); // Witnesses of the player's transformation will make them a globally known werewolf - std::vector closeActors; + std::vector neighbors; const MWWorld::Store& gmst = MWBase::Environment::get().getWorld()->getStore().get(); - getActorsInRange(actor.getRefData().getPosition().asVec3(), gmst.find("fAlarmRadius")->mValue.getFloat(), closeActors); + getActorsInRange(actor.getRefData().getPosition().asVec3(), gmst.find("fAlarmRadius")->mValue.getFloat(), neighbors); bool detected = false, reported = false; - for (std::vector::const_iterator it = closeActors.begin(); it != closeActors.end(); ++it) + for (const MWWorld::Ptr& neighbor : neighbors) { - if (*it == actor) + if (neighbor == actor || !neighbor.getClass().isNpc()) continue; - if (!it->getClass().isNpc()) - continue; - - if (MWBase::Environment::get().getWorld()->getLOS(*it, actor) && awarenessCheck(actor, *it)) + if (MWBase::Environment::get().getWorld()->getLOS(neighbor, actor) && awarenessCheck(actor, neighbor)) + { detected = true; - if (it->getClass().getCreatureStats(*it).getAiSetting(MWMechanics::CreatureStats::AI_Alarm).getModified() > 0) - reported = true; + if (neighbor.getClass().getCreatureStats(neighbor).getAiSetting(MWMechanics::CreatureStats::AI_Alarm).getModified() > 0) + { + reported = true; + break; + } + } } if (detected) From fcb2cde1dd0834b855f455ddf609506215acd1c7 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Wed, 7 Nov 2018 17:47:05 +0300 Subject: [PATCH 19/53] Tweak the default chargen and level up messages --- files/openmw.cfg | 80 ++++++++++++++++++++++++------------------------ 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/files/openmw.cfg b/files/openmw.cfg index 9c51df389..f2972aef4 100644 --- a/files/openmw.cfg +++ b/files/openmw.cfg @@ -117,69 +117,69 @@ fallback=FontColor_color_negative,200,60,30 fallback=FontColor_color_count,223,201,159 # leveling -fallback=Level_Up_Level2,You've just realized that there is more to the life than you thought, the last few days have opened your eyes. An uncertain future awaits you. -fallback=Level_Up_Level3,Hard work and determination have served you well, keep this up and you'll be bound for great things. -fallback=Level_Up_Level4,It's as clear as day to you now, all that time you pissed away in your youth. This... this is life and all the experience you've gained giving you the tools you need to succeed in life. -fallback=Level_Up_Level5,Things seem to be working out for you, the world is opening up as you become more capable. -fallback=Level_Up_Level6,There is so much more to learn but with every push you gain more knowledge, only increasing your thirst for it. -fallback=Level_Up_Level7,You've dug deep within yourself, thinking you'll finally be who you thought you would be... but there is more and you keep digging. +fallback=Level_Up_Level2,The last few days have opened your eyes: you realize now that there is more to the life than you thought. An uncertain future awaits you. +fallback=Level_Up_Level3,You finally come to understand that great doings start from hard work and determination. +fallback=Level_Up_Level4,After reminiscing upon all that time you lost in your youth, it's as clear as day to you now: this - this is the life, and all the experience you gain gives you the tools you need to succeed in it. +fallback=Level_Up_Level5,Things seem to be working out for you. As you become more capable, the world is opening up. +fallback=Level_Up_Level6,With every push you did to gain knowledge, and yet your thirst for it increased, you realize there is so much more to learn still. +fallback=Level_Up_Level7,You dig deep within yourself, trying to finally become the one who you thought you would be... but there's much to keep digging through. fallback=Level_Up_Level8,Success doesn't come easily, but you make it appear that way to others. -fallback=Level_Up_Level9,Everything is becoming second nature to you but also just a bit more dangerous. -fallback=Level_Up_Level10,Nothing in life is easy or it wouldn't be worth the blood, sweat and tears. +fallback=Level_Up_Level9,Everything may slowly become a second nature to you, but it also can turn just a bit more dangerous. +fallback=Level_Up_Level10,Nothing in life is easy, or it wouldn't have been worth the blood, sweat and tears. fallback=Level_Up_Level11,Working smarter, not harder is something even a barbarian could benefit from. -fallback=Level_Up_Level12,Some would call you crazy for keeping at it, but you know better. It'll all pay off, it already has. -fallback=Level_Up_Level13,One nights sleep was all the difference between between something being difficult and then it being easy. -fallback=Level_Up_Level14,Waking up today was the best day ever for you, you await more days like these in your future. -fallback=Level_Up_Level15,Ouch! You lean back feeling your whole body for what seems the first time. You'll be more mindful in the future, you only have by one life. -fallback=Level_Up_Level16,Trusting your instincts have gotten you this far, but you'll need to be smarter to keep from being dead. +fallback=Level_Up_Level12,Perhaps some would call you delusional for keeping at it, but you know better. Your dilegence has already paid off, after all. +fallback=Level_Up_Level13,One night's sleep is all the difference between something being difficult and something being easy. +fallback=Level_Up_Level14,After waking up, you wonder: could today be the best day in your life? You never know. +fallback=Level_Up_Level15,Ouch! You lean back feeling your whole body for what seems the first time. You'll be more mindful in the future -- you can only live once. +fallback=Level_Up_Level16,Trusting your instincts has gotten you this far, but now it's clear to you that you need to be smarter to survive. fallback=Level_Up_Level17,You're forging your spirit in the crucible that is experience. Be ever vigilant. -fallback=Level_Up_Level18,The frustrations of the day before melt away as you wake up, today is a new day. -fallback=Level_Up_Level19,Today isn't yesterday, you feel it deep inside but you know that there is still more to this life. -fallback=Level_Up_Level20,Luck has never been a factor in your success, just look at the scars on your body and the trails you've endured. -fallback=Level_Up_Default,Through sheer force of will, you push onwards and upwards. The toil of your persistence has paid off. +fallback=Level_Up_Level18,The frustrations of the day before vanish as you wake up. Today is a new day. +fallback=Level_Up_Level19,Today isn't yesterday, you feel that deep inside. You know that there is still more to this life. +fallback=Level_Up_Level20,Luck has never been a factor in your success -- that's obvious from the scars on your body and from the number of trails you've endured. +fallback=Level_Up_Default,Through sheer force of will, you push onwards and upwards. The toil of your persistence is paying off. # character creation -fallback=Question_1_Question,Before you lies some kind of creation you never seen before, it's hind leg caught in a hunter's trap. You can tell that its leg is broken. +fallback=Question_1_Question,Before you lies a creature you have never seen before. Its hind leg is caught in a hunter's trap, and you can tell that the leg is broken. What will you do? fallback=Question_1_AnswerOne,Pull out you knife for a short and merciful kill. fallback=Question_1_AnswerTwo,Reach into your herbal pouch to find something to ease its suffering before putting it to sleep. -fallback=Question_1_AnswerThree,Leave it alone but take the time to observe and learn from this new creation +fallback=Question_1_AnswerThree,Leave the leg alone, but take the time to observe and learn from this odd creature. fallback=Question_1_Sound,Voice\CharGen\QA1.mp3 -fallback=Question_2_Question,Your mother has given everyone a list of choice, pick one. -fallback=Question_2_AnswerOne,The fence posts need replacing, help your father? -fallback=Question_2_AnswerTwo,Tonight's supper needs a few herbs, head out into the forest to collect them? -fallback=Question_2_AnswerThree,Fish aren't going find their way into the kitchen without your help. Go fishing? +fallback=Question_2_Question,Your mother suggests you to help with work around your family household. Do you decide to... +fallback=Question_2_AnswerOne,Help your father with fence post replacement? +fallback=Question_2_AnswerTwo,Collect a few herbs in the forest for tonight's supper? +fallback=Question_2_AnswerThree,Help the fish to find their way into the kitchen? fallback=Question_2_Sound,Voice\CharGen\QA2.mp3 -fallback=Question_3_Question,One of your brother teases you mercilessly in front of everyone about embarrassing details you would rather be forgotten. -fallback=Question_3_AnswerOne,Give him a black-eye and dare him to keep it up? -fallback=Question_3_AnswerTwo,Beat him to the punch and play it up, if you can control the negative then he can't embarrass you? -fallback=Question_3_AnswerThree,Wait until he sleeps, put his hand in a bowl of water and call everyone around the next day to see the results? +fallback=Question_3_Question,Your brother teases you mercilessly in front of everyone with embarrassing details you would rather have forgotten. +fallback=Question_3_AnswerOne,Give him a black-eye and dare him to keep it up. +fallback=Question_3_AnswerTwo,Beat him to the punch and play it up -- if you can control the negative, then he can't embarrass you. +fallback=Question_3_AnswerThree,Wait until he sleeps, put his hand in a bowl of water and call everyone around the next day to see the results. fallback=Question_3_Sound,Voice\CharGen\QA3.mp3 -fallback=Question_4_Question,Rumor has it that the King's security console has a new tool at their disposal for sniffing out the truth, people able to read minds. -fallback=Question_4_AnswerOne,You recoil at the thought of someone being able to read your thoughts. It's not that you think something that you'll ever act on it? -fallback=Question_4_AnswerTwo,For those who have done nothing, there is nothing to fear. This is just yet another tool for sniffing out thieves, murderers and plots against the crown? -fallback=Question_4_AnswerThree,While you loath the idea of someone reading your mind, you accept that it does have its uses if tempered by law and common sense. You wouldn't believe the mind of a madman for example? +fallback=Question_4_Question,Rumor has it that the King's security console has a new tool at their disposal for sniffing out the truth, which could be used for reading minds. +fallback=Question_4_AnswerOne,You recoil at the thought of someone being able to read your thoughts. It's not that you think something that you'll ever act on it. +fallback=Question_4_AnswerTwo,For those who have done nothing, there is nothing to fear. This is just yet another utility for catching thieves, murderers and plots against the crown. +fallback=Question_4_AnswerThree,While you loath the idea of someone reading your mind, you accept that it does have its uses if tempered by law and common sense. You wouldn't believe the mind of a madman. fallback=Question_4_Sound,Voice\CharGen\QA4.mp3 -fallback=Question_5_Question,You're off to market for supplies. You notice that one of the merchants had given you too much back in change. -fallback=Question_5_AnswerOne,How dreadful, what if it was you. You head back to the merchant? -fallback=Question_5_AnswerTwo,Happy days indeed, you put that extra money towards the needs of your family? -fallback=Question_5_AnswerThree,You win some and you lose some. In this case you won and they lost, the oversight is the merchant's problem, not yours? +fallback=Question_5_Question,You are returning home from the market after acquiring supplies, and notice that one of the merchants had given you too much back in change. +fallback=Question_5_AnswerOne,How dreadful, what if it was you? You head back to the merchant. +fallback=Question_5_AnswerTwo,Happy days indeed. Put that extra money towards the needs of your family? +fallback=Question_5_AnswerThree,You win some and you lose some. In this case you have won and they have lost, and the oversight is the merchant's problem, not yours, right? fallback=Question_5_Sound,Voice\CharGen\QA5.mp3 fallback=Question_6_Question,While at market, a noble yells out to the city watch about a theft. As you turn around a man bumps into you and drops a sack, startled he darts into the crowd. fallback=Question_6_AnswerOne,You pick up the sack and head over to the noble to return his property? fallback=Question_6_AnswerTwo,Just walk away, the last thing you need is someone thinking you had anything to do with it? -fallback=Question_6_AnswerThree,Finders, keepers... you whistle as you walk away? +fallback=Question_6_AnswerThree,Finders, keepers... you whistle as you walk away with the bag hidden? fallback=Question_6_Sound,Voice\CharGen\QA6.mp3 fallback=Question_7_Question,If it's one thing you hate, it's cleaning out the stalls. It has to be done before sunset before the critters return. On your way there an old friend greets you and offers to help if you promise to help them in the future. fallback=Question_7_AnswerOne,You thank him for the offer but would rather get it over with and not make promises you can't keep? fallback=Question_7_AnswerTwo,You reason that two pairs of hands are better than one regardless of the task? -fallback=Question_7_AnswerThree,Sounds great, anything is better than cleaning the stalls. +fallback=Question_7_AnswerThree,You say that it sounds great and anything is better than cleaning the stalls? fallback=Question_7_Sound,Voice\CharGen\QA7.mp3 -fallback=Question_8_Question,You just climbed down ladder from working on the roof. You're mother thanks you for the hard work but just at the moment you notice the hammer about to fall down on her head. +fallback=Question_8_Question,You just climbed down the ladder after working on the roof. Your mother thanks you for the hard work but just at the moment you notice that a hammer is about to fall down on her head. fallback=Question_8_AnswerOne,You lunge at your mother, pushing her out the way while the hammer falls on top of you? -fallback=Question_8_AnswerTwo,Use the ladder to intercept the hammer before it lands on her? +fallback=Question_8_AnswerTwo,Try to use the ladder to intercept the hammer before it lands on her? fallback=Question_8_AnswerThree,Warn her to take a step back? fallback=Question_8_Sound,Voice\CharGen\QA8.mp3 -fallback=Question_9_Question,It's the end of the week and you just got your wages for your hard work. You decide to take the quick way back home, darting into a alley only to be confronted by ruffians who demand that you empty your pockets. +fallback=Question_9_Question,It's the end of the week, and you have just got your wages for your hard work. You decide to take the quick way back home, darting into a alley only to be confronted by ruffians who demand that you empty your pockets. fallback=Question_9_AnswerOne,You tell them to go pack sand, planting your feet and raising your fists? fallback=Question_9_AnswerTwo,Acting dejected, you turn your wages over. You know that you can count on your friends to help you track these brigands down and recover what's yours? fallback=Question_9_AnswerThree,Tossing the sack into the air, you charge the leader who's attention is squarely focused on the coins in flight? From 4cb4f82431df63dc9ee96eae0f8c20aeb5297842 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Wed, 7 Nov 2018 19:41:28 +0300 Subject: [PATCH 20/53] Don't use bitwise AND --- apps/openmw/mwmechanics/actors.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 4d87637a5..87338c912 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -491,8 +491,8 @@ namespace MWMechanics // If any of the above conditions turned actor1 aggressive towards actor2, do an awareness check. If it passes, start combat with actor2. if (aggressive) { - bool LOS = MWBase::Environment::get().getWorld()->getLOS(actor1, actor2); - LOS &= MWBase::Environment::get().getMechanicsManager()->awarenessCheck(actor2, actor1); + bool LOS = MWBase::Environment::get().getWorld()->getLOS(actor1, actor2) + && MWBase::Environment::get().getMechanicsManager()->awarenessCheck(actor2, actor1); if (LOS) MWBase::Environment::get().getMechanicsManager()->startCombat(actor1, actor2); @@ -1205,8 +1205,8 @@ namespace MWMechanics if (neighbor == actor) continue; - bool result = MWBase::Environment::get().getWorld()->getLOS(neighbor, actor); - result &= MWBase::Environment::get().getMechanicsManager()->awarenessCheck(actor, neighbor); + bool result = MWBase::Environment::get().getWorld()->getLOS(neighbor, actor) + && MWBase::Environment::get().getMechanicsManager()->awarenessCheck(actor, neighbor); if (result) return true; From 8fd71fe4ad8e8a6aaa8d1e71abfb3db16e9fd0bd Mon Sep 17 00:00:00 2001 From: Stanislaw Halik Date: Sun, 4 Nov 2018 12:42:34 +0100 Subject: [PATCH 21/53] fix rain delay when exiting water When the particle system updates its internal state noting that it's been culled, it stops emitting any further particles. Prevent it from having that knowledge. v2: Fix off-by-one-frame error following review by @AnyOldName3 --- CHANGELOG.md | 1 + apps/openmw/mwrender/sky.cpp | 19 +++++++++++++++++-- apps/openmw/mwrender/sky.hpp | 1 + 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d50c96ef4..7880bb2a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ 0.46.0 ------ + Bug #4540: Rain delay when exiting water Feature #2229: Improve pathfinding AI Feature #3442: Default values for fallbacks from ini file Task #4686: Upgrade media decoder to a more current FFmpeg API diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index d6a0b9f01..0ea0905a6 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -401,11 +401,15 @@ public: { } - virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) + bool isUnderwater() { osg::Vec3f eyePoint = mCameraRelativeTransform->getLastEyePoint(); + return mEnabled && eyePoint.z() < mWaterLevel; + } - if (mEnabled && eyePoint.z() < mWaterLevel) + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) + { + if (isUnderwater()) return; traverse(node, nv); @@ -1575,6 +1579,8 @@ void SkyManager::update(float duration) mRainIntensityUniform->set((float) mWeatherAlpha); } + switchUnderwaterRain(); + if (mIsStorm) { osg::Quat quat; @@ -1626,6 +1632,15 @@ void SkyManager::updateRainParameters() } } +void SkyManager::switchUnderwaterRain() +{ + if (!mRainParticleSystem) + return; + + bool freeze = mUnderwaterSwitch->isUnderwater(); + mRainParticleSystem->setFrozen(freeze); +} + void SkyManager::setWeather(const WeatherResult& weather) { if (!mCreated) return; diff --git a/apps/openmw/mwrender/sky.hpp b/apps/openmw/mwrender/sky.hpp index a9345cdb4..e70f16521 100644 --- a/apps/openmw/mwrender/sky.hpp +++ b/apps/openmw/mwrender/sky.hpp @@ -180,6 +180,7 @@ namespace MWRender void createRain(); void destroyRain(); + void switchUnderwaterRain(); void updateRainParameters(); Resource::SceneManager* mSceneManager; From ad368346718eb163159468abce09e7b4ee932838 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 8 Nov 2018 12:44:48 +0400 Subject: [PATCH 22/53] Fix some compiler warnings --- apps/opencs/model/world/infoselectwrapper.cpp | 1 - apps/wizard/existinginstallationpage.cpp | 8 +++++--- components/contentselector/model/contentmodel.cpp | 2 +- components/myguiplatform/myguirendermanager.cpp | 6 +++--- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/apps/opencs/model/world/infoselectwrapper.cpp b/apps/opencs/model/world/infoselectwrapper.cpp index 3bc9bf4d2..3200d39fc 100644 --- a/apps/opencs/model/world/infoselectwrapper.cpp +++ b/apps/opencs/model/world/infoselectwrapper.cpp @@ -520,7 +520,6 @@ std::pair CSMWorld::ConstInfoSelectWrapper::getConditionFloatRange const float FloatMax = std::numeric_limits::infinity(); const float FloatMin = -std::numeric_limits::infinity(); const float Epsilon = std::numeric_limits::epsilon(); - const std::pair InvalidRange(FloatMax, FloatMin); float value = mConstSelect.mValue.getFloat(); diff --git a/apps/wizard/existinginstallationpage.cpp b/apps/wizard/existinginstallationpage.cpp index 3ec98a935..4c05f5500 100644 --- a/apps/wizard/existinginstallationpage.cpp +++ b/apps/wizard/existinginstallationpage.cpp @@ -31,11 +31,13 @@ void Wizard::ExistingInstallationPage::initializePage() // Hide the default item if there are installations to choose from installationsList->item(0)->setHidden(!paths.isEmpty()); - foreach (const QString &path, paths) { - QListWidgetItem *item = new QListWidgetItem(path); - + foreach (const QString &path, paths) + { if (installationsList->findItems(path, Qt::MatchExactly).isEmpty()) + { + QListWidgetItem *item = new QListWidgetItem(path); installationsList->addItem(item); + } } connect(installationsList, SIGNAL(currentTextChanged(QString)), diff --git a/components/contentselector/model/contentmodel.cpp b/components/contentselector/model/contentmodel.cpp index 41407ec88..39e91d5a3 100644 --- a/components/contentselector/model/contentmodel.cpp +++ b/components/contentselector/model/contentmodel.cpp @@ -168,7 +168,7 @@ QVariant ContentSelectorModel::ContentModel::data(const QModelIndex &index, int case Qt::DisplayRole: { if (column >=0 && column <=EsmFile::FileProperty_GameFile) - return file->fileProperty(static_cast(column)); + return file->fileProperty(static_cast(column)); return QVariant(); } diff --git a/components/myguiplatform/myguirendermanager.cpp b/components/myguiplatform/myguirendermanager.cpp index 845d0c484..4781ef3fc 100644 --- a/components/myguiplatform/myguirendermanager.cpp +++ b/components/myguiplatform/myguirendermanager.cpp @@ -134,9 +134,9 @@ public: { state->bindVertexBufferObject(bufferobject); - glVertexPointer(3, GL_FLOAT, sizeof(MyGUI::Vertex), (char*)nullptr); - glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(MyGUI::Vertex), (char*)nullptr + 12); - glTexCoordPointer(2, GL_FLOAT, sizeof(MyGUI::Vertex), (char*)nullptr + 16); + glVertexPointer(3, GL_FLOAT, sizeof(MyGUI::Vertex), static_cast(0)); + glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(MyGUI::Vertex), static_cast(0) + 12); + glTexCoordPointer(2, GL_FLOAT, sizeof(MyGUI::Vertex), static_cast(0) + 16); } else { From 6d48d9329e04bbc81d487f3561f0dc8bd3f86cea Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Thu, 8 Nov 2018 17:03:29 +0300 Subject: [PATCH 23/53] Avoid making string copies instead of references --- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 224321383..a907fb89f 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -129,7 +129,7 @@ namespace MWMechanics npcStats.getSkill (i).setBase (5 + bonus); } - for (const std::string power : race->mPowers.mList) + for (const std::string &power : race->mPowers.mList) { creatureStats.getSpells().add(power); } @@ -144,7 +144,7 @@ namespace MWMechanics const ESM::BirthSign *sign = esmStore.get().find(signId); - for (const std::string power : sign->mPowers.mList) + for (const std::string &power : sign->mPowers.mList) { creatureStats.getSpells().add(power); } @@ -216,7 +216,7 @@ namespace MWMechanics std::vector selectedSpells = autoCalcPlayerSpells(skills, attributes, race); - for (const std::string spell : selectedSpells) + for (const std::string &spell : selectedSpells) creatureStats.getSpells().add(spell); // forced update and current value adjustments From 9ae077c0332ded59c95a50348524df336b029eb7 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Thu, 8 Nov 2018 17:38:09 +0100 Subject: [PATCH 24/53] use c++11 std::align from (#2026) * use c++11 std::align from * for Ubuntu, use gcc5 instead of 4.8 * use travis to set gcc to 5 eval and sudo * use eval in .travis.yml * use gcc-8 * replace precise with trusty llvm toolchain, because we have been using trusty for awhile now * push things to matrix, so we can support multiple releases if we want * we should not be allowing for failures, we are ready to start trusting clang and its analyzer * scan-build was pushed to another package * use gcc-8 still but wrap in scan-build * travis.yml cleanup, have output of scripts go to stdout, make search for substring a regex use double [] fix missing , use bash to use regex black spaces matter * set human readable names for our various builds, split out our static analysis between openmw and openmw-cs * test if not set, then set otherwise ignore * use quotes * do not eval it, set it in travis env * no more && * what does clang7 have to say? * use sourceline for now * use clang-7 instead of clang-7.0 * yes, llvm-toolchain-trusty-7 not llvm-toolchain-trusty-7.0 * for static analysis, openmw is compiled and checked on its own while openmw-cs is build with all the rest. this might change in the future. and actually do it the other way around --- .travis.yml | 54 +++++++++++++------ CI/before_install.linux.sh | 7 +-- CI/before_script.linux.sh | 20 +++++-- .../detournavigator/recastallocutils.hpp | 11 ---- .../detournavigator/recasttempallocator.hpp | 2 +- 5 files changed, 59 insertions(+), 35 deletions(-) diff --git a/.travis.yml b/.travis.yml index 781f498e2..6d8970b67 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,4 @@ -os: - - linux - - osx -osx_image: xcode9.4 language: cpp -sudo: required -dist: trusty branches: only: - master @@ -20,16 +14,16 @@ addons: sources: - sourceline: 'ppa:openmw/openmw' - ubuntu-toolchain-r-test - - llvm-toolchain-precise-3.6 + - llvm-toolchain-trusty-7 packages: [ # Dev - cmake, clang-3.6, libunshield-dev, libtinyxml-dev, + cmake, clang-7, clang-tools-7, gcc-8, g++-8, # Boost libboost-filesystem-dev, libboost-program-options-dev, libboost-system-dev, # FFmpeg libavcodec-dev, libavformat-dev, libavutil-dev, libswscale-dev, - # Audio & Video - libsdl2-dev, libqt4-dev, libopenal-dev, + # Audio, Video and Misc. deps + libsdl2-dev, libqt4-dev, libopenal-dev, libunshield-dev, libtinyxml-dev, # The other ones from OpenMW ppa libbullet-dev, libswresample-dev, libopenscenegraph-3.4-dev, libmygui-dev ] @@ -44,18 +38,44 @@ addons: branch_pattern: coverity_scan matrix: include: - - os: linux + - name: OpenMW (all) on MacOS xcode9.4 + os: osx + osx_image: xcode9.4 + - name: OpenMW (all) on Ubuntu Trusty GCC-8 + os: linux + dist: trusty + sudo: required env: - ANALYZE="scan-build-3.6 --use-cc clang-3.6 --use-c++ clang++-3.6 " + - MATRIX_EVAL="CC=gcc-8 && CXX=g++-8" + - name: OpenMW (openmw) on Ubuntu Trusty Clang-7 with Static Analysis + os: linux + dist: trusty + sudo: required + env: + - MATRIX_EVAL="CC=clang-7 && CXX=clang++-7" + - ANALYZE="scan-build-7 --use-cc clang-7 --use-c++ clang++-7" + - BUILD_OPENMW_CS="OFF" compiler: clang - allow_failures: - - env: ANALYZE="scan-build-3.6 --use-cc clang-3.6 --use-c++ clang++-3.6 " + - name: OpenMW (openmw-cs) on Ubuntu Trusty Clang-7 with Static Analysis + os: linux + dist: trusty + sudo: required + env: + - MATRIX_EVAL="CC=clang-7 && CXX=clang++-7" + - ANALYZE="scan-build-7 --use-cc clang-7 --use-c++ clang++-7" + - BUILD_OPENMW="OFF" + compiler: clang +# allow_failures: +# - name: OpenMW (openmw) on Ubuntu Trusty Clang-7 with Static Analysis -before_install: ./CI/before_install.${TRAVIS_OS_NAME}.sh -before_script: ./CI/before_script.${TRAVIS_OS_NAME}.sh +before_install: + - if [ "${TRAVIS_OS_NAME}" = "linux" ]; then eval "${MATRIX_EVAL}"; fi + - ./CI/before_install.${TRAVIS_OS_NAME}.sh +before_script: + - ./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 ]; 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}" = "linux" ]; then ./openmw_test_suite; fi - if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ]; then cd .. && ./CI/check_tabs.sh; fi diff --git a/CI/before_install.linux.sh b/CI/before_install.linux.sh index 1ec88fb9e..fd4e4829c 100755 --- a/CI/before_install.linux.sh +++ b/CI/before_install.linux.sh @@ -1,3 +1,4 @@ -#!/bin/sh -e -sudo ln -s /usr/bin/clang-3.6 /usr/local/bin/clang -sudo ln -s /usr/bin/clang++-3.6 /usr/local/bin/clang++ +#!/bin/bash -ex + +sudo ln -sf /usr/bin/clang-7 /usr/local/bin/clang +sudo ln -sf /usr/bin/clang++-7 /usr/local/bin/clang++ diff --git a/CI/before_script.linux.sh b/CI/before_script.linux.sh index dd879989a..c8c4f8f85 100755 --- a/CI/before_script.linux.sh +++ b/CI/before_script.linux.sh @@ -1,4 +1,4 @@ -#!/bin/sh -e +#!/bin/bash -ex free -m @@ -8,8 +8,22 @@ GOOGLETEST_DIR="$(pwd)/googletest/build" mkdir build cd build export CODE_COVERAGE=1 -if [ "${CC}" = "clang" ]; then export CODE_COVERAGE=0; fi -${ANALYZE}cmake \ + +if [[ "${CC}" =~ "clang" ]]; then export CODE_COVERAGE=0; fi +if [[ -z "${BUILD_OPENMW}" ]]; then export BUILD_OPENMW=ON; fi +if [[ -z "${BUILD_OPENMW_CS}" ]]; then export BUILD_OPENMW_CS=ON; fi + +${ANALYZE} cmake \ + -DBUILD_OPENMW=${BUILD_OPENMW} \ + -DBUILD_OPENCS=${BUILD_OPENMW_CS} \ + -DBUILD_LAUNCHER=${BUILD_OPENMW_CS} \ + -DBUILD_BSATOOL=${BUILD_OPENMW_CS} \ + -DBUILD_ESMTOOL=${BUILD_OPENMW_CS} \ + -DBUILD_MWINIIMPORTER=${BUILD_OPENMW_CS} \ + -DBUILD_ESSIMPORTER=${BUILD_OPENMW_CS} \ + -DBUILD_WIZARD=${BUILD_OPENMW_CS} \ + -DBUILD_NIFTEST=${BUILD_OPENMW_CS} \ + -DBUILD_MYGUI_PLUGIN=${BUILD_OPENMW_CS} \ -DBUILD_WITH_CODE_COVERAGE=${CODE_COVERAGE} \ -DBUILD_UNITTESTS=1 \ -DCMAKE_INSTALL_PREFIX=/usr \ diff --git a/components/detournavigator/recastallocutils.hpp b/components/detournavigator/recastallocutils.hpp index 7b083d139..69a5cfa8b 100644 --- a/components/detournavigator/recastallocutils.hpp +++ b/components/detournavigator/recastallocutils.hpp @@ -86,17 +86,6 @@ namespace DetourNavigator return static_cast(ptr) + 1; } - // TODO: use std::align - inline void* align(std::size_t align, std::size_t size, void*& ptr, std::size_t& space) noexcept - { - const auto intptr = reinterpret_cast(ptr); - const auto aligned = (intptr - 1u + align) & - align; - const auto diff = aligned - intptr; - if ((size + diff) > space) - return nullptr; - space -= diff; - return ptr = reinterpret_cast(aligned); - } } #endif diff --git a/components/detournavigator/recasttempallocator.hpp b/components/detournavigator/recasttempallocator.hpp index e369b4224..fbf9fd62c 100644 --- a/components/detournavigator/recasttempallocator.hpp +++ b/components/detournavigator/recasttempallocator.hpp @@ -21,7 +21,7 @@ namespace DetourNavigator std::size_t space = mStack.size() - getUsedSize(); void* top = mTop; const auto itemSize = 2 * sizeof(std::size_t) + size; - if (rcUnlikely(!align(sizeof(std::size_t), itemSize, top, space))) + if (rcUnlikely(!std::align(sizeof(std::size_t), itemSize, top, space))) return nullptr; setTempPtrBufferType(top, BufferType_temp); setTempPtrPrev(top, mPrev); From e5b8491b58c4ae0de8524a7d46b7c6bd92353571 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 8 Nov 2018 21:09:42 +0400 Subject: [PATCH 25/53] Make Clang analyzer to take assertions in account, even for release builds --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6d8970b67..b4301680f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -53,7 +53,7 @@ matrix: sudo: required env: - MATRIX_EVAL="CC=clang-7 && CXX=clang++-7" - - ANALYZE="scan-build-7 --use-cc clang-7 --use-c++ clang++-7" + - ANALYZE="scan-build-7 --force-analyze-debug-code --use-cc clang-7 --use-c++ clang++-7" - BUILD_OPENMW_CS="OFF" compiler: clang - name: OpenMW (openmw-cs) on Ubuntu Trusty Clang-7 with Static Analysis @@ -62,7 +62,7 @@ matrix: sudo: required env: - MATRIX_EVAL="CC=clang-7 && CXX=clang++-7" - - ANALYZE="scan-build-7 --use-cc clang-7 --use-c++ clang++-7" + - ANALYZE="scan-build-7 --force-analyze-debug-code --use-cc clang-7 --use-c++ clang++-7" - BUILD_OPENMW="OFF" compiler: clang # allow_failures: From 0065bccf24cbc226981264960770fa82843d6807 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 8 Nov 2018 11:47:41 +0400 Subject: [PATCH 26/53] Handle case in the editor when actor's bodypart was not found --- apps/opencs/model/world/actoradapter.cpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/apps/opencs/model/world/actoradapter.cpp b/apps/opencs/model/world/actoradapter.cpp index ea40a1f8d..b89240a80 100644 --- a/apps/opencs/model/world/actoradapter.cpp +++ b/apps/opencs/model/world/actoradapter.cpp @@ -113,17 +113,22 @@ namespace CSMWorld const std::string ActorAdapter::ActorData::getPart(ESM::PartReferenceType index) const { auto it = mParts.find(index); - if (it == mParts.end() && mRaceData && mRaceData->handlesPart(index)) + if (it == mParts.end()) { - if (mFemale) + if (mRaceData && mRaceData->handlesPart(index)) { - // Note: we should use male parts for females as fallback - const std::string femalePart = mRaceData->getFemalePart(index); - if (!femalePart.empty()) - return femalePart; + if (mFemale) + { + // Note: we should use male parts for females as fallback + const std::string femalePart = mRaceData->getFemalePart(index); + if (!femalePart.empty()) + return femalePart; + } + + return mRaceData->getMalePart(index); } - return mRaceData->getMalePart(index); + return ""; } const std::string& partName = it->second.first; From d2613e35a27ee099ce0a242ce0072ea638473e6f Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 8 Nov 2018 21:10:23 +0400 Subject: [PATCH 27/53] Fix some Clang 7 warnings --- apps/openmw/mwgui/bookpage.cpp | 1 + apps/openmw/mwgui/keyboardnavigation.cpp | 2 ++ apps/openmw/mwsound/ffmpeg_decoder.cpp | 2 +- apps/openmw_test_suite/nifloader/testbulletnifloader.cpp | 2 -- components/myguiplatform/myguirendermanager.cpp | 6 +++--- components/nif/niffile.hpp | 8 ++------ 6 files changed, 9 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwgui/bookpage.cpp b/apps/openmw/mwgui/bookpage.cpp index 80e92f15a..64eecdb79 100644 --- a/apps/openmw/mwgui/bookpage.cpp +++ b/apps/openmw/mwgui/bookpage.cpp @@ -743,6 +743,7 @@ namespace mVertices (vertices), mRenderXform (renderXform) { + assert(font != nullptr); mVertexColourType = MyGUI::RenderManager::getInstance().getVertexFormat(); } diff --git a/apps/openmw/mwgui/keyboardnavigation.cpp b/apps/openmw/mwgui/keyboardnavigation.cpp index 52ff0b2d9..7355dc1f4 100644 --- a/apps/openmw/mwgui/keyboardnavigation.cpp +++ b/apps/openmw/mwgui/keyboardnavigation.cpp @@ -25,6 +25,8 @@ bool shouldAcceptKeyFocus(MyGUI::Widget* w) /// Recursively get all child widgets that accept keyboard input void getKeyFocusWidgets(MyGUI::Widget* parent, std::vector& results) { + assert(parent != nullptr); + if (!parent->getVisible() || !parent->getEnabled()) return; diff --git a/apps/openmw/mwsound/ffmpeg_decoder.cpp b/apps/openmw/mwsound/ffmpeg_decoder.cpp index 447c386c9..f7ccebc96 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.cpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.cpp @@ -90,7 +90,7 @@ bool FFmpeg_Decoder::getNextPacket() bool FFmpeg_Decoder::getAVAudioData() { - bool got_frame; + bool got_frame = false; if(mCodecCtx->codec_type != AVMEDIA_TYPE_AUDIO) return false; diff --git a/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp b/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp index 29f60b1f5..c94907e68 100644 --- a/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp +++ b/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp @@ -267,8 +267,6 @@ namespace struct NifFileMock : Nif::File { - MOCK_CONST_METHOD1(fail, void (const std::string&)); - MOCK_CONST_METHOD1(warn, void (const std::string&)); MOCK_CONST_METHOD1(getRecord, Nif::Record* (std::size_t)); MOCK_CONST_METHOD0(numRecords, std::size_t ()); MOCK_CONST_METHOD1(getRoot, Nif::Record* (std::size_t)); diff --git a/components/myguiplatform/myguirendermanager.cpp b/components/myguiplatform/myguirendermanager.cpp index 4781ef3fc..a3f7f76a2 100644 --- a/components/myguiplatform/myguirendermanager.cpp +++ b/components/myguiplatform/myguirendermanager.cpp @@ -134,9 +134,9 @@ public: { state->bindVertexBufferObject(bufferobject); - glVertexPointer(3, GL_FLOAT, sizeof(MyGUI::Vertex), static_cast(0)); - glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(MyGUI::Vertex), static_cast(0) + 12); - glTexCoordPointer(2, GL_FLOAT, sizeof(MyGUI::Vertex), static_cast(0) + 16); + glVertexPointer(3, GL_FLOAT, sizeof(MyGUI::Vertex), reinterpret_cast(0)); + glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(MyGUI::Vertex), reinterpret_cast(12)); + glTexCoordPointer(2, GL_FLOAT, sizeof(MyGUI::Vertex), reinterpret_cast(16)); } else { diff --git a/components/nif/niffile.hpp b/components/nif/niffile.hpp index 0893db72f..15001f511 100644 --- a/components/nif/niffile.hpp +++ b/components/nif/niffile.hpp @@ -18,10 +18,6 @@ struct File { virtual ~File() = default; - virtual void fail(const std::string &msg) const = 0; - - virtual void warn(const std::string &msg) const = 0; - virtual Record *getRecord(size_t index) const = 0; virtual size_t numRecords() const = 0; @@ -71,14 +67,14 @@ class NIFFile final : public File public: /// Used if file parsing fails - void fail(const std::string &msg) const override + void fail(const std::string &msg) const { std::string err = " NIFFile Error: " + msg; err += "\nFile: " + filename; throw std::runtime_error(err); } /// Used when something goes wrong, but not catastrophically so - void warn(const std::string &msg) const override + void warn(const std::string &msg) const { Log(Debug::Warning) << " NIFFile Warning: " << msg << "\nFile: " << filename; } From caefe8cda028723ab33d24d0024f3f4eca300c99 Mon Sep 17 00:00:00 2001 From: Stanislaw Halik Date: Sun, 11 Nov 2018 21:46:15 +0100 Subject: [PATCH 28/53] fix win32 app manifest copy-paste error --- files/windows/openmw.exe.manifest | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/windows/openmw.exe.manifest b/files/windows/openmw.exe.manifest index c0b358a4a..4c5d4d2e4 100644 --- a/files/windows/openmw.exe.manifest +++ b/files/windows/openmw.exe.manifest @@ -1,7 +1,7 @@ - Q2PRO + openmw From f8ba0acd1f34c647ba23bcda260308ca719bc487 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Mon, 12 Nov 2018 14:39:50 +0100 Subject: [PATCH 29/53] give this a try --- .travis.yml | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/.travis.yml b/.travis.yml index 276029c5e..a364e37e6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,15 +27,6 @@ addons: # The other ones from OpenMW ppa libbullet-dev, libswresample-dev, libopenscenegraph-3.4-dev, libmygui-dev ] - - coverity_scan: - project: - name: "OpenMW/openmw" - description: "" - notification_email: 720642+scrawl@users.noreply.github.com - build_command_prepend: "cmake . -DBUILD_UNITTESTS=FALSE -DBUILD_OPENCS=FALSE -DBUILD_BSATOOL=FALSE -DBUILD_ESMTOOL=FALSE -DBUILD_LAUNCHER=FALSE -DBUILD_MWINIIMPORTER=FALSE -DBUILD_WIZARD=FALSE" - build_command: "make -j3" - branch_pattern: coverity_scan matrix: include: - name: OpenMW (all) on MacOS xcode9.4 @@ -65,20 +56,34 @@ matrix: - ANALYZE="scan-build-7 --use-cc clang-7 --use-c++ clang++-7" - BUILD_OPENMW="OFF" compiler: clang + - name: OpenMW Coverity Scan + os: linux + dist: trusty + sudo: required + coverity_scan: + project: + name: "OpenMW/openmw" + description: "" + branch_pattern: coverity_scan + notification_email: 720642+scrawl@users.noreply.github.com + build_command_prepend: + - "cov-configure --comptype gcc --compiler gcc-8 --template" + - "cmake . -DBUILD_OPENCS=FALSE -DBUILD_WIZARD=FALSE -DBUILD_UNITTESTS=FALSE -DBUILD_BSATOOL=FALSE -DBUILD_ESMTOOL=FALSE -DBUILD_MWINIIMPORTER=FALSE -DBUILD_ESSIMPORTER=FALSE -DBUILD_LAUNCHER=FALSE -DBUILD_MYGUI_PLUGIN=FALSE" + build_command: "make VERBOSE=1 -j3" # allow_failures: # - name: OpenMW (openmw) on Ubuntu Trusty Clang-7 with Static Analysis before_install: - if [ "${TRAVIS_OS_NAME}" = "linux" ]; then eval "${MATRIX_EVAL}"; fi - - ./CI/before_install.${TRAVIS_OS_NAME}.sh + - if [ "${COVERITY_SCAN_BRANCH}" != 1 ]; then ./CI/before_install.${TRAVIS_OS_NAME}.sh; fi before_script: - - ./CI/before_script.${TRAVIS_OS_NAME}.sh + - if [ "${COVERITY_SCAN_BRANCH}" != 1 ]; then ./CI/before_script.${TRAVIS_OS_NAME}.sh; fi 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}" = "linux" ]; then ./openmw_test_suite; fi - - if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ]; then cd .. && ./CI/check_tabs.sh; fi + - 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}" = "linux" ]; then ./openmw_test_suite; fi + - if [ "${COVERITY_SCAN_BRANCH}" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ]; then cd .. && ./CI/check_tabs.sh; fi - cd "${TRAVIS_BUILD_DIR}" deploy: provider: script From 8ad9d26c2c7293eae636863313f3d07a491649c1 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Mon, 12 Nov 2018 15:05:09 +0100 Subject: [PATCH 30/53] try using conditionals --- .travis.yml | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/.travis.yml b/.travis.yml index a364e37e6..887d4dc09 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,12 +32,14 @@ matrix: - name: OpenMW (all) on MacOS xcode9.4 os: osx osx_image: xcode9.4 + if: branch != coverity_scan - name: OpenMW (all) on Ubuntu Trusty GCC-8 os: linux dist: trusty sudo: required env: - MATRIX_EVAL="CC=gcc-8 && CXX=g++-8" + if: branch != coverity_scan - name: OpenMW (openmw) on Ubuntu Trusty Clang-7 with Static Analysis os: linux dist: trusty @@ -46,6 +48,7 @@ matrix: - MATRIX_EVAL="CC=clang-7 && CXX=clang++-7" - ANALYZE="scan-build-7 --use-cc clang-7 --use-c++ clang++-7" - BUILD_OPENMW_CS="OFF" + if: branch != coverity_scan compiler: clang - name: OpenMW (openmw-cs) on Ubuntu Trusty Clang-7 with Static Analysis os: linux @@ -55,21 +58,27 @@ matrix: - MATRIX_EVAL="CC=clang-7 && CXX=clang++-7" - ANALYZE="scan-build-7 --use-cc clang-7 --use-c++ clang++-7" - BUILD_OPENMW="OFF" + if: branch != coverity_scan compiler: clang - name: OpenMW Coverity Scan os: linux dist: trusty sudo: required - coverity_scan: - project: - name: "OpenMW/openmw" - description: "" - branch_pattern: coverity_scan - notification_email: 720642+scrawl@users.noreply.github.com - build_command_prepend: - - "cov-configure --comptype gcc --compiler gcc-8 --template" - - "cmake . -DBUILD_OPENCS=FALSE -DBUILD_WIZARD=FALSE -DBUILD_UNITTESTS=FALSE -DBUILD_BSATOOL=FALSE -DBUILD_ESMTOOL=FALSE -DBUILD_MWINIIMPORTER=FALSE -DBUILD_ESSIMPORTER=FALSE -DBUILD_LAUNCHER=FALSE -DBUILD_MYGUI_PLUGIN=FALSE" - build_command: "make VERBOSE=1 -j3" + env: + - MATRIX_EVAL="CC=gcc-8 && CXX=g++-8" + if: branch = coverity_scan + addons: + coverity_scan: + project: + name: "OpenMW/openmw" + description: "" + branch_pattern: coverity_scan + notification_email: 720642+scrawl@users.noreply.github.com + build_command_prepend: + - "cov-configure --comptype gcc --compiler gcc-8 --template" + - "cmake . -DBUILD_OPENCS=FALSE -DBUILD_WIZARD=FALSE -DBUILD_UNITTESTS=FALSE -DBUILD_BSATOOL=FALSE -DBUILD_ESMTOOL=FALSE -DBUILD_MWINIIMPORTER=FALSE -DBUILD_ESSIMPORTER=FALSE -DBUILD_LAUNCHER=FALSE -DBUILD_MYGUI_PLUGIN=FALSE" + build_command: "make VERBOSE=1 -j3" + # allow_failures: # - name: OpenMW (openmw) on Ubuntu Trusty Clang-7 with Static Analysis From e6774743194dec4de33153eca247a03c1de7cfe4 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Mon, 12 Nov 2018 15:09:49 +0100 Subject: [PATCH 31/53] try and set this globally --- .travis.yml | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/.travis.yml b/.travis.yml index 887d4dc09..c506e7b22 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,6 +27,16 @@ addons: # The other ones from OpenMW ppa libbullet-dev, libswresample-dev, libopenscenegraph-3.4-dev, libmygui-dev ] + coverity_scan: + project: + name: "OpenMW/openmw" + description: "" + branch_pattern: coverity_scan + notification_email: 720642+scrawl@users.noreply.github.com + build_command_prepend: + - "cov-configure --comptype gcc --compiler gcc-8 --template" + - "cmake . -DBUILD_OPENCS=FALSE -DBUILD_WIZARD=FALSE -DBUILD_UNITTESTS=FALSE -DBUILD_BSATOOL=FALSE -DBUILD_ESMTOOL=FALSE -DBUILD_MWINIIMPORTER=FALSE -DBUILD_ESSIMPORTER=FALSE -DBUILD_LAUNCHER=FALSE -DBUILD_MYGUI_PLUGIN=FALSE" + build_command: "make VERBOSE=1 -j3" matrix: include: - name: OpenMW (all) on MacOS xcode9.4 @@ -67,18 +77,6 @@ matrix: env: - MATRIX_EVAL="CC=gcc-8 && CXX=g++-8" if: branch = coverity_scan - addons: - coverity_scan: - project: - name: "OpenMW/openmw" - description: "" - branch_pattern: coverity_scan - notification_email: 720642+scrawl@users.noreply.github.com - build_command_prepend: - - "cov-configure --comptype gcc --compiler gcc-8 --template" - - "cmake . -DBUILD_OPENCS=FALSE -DBUILD_WIZARD=FALSE -DBUILD_UNITTESTS=FALSE -DBUILD_BSATOOL=FALSE -DBUILD_ESMTOOL=FALSE -DBUILD_MWINIIMPORTER=FALSE -DBUILD_ESSIMPORTER=FALSE -DBUILD_LAUNCHER=FALSE -DBUILD_MYGUI_PLUGIN=FALSE" - build_command: "make VERBOSE=1 -j3" - # allow_failures: # - name: OpenMW (openmw) on Ubuntu Trusty Clang-7 with Static Analysis From 8b4e7bc73ccfe4e611dbc036f0ac01392ef755e7 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Mon, 12 Nov 2018 15:36:13 +0100 Subject: [PATCH 32/53] all on one line --- .travis.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index c506e7b22..aa046d6c0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,9 +33,7 @@ addons: description: "" branch_pattern: coverity_scan notification_email: 720642+scrawl@users.noreply.github.com - build_command_prepend: - - "cov-configure --comptype gcc --compiler gcc-8 --template" - - "cmake . -DBUILD_OPENCS=FALSE -DBUILD_WIZARD=FALSE -DBUILD_UNITTESTS=FALSE -DBUILD_BSATOOL=FALSE -DBUILD_ESMTOOL=FALSE -DBUILD_MWINIIMPORTER=FALSE -DBUILD_ESSIMPORTER=FALSE -DBUILD_LAUNCHER=FALSE -DBUILD_MYGUI_PLUGIN=FALSE" + build_command_prepend: "cov-configure --comptype gcc --compiler gcc-8 --template; cmake . -DBUILD_OPENCS=FALSE -DBUILD_WIZARD=FALSE -DBUILD_UNITTESTS=FALSE -DBUILD_BSATOOL=FALSE -DBUILD_ESMTOOL=FALSE -DBUILD_MWINIIMPORTER=FALSE -DBUILD_ESSIMPORTER=FALSE -DBUILD_LAUNCHER=FALSE -DBUILD_MYGUI_PLUGIN=FALSE" build_command: "make VERBOSE=1 -j3" matrix: include: From beb22758a681b464cbbeb736b167145b0fb86eae Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Mon, 12 Nov 2018 22:18:57 +0100 Subject: [PATCH 33/53] try gcc-7 instead --- .travis.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index aa046d6c0..b416650dc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,7 +17,7 @@ addons: - llvm-toolchain-trusty-7 packages: [ # Dev - cmake, clang-7, clang-tools-7, gcc-8, g++-8, + cmake, clang-7, clang-tools-7, gcc-7, g++-7, # Boost libboost-filesystem-dev, libboost-program-options-dev, libboost-system-dev, # FFmpeg @@ -33,7 +33,7 @@ addons: description: "" branch_pattern: coverity_scan notification_email: 720642+scrawl@users.noreply.github.com - build_command_prepend: "cov-configure --comptype gcc --compiler gcc-8 --template; cmake . -DBUILD_OPENCS=FALSE -DBUILD_WIZARD=FALSE -DBUILD_UNITTESTS=FALSE -DBUILD_BSATOOL=FALSE -DBUILD_ESMTOOL=FALSE -DBUILD_MWINIIMPORTER=FALSE -DBUILD_ESSIMPORTER=FALSE -DBUILD_LAUNCHER=FALSE -DBUILD_MYGUI_PLUGIN=FALSE" + build_command_prepend: "cov-configure --comptype gcc --compiler gcc-7 --template; cmake . -DBUILD_OPENCS=FALSE -DBUILD_WIZARD=FALSE -DBUILD_UNITTESTS=FALSE -DBUILD_BSATOOL=FALSE -DBUILD_ESMTOOL=FALSE -DBUILD_MWINIIMPORTER=FALSE -DBUILD_ESSIMPORTER=FALSE -DBUILD_LAUNCHER=FALSE -DBUILD_MYGUI_PLUGIN=FALSE" build_command: "make VERBOSE=1 -j3" matrix: include: @@ -41,12 +41,12 @@ matrix: os: osx osx_image: xcode9.4 if: branch != coverity_scan - - name: OpenMW (all) on Ubuntu Trusty GCC-8 + - name: OpenMW (all) on Ubuntu Trusty GCC-7 os: linux dist: trusty sudo: required env: - - MATRIX_EVAL="CC=gcc-8 && CXX=g++-8" + - MATRIX_EVAL="CC=gcc-7 && CXX=g++-7" if: branch != coverity_scan - name: OpenMW (openmw) on Ubuntu Trusty Clang-7 with Static Analysis os: linux @@ -73,7 +73,7 @@ matrix: dist: trusty sudo: required env: - - MATRIX_EVAL="CC=gcc-8 && CXX=g++-8" + - MATRIX_EVAL="CC=gcc-7 && CXX=g++-7" if: branch = coverity_scan # allow_failures: # - name: OpenMW (openmw) on Ubuntu Trusty Clang-7 with Static Analysis From 6c5e40ef7a337ccbee049286003582f92a8ff468 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Mon, 12 Nov 2018 23:54:22 +0100 Subject: [PATCH 34/53] try with gcc-6 --- .travis.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index b416650dc..2ace0ac48 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,7 +17,7 @@ addons: - llvm-toolchain-trusty-7 packages: [ # Dev - cmake, clang-7, clang-tools-7, gcc-7, g++-7, + cmake, clang-7, clang-tools-7, gcc-6, g++-6, # Boost libboost-filesystem-dev, libboost-program-options-dev, libboost-system-dev, # FFmpeg @@ -33,7 +33,7 @@ addons: description: "" branch_pattern: coverity_scan notification_email: 720642+scrawl@users.noreply.github.com - build_command_prepend: "cov-configure --comptype gcc --compiler gcc-7 --template; cmake . -DBUILD_OPENCS=FALSE -DBUILD_WIZARD=FALSE -DBUILD_UNITTESTS=FALSE -DBUILD_BSATOOL=FALSE -DBUILD_ESMTOOL=FALSE -DBUILD_MWINIIMPORTER=FALSE -DBUILD_ESSIMPORTER=FALSE -DBUILD_LAUNCHER=FALSE -DBUILD_MYGUI_PLUGIN=FALSE" + build_command_prepend: "cov-configure --comptype gcc --compiler gcc-6 --template; cmake . -DBUILD_OPENCS=FALSE -DBUILD_WIZARD=FALSE -DBUILD_UNITTESTS=FALSE -DBUILD_BSATOOL=FALSE -DBUILD_ESMTOOL=FALSE -DBUILD_MWINIIMPORTER=FALSE -DBUILD_ESSIMPORTER=FALSE -DBUILD_LAUNCHER=FALSE -DBUILD_MYGUI_PLUGIN=FALSE" build_command: "make VERBOSE=1 -j3" matrix: include: @@ -41,12 +41,12 @@ matrix: os: osx osx_image: xcode9.4 if: branch != coverity_scan - - name: OpenMW (all) on Ubuntu Trusty GCC-7 + - name: OpenMW (all) on Ubuntu Trusty GCC-6 os: linux dist: trusty sudo: required env: - - MATRIX_EVAL="CC=gcc-7 && CXX=g++-7" + - MATRIX_EVAL="CC=gcc-6 && CXX=g++-6" if: branch != coverity_scan - name: OpenMW (openmw) on Ubuntu Trusty Clang-7 with Static Analysis os: linux @@ -73,7 +73,7 @@ matrix: dist: trusty sudo: required env: - - MATRIX_EVAL="CC=gcc-7 && CXX=g++-7" + - MATRIX_EVAL="CC=gcc-6 && CXX=g++-6" if: branch = coverity_scan # allow_failures: # - name: OpenMW (openmw) on Ubuntu Trusty Clang-7 with Static Analysis From 71f1a53090fc8d42b539101eff8bae372698a36d Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 13 Nov 2018 10:00:12 +0400 Subject: [PATCH 35/53] Improve tooltips cleanup (bug #4714) --- CHANGELOG.md | 1 + apps/openmw/mwgui/tooltips.cpp | 15 +++++++++++++++ apps/openmw/mwgui/tooltips.hpp | 2 ++ apps/openmw/mwgui/windowmanagerimp.cpp | 2 +- 4 files changed, 19 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d686b668..18b0b99fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ Bug #3623: Fix HiDPI on Windows Bug #4540: Rain delay when exiting water Bug #4701: PrisonMarker record is not hardcoded like other markers + Bug #4714: Crash upon game load in the repair menu while the "Your repair failed!" message is active Feature #2229: Improve pathfinding AI Feature #3442: Default values for fallbacks from ini file Feature #4673: Weapon sheathing diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index 0bd0d191c..b8a866402 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -330,6 +330,21 @@ namespace MWGui } } + void ToolTips::clear() + { + mFocusObject = MWWorld::Ptr(); + + while (mDynamicToolTipBox->getChildCount()) + { + MyGUI::Gui::getInstance().destroyWidget(mDynamicToolTipBox->getChildAt(0)); + } + + for (unsigned int i=0; i < mMainWidget->getChildCount(); ++i) + { + mMainWidget->getChildAt(i)->setVisible(false); + } + } + void ToolTips::setFocusObject(const MWWorld::Ptr& focus) { mFocusObject = focus; diff --git a/apps/openmw/mwgui/tooltips.hpp b/apps/openmw/mwgui/tooltips.hpp index 1ef473b5a..3b8e8b2cc 100644 --- a/apps/openmw/mwgui/tooltips.hpp +++ b/apps/openmw/mwgui/tooltips.hpp @@ -59,6 +59,8 @@ namespace MWGui void setDelay(float delay); + void clear(); + void setFocusObject(const MWWorld::Ptr& focus); void setFocusObjectScreenCoords(float min_x, float min_y, float max_x, float max_y); ///< set the screen-space position of the tooltip for focused object diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index f02314de6..dc82cad9c 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -1728,7 +1728,7 @@ namespace MWGui mMessageBoxManager->clear(); - mToolTips->setFocusObject(MWWorld::Ptr()); + mToolTips->clear(); mSelectedSpell.clear(); mCustomMarkers.clear(); From 5e071e3eb02685a6da6988ce4fff40bc72c5040b Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 13 Nov 2018 10:32:23 +0400 Subject: [PATCH 36/53] Add a missing check if mPtr is empty (bug #4715) --- CHANGELOG.md | 1 + apps/openmw/mwgui/dialogue.cpp | 3 +++ 2 files changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 18b0b99fc..2bb06874b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ Bug #4540: Rain delay when exiting water Bug #4701: PrisonMarker record is not hardcoded like other markers Bug #4714: Crash upon game load in the repair menu while the "Your repair failed!" message is active + Bug #4715: "Cannot get class of an empty object" exception after pressing ESC in the dialogue mode Feature #2229: Improve pathfinding AI Feature #3442: Default values for fallbacks from ini file Feature #4673: Weapon sheathing diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index 3ac8839e2..dfb2b15b9 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -740,6 +740,9 @@ namespace MWGui bool DialogueWindow::isCompanion(const MWWorld::Ptr& actor) { + if (actor.isEmpty()) + return false; + return !actor.getClass().getScript(actor).empty() && actor.getRefData().getLocals().getIntVar(actor.getClass().getScript(actor), "companion"); } From 7b7138fc0abe62eefe1a082679737e5ca9d65874 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Tue, 13 Nov 2018 10:24:07 +0100 Subject: [PATCH 37/53] try gcc5 as last resort --- .travis.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2ace0ac48..660337cdf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,7 +17,7 @@ addons: - llvm-toolchain-trusty-7 packages: [ # Dev - cmake, clang-7, clang-tools-7, gcc-6, g++-6, + cmake, clang-7, clang-tools-7, gcc-5, g++-5, # Boost libboost-filesystem-dev, libboost-program-options-dev, libboost-system-dev, # FFmpeg @@ -33,7 +33,7 @@ addons: description: "" branch_pattern: coverity_scan notification_email: 720642+scrawl@users.noreply.github.com - build_command_prepend: "cov-configure --comptype gcc --compiler gcc-6 --template; cmake . -DBUILD_OPENCS=FALSE -DBUILD_WIZARD=FALSE -DBUILD_UNITTESTS=FALSE -DBUILD_BSATOOL=FALSE -DBUILD_ESMTOOL=FALSE -DBUILD_MWINIIMPORTER=FALSE -DBUILD_ESSIMPORTER=FALSE -DBUILD_LAUNCHER=FALSE -DBUILD_MYGUI_PLUGIN=FALSE" + build_command_prepend: "cov-configure --comptype gcc --compiler gcc-5 --template; cmake . -DBUILD_OPENCS=FALSE -DBUILD_WIZARD=FALSE -DBUILD_UNITTESTS=FALSE -DBUILD_BSATOOL=FALSE -DBUILD_ESMTOOL=FALSE -DBUILD_MWINIIMPORTER=FALSE -DBUILD_ESSIMPORTER=FALSE -DBUILD_LAUNCHER=FALSE -DBUILD_MYGUI_PLUGIN=FALSE" build_command: "make VERBOSE=1 -j3" matrix: include: @@ -41,12 +41,12 @@ matrix: os: osx osx_image: xcode9.4 if: branch != coverity_scan - - name: OpenMW (all) on Ubuntu Trusty GCC-6 + - name: OpenMW (all) on Ubuntu Trusty GCC-5 os: linux dist: trusty sudo: required env: - - MATRIX_EVAL="CC=gcc-6 && CXX=g++-6" + - MATRIX_EVAL="CC=gcc-5 && CXX=g++-5" if: branch != coverity_scan - name: OpenMW (openmw) on Ubuntu Trusty Clang-7 with Static Analysis os: linux @@ -73,7 +73,7 @@ matrix: dist: trusty sudo: required env: - - MATRIX_EVAL="CC=gcc-6 && CXX=g++-6" + - MATRIX_EVAL="CC=gcc-5 && CXX=g++-5" if: branch = coverity_scan # allow_failures: # - name: OpenMW (openmw) on Ubuntu Trusty Clang-7 with Static Analysis From f8f66b83b64f7cad57e01602933840ccd2fffe8f Mon Sep 17 00:00:00 2001 From: Alexander Olofsson Date: Tue, 13 Nov 2018 22:08:06 +0100 Subject: [PATCH 38/53] Update OpenAL-soft to 1.19.1 --- CI/before_script.msvc.sh | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/CI/before_script.msvc.sh b/CI/before_script.msvc.sh index 55dda74af..b0d4a1ae1 100644 --- a/CI/before_script.msvc.sh +++ b/CI/before_script.msvc.sh @@ -353,9 +353,9 @@ if [ -z $SKIP_DOWNLOAD ]; then "MyGUI-3.2.2-msvc${MSVC_YEAR}-win${BITS}.7z" # OpenAL - download "OpenAL-Soft 1.17.2" \ - "http://openal-soft.org/openal-binaries/openal-soft-1.17.2-bin.zip" \ - "OpenAL-Soft-1.17.2.zip" + download "OpenAL-Soft 1.19.1" \ + "http://openal-soft.org/openal-binaries/openal-soft-1.19.1-bin.zip" \ + "OpenAL-Soft-1.19.1.zip" # OSG download "OpenSceneGraph 3.4.1-scrawl" \ @@ -533,18 +533,18 @@ printf "MyGUI 3.2.2... " cd $DEPS echo # OpenAL -printf "OpenAL-Soft 1.17.2... " +printf "OpenAL-Soft 1.19.1... " { - if [ -d openal-soft-1.17.2-bin ]; then + if [ -d openal-soft-1.19.1-bin ]; then printf "Exists. " elif [ -z $SKIP_EXTRACT ]; then - rm -rf openal-soft-1.17.2-bin - eval 7z x -y OpenAL-Soft-1.17.2.zip $STRIP + rm -rf openal-soft-1.19.1-bin + eval 7z x -y OpenAL-Soft-1.19.1.zip $STRIP fi - OPENAL_SDK="$(real_pwd)/openal-soft-1.17.2-bin" + OPENAL_SDK="$(real_pwd)/openal-soft-1.19.1-bin" add_cmake_opts -DOPENAL_INCLUDE_DIR="${OPENAL_SDK}/include/AL" \ -DOPENAL_LIBRARY="${OPENAL_SDK}/libs/Win${BITS}/OpenAL32.lib" - add_runtime_dlls "$(pwd)/openal-soft-1.17.2-bin/bin/WIN${BITS}/soft_oal.dll:OpenAL32.dll" + add_runtime_dlls "$(pwd)/openal-soft-1.19.1-bin/bin/WIN${BITS}/soft_oal.dll:OpenAL32.dll" echo Done. } cd $DEPS From 4ee15ddcb9d637c74c7c2b18dab1d038c3aa1289 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 13 Nov 2018 23:07:01 +0400 Subject: [PATCH 39/53] Fix many Coverity Scan warnings --- apps/esmtool/esmtool.cpp | 2 +- apps/mwiniimporter/importer.cpp | 7 ++++--- apps/opencs/model/doc/documentmanager.cpp | 2 +- apps/opencs/model/prefs/shortcut.cpp | 11 ++++++++++- apps/opencs/model/world/actoradapter.cpp | 11 +++++++++++ apps/opencs/model/world/actoradapter.hpp | 4 ++++ apps/opencs/model/world/commands.cpp | 6 +++++- apps/opencs/view/prefs/dialogue.cpp | 13 +++++++++++-- apps/opencs/view/render/cameracontroller.cpp | 1 + apps/opencs/view/render/pathgrid.cpp | 8 ++++++++ apps/opencs/view/render/scenewidget.cpp | 10 +++++++++- apps/opencs/view/render/terraintexturemode.cpp | 6 +++--- apps/opencs/view/world/cellcreator.cpp | 2 +- apps/opencs/view/world/dialoguesubview.cpp | 6 ++++-- apps/opencs/view/world/scripthighlighter.cpp | 7 +++++-- apps/openmw/mwmechanics/aiwander.cpp | 1 + apps/openmw/mwsound/ffmpeg_decoder.cpp | 1 + components/detournavigator/makenavmesh.cpp | 1 + 18 files changed, 81 insertions(+), 18 deletions(-) diff --git a/apps/esmtool/esmtool.cpp b/apps/esmtool/esmtool.cpp index 6d2b59ad9..d4569000b 100644 --- a/apps/esmtool/esmtool.cpp +++ b/apps/esmtool/esmtool.cpp @@ -507,7 +507,7 @@ int clone(Arguments& info) esm.endRecord(typeName.toString()); saved++; - int perc = (int)((saved / (float)recordCount)*100); + int perc = recordCount == 0 ? 100 : (int)((saved / (float)recordCount)*100); if (perc % 10 == 0) { std::cerr << "\r" << perc << "%"; diff --git a/apps/mwiniimporter/importer.cpp b/apps/mwiniimporter/importer.cpp index 4fbe97720..5b229dc92 100644 --- a/apps/mwiniimporter/importer.cpp +++ b/apps/mwiniimporter/importer.cpp @@ -860,11 +860,12 @@ std::vector::iterator MwIniImporter::findString(std::vector& output, std::vector input) { - for (auto& path : input) { + for (auto& path : input) + { if (path.front() == '"') { - path.erase(path.begin()); - path.erase(path.end() - 1); + // Drop first and last characters - quotation marks + path = path.substr(1, path.size() - 2); } output.emplace_back(path); } diff --git a/apps/opencs/model/doc/documentmanager.cpp b/apps/opencs/model/doc/documentmanager.cpp index 531cfd267..a2859beb5 100644 --- a/apps/opencs/model/doc/documentmanager.cpp +++ b/apps/opencs/model/doc/documentmanager.cpp @@ -9,7 +9,7 @@ #include "document.hpp" CSMDoc::DocumentManager::DocumentManager (const Files::ConfigurationManager& configuration) -: mConfiguration (configuration), mEncoding (ToUTF8::WINDOWS_1252) +: mConfiguration (configuration), mEncoding (ToUTF8::WINDOWS_1252), mFsStrict(false) { boost::filesystem::path projectPath = configuration.getUserDataPath() / "projects"; diff --git a/apps/opencs/model/prefs/shortcut.cpp b/apps/opencs/model/prefs/shortcut.cpp index 27077ac83..924b9535e 100644 --- a/apps/opencs/model/prefs/shortcut.cpp +++ b/apps/opencs/model/prefs/shortcut.cpp @@ -5,6 +5,8 @@ #include #include +#include + #include "state.hpp" #include "shortcutmanager.hpp" @@ -71,7 +73,14 @@ namespace CSMPrefs Shortcut::~Shortcut() { - State::get().getShortcutManager().removeShortcut(this); + try + { + State::get().getShortcutManager().removeShortcut(this); + } + catch(const std::exception& e) + { + Log(Debug::Error) << "Error in the destructor: " << e.what(); + } } bool Shortcut::isEnabled() const diff --git a/apps/opencs/model/world/actoradapter.cpp b/apps/opencs/model/world/actoradapter.cpp index b89240a80..2c827373d 100644 --- a/apps/opencs/model/world/actoradapter.cpp +++ b/apps/opencs/model/world/actoradapter.cpp @@ -21,6 +21,11 @@ namespace CSMWorld return mIsBeast; } + ActorAdapter::RaceData::RaceData() + { + mIsBeast = false; + } + bool ActorAdapter::RaceData::handlesPart(ESM::PartReferenceType type) const { switch (type) @@ -83,6 +88,12 @@ namespace CSMWorld } + ActorAdapter::ActorData::ActorData() + { + mCreature = false; + mFemale = false; + } + const std::string& ActorAdapter::ActorData::getId() const { return mId; diff --git a/apps/opencs/model/world/actoradapter.hpp b/apps/opencs/model/world/actoradapter.hpp index 1c9265be4..912a6bcb3 100644 --- a/apps/opencs/model/world/actoradapter.hpp +++ b/apps/opencs/model/world/actoradapter.hpp @@ -43,6 +43,8 @@ namespace CSMWorld class RaceData { public: + RaceData(); + /// Retrieves the id of the race represented const std::string& getId() const; /// Checks if it's a beast race @@ -80,6 +82,8 @@ namespace CSMWorld class ActorData { public: + ActorData(); + /// Retrieves the id of the actor represented const std::string& getId() const; /// Checks if the actor is a creature diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index 4c7096479..5ddb753c5 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -198,7 +198,11 @@ CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelI if (mIndex.parent().isValid()) { - setText ("Modify " + dynamic_cast(mModel)->nestedHeaderData ( + CSMWorld::IdTree* tree = dynamic_cast(mModel); + + assert(tree != nullptr); + + setText ("Modify " + tree->nestedHeaderData ( mIndex.parent().column(), mIndex.column(), Qt::Horizontal, Qt::DisplayRole).toString()); } else diff --git a/apps/opencs/view/prefs/dialogue.cpp b/apps/opencs/view/prefs/dialogue.cpp index 1f5772f18..0d32ebd0a 100644 --- a/apps/opencs/view/prefs/dialogue.cpp +++ b/apps/opencs/view/prefs/dialogue.cpp @@ -8,6 +8,8 @@ #include #include +#include + #include "../../model/prefs/state.hpp" #include "page.hpp" @@ -77,8 +79,15 @@ CSVPrefs::Dialogue::Dialogue() CSVPrefs::Dialogue::~Dialogue() { - if (isVisible()) - CSMPrefs::State::get().save(); + try + { + if (isVisible()) + CSMPrefs::State::get().save(); + } + catch(const std::exception& e) + { + Log(Debug::Error) << "Error in the destructor: " << e.what(); + } } void CSVPrefs::Dialogue::closeEvent (QCloseEvent *event) diff --git a/apps/opencs/view/render/cameracontroller.cpp b/apps/opencs/view/render/cameracontroller.cpp index 524a79821..5dbb7a28c 100644 --- a/apps/opencs/view/render/cameracontroller.cpp +++ b/apps/opencs/view/render/cameracontroller.cpp @@ -463,6 +463,7 @@ namespace CSVRender , mDistance(0) , mOrbitSpeed(osg::PI / 4) , mOrbitSpeedMult(4) + , mConstRoll(false) { CSMPrefs::Shortcut* naviPrimaryShortcut = new CSMPrefs::Shortcut("scene-navi-primary", widget); naviPrimaryShortcut->enable(false); diff --git a/apps/opencs/view/render/pathgrid.cpp b/apps/opencs/view/render/pathgrid.cpp index 9eb2765d3..1fa63bedc 100644 --- a/apps/opencs/view/render/pathgrid.cpp +++ b/apps/opencs/view/render/pathgrid.cpp @@ -227,6 +227,8 @@ namespace CSVRender CSMWorld::IdTree* model = dynamic_cast(mData.getTableModel( CSMWorld::UniversalId::Type_Pathgrids)); + assert(model != nullptr); + const CSMWorld::Pathgrid* source = getPathgridSource(); if (source) { @@ -360,6 +362,8 @@ namespace CSVRender CSMWorld::IdTree* model = dynamic_cast(mData.getTableModel( CSMWorld::UniversalId::Type_Pathgrids)); + assert(model != nullptr); + // Want to remove nodes from end of list first std::sort(mSelected.begin(), mSelected.end(), std::greater()); @@ -461,6 +465,8 @@ namespace CSVRender CSMWorld::IdTree* model = dynamic_cast(mData.getTableModel( CSMWorld::UniversalId::Type_Pathgrids)); + assert(model != nullptr); + int parentColumn = mPathgridCollection.findColumnIndex(CSMWorld::Columns::ColumnId_PathgridEdges); std::set >::iterator row; @@ -636,6 +642,8 @@ namespace CSVRender CSMWorld::IdTree* model = dynamic_cast(mData.getTableModel( CSMWorld::UniversalId::Type_Pathgrids)); + assert(model != nullptr); + int recordIndex = mPathgridCollection.getIndex(mId); int parentColumn = mPathgridCollection.findColumnIndex(CSMWorld::Columns::ColumnId_PathgridEdges); diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index 6cc64f653..7e1666dc1 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -96,7 +97,14 @@ RenderWidget::RenderWidget(QWidget *parent, Qt::WindowFlags f) RenderWidget::~RenderWidget() { - CompositeViewer::get().removeView(mView); + try + { + CompositeViewer::get().removeView(mView); + } + catch(const std::exception& e) + { + Log(Debug::Error) << "Error in the destructor: " << e.what(); + } } void RenderWidget::flagAsModified() diff --git a/apps/opencs/view/render/terraintexturemode.cpp b/apps/opencs/view/render/terraintexturemode.cpp index 376258c5e..274e64742 100644 --- a/apps/opencs/view/render/terraintexturemode.cpp +++ b/apps/opencs/view/render/terraintexturemode.cpp @@ -256,9 +256,9 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe std::string mBrushTextureInt = mBrushTexture.substr (hashlocation+1); int brushInt = stoi(mBrushTexture.substr (hashlocation+1))+1; // All indices are offset by +1 - float rf = mBrushSize/2; - int r = (mBrushSize/2)+1; - float distance = 0; + int rf = mBrushSize / 2; + int r = mBrushSize / 2 + 1; + int distance = 0; if (mBrushShape == 0) { diff --git a/apps/opencs/view/world/cellcreator.cpp b/apps/opencs/view/world/cellcreator.cpp index a42e7ead4..16338a9a2 100644 --- a/apps/opencs/view/world/cellcreator.cpp +++ b/apps/opencs/view/world/cellcreator.cpp @@ -25,7 +25,7 @@ std::string CSVWorld::CellCreator::getId() const void CSVWorld::CellCreator::configureCreateCommand(CSMWorld::CreateCommand& command) const { CSMWorld::IdTree *model = dynamic_cast(getData().getTableModel(getCollectionId())); - Q_ASSERT(model != nullptr); + assert(model != nullptr); int parentIndex = model->findColumnIndex(CSMWorld::Columns::ColumnId_Cell); int index = model->findNestedColumnIndex(parentIndex, CSMWorld::Columns::ColumnId_Interior); command.addNestedValue(parentIndex, index, mType->currentIndex() == 0); diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index b32e2c7a1..0b04fe6c9 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -555,8 +555,10 @@ void CSVWorld::EditWidget::remake(int row) if (mTable->hasChildren(mTable->index(row, i)) && !(flags & CSMWorld::ColumnBase::Flag_Dialogue_List)) { - mNestedModels.push_back(new CSMWorld::NestedTableProxyModel ( - mTable->index(row, i), display, dynamic_cast(mTable))); + CSMWorld::IdTree *innerTable = dynamic_cast(mTable); + assert(innerTable != nullptr); + + mNestedModels.push_back(new CSMWorld::NestedTableProxyModel (mTable->index(row, i), display, innerTable)); int idColumn = mTable->findColumnIndex (CSMWorld::Columns::ColumnId_Id); int typeColumn = mTable->findColumnIndex (CSMWorld::Columns::ColumnId_RecordType); diff --git a/apps/opencs/view/world/scripthighlighter.cpp b/apps/opencs/view/world/scripthighlighter.cpp index 6aba66053..3fb82fad8 100644 --- a/apps/opencs/view/world/scripthighlighter.cpp +++ b/apps/opencs/view/world/scripthighlighter.cpp @@ -81,8 +81,11 @@ void CSVWorld::ScriptHighlighter::highlight (const Compiler::TokenLoc& loc, Type CSVWorld::ScriptHighlighter::ScriptHighlighter (const CSMWorld::Data& data, Mode mode, QTextDocument *parent) -: QSyntaxHighlighter (parent), Compiler::Parser (mErrorHandler, mContext), mContext (data), - mMode (mode) + : QSyntaxHighlighter (parent) + , Compiler::Parser (mErrorHandler, mContext) + , mContext (data) + , mMode (mode) + , mMarkOccurrences (false) { QColor color ("black"); QTextCharFormat format; diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 833a37127..129d1347b 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -955,6 +955,7 @@ namespace MWMechanics , mStoredInitialActorPosition(wander->mStoredInitialActorPosition) , mHasDestination(false) , mDestination(osg::Vec3f(0, 0, 0)) + , mUsePathgrid(false) { if (mStoredInitialActorPosition) mInitialActorPosition = wander->mInitialActorPosition; diff --git a/apps/openmw/mwsound/ffmpeg_decoder.cpp b/apps/openmw/mwsound/ffmpeg_decoder.cpp index 447c386c9..d1c0dfee5 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.cpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.cpp @@ -431,6 +431,7 @@ size_t FFmpeg_Decoder::getSampleOffset() FFmpeg_Decoder::FFmpeg_Decoder(const VFS::Manager* vfs) : Sound_Decoder(vfs) , mFormatCtx(nullptr) + , mCodecCtx(nullptr) , mStream(nullptr) , mFrame(nullptr) , mFrameSize(0) diff --git a/components/detournavigator/makenavmesh.cpp b/components/detournavigator/makenavmesh.cpp index 3ee730386..26f86da32 100644 --- a/components/detournavigator/makenavmesh.cpp +++ b/components/detournavigator/makenavmesh.cpp @@ -129,6 +129,7 @@ namespace config.bmin[2] -= getBorderSize(settings); config.bmax[0] += getBorderSize(settings); config.bmax[2] += getBorderSize(settings); + config.tileSize = settings.mTileSize; return config; } From 25e4156940a5a6a8acba469b538c8c3c9bc81e7a Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Wed, 14 Nov 2018 10:01:12 +0100 Subject: [PATCH 40/53] enable niftest by default, coverity branch covers everything but openmw now due to timeouts; we run coverity manually for openmw for now; re-order options to be readable and logical sequence --- .travis.yml | 2 +- CMakeLists.txt | 24 ++++++++++++------------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.travis.yml b/.travis.yml index 660337cdf..8c44cb60b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,7 +33,7 @@ addons: description: "" branch_pattern: coverity_scan notification_email: 720642+scrawl@users.noreply.github.com - build_command_prepend: "cov-configure --comptype gcc --compiler gcc-5 --template; cmake . -DBUILD_OPENCS=FALSE -DBUILD_WIZARD=FALSE -DBUILD_UNITTESTS=FALSE -DBUILD_BSATOOL=FALSE -DBUILD_ESMTOOL=FALSE -DBUILD_MWINIIMPORTER=FALSE -DBUILD_ESSIMPORTER=FALSE -DBUILD_LAUNCHER=FALSE -DBUILD_MYGUI_PLUGIN=FALSE" + build_command_prepend: "cov-configure --comptype gcc --compiler gcc-5 --template; cmake . -DBUILD_OPENMW=FALSE" build_command: "make VERBOSE=1 -j3" matrix: include: diff --git a/CMakeLists.txt b/CMakeLists.txt index 606d27bfd..28dd70973 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,17 +1,17 @@ # Apps and tools -option(BUILD_OPENMW "build OpenMW" ON) -option(BUILD_BSATOOL "build BSA extractor" ON) -option(BUILD_ESMTOOL "build ESM inspector" ON) -option(BUILD_LAUNCHER "build Launcher" ON) -option(BUILD_MWINIIMPORTER "build MWiniImporter" ON) -option(BUILD_ESSIMPORTER "build ESS (Morrowind save game) importer" ON) -option(BUILD_OPENCS "build OpenMW Construction Set" ON) -option(BUILD_WIZARD "build Installation Wizard" ON) +option(BUILD_OPENMW "Build OpenMW" ON) +option(BUILD_LAUNCHER "Build Launcher" ON) +option(BUILD_WIZARD "Build Installation Wizard" ON) +option(BUILD_MWINIIMPORTER "Build MWiniImporter" ON) +option(BUILD_OPENCS "Build OpenMW Construction Set" ON) +option(BUILD_ESSIMPORTER "Build ESS (Morrowind save game) importer" ON) +option(BUILD_BSATOOL "Build BSA extractor" ON) +option(BUILD_ESMTOOL "Build ESM inspector" ON) +option(BUILD_NIFTEST "Build nif file tester" ON) +option(BUILD_MYGUI_PLUGIN "Build MyGUI plugin for OpenMW resources, to use with MyGUI tools" ON) +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_NIFTEST "build nif file tester" OFF) -option(BUILD_MYGUI_PLUGIN "build MyGUI plugin for OpenMW resources, to use with MyGUI tools" ON) -option(BUILD_DOCS "build documentation." OFF ) +option(BUILD_UNITTESTS "Enable Unittests with Google C++ Unittest" OFF) if (NOT BUILD_LAUNCHER AND NOT BUILD_OPENCS AND NOT BUILD_WIZARD) set(USE_QT FALSE) From f20d1b1b72ec6b8fd4ee990d8d0ff22f9195d723 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 14 Nov 2018 15:52:36 +0400 Subject: [PATCH 41/53] Catch exception in the NIFTest --- apps/niftest/niftest.cpp | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/apps/niftest/niftest.cpp b/apps/niftest/niftest.cpp index 572a58f26..6c0c74597 100644 --- a/apps/niftest/niftest.cpp +++ b/apps/niftest/niftest.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -128,14 +129,24 @@ std::vector parseOptions (int argc, char** argv) int main(int argc, char **argv) { - std::vector files = parseOptions (argc, argv); + std::vector files; + try + { + files = parseOptions (argc, argv); + } + catch( boost::exception &e ) + { + std::cout << "ERROR parsing arguments: " << boost::diagnostic_information(e) << std::endl; + exit(1); + } // std::cout << "Reading Files" << std::endl; for(std::vector::const_iterator it=files.begin(); it!=files.end(); ++it) { - std::string name = *it; + std::string name = *it; - try{ + try + { if(isNIF(name)) { //std::cout << "Decoding: " << name << std::endl; From 5ac81cfbff1c93db780476c7cc660214a91677db Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 14 Nov 2018 15:53:43 +0400 Subject: [PATCH 42/53] Throw exceptions with some info int the editor if dynamic_cast failed --- apps/opencs/model/world/commands.cpp | 6 ++-- apps/opencs/view/render/pathgrid.cpp | 36 ++++++++++++---------- apps/opencs/view/world/cellcreator.cpp | 6 +++- apps/opencs/view/world/dialoguesubview.cpp | 5 ++- 4 files changed, 33 insertions(+), 20 deletions(-) diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index 5ddb753c5..01ee2f738 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -199,8 +199,10 @@ CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelI if (mIndex.parent().isValid()) { CSMWorld::IdTree* tree = dynamic_cast(mModel); - - assert(tree != nullptr); + if (tree == nullptr) + { + throw std::logic_error("CSMWorld::ModifyCommand: Attempt to add nested values to the non-nested model"); + } setText ("Modify " + tree->nestedHeaderData ( mIndex.parent().column(), mIndex.column(), Qt::Horizontal, Qt::DisplayRole).toString()); diff --git a/apps/opencs/view/render/pathgrid.cpp b/apps/opencs/view/render/pathgrid.cpp index 1fa63bedc..d184b4938 100644 --- a/apps/opencs/view/render/pathgrid.cpp +++ b/apps/opencs/view/render/pathgrid.cpp @@ -224,10 +224,11 @@ namespace CSVRender void Pathgrid::applyPoint(CSMWorld::CommandMacro& commands, const osg::Vec3d& worldPos) { - CSMWorld::IdTree* model = dynamic_cast(mData.getTableModel( - CSMWorld::UniversalId::Type_Pathgrids)); - - assert(model != nullptr); + CSMWorld::IdTree* model = dynamic_cast(mData.getTableModel(CSMWorld::UniversalId::Type_Pathgrids)); + if (model == nullptr) + { + throw std::logic_error("CSVRender::Pathgrid: Attempt to add nested values to the non-nested model"); + } const CSMWorld::Pathgrid* source = getPathgridSource(); if (source) @@ -359,10 +360,11 @@ namespace CSVRender const CSMWorld::Pathgrid* source = getPathgridSource(); if (source) { - CSMWorld::IdTree* model = dynamic_cast(mData.getTableModel( - CSMWorld::UniversalId::Type_Pathgrids)); - - assert(model != nullptr); + CSMWorld::IdTree* model = dynamic_cast(mData.getTableModel(CSMWorld::UniversalId::Type_Pathgrids)); + if (model == nullptr) + { + throw std::logic_error("CSVRender::Pathgrid: Attempt to add nested values to the non-nested model"); + } // Want to remove nodes from end of list first std::sort(mSelected.begin(), mSelected.end(), std::greater()); @@ -462,10 +464,11 @@ namespace CSVRender } } - CSMWorld::IdTree* model = dynamic_cast(mData.getTableModel( - CSMWorld::UniversalId::Type_Pathgrids)); - - assert(model != nullptr); + CSMWorld::IdTree* model = dynamic_cast(mData.getTableModel(CSMWorld::UniversalId::Type_Pathgrids)); + if (model == nullptr) + { + throw std::logic_error("CSVRender::Pathgrid: Attempt to add nested values to the non-nested model"); + } int parentColumn = mPathgridCollection.findColumnIndex(CSMWorld::Columns::ColumnId_PathgridEdges); @@ -639,10 +642,11 @@ namespace CSVRender void Pathgrid::addEdge(CSMWorld::CommandMacro& commands, const CSMWorld::Pathgrid& source, unsigned short node1, unsigned short node2) { - CSMWorld::IdTree* model = dynamic_cast(mData.getTableModel( - CSMWorld::UniversalId::Type_Pathgrids)); - - assert(model != nullptr); + CSMWorld::IdTree* model = dynamic_cast(mData.getTableModel(CSMWorld::UniversalId::Type_Pathgrids)); + if (model == nullptr) + { + throw std::logic_error("CSVRender::Pathgrid: Attempt to add nested values to the non-nested model"); + } int recordIndex = mPathgridCollection.getIndex(mId); int parentColumn = mPathgridCollection.findColumnIndex(CSMWorld::Columns::ColumnId_PathgridEdges); diff --git a/apps/opencs/view/world/cellcreator.cpp b/apps/opencs/view/world/cellcreator.cpp index 16338a9a2..54e618cd9 100644 --- a/apps/opencs/view/world/cellcreator.cpp +++ b/apps/opencs/view/world/cellcreator.cpp @@ -25,7 +25,11 @@ std::string CSVWorld::CellCreator::getId() const void CSVWorld::CellCreator::configureCreateCommand(CSMWorld::CreateCommand& command) const { CSMWorld::IdTree *model = dynamic_cast(getData().getTableModel(getCollectionId())); - assert(model != nullptr); + if (model == nullptr) + { + throw std::logic_error("CSVWorld::CellCreator: Attempt to add nested values to the non-nested model"); + } + int parentIndex = model->findColumnIndex(CSMWorld::Columns::ColumnId_Cell); int index = model->findNestedColumnIndex(parentIndex, CSMWorld::Columns::ColumnId_Interior); command.addNestedValue(parentIndex, index, mType->currentIndex() == 0); diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index 0b04fe6c9..9f73430f0 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -556,7 +556,10 @@ void CSVWorld::EditWidget::remake(int row) !(flags & CSMWorld::ColumnBase::Flag_Dialogue_List)) { CSMWorld::IdTree *innerTable = dynamic_cast(mTable); - assert(innerTable != nullptr); + if (innerTable == nullptr) + { + throw std::logic_error("CSVWorld::EditWidget: Attempt to add nested values to the non-nested model"); + } mNestedModels.push_back(new CSMWorld::NestedTableProxyModel (mTable->index(row, i), display, innerTable)); From 475d8f991594a8bdfa706806a9f7f3bab8f175fc Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Wed, 14 Nov 2018 13:14:54 +0100 Subject: [PATCH 43/53] disable openmw-cs as well, only build the rest due to time constraints --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 8c44cb60b..3e9745318 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,7 +33,7 @@ addons: description: "" branch_pattern: coverity_scan notification_email: 720642+scrawl@users.noreply.github.com - build_command_prepend: "cov-configure --comptype gcc --compiler gcc-5 --template; cmake . -DBUILD_OPENMW=FALSE" + 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: From 815909d3cf50ed1bccb5d3025f4011e1dfdd1ee8 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Wed, 14 Nov 2018 16:09:30 +0100 Subject: [PATCH 44/53] fix typo --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 5a8389fc5..8b1518d3f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,7 +33,7 @@ addons: description: "" branch_pattern: coverity_scan notification_email: 720642+scrawl@users.noreply.github.com - build_command_prepend: "cov-configure --comptype gcc --compiler gcc-5 --template; cmake . -DBUILD_OPENMW=FALSE" -DBUILD_OPENCS=FALSE" + 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: From caad388c269a1b6ed1b43eb0c138474e5ca5e360 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Wed, 14 Nov 2018 16:32:24 +0100 Subject: [PATCH 45/53] bump our versions to 0.46, keep this commit in mind when trying to automate this into one place --- CMakeLists.txt | 2 +- README.md | 2 +- files/openmw.appdata.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 28dd70973..7628e65eb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,7 +54,7 @@ endif() message(STATUS "Configuring OpenMW...") set(OPENMW_VERSION_MAJOR 0) -set(OPENMW_VERSION_MINOR 44) +set(OPENMW_VERSION_MINOR 46) set(OPENMW_VERSION_RELEASE 0) set(OPENMW_VERSION_COMMITHASH "") diff --git a/README.md b/README.md index 333eedff1..9fbfc0178 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ OpenMW is an open-source game engine that supports playing Morrowind by Bethesda OpenMW also comes with OpenMW-CS, a replacement for Bethesda's Construction Set. -* Version: 0.44.0 +* Version: 0.46.0 * License: GPLv3 (see [LICENSE](https://github.com/OpenMW/openmw/blob/master/LICENSE) for more information) * Website: https://www.openmw.org * IRC: #openmw on irc.freenode.net diff --git a/files/openmw.appdata.xml b/files/openmw.appdata.xml index e66fb2978..5c093e3c4 100644 --- a/files/openmw.appdata.xml +++ b/files/openmw.appdata.xml @@ -43,7 +43,7 @@ Copyright 2017 Bret Curtis RolePlaying - + https://openmw.org https://bugs.openmw.org/ From 68f62c974a9de64c5d241b27a38760b3e7b35152 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Wed, 14 Nov 2018 20:53:33 +0100 Subject: [PATCH 46/53] give Xenial a try --- .travis.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8b1518d3f..939304b4d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,7 +14,7 @@ addons: sources: - sourceline: 'ppa:openmw/openmw' - ubuntu-toolchain-r-test - - llvm-toolchain-trusty-7 + - llvm-toolchain-xenial-7 packages: [ # Dev cmake, clang-7, clang-tools-7, gcc-5, g++-5, @@ -41,16 +41,16 @@ matrix: os: osx osx_image: xcode9.4 if: branch != coverity_scan - - name: OpenMW (all) on Ubuntu Trusty GCC-5 + - name: OpenMW (all) on Ubuntu Xenial GCC-5 os: linux - dist: trusty + dist: xenial sudo: required env: - MATRIX_EVAL="CC=gcc-5 && CXX=g++-5" if: branch != coverity_scan - - name: OpenMW (openmw) on Ubuntu Trusty Clang-7 with Static Analysis + - name: OpenMW (openmw) on Ubuntu Xenial Clang-7 with Static Analysis os: linux - dist: trusty + dist: xenial sudo: required env: - MATRIX_EVAL="CC=clang-7 && CXX=clang++-7" @@ -58,9 +58,9 @@ matrix: - BUILD_OPENMW_CS="OFF" if: branch != coverity_scan compiler: clang - - name: OpenMW (openmw-cs) on Ubuntu Trusty Clang-7 with Static Analysis + - name: OpenMW (openmw-cs) on Ubuntu Xenial Clang-7 with Static Analysis os: linux - dist: trusty + dist: xenial sudo: required env: - MATRIX_EVAL="CC=clang-7 && CXX=clang++-7" @@ -70,13 +70,13 @@ matrix: compiler: clang - name: OpenMW Coverity Scan os: linux - dist: trusty + dist: xenial sudo: required env: - MATRIX_EVAL="CC=gcc-5 && CXX=g++-5" if: branch = coverity_scan # allow_failures: -# - name: OpenMW (openmw) on Ubuntu Trusty Clang-7 with Static Analysis +# - name: OpenMW (openmw) on Ubuntu Xenial Clang-7 with Static Analysis before_install: - if [ "${TRAVIS_OS_NAME}" = "linux" ]; then eval "${MATRIX_EVAL}"; fi From fc15fa0a89d62af7ef2498fb8bc23490f2537f69 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Wed, 14 Nov 2018 21:14:14 +0100 Subject: [PATCH 47/53] we now have gcc-5 _and_ gcc-8, along with QT5 and use system tinyxml --- .travis.yml | 25 ++++++++++++++----------- CI/before_script.linux.sh | 2 ++ 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/.travis.yml b/.travis.yml index 939304b4d..9b3a89da6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,15 +17,15 @@ addons: - llvm-toolchain-xenial-7 packages: [ # Dev - cmake, clang-7, clang-tools-7, gcc-5, g++-5, + cmake, clang-7, clang-tools-7, gcc-8, g++-8, # Boost libboost-filesystem-dev, libboost-program-options-dev, libboost-system-dev, # FFmpeg - libavcodec-dev, libavformat-dev, libavutil-dev, libswscale-dev, + libavcodec-dev, libavformat-dev, libavutil-dev, libswresample-dev, libswscale-dev, # Audio, Video and Misc. deps - libsdl2-dev, libqt4-dev, libopenal-dev, libunshield-dev, libtinyxml-dev, + libsdl2-dev, libqt5opengl5-dev, libopenal-dev, libunshield-dev, libtinyxml-dev, # The other ones from OpenMW ppa - libbullet-dev, libswresample-dev, libopenscenegraph-3.4-dev, libmygui-dev + libbullet-dev, libopenscenegraph-3.4-dev, libmygui-dev ] coverity_scan: project: @@ -42,11 +42,16 @@ matrix: osx_image: xcode9.4 if: branch != coverity_scan - name: OpenMW (all) on Ubuntu Xenial GCC-5 + os: linux + dist: xenial + sudo: required + if: branch != coverity_scan + - name: OpenMW (all) on Ubuntu Xenial GCC-8 os: linux dist: xenial sudo: required env: - - MATRIX_EVAL="CC=gcc-5 && CXX=g++-5" + - MATRIX_EVAL="CC=gcc-8 && CXX=g++-8" if: branch != coverity_scan - name: OpenMW (openmw) on Ubuntu Xenial Clang-7 with Static Analysis os: linux @@ -63,17 +68,15 @@ matrix: dist: xenial sudo: required env: - - MATRIX_EVAL="CC=clang-7 && CXX=clang++-7" - - ANALYZE="scan-build-7 --force-analyze-debug-code --use-cc clang-7 --use-c++ clang++-7" - - BUILD_OPENMW="OFF" + - MATRIX_EVAL="CC=clang-7 && CXX=clang++-7" + - ANALYZE="scan-build-7 --force-analyze-debug-code --use-cc clang-7 --use-c++ clang++-7" + - BUILD_OPENMW="OFF" if: branch != coverity_scan compiler: clang - - name: OpenMW Coverity Scan + - name: OpenMW Components Coverity Scan os: linux dist: xenial sudo: required - env: - - MATRIX_EVAL="CC=gcc-5 && CXX=g++-5" if: branch = coverity_scan # allow_failures: # - name: OpenMW (openmw) on Ubuntu Xenial Clang-7 with Static Analysis diff --git a/CI/before_script.linux.sh b/CI/before_script.linux.sh index c8c4f8f85..3ce005874 100755 --- a/CI/before_script.linux.sh +++ b/CI/before_script.linux.sh @@ -26,6 +26,8 @@ ${ANALYZE} cmake \ -DBUILD_MYGUI_PLUGIN=${BUILD_OPENMW_CS} \ -DBUILD_WITH_CODE_COVERAGE=${CODE_COVERAGE} \ -DBUILD_UNITTESTS=1 \ + -DUSE_SYSTEM_TINYXML=1 \ + -DDESIRED_QT_VERSION=5 \ -DCMAKE_INSTALL_PREFIX=/usr \ -DBINDIR=/usr/games \ -DCMAKE_BUILD_TYPE="None" \ From 059a8fd32ac1a4b3342dab523273353ce6de69bf Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 15 Nov 2018 17:50:23 +0400 Subject: [PATCH 48/53] Refactor dynamic casts in the editor --- apps/opencs/model/world/commands.cpp | 14 ++---------- apps/opencs/view/render/pathgrid.cpp | 25 ++++------------------ apps/opencs/view/world/cellcreator.cpp | 6 +----- apps/opencs/view/world/dialoguesubview.cpp | 7 +----- 4 files changed, 8 insertions(+), 44 deletions(-) diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index 01ee2f738..e9682d7c9 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -198,12 +198,7 @@ CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelI if (mIndex.parent().isValid()) { - CSMWorld::IdTree* tree = dynamic_cast(mModel); - if (tree == nullptr) - { - throw std::logic_error("CSMWorld::ModifyCommand: Attempt to add nested values to the non-nested model"); - } - + CSMWorld::IdTree* tree = &dynamic_cast(*mModel); setText ("Modify " + tree->nestedHeaderData ( mIndex.parent().column(), mIndex.column(), Qt::Horizontal, Qt::DisplayRole).toString()); } @@ -249,12 +244,7 @@ void CSMWorld::CreateCommand::applyModifications() { if (!mNestedValues.empty()) { - CSMWorld::IdTree *tree = dynamic_cast(&mModel); - if (tree == nullptr) - { - throw std::logic_error("CSMWorld::CreateCommand: Attempt to add nested values to the non-nested model"); - } - + CSMWorld::IdTree* tree = &dynamic_cast(mModel); std::map >::const_iterator current = mNestedValues.begin(); std::map >::const_iterator end = mNestedValues.end(); for (; current != end; ++current) diff --git a/apps/opencs/view/render/pathgrid.cpp b/apps/opencs/view/render/pathgrid.cpp index d184b4938..d8acfe2e1 100644 --- a/apps/opencs/view/render/pathgrid.cpp +++ b/apps/opencs/view/render/pathgrid.cpp @@ -224,11 +224,7 @@ namespace CSVRender void Pathgrid::applyPoint(CSMWorld::CommandMacro& commands, const osg::Vec3d& worldPos) { - CSMWorld::IdTree* model = dynamic_cast(mData.getTableModel(CSMWorld::UniversalId::Type_Pathgrids)); - if (model == nullptr) - { - throw std::logic_error("CSVRender::Pathgrid: Attempt to add nested values to the non-nested model"); - } + CSMWorld::IdTree* model = &dynamic_cast(*mData.getTableModel(CSMWorld::UniversalId::Type_Pathgrids)); const CSMWorld::Pathgrid* source = getPathgridSource(); if (source) @@ -360,11 +356,7 @@ namespace CSVRender const CSMWorld::Pathgrid* source = getPathgridSource(); if (source) { - CSMWorld::IdTree* model = dynamic_cast(mData.getTableModel(CSMWorld::UniversalId::Type_Pathgrids)); - if (model == nullptr) - { - throw std::logic_error("CSVRender::Pathgrid: Attempt to add nested values to the non-nested model"); - } + CSMWorld::IdTree* model = &dynamic_cast(*mData.getTableModel(CSMWorld::UniversalId::Type_Pathgrids)); // Want to remove nodes from end of list first std::sort(mSelected.begin(), mSelected.end(), std::greater()); @@ -464,12 +456,7 @@ namespace CSVRender } } - CSMWorld::IdTree* model = dynamic_cast(mData.getTableModel(CSMWorld::UniversalId::Type_Pathgrids)); - if (model == nullptr) - { - throw std::logic_error("CSVRender::Pathgrid: Attempt to add nested values to the non-nested model"); - } - + CSMWorld::IdTree* model = &dynamic_cast(*mData.getTableModel(CSMWorld::UniversalId::Type_Pathgrids)); int parentColumn = mPathgridCollection.findColumnIndex(CSMWorld::Columns::ColumnId_PathgridEdges); std::set >::iterator row; @@ -642,11 +629,7 @@ namespace CSVRender void Pathgrid::addEdge(CSMWorld::CommandMacro& commands, const CSMWorld::Pathgrid& source, unsigned short node1, unsigned short node2) { - CSMWorld::IdTree* model = dynamic_cast(mData.getTableModel(CSMWorld::UniversalId::Type_Pathgrids)); - if (model == nullptr) - { - throw std::logic_error("CSVRender::Pathgrid: Attempt to add nested values to the non-nested model"); - } + CSMWorld::IdTree* model = &dynamic_cast(*mData.getTableModel(CSMWorld::UniversalId::Type_Pathgrids)); int recordIndex = mPathgridCollection.getIndex(mId); int parentColumn = mPathgridCollection.findColumnIndex(CSMWorld::Columns::ColumnId_PathgridEdges); diff --git a/apps/opencs/view/world/cellcreator.cpp b/apps/opencs/view/world/cellcreator.cpp index 54e618cd9..5b428a4b3 100644 --- a/apps/opencs/view/world/cellcreator.cpp +++ b/apps/opencs/view/world/cellcreator.cpp @@ -24,11 +24,7 @@ std::string CSVWorld::CellCreator::getId() const void CSVWorld::CellCreator::configureCreateCommand(CSMWorld::CreateCommand& command) const { - CSMWorld::IdTree *model = dynamic_cast(getData().getTableModel(getCollectionId())); - if (model == nullptr) - { - throw std::logic_error("CSVWorld::CellCreator: Attempt to add nested values to the non-nested model"); - } + CSMWorld::IdTree* model = &dynamic_cast(*getData().getTableModel(getCollectionId())); int parentIndex = model->findColumnIndex(CSMWorld::Columns::ColumnId_Cell); int index = model->findNestedColumnIndex(parentIndex, CSMWorld::Columns::ColumnId_Interior); diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index 9f73430f0..eeba29d95 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -555,12 +555,7 @@ void CSVWorld::EditWidget::remake(int row) if (mTable->hasChildren(mTable->index(row, i)) && !(flags & CSMWorld::ColumnBase::Flag_Dialogue_List)) { - CSMWorld::IdTree *innerTable = dynamic_cast(mTable); - if (innerTable == nullptr) - { - throw std::logic_error("CSVWorld::EditWidget: Attempt to add nested values to the non-nested model"); - } - + CSMWorld::IdTree* innerTable = &dynamic_cast(*mTable); mNestedModels.push_back(new CSMWorld::NestedTableProxyModel (mTable->index(row, i), display, innerTable)); int idColumn = mTable->findColumnIndex (CSMWorld::Columns::ColumnId_Id); From 68c170f065b04392bdf68bf9acbfe1ae7c47dd67 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 15 Nov 2018 18:10:19 +0400 Subject: [PATCH 49/53] Refactor tools to do not use boost exceptions and exit() command --- apps/esmtool/esmtool.cpp | 9 ++------- apps/niftest/niftest.cpp | 23 ++++++++--------------- 2 files changed, 10 insertions(+), 22 deletions(-) diff --git a/apps/esmtool/esmtool.cpp b/apps/esmtool/esmtool.cpp index d4569000b..699ff855c 100644 --- a/apps/esmtool/esmtool.cpp +++ b/apps/esmtool/esmtool.cpp @@ -123,14 +123,9 @@ bool parseOptions (int argc, char** argv, Arguments &info) bpo::store(valid_opts, variables); } - catch(boost::program_options::unknown_option & x) + catch(std::exception &e) { - std::cerr << "ERROR: " << x.what() << std::endl; - return false; - } - catch(boost::program_options::invalid_command_line_syntax & x) - { - std::cerr << "ERROR: " << x.what() << std::endl; + std::cout << "ERROR parsing arguments: " << e.what() << std::endl; return false; } diff --git a/apps/niftest/niftest.cpp b/apps/niftest/niftest.cpp index 6c0c74597..e3f2b89f8 100644 --- a/apps/niftest/niftest.cpp +++ b/apps/niftest/niftest.cpp @@ -10,7 +10,6 @@ #include #include -#include #include #include @@ -81,7 +80,7 @@ void readVFS(VFS::Archive* anArchive,std::string archivePath = "") } } -std::vector parseOptions (int argc, char** argv) +bool parseOptions (int argc, char** argv, std::vector& files) { bpo::options_description desc("Ensure that OpenMW can use the provided NIF and BSA files\n\n" "Usages:\n" @@ -108,37 +107,31 @@ std::vector parseOptions (int argc, char** argv) { std::cout << "ERROR parsing arguments: " << e.what() << "\n\n" << desc << std::endl; - exit(1); + return false; } bpo::notify(variables); if (variables.count ("help")) { std::cout << desc << std::endl; - exit(1); + return false; } if (variables.count("input-file")) { - return variables["input-file"].as< std::vector >(); + files = variables["input-file"].as< std::vector >(); + return true; } std::cout << "No input files or directories specified!" << std::endl; std::cout << desc << std::endl; - exit(1); + return false; } int main(int argc, char **argv) { std::vector files; - try - { - files = parseOptions (argc, argv); - } - catch( boost::exception &e ) - { - std::cout << "ERROR parsing arguments: " << boost::diagnostic_information(e) << std::endl; - exit(1); - } + if(!parseOptions (argc, argv, files)) + return 1; // std::cout << "Reading Files" << std::endl; for(std::vector::const_iterator it=files.begin(); it!=files.end(); ++it) From 53c98bd8e1e914ce265973eac73b7da38b1e348d Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Fri, 16 Nov 2018 10:00:54 +0100 Subject: [PATCH 50/53] added TODO and updated notification email for coverity --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9b3a89da6..ef0a945a5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,12 +27,12 @@ addons: # The other ones from OpenMW ppa libbullet-dev, libopenscenegraph-3.4-dev, libmygui-dev ] - coverity_scan: + coverity_scan: # TODO: currently takes too long, disabled openmw/openmw-cs for now. project: name: "OpenMW/openmw" description: "" branch_pattern: coverity_scan - notification_email: 720642+scrawl@users.noreply.github.com + 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: From 7438e20ee1a8e6771c71dfa5beb0901f89ea12a8 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 17 Nov 2018 22:19:52 +0400 Subject: [PATCH 51/53] Fix shield displaying on inventory avatar (bug #4720) --- CHANGELOG.md | 1 + apps/openmw/mwrender/characterpreview.cpp | 27 +++++++++++++++++------ 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2bb06874b..cbea8d324 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ Bug #4701: PrisonMarker record is not hardcoded like other markers Bug #4714: Crash upon game load in the repair menu while the "Your repair failed!" message is active Bug #4715: "Cannot get class of an empty object" exception after pressing ESC in the dialogue mode + Bug #4720: Inventory avatar has shield with two-handed weapon during [un]equipping animation Feature #2229: Improve pathfinding AI Feature #3442: Default values for fallbacks from ini file Feature #4673: Weapon sheathing diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index 075d2e03c..ce6a27389 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -305,24 +305,37 @@ namespace MWRender type == ESM::Weapon::LongBladeOneHand || type == ESM::Weapon::BluntOneHand || type == ESM::Weapon::AxeOneHand || - type == ESM::Weapon::MarksmanThrown || - type == ESM::Weapon::MarksmanCrossbow || - type == ESM::Weapon::MarksmanBow) + type == ESM::Weapon::MarksmanThrown) + { groupname = "inventoryweapononehand"; + } + else if(type == ESM::Weapon::MarksmanCrossbow || + type == ESM::Weapon::MarksmanBow) + { + groupname = "inventoryweapononehand"; + showCarriedLeft = false; + } else if(type == ESM::Weapon::LongBladeTwoHand || type == ESM::Weapon::BluntTwoClose || type == ESM::Weapon::AxeTwoHand) + { groupname = "inventoryweapontwohand"; + showCarriedLeft = false; + } else if(type == ESM::Weapon::BluntTwoWide || type == ESM::Weapon::SpearTwoWide) + { groupname = "inventoryweapontwowide"; + showCarriedLeft = false; + } else + { groupname = "inventoryhandtohand"; - - showCarriedLeft = (iter->getClass().canBeEquipped(*iter, mCharacter).first != 2); + showCarriedLeft = false; + } } - else - groupname = "inventoryhandtohand"; + else + groupname = "inventoryhandtohand"; } mAnimation->showCarriedLeft(showCarriedLeft); From e8b3ae87068cb2cd8aeab623e7391b56b97ae0d7 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 20 Nov 2018 21:53:27 +0400 Subject: [PATCH 52/53] Improve ResetActors command (bug #4723) --- CHANGELOG.md | 1 + apps/openmw/mwworld/cellstore.cpp | 11 +++++++++++ apps/openmw/mwworld/cellstore.hpp | 2 ++ apps/openmw/mwworld/worldimp.cpp | 5 ++++- 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cbea8d324..1f44e83b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ Bug #4714: Crash upon game load in the repair menu while the "Your repair failed!" message is active Bug #4715: "Cannot get class of an empty object" exception after pressing ESC in the dialogue mode Bug #4720: Inventory avatar has shield with two-handed weapon during [un]equipping animation + Bug #4723: ResetActors command works incorrectly Feature #2229: Improve pathfinding AI Feature #3442: Default values for fallbacks from ini file Feature #4673: Weapon sheathing diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 520630bac..e291951ae 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -330,6 +330,17 @@ namespace MWWorld visitor.merge(); } + bool CellStore::movedHere(const MWWorld::Ptr& ptr) const + { + if (ptr.isEmpty()) + return false; + + if (mMovedHere.find(ptr.getBase()) != mMovedHere.end()) + return true; + + return false; + } + CellStore::CellStore (const ESM::Cell *cell, const MWWorld::ESMStore& esmStore, std::vector& readerList) : mStore(esmStore), mReader(readerList), mCell (cell), mState (State_Unloaded), mHasState (false), mLastRespawn(0,0) { diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index 49bc0b363..ce8bb3da4 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -233,6 +233,8 @@ namespace MWWorld float getWaterLevel() const; + bool movedHere(const MWWorld::Ptr& ptr) const; + void setWaterLevel (float level); void setFog (ESM::FogState* fog); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 733b961a7..6fd11c2af 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -3698,10 +3698,13 @@ namespace MWWorld { if (ptr.getClass().isActor() && ptr.getCellRef().hasContentFile()) { + if (ptr.getCell()->movedHere(ptr)) + return true; + const ESM::Position& origPos = ptr.getCellRef().getPosition(); MWBase::Environment::get().getWorld()->moveObject(ptr, origPos.pos[0], origPos.pos[1], origPos.pos[2]); MWBase::Environment::get().getWorld()->rotateObject(ptr, origPos.rot[0], origPos.rot[1], origPos.rot[2]); - ptr.getClass().adjustPosition(ptr, false); + ptr.getClass().adjustPosition(ptr, true); } return true; } From 880ec3ce7b3de9c77405fa3bf654e7f174df5b41 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 24 Nov 2018 14:57:41 +0400 Subject: [PATCH 53/53] Fix visible sheaths for invisible actors --- apps/openmw/mwrender/actoranimation.cpp | 4 ---- apps/openmw/mwrender/npcanimation.cpp | 4 ++++ 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwrender/actoranimation.cpp b/apps/openmw/mwrender/actoranimation.cpp index e4cf28394..9f2f961ec 100644 --- a/apps/openmw/mwrender/actoranimation.cpp +++ b/apps/openmw/mwrender/actoranimation.cpp @@ -355,10 +355,6 @@ void ActorAnimation::updateQuiver() addGlow(arrow, glowColor); } } - - // recreate shaders for invisible actors, otherwise new nodes will be visible - if (mAlpha != 1.f) - mResourceSystem->getSceneManager()->recreateShaders(mObjectRoot); } void ActorAnimation::itemAdded(const MWWorld::ConstPtr& item, int /*count*/) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index f21f24884..e088728a0 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -925,6 +925,10 @@ void NpcAnimation::showWeapons(bool showWeapon) updateHolsteredWeapon(!mShowWeapons); updateQuiver(); + + // Recreate shaders for invisible actors, otherwise sheath nodes will be visible + if (mAlpha != 1.f && mWeaponSheathing) + mResourceSystem->getSceneManager()->recreateShaders(mObjectRoot); } void NpcAnimation::showCarriedLeft(bool show)