From 89055f1c6cded33e0ec63d4e0ff983775ce2da22 Mon Sep 17 00:00:00 2001 From: capostrophic Date: Fri, 10 Jan 2020 15:49:57 +0300 Subject: [PATCH 01/99] Generate WNAM upon saving terrain --- apps/opencs/model/world/columnimp.cpp | 47 -------------------- apps/opencs/model/world/columnimp.hpp | 14 +----- apps/opencs/model/world/data.cpp | 1 - apps/opencs/view/render/terrainshapemode.cpp | 34 -------------- apps/opencs/view/render/terrainshapemode.hpp | 4 -- components/esm/loadland.cpp | 20 ++++++++- components/esm/loadland.hpp | 2 + 7 files changed, 21 insertions(+), 101 deletions(-) diff --git a/apps/opencs/model/world/columnimp.cpp b/apps/opencs/model/world/columnimp.cpp index 948174b30..bec5008d3 100644 --- a/apps/opencs/model/world/columnimp.cpp +++ b/apps/opencs/model/world/columnimp.cpp @@ -76,53 +76,6 @@ namespace CSMWorld return false; } - /* LandMapLodColumn */ - LandMapLodColumn::LandMapLodColumn() - : Column(Columns::ColumnId_LandMapLodIndex, ColumnBase::Display_String, 0) - { - } - - QVariant LandMapLodColumn::get(const Record& record) const - { - const int Size = Land::LAND_GLOBAL_MAP_LOD_SIZE; - const Land& land = record.get(); - - DataType values(Size, 0); - - if (land.mDataTypes & Land::DATA_WNAM) - { - for (int i = 0; i < Size; ++i) - values[i] = land.mWnam[i]; - } - - QVariant variant; - variant.setValue(values); - return variant; - } - - void LandMapLodColumn::set(Record& record, const QVariant& data) - { - DataType values = data.value(); - - if (values.size() != Land::LAND_GLOBAL_MAP_LOD_SIZE) - throw std::runtime_error("invalid land map LOD data"); - - Land copy = record.get(); - copy.add(Land::DATA_WNAM); - - for (int i = 0; i < values.size(); ++i) - { - copy.mWnam[i] = values[i]; - } - - record.setModified(copy); - } - - bool LandMapLodColumn::isEditable() const - { - return true; - } - /* LandNormalsColumn */ LandNormalsColumn::LandNormalsColumn() : Column(Columns::ColumnId_LandNormalsIndex, ColumnBase::Display_String, 0) diff --git a/apps/opencs/model/world/columnimp.hpp b/apps/opencs/model/world/columnimp.hpp index 87690423f..25f09cb0f 100644 --- a/apps/opencs/model/world/columnimp.hpp +++ b/apps/opencs/model/world/columnimp.hpp @@ -2461,17 +2461,6 @@ namespace CSMWorld bool isEditable() const override; }; - struct LandMapLodColumn : public Column - { - using DataType = QVector; - - LandMapLodColumn(); - - QVariant get(const Record& record) const override; - void set(Record& record, const QVariant& data) override; - bool isEditable() const override; - }; - struct LandNormalsColumn : public Column { using DataType = QVector; @@ -2529,8 +2518,7 @@ namespace CSMWorld } // This is required to access the type as a QVariant. -Q_DECLARE_METATYPE(CSMWorld::LandMapLodColumn::DataType) -//Q_DECLARE_METATYPE(CSMWorld::LandNormalsColumn::DataType) // Same as LandMapLodColumn::DataType +Q_DECLARE_METATYPE(CSMWorld::LandNormalsColumn::DataType) Q_DECLARE_METATYPE(CSMWorld::LandHeightsColumn::DataType) Q_DECLARE_METATYPE(CSMWorld::LandColoursColumn::DataType) Q_DECLARE_METATYPE(CSMWorld::LandTexturesColumn::DataType) diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index bc630806a..0753569bf 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -443,7 +443,6 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, bool fsStrict, const Files::Pat mLand.addColumn (new RecordStateColumn); mLand.addColumn (new FixedRecordTypeColumn(UniversalId::Type_Land)); mLand.addColumn (new LandPluginIndexColumn); - mLand.addColumn (new LandMapLodColumn); mLand.addColumn (new LandNormalsColumn); mLand.addColumn (new LandHeightsColumn); mLand.addColumn (new LandColoursColumn); diff --git a/apps/opencs/view/render/terrainshapemode.cpp b/apps/opencs/view/render/terrainshapemode.cpp index 77a178c2a..5423cd3dd 100644 --- a/apps/opencs/view/render/terrainshapemode.cpp +++ b/apps/opencs/view/render/terrainshapemode.cpp @@ -273,7 +273,6 @@ void CSVRender::TerrainShapeMode::applyTerrainEditChanges() *document.getData().getTableModel (CSMWorld::UniversalId::Type_LandTextures)); int landshapeColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandHeightsIndex); - int landMapLodColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandMapLodIndex); int landnormalsColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandNormalsIndex); QUndoStack& undoStack = document.getUndoStack(); @@ -287,9 +286,7 @@ void CSVRender::TerrainShapeMode::applyTerrainEditChanges() std::string cellId = CSMWorld::CellCoordinates::generateId(cellCoordinates.getX(), cellCoordinates.getY()); undoStack.push (new CSMWorld::TouchLandCommand(landTable, ltexTable, cellId)); const CSMWorld::LandHeightsColumn::DataType landShapePointer = landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value(); - const CSMWorld::LandMapLodColumn::DataType landMapLodPointer = landTable.data(landTable.getModelIndex(cellId, landMapLodColumn)).value(); CSMWorld::LandHeightsColumn::DataType landShapeNew(landShapePointer); - CSMWorld::LandMapLodColumn::DataType mapLodShapeNew(landMapLodPointer); CSVRender::PagedWorldspaceWidget *paged = dynamic_cast (&getWorldspaceWidget()); // Generate land height record @@ -304,26 +301,7 @@ void CSVRender::TerrainShapeMode::applyTerrainEditChanges() } } - // Generate WNAM record - int sqrtLandGlobalMapLodSize = sqrt(ESM::Land::LAND_GLOBAL_MAP_LOD_SIZE); - for(int i = 0; i < sqrtLandGlobalMapLodSize; ++i) - { - for(int j = 0; j < sqrtLandGlobalMapLodSize; ++j) - { - int col = (static_cast(j) / sqrtLandGlobalMapLodSize) * (ESM::Land::LAND_SIZE - 1); - int row = (static_cast(i) / sqrtLandGlobalMapLodSize) * (ESM::Land::LAND_SIZE - 1); - signed char lodHeight = 0; - float floatLodHeight = 0; - if (landShapeNew[col * ESM::Land::LAND_SIZE + row] > 0) floatLodHeight = landShapeNew[col * ESM::Land::LAND_SIZE + row] / 128; - if (landShapeNew[col * ESM::Land::LAND_SIZE + row] <= 0) floatLodHeight = landShapeNew[col * ESM::Land::LAND_SIZE + row] / 16; - if (floatLodHeight > std::numeric_limits::max()) lodHeight = std::numeric_limits::max(); - else if (floatLodHeight < std::numeric_limits::min()) lodHeight = std::numeric_limits::min(); - else lodHeight = static_cast(floatLodHeight); - mapLodShapeNew[j * sqrtLandGlobalMapLodSize + i] = lodHeight; - } - } pushEditToCommand(landShapeNew, document, landTable, cellId); - pushLodToCommand(mapLodShapeNew, document, landTable, cellId); } for(CSMWorld::CellCoordinates cellCoordinates: mAlteredCells) @@ -1136,18 +1114,6 @@ void CSVRender::TerrainShapeMode::pushNormalsEditToCommand(const CSMWorld::LandN undoStack.push (new CSMWorld::ModifyCommand(landTable, index, changedLand)); } -void CSVRender::TerrainShapeMode::pushLodToCommand(const CSMWorld::LandMapLodColumn::DataType& newLandMapLod, CSMDoc::Document& document, - CSMWorld::IdTable& landTable, const std::string& cellId) -{ - QVariant changedLod; - changedLod.setValue(newLandMapLod); - - QModelIndex index(landTable.getModelIndex (cellId, landTable.findColumnIndex (CSMWorld::Columns::ColumnId_LandMapLodIndex))); - - QUndoStack& undoStack = document.getUndoStack(); - undoStack.push (new CSMWorld::ModifyCommand(landTable, index, changedLod)); -} - bool CSVRender::TerrainShapeMode::noCell(const std::string& cellId) { CSMDoc::Document& document = getWorldspaceWidget().getDocument(); diff --git a/apps/opencs/view/render/terrainshapemode.hpp b/apps/opencs/view/render/terrainshapemode.hpp index ce2ea5465..68f2fbf9d 100644 --- a/apps/opencs/view/render/terrainshapemode.hpp +++ b/apps/opencs/view/render/terrainshapemode.hpp @@ -148,10 +148,6 @@ namespace CSVRender void pushNormalsEditToCommand(const CSMWorld::LandNormalsColumn::DataType& newLandGrid, CSMDoc::Document& document, CSMWorld::IdTable& landTable, const std::string& cellId); - /// Generate new land map LOD - void pushLodToCommand(const CSMWorld::LandMapLodColumn::DataType& newLandMapLod, CSMDoc::Document& document, - CSMWorld::IdTable& landTable, const std::string& cellId); - bool noCell(const std::string& cellId); bool noLand(const std::string& cellId); diff --git a/components/esm/loadland.cpp b/components/esm/loadland.cpp index c52483681..0d9e68eb4 100644 --- a/components/esm/loadland.cpp +++ b/components/esm/loadland.cpp @@ -158,8 +158,24 @@ namespace ESM } } - if (mDataTypes & Land::DATA_WNAM) { - esm.writeHNT("WNAM", mWnam, 81); + if (mDataTypes & Land::DATA_WNAM) + { + // Generate WNAM record + signed char wnam[LAND_GLOBAL_MAP_LOD_SIZE]; + float max = std::numeric_limits::max(); + float min = std::numeric_limits::min(); + float vertMult = static_cast(ESM::Land::LAND_SIZE - 1) / LAND_GLOBAL_MAP_LOD_SIZE_SQRT; + for (int row = 0; row < LAND_GLOBAL_MAP_LOD_SIZE_SQRT; ++row) + { + for (int col = 0; col < LAND_GLOBAL_MAP_LOD_SIZE_SQRT; ++col) + { + float height = mLandData->mHeights[int(row * vertMult) * ESM::Land::LAND_SIZE + int(col * vertMult)]; + height /= height > 0 ? 128.f : 16.f; + height = std::min(max, std::max(min, height)); + wnam[row * LAND_GLOBAL_MAP_LOD_SIZE_SQRT + col] = static_cast(height); + } + } + esm.writeHNT("WNAM", wnam, 81); } if (mLandData) diff --git a/components/esm/loadland.hpp b/components/esm/loadland.hpp index b4b66c601..e5faf4b31 100644 --- a/components/esm/loadland.hpp +++ b/components/esm/loadland.hpp @@ -70,6 +70,8 @@ struct Land static const int LAND_GLOBAL_MAP_LOD_SIZE = 81; + static const int LAND_GLOBAL_MAP_LOD_SIZE_SQRT = 9; + #pragma pack(push,1) struct VHGT { From 3cdff837c872c68db80ae4e0962de7c5807affc4 Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 26 Jan 2020 20:17:33 +0100 Subject: [PATCH 02/99] Use default ccache settings --- .travis.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index c93795241..f490b34d6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,10 +9,7 @@ env: # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created # via the "travis encrypt" command using the project repo's public key - secure: "jybGzAdUbqt9vWR/GEnRd96BgAi/7Zd1+2HK68j/i/8+/1YH2XxLOy4Jv/DUBhBlJIkxs/Xv8dRcUlFOclZDHX1d/9Qnsqd3oUVkD7k1y7cTOWy9TBQaE/v/kZo3LpzA3xPwwthrb0BvqIbOfIELi5fS5s8ba85WFRg3AX70wWE=" -cache: - ccache: true - directories: - - ${HOME}/.ccache +cache: ccache addons: apt: sources: From 0147b0e398bbeaa406ba86c81f74813c1ebf9abc Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 26 Jan 2020 20:44:31 +0100 Subject: [PATCH 03/99] Print ccache stats --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index f490b34d6..89b5317a2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -86,6 +86,7 @@ before_install: - if [ "${TRAVIS_OS_NAME}" = "linux" ]; then eval "${MATRIX_EVAL}"; fi - if [ "${COVERITY_SCAN_BRANCH}" != 1 ]; then ./CI/before_install.${TRAVIS_OS_NAME}.sh; fi before_script: + - ccache -z - if [ "${COVERITY_SCAN_BRANCH}" != 1 ]; then ./CI/before_script.${TRAVIS_OS_NAME}.sh; fi script: - cd ./build @@ -95,6 +96,7 @@ script: - 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}" + - ccache -s deploy: provider: script script: ./CI/deploy.osx.sh From c690f2d43dc181890490dbf208368ade48d8277e Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 26 Jan 2020 21:46:15 +0100 Subject: [PATCH 04/99] Disable coverage for travis CI --- CI/before_script.linux.sh | 3 --- 1 file changed, 3 deletions(-) diff --git a/CI/before_script.linux.sh b/CI/before_script.linux.sh index 250955e49..c9b5b55bf 100755 --- a/CI/before_script.linux.sh +++ b/CI/before_script.linux.sh @@ -7,9 +7,7 @@ GOOGLETEST_DIR="$(pwd)/googletest/build" mkdir build cd build -export CODE_COVERAGE=1 -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 @@ -28,7 +26,6 @@ ${ANALYZE} cmake \ -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 \ -DUSE_SYSTEM_TINYXML=1 \ -DDESIRED_QT_VERSION=5 \ From 032513ae18235294ba1e099921d90b4d66d1d057 Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Tue, 28 Jan 2020 11:33:44 +0200 Subject: [PATCH 05/99] Convert radians to degrees --- apps/opencs/view/render/object.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/view/render/object.cpp b/apps/opencs/view/render/object.cpp index fe4adfcb6..484f1a48a 100644 --- a/apps/opencs/view/render/object.cpp +++ b/apps/opencs/view/render/object.cpp @@ -705,7 +705,7 @@ void CSVRender::Object::apply (CSMWorld::CommandMacro& commands) CSMWorld::Columns::ColumnId_PositionXRot+i)); commands.push (new CSMWorld::ModifyCommand (*model, - model->index (recordIndex, column), mPositionOverride.rot[i])); + model->index (recordIndex, column), osg::RadiansToDegrees(mPositionOverride.rot[i]))); } } From 8e0b638145843794eb8aa2ba1698b3a718d3c5e1 Mon Sep 17 00:00:00 2001 From: elsid Date: Tue, 28 Jan 2020 20:29:52 +0100 Subject: [PATCH 06/99] Fix start position for AiWander random point selection --- apps/openmw/mwmechanics/aiwander.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index b20b1cb97..120d6ca09 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -330,7 +330,7 @@ namespace MWMechanics if (!isWaterCreature && !isFlyingCreature) { // findRandomPointAroundCircle uses wanderDistance as limit for random and not as exact distance - if (const auto destination = navigator->findRandomPointAroundCircle(halfExtents, currentPosition, wanderDistance, navigatorFlags)) + if (const auto destination = navigator->findRandomPointAroundCircle(halfExtents, mInitialActorPosition, wanderDistance, navigatorFlags)) mDestination = *destination; else mDestination = getRandomPointAround(mInitialActorPosition, wanderRadius); From f7caeefddbb84c98396e31fc8f703ab29eb86eae Mon Sep 17 00:00:00 2001 From: elsid Date: Tue, 28 Jan 2020 22:22:24 +0100 Subject: [PATCH 07/99] Fallback to straight path when navmesh and pathgrind are not available --- apps/openmw/mwmechanics/pathfinding.cpp | 9 ++++++--- apps/openmw/mwmechanics/pathfinding.hpp | 2 +- components/detournavigator/navigator.hpp | 4 ++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index f7e7c277c..f8736c062 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -314,7 +314,9 @@ namespace MWMechanics { mPath.clear(); - buildPathByNavigatorImpl(actor, startPoint, endPoint, halfExtents, flags, std::back_inserter(mPath)); + // If it's not possible to build path over navmesh due to disabled navmesh generation fallback to straight path + if (!buildPathByNavigatorImpl(actor, startPoint, endPoint, halfExtents, flags, std::back_inserter(mPath))) + mPath.push_back(endPoint); mConstructed = true; } @@ -335,7 +337,7 @@ namespace MWMechanics mConstructed = true; } - void PathFinder::buildPathByNavigatorImpl(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, + bool PathFinder::buildPathByNavigatorImpl(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, const osg::Vec3f& endPoint, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags, std::back_insert_iterator> out) { @@ -344,7 +346,7 @@ namespace MWMechanics const auto world = MWBase::Environment::get().getWorld(); const auto stepSize = getPathStepSize(actor); const auto navigator = world->getNavigator(); - navigator->findPath(halfExtents, stepSize, startPoint, endPoint, flags, out); + return navigator->findPath(halfExtents, stepSize, startPoint, endPoint, flags, out).is_initialized(); } catch (const DetourNavigator::NavigatorException& exception) { @@ -352,6 +354,7 @@ namespace MWMechanics << "\" for \"" << actor.getClass().getName(actor) << "\" (" << actor.getBase() << ") from " << startPoint << " to " << endPoint << " with flags (" << DetourNavigator::WriteFlags {flags} << ")"; + return true; } } diff --git a/apps/openmw/mwmechanics/pathfinding.hpp b/apps/openmw/mwmechanics/pathfinding.hpp index b413810f4..06b4aa10d 100644 --- a/apps/openmw/mwmechanics/pathfinding.hpp +++ b/apps/openmw/mwmechanics/pathfinding.hpp @@ -201,7 +201,7 @@ namespace MWMechanics void buildPathByPathgridImpl(const osg::Vec3f& startPoint, const osg::Vec3f& endPoint, const PathgridGraph& pathgridGraph, std::back_insert_iterator> out); - void buildPathByNavigatorImpl(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, + bool buildPathByNavigatorImpl(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint, const osg::Vec3f& endPoint, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags, std::back_insert_iterator> out); }; diff --git a/components/detournavigator/navigator.hpp b/components/detournavigator/navigator.hpp index 74daab5d7..1b7af7e8b 100644 --- a/components/detournavigator/navigator.hpp +++ b/components/detournavigator/navigator.hpp @@ -159,7 +159,7 @@ namespace DetourNavigator * Equal to out if no path is found. */ template - OutputIterator findPath(const osg::Vec3f& agentHalfExtents, const float stepSize, const osg::Vec3f& start, + boost::optional findPath(const osg::Vec3f& agentHalfExtents, const float stepSize, const osg::Vec3f& start, const osg::Vec3f& end, const Flags includeFlags, OutputIterator out) const { static_assert( @@ -171,7 +171,7 @@ namespace DetourNavigator ); const auto navMesh = getNavMesh(agentHalfExtents); if (!navMesh) - return out; + return {}; const auto settings = getSettings(); return findSmoothPath(navMesh->lockConst()->getImpl(), toNavMeshCoordinates(settings, agentHalfExtents), toNavMeshCoordinates(settings, stepSize), toNavMeshCoordinates(settings, start), From 1522883fdd009c4c6aeeb997fa478dc923193b57 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 29 Jan 2020 16:31:09 +0400 Subject: [PATCH 08/99] Do not interrupt greeting update when Hello = 0 or actor starts to swim (bug #5248) --- apps/openmw/mwmechanics/actors.cpp | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index e2bf08d82..c264d63df 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -452,22 +452,12 @@ namespace MWMechanics return; CreatureStats &stats = actor.getClass().getCreatureStats(actor); - int hello = stats.getAiSetting(CreatureStats::AI_Hello).getModified(); - if (hello == 0) - return; - - if (MWBase::Environment::get().getWorld()->isSwimming(actor)) - return; - - MWWorld::Ptr player = getPlayer(); - osg::Vec3f playerPos(player.getRefData().getPosition().asVec3()); - osg::Vec3f actorPos(actor.getRefData().getPosition().asVec3()); - osg::Vec3f dir = playerPos - actorPos; - const MWMechanics::AiSequence& seq = stats.getAiSequence(); int packageId = seq.getTypeId(); - if (seq.isInCombat() || (packageId != AiPackage::TypeIdWander && packageId != AiPackage::TypeIdTravel && packageId != -1)) + if (seq.isInCombat() || + MWBase::Environment::get().getWorld()->isSwimming(actor) || + (packageId != AiPackage::TypeIdWander && packageId != AiPackage::TypeIdTravel && packageId != -1)) { stats.setTurningToPlayer(false); stats.setGreetingTimer(0); @@ -475,6 +465,11 @@ namespace MWMechanics return; } + MWWorld::Ptr player = getPlayer(); + osg::Vec3f playerPos(player.getRefData().getPosition().asVec3()); + osg::Vec3f actorPos(actor.getRefData().getPosition().asVec3()); + osg::Vec3f dir = playerPos - actorPos; + if (stats.isTurningToPlayer()) { // Reduce the turning animation glitch by using a *HUGE* value of @@ -492,11 +487,10 @@ namespace MWMechanics return; // Play a random voice greeting if the player gets too close - float helloDistance = static_cast(hello); static int iGreetDistanceMultiplier = MWBase::Environment::get().getWorld()->getStore() .get().find("iGreetDistanceMultiplier")->mValue.getInteger(); - helloDistance *= iGreetDistanceMultiplier; + float helloDistance = static_cast(stats.getAiSetting(CreatureStats::AI_Hello).getModified() * iGreetDistanceMultiplier); int greetingTimer = stats.getGreetingTimer(); GreetingState greetingState = stats.getGreetingState(); From dcafe719a506954ccc01ddb414d48c4f0992ee2c Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Fri, 31 Jan 2020 00:51:35 +0300 Subject: [PATCH 09/99] Don't apply 1.6 magic value to NiGravity decay factor (#5266) --- CHANGELOG.md | 1 + components/nifosg/particle.cpp | 10 ++++------ 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 11145e61b..e04a1b37c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -195,6 +195,7 @@ Bug #5249: Wandering NPCs start walking too soon after they hello Bug #5250: Creatures display shield ground mesh instead of shield body part Bug #5255: "GetTarget, player" doesn't return 1 during NPC hello + Bug #5266: Incorrect NiGravity decay implementation Feature #1774: Handle AvoidNode Feature #2229: Improve pathfinding AI Feature #3025: Analogue gamepad movement controls diff --git a/components/nifosg/particle.cpp b/components/nifosg/particle.cpp index c1ccede1a..63019fe7f 100644 --- a/components/nifosg/particle.cpp +++ b/components/nifosg/particle.cpp @@ -203,12 +203,12 @@ void GravityAffector::beginOperate(osgParticle::Program* program) void GravityAffector::operate(osgParticle::Particle *particle, double dt) { - const float magic = 1.6f; + // Reverse-engineered value. + float decayFactor = 1.6f; switch (mType) { case Type_Wind: { - float decayFactor = 1.f; if (mDecay != 0.f) { osg::Plane gravityPlane(mCachedWorldDirection, mCachedWorldPosition); @@ -216,21 +216,19 @@ void GravityAffector::operate(osgParticle::Particle *particle, double dt) decayFactor = std::exp(-1.f * mDecay * distance); } - particle->addVelocity(mCachedWorldDirection * mForce * dt * decayFactor * magic); + particle->addVelocity(mCachedWorldDirection * mForce * dt * decayFactor); break; } case Type_Point: { osg::Vec3f diff = mCachedWorldPosition - particle->getPosition(); - - float decayFactor = 1.f; if (mDecay != 0.f) decayFactor = std::exp(-1.f * mDecay * diff.length()); diff.normalize(); - particle->addVelocity(diff * mForce * dt * decayFactor * magic); + particle->addVelocity(diff * mForce * dt * decayFactor); break; } } From 73722eedf3591f261d038c5969670097fef7469b Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Thu, 30 Jan 2020 23:12:54 +0000 Subject: [PATCH 10/99] Add default font releaseGLObjects workaround for OSG <3.65 This resolves https://gitlab.com/OpenMW/openmw/issues/4704 I've not compiled this or anything and I'm going off my memory from around ten or eleven months ago, so if this fails CI or causes more problems, that's why. The most likely issue is that we'll need to make the context current before releasing the program. --- apps/opencs/view/render/scenewidget.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index 8ae9d8a62..ed232722d 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -100,6 +100,14 @@ RenderWidget::~RenderWidget() try { CompositeViewer::get().removeView(mView); + +#if OSG_VERSION_LESS_THAN(3,6,5) + // before OSG 3.6.4, the default font was a static object, and if it wasn't attached to the scene when a graphics context was destroyed, it's program wouldn't be released. + // 3.6.4 moved it into the object cache, which meant it usually got released, but not here. + // 3.6.5 improved cleanup with osgViewer::CompositeViewer::removeView so it more reliably released associated state for objects in the object cache. + osg::ref_ptr graphicsContext = mView->getCamera()->getGraphicsContext(); + osgText::Font::getDefaultFont()->releaseGLObjects(graphicsContext->getState()); +#endif } catch(const std::exception& e) { From 234fdfefb71b1a1dab3b99cd98db2d2fc671257f Mon Sep 17 00:00:00 2001 From: Alexei Dobrohotov Date: Fri, 31 Jan 2020 15:11:29 +0300 Subject: [PATCH 11/99] Revert "Don't apply 1.6 magic value to NiGravity decay factor (#5266)" --- CHANGELOG.md | 1 - components/nifosg/particle.cpp | 10 ++++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e04a1b37c..11145e61b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -195,7 +195,6 @@ Bug #5249: Wandering NPCs start walking too soon after they hello Bug #5250: Creatures display shield ground mesh instead of shield body part Bug #5255: "GetTarget, player" doesn't return 1 during NPC hello - Bug #5266: Incorrect NiGravity decay implementation Feature #1774: Handle AvoidNode Feature #2229: Improve pathfinding AI Feature #3025: Analogue gamepad movement controls diff --git a/components/nifosg/particle.cpp b/components/nifosg/particle.cpp index 63019fe7f..c1ccede1a 100644 --- a/components/nifosg/particle.cpp +++ b/components/nifosg/particle.cpp @@ -203,12 +203,12 @@ void GravityAffector::beginOperate(osgParticle::Program* program) void GravityAffector::operate(osgParticle::Particle *particle, double dt) { - // Reverse-engineered value. - float decayFactor = 1.6f; + const float magic = 1.6f; switch (mType) { case Type_Wind: { + float decayFactor = 1.f; if (mDecay != 0.f) { osg::Plane gravityPlane(mCachedWorldDirection, mCachedWorldPosition); @@ -216,19 +216,21 @@ void GravityAffector::operate(osgParticle::Particle *particle, double dt) decayFactor = std::exp(-1.f * mDecay * distance); } - particle->addVelocity(mCachedWorldDirection * mForce * dt * decayFactor); + particle->addVelocity(mCachedWorldDirection * mForce * dt * decayFactor * magic); break; } case Type_Point: { osg::Vec3f diff = mCachedWorldPosition - particle->getPosition(); + + float decayFactor = 1.f; if (mDecay != 0.f) decayFactor = std::exp(-1.f * mDecay * diff.length()); diff.normalize(); - particle->addVelocity(diff * mForce * dt * decayFactor); + particle->addVelocity(diff * mForce * dt * decayFactor * magic); break; } } From 1db51a9e083401dd64ba5414a1517ce8637d731e Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sun, 2 Feb 2020 11:02:19 +0400 Subject: [PATCH 12/99] Re-work wandering outside of initial cell (bug #5261, bug #5262) --- CHANGELOG.md | 1 + apps/openmw/mwmechanics/aitravel.cpp | 3 ++- apps/openmw/mwmechanics/aiwander.cpp | 22 ++++------------------ apps/openmw/mwmechanics/aiwander.hpp | 8 ++------ 4 files changed, 9 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 11145e61b..09f7b83e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -195,6 +195,7 @@ Bug #5249: Wandering NPCs start walking too soon after they hello Bug #5250: Creatures display shield ground mesh instead of shield body part Bug #5255: "GetTarget, player" doesn't return 1 during NPC hello + Bug #5261: Creatures can sometimes become stuck playing idles and never wander again Feature #1774: Handle AvoidNode Feature #2229: Improve pathfinding AI Feature #3025: Analogue gamepad movement controls diff --git a/apps/openmw/mwmechanics/aitravel.cpp b/apps/openmw/mwmechanics/aitravel.cpp index 0f3b22e11..dba70316b 100644 --- a/apps/openmw/mwmechanics/aitravel.cpp +++ b/apps/openmw/mwmechanics/aitravel.cpp @@ -54,8 +54,9 @@ namespace MWMechanics stats.setMovementFlag(CreatureStats::Flag_Run, false); stats.setDrawState(DrawState_Nothing); + // Note: we should cancel internal "return after combat" package, if original location is too far away if (!isWithinMaxRange(targetPos, actorPos)) - return false; + return mHidden; // Unfortunately, with vanilla assets destination is sometimes blocked by other actor. // If we got close to target, check for actors nearby. If they are, finish AI package. diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 120d6ca09..442ba0499 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -145,15 +145,6 @@ namespace MWMechanics // get or create temporary storage AiWanderStorage& storage = state.get(); - const MWWorld::CellStore*& currentCell = storage.mCell; - bool cellChange = currentCell && (actor.getCell() != currentCell); - if(!currentCell || cellChange) - { - stopWalking(actor, storage); - currentCell = actor.getCell(); - storage.mPopulateAvailableNodes = true; - mStoredInitialActorPosition = false; - } mRemainingDuration -= ((duration*MWBase::Environment::get().getWorld()->getTimeScaleFactor()) / 3600); @@ -200,14 +191,13 @@ namespace MWMechanics if (AI_REACTION_TIME <= lastReaction) { lastReaction = 0; - return reactionTimeActions(actor, storage, currentCell, cellChange, pos); + return reactionTimeActions(actor, storage, pos); } else return false; } - bool AiWander::reactionTimeActions(const MWWorld::Ptr& actor, AiWanderStorage& storage, - const MWWorld::CellStore*& currentCell, bool cellChange, ESM::Position& pos) + bool AiWander::reactionTimeActions(const MWWorld::Ptr& actor, AiWanderStorage& storage, ESM::Position& pos) { if (mDistance <= 0) storage.mCanWanderAlongPathGrid = false; @@ -229,7 +219,7 @@ namespace MWMechanics // Initialization to discover & store allowed node points for this actor. if (storage.mPopulateAvailableNodes) { - getAllowedNodes(actor, currentCell->getCell(), storage); + getAllowedNodes(actor, actor.getCell()->getCell(), storage); } if (canActorMoveByZAxis(actor) && mDistance > 0) { @@ -258,10 +248,6 @@ namespace MWMechanics completeManualWalking(actor, storage); } - // Don't try to move if you are in a new cell (ie: positioncell command called) but still play idles. - if(mDistance && cellChange) - mDistance = 0; - AiWanderStorage::WanderState& wanderState = storage.mState; if ((wanderState == AiWanderStorage::Wander_MoveNow) && storage.mCanWanderAlongPathGrid) { @@ -529,7 +515,7 @@ namespace MWMechanics unsigned int randNode = Misc::Rng::rollDice(storage.mAllowedNodes.size()); ESM::Pathgrid::Point dest(storage.mAllowedNodes[randNode]); - ToWorldCoordinates(dest, storage.mCell->getCell()); + ToWorldCoordinates(dest, actor.getCell()->getCell()); // actor position is already in world coordinates const osg::Vec3f start = actorPos.asVec3(); diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index 72f9b3228..38123a970 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -27,8 +27,6 @@ namespace MWMechanics { float mReaction; // update some actions infrequently - const MWWorld::CellStore* mCell; // for detecting cell change - // AiWander states enum WanderState { @@ -60,7 +58,6 @@ namespace MWMechanics AiWanderStorage(): mReaction(0), - mCell(nullptr), mState(Wander_ChooseAction), mIsWanderingManually(false), mCanWanderAlongPathGrid(true), @@ -125,8 +122,7 @@ namespace MWMechanics void onIdleStatePerFrameActions(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage); void onWalkingStatePerFrameActions(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage); void onChooseActionStatePerFrameActions(const MWWorld::Ptr& actor, AiWanderStorage& storage); - bool reactionTimeActions(const MWWorld::Ptr& actor, AiWanderStorage& storage, - const MWWorld::CellStore*& currentCell, bool cellChange, ESM::Position& pos); + bool reactionTimeActions(const MWWorld::Ptr& actor, AiWanderStorage& storage, ESM::Position& pos); bool isPackageCompleted(const MWWorld::Ptr& actor, AiWanderStorage& storage); void wanderNearStart(const MWWorld::Ptr &actor, AiWanderStorage &storage, int wanderDistance); bool destinationIsAtWater(const MWWorld::Ptr &actor, const osg::Vec3f& destination); @@ -141,7 +137,7 @@ namespace MWMechanics bool mRepeat; bool mStoredInitialActorPosition; - osg::Vec3f mInitialActorPosition; + osg::Vec3f mInitialActorPosition; // Note: an original engine does not reset coordinates even when actor changes a cell bool mHasDestination; osg::Vec3f mDestination; From 4a78674583be84d9ba78336553a802e073035c1f Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 2 Feb 2020 15:39:29 +0300 Subject: [PATCH 13/99] Make sure not to pick the ground shield model incorrectly --- apps/openmw/mwrender/npcanimation.cpp | 67 +++++++++++++++------------ 1 file changed, 38 insertions(+), 29 deletions(-) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index b63962b3e..9633e9e1b 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -81,6 +81,34 @@ std::string getVampireHead(const std::string& race, bool female) return "meshes\\" + bodyPart->mModel; } +std::string getShieldBodypartMesh(const std::vector& bodyparts, bool female) +{ + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + const MWWorld::Store &partStore = store.get(); + for (const auto& part : bodyparts) + { + if (part.mPart != ESM::PRT_Shield) + continue; + + std::string bodypartName; + if (female && !part.mFemale.empty()) + bodypartName = part.mFemale; + else if (!part.mMale.empty()) + bodypartName = part.mMale; + + if (!bodypartName.empty()) + { + const ESM::BodyPart *bodypart = partStore.search(bodypartName); + if (bodypart == nullptr || bodypart->mData.mType != ESM::BodyPart::MT_Armor) + return std::string(); + if (!bodypart->mModel.empty()) + return "meshes\\" + bodypart->mModel; + } + } + + return std::string(); +} + } @@ -519,36 +547,9 @@ std::string NpcAnimation::getShieldMesh(MWWorld::ConstPtr shield) const std::string mesh = shield.getClass().getModel(shield); const ESM::Armor *armor = shield.get()->mBase; const std::vector& bodyparts = armor->mParts.mParts; + // Try to recover the body part model, use ground model as a fallback otherwise. if (!bodyparts.empty()) - { - const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - const MWWorld::Store &partStore = store.get(); - - // Try to get shield model from bodyparts first, with ground model as fallback - for (const auto& part : bodyparts) - { - if (part.mPart != ESM::PRT_Shield) - continue; - - std::string bodypartName; - if (!mNpc->isMale() && !part.mFemale.empty()) - bodypartName = part.mFemale; - else if (!part.mMale.empty()) - bodypartName = part.mMale; - - if (!bodypartName.empty()) - { - const ESM::BodyPart *bodypart = partStore.search(bodypartName); - if (bodypart == nullptr || bodypart->mData.mType != ESM::BodyPart::MT_Armor) - return std::string(); - else if (!bodypart->mModel.empty()) - { - mesh = "meshes\\" + bodypart->mModel; - break; - } - } - } - } + mesh = getShieldBodypartMesh(bodyparts, !mNpc->isMale()); if (mesh.empty()) return std::string(); @@ -1002,6 +1003,14 @@ void NpcAnimation::showCarriedLeft(bool show) { osg::Vec4f glowColor = iter->getClass().getEnchantmentColor(*iter); std::string mesh = iter->getClass().getModel(*iter); + // For shields we must try to use the body part model + if (iter->getTypeName() == typeid(ESM::Armor).name()) + { + const ESM::Armor *armor = iter->get()->mBase; + const std::vector& bodyparts = armor->mParts.mParts; + if (!bodyparts.empty()) + mesh = getShieldBodypartMesh(bodyparts, !mNpc->isMale()); + } if (addOrReplaceIndividualPart(ESM::PRT_Shield, MWWorld::InventoryStore::Slot_CarriedLeft, 1, mesh, !iter->getClass().getEnchantment(*iter).empty(), &glowColor)) { From e363d5df21664da160a5eebe23e4fca4bfce8a77 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 2 Feb 2020 17:08:17 +0300 Subject: [PATCH 14/99] Some more minor NIF support fixes --- components/nif/base.hpp | 2 +- components/nif/controlled.cpp | 21 +++++++++------------ components/nif/controlled.hpp | 6 +++--- components/nif/controller.cpp | 9 ++++++++- components/nif/controller.hpp | 3 ++- components/nif/data.cpp | 3 ++- components/nif/niffile.cpp | 2 +- components/nif/niffile.hpp | 22 ++++++---------------- components/nif/nifstream.hpp | 6 ++++++ components/nif/node.hpp | 3 ++- components/nifosg/nifloader.cpp | 7 +------ 11 files changed, 41 insertions(+), 43 deletions(-) diff --git a/components/nif/base.hpp b/components/nif/base.hpp index f67de0221..6e26f525e 100644 --- a/components/nif/base.hpp +++ b/components/nif/base.hpp @@ -59,7 +59,7 @@ public: controller.post(nif); } }; -typedef Named NiSequenceStreamHelper; +using NiSequenceStreamHelper = Named; } // Namespace #endif diff --git a/components/nif/controlled.cpp b/components/nif/controlled.cpp index 9b7c9319b..0b5c32a10 100644 --- a/components/nif/controlled.cpp +++ b/components/nif/controlled.cpp @@ -10,17 +10,18 @@ namespace Nif Named::read(nif); external = nif->getChar() != 0; - if(external) + bool internal = false; + if (external) filename = nif->getString(); else - { - nif->getChar(); // always 1 - data.read(nif); - } + internal = nif->getChar(); - pixel = nif->getInt(); - mipmap = nif->getInt(); - alpha = nif->getInt(); + if (!external && internal) + data.read(nif); + + pixel = nif->getUInt(); + mipmap = nif->getUInt(); + alpha = nif->getUInt(); nif->getChar(); // always 1 } @@ -113,8 +114,4 @@ namespace Nif mCenter = nif->getVector3(); } - - - - } diff --git a/components/nif/controlled.hpp b/components/nif/controlled.hpp index 00ff45eda..8396eae04 100644 --- a/components/nif/controlled.hpp +++ b/components/nif/controlled.hpp @@ -46,13 +46,13 @@ public: 3 - Compressed 4 - Bumpmap 5 - Default */ - int pixel; + unsigned int pixel; /* Mipmap format 0 - no 1 - yes 2 - default */ - int mipmap; + unsigned int mipmap; /* Alpha 0 - none @@ -60,7 +60,7 @@ public: 2 - smooth 3 - default (use material alpha, or multiply material with texture if present) */ - int alpha; + unsigned int alpha; void read(NIFStream *nif); void post(NIFFile *nif); diff --git a/components/nif/controller.cpp b/components/nif/controller.cpp index 6de720b52..9a4c1b065 100644 --- a/components/nif/controller.cpp +++ b/components/nif/controller.cpp @@ -92,6 +92,12 @@ namespace Nif void NiMaterialColorController::read(NIFStream *nif) { Controller::read(nif); + // Two bits that correspond to the controlled material color. + // 00: Ambient + // 01: Diffuse + // 10: Specular + // 11: Emissive + targetColor = (flags >> 4) & 3; data.read(nif); } @@ -189,7 +195,8 @@ namespace Nif { Controller::read(nif); data.read(nif); - nif->getChar(); // always 0 + if (nif->getVersion() >= NIFFile::NIFVersion::VER_MW) + /*bool alwaysActive = */nif->getChar(); // Always 0 } void NiGeomMorpherController::post(NIFFile *nif) diff --git a/components/nif/controller.hpp b/components/nif/controller.hpp index 2fe28fe1d..364eff1cd 100644 --- a/components/nif/controller.hpp +++ b/components/nif/controller.hpp @@ -78,12 +78,13 @@ public: void read(NIFStream *nif); void post(NIFFile *nif); }; -typedef NiParticleSystemController NiBSPArrayController; +using NiBSPArrayController = NiParticleSystemController; class NiMaterialColorController : public Controller { public: NiPosDataPtr data; + unsigned int targetColor; void read(NIFStream *nif); void post(NIFFile *nif); diff --git a/components/nif/data.cpp b/components/nif/data.cpp index 828f5c368..e46c0e84d 100644 --- a/components/nif/data.cpp +++ b/components/nif/data.cpp @@ -225,7 +225,8 @@ void NiSkinData::read(NIFStream *nif) trafo.scale = nif->getFloat(); int boneNum = nif->getInt(); - nif->getInt(); // -1 + if (nif->getVersion() >= NIFFile::NIFVersion::VER_MW && nif->getVersion() <= NIFFile::NIFVersion::VER_GAMEBRYO) + nif->skip(4); // NiSkinPartition link bones.resize(boneNum); for (BoneInfo &bi : bones) diff --git a/components/nif/niffile.cpp b/components/nif/niffile.cpp index bced4d8e2..87bafa6b1 100644 --- a/components/nif/niffile.cpp +++ b/components/nif/niffile.cpp @@ -143,7 +143,7 @@ void NIFFile::parse(Files::IStreamPtr stream) ver = nif.getUInt(); // 4.0.0.0 is an older, practically identical version of the format. // It's not used by Morrowind assets but Morrowind supports it. - if(ver != VER_4_0_0_0 && ver != VER_MW) + if(ver != nif.generateVersion(4,0,0,0) && ver != VER_MW) fail("Unsupported NIF version: " + printVersion(ver)); // Number of records size_t recNum = nif.getInt(); diff --git a/components/nif/niffile.hpp b/components/nif/niffile.hpp index a85e46ea5..e63a7ce73 100644 --- a/components/nif/niffile.hpp +++ b/components/nif/niffile.hpp @@ -75,26 +75,16 @@ class NIFFile final : public File void operator = (NIFFile const &); public: + // For generic versions NIFStream::generateVersion() is used instead enum NIFVersion { - // Feature-relevant - VER_4_1_0_0 = 0x04010000, // 1-byte booleans (previously 4-byte) - VER_5_0_0_1 = 0x05000001, // Optimized record type listings - VER_5_0_0_6 = 0x05000006, // Record groups - VER_10_0_1_8 = 0x0A000108, // The last version without user version - VER_20_1_0_1 = 0x14010001, // String tables - VER_20_2_0_5 = 0x14020005, // Record sizes - // Game-relevant - VER_4_0_0_0 = 0x04000000, // Freedom Force NIFs, supported by Morrowind - VER_MW = 0x04000002, // 4.0.0.2. Morrowind and Freedom Force NIFs - VER_4_2_1_0 = 0x04020100, // Used in Civ4 and Dark Age of Camelot - VER_CI = 0x04020200, // 4.2.2.0. Main Culpa Innata NIF version, also used in Civ4 - VER_ZT2 = 0x0A000100, // 10.0.1.0. Main Zoo Tycoon 2 NIF version, also used in Oblivion and Civ4 - VER_OB_OLD = 0x0A000102, // 10.0.1.2. Main older Oblivion NIF version + VER_MW = 0x04000002, // 4.0.0.2. Main Morrowind NIF version. + VER_CI = 0x04020200, // 4.2.2.0. Main Culpa Innata NIF version, also used in Civ4. + VER_ZT2 = 0x0A000100, // 10.0.1.0. Main Zoo Tycoon 2 NIF version, also used in Oblivion and Civ4. + VER_OB_OLD = 0x0A000102, // 10.0.1.2. Main older Oblivion NIF version. VER_GAMEBRYO = 0x0A010000, // 10.1.0.0. Lots of games use it. The first version that has Gamebryo File Format header. - VER_10_2_0_0 = 0x0A020000, // Lots of games use this version as well. VER_CIV4 = 0x14000004, // 20.0.0.4. Main Civilization IV NIF version. - VER_OB = 0x14000005, // 20.0.0.5. Main Oblivion NIF version + VER_OB = 0x14000005, // 20.0.0.5. Main Oblivion NIF version. VER_BGS = 0x14020007 // 20.2.0.7. Main Fallout 3/4/76/New Vegas and Skyrim/SkyrimSE NIF version. }; enum BethVersion diff --git a/components/nif/nifstream.hpp b/components/nif/nifstream.hpp index 7ecd66084..62e334e01 100644 --- a/components/nif/nifstream.hpp +++ b/components/nif/nifstream.hpp @@ -163,6 +163,12 @@ public: unsigned int getUserVersion(); unsigned int getBethVersion(); + // Convert human-readable version numbers into a number that can be compared. + uint32_t generateVersion(uint8_t major, uint8_t minor, uint8_t patch, uint8_t rev) + { + return (major << 24) + (minor << 16) + (patch << 8) + rev; + } + ///Read in a string of the given length std::string getSizedString(size_t length) { diff --git a/components/nif/node.hpp b/components/nif/node.hpp index 71a0a93ca..de4bfb22e 100644 --- a/components/nif/node.hpp +++ b/components/nif/node.hpp @@ -284,7 +284,8 @@ struct NiLODNode : public NiSwitchNode void read(NIFStream *nif) { NiSwitchNode::read(nif); - lodCenter = nif->getVector3(); + if (nif->getVersion() >= NIFFile::NIFVersion::VER_MW && nif->getVersion() <= NIFFile::NIFVersion::VER_ZT2) + lodCenter = nif->getVector3(); unsigned int numLodLevels = nif->getUInt(); for (unsigned int i=0; i(ctrl.getPtr()); if (matctrl->data.empty()) continue; - // Two bits that correspond to the controlled material color. - // 00: Ambient - // 01: Diffuse - // 10: Specular - // 11: Emissive - MaterialColorController::TargetColor targetColor = static_cast((matctrl->flags >> 4) & 3); + auto targetColor = static_cast(matctrl->targetColor); osg::ref_ptr osgctrl(new MaterialColorController(matctrl->data.getPtr(), targetColor)); setupController(matctrl, osgctrl, animflags); composite->addController(osgctrl); From 0479311c25f421c9266c2d994e8a82ea65ecbcb9 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 2 Feb 2020 18:40:11 +0300 Subject: [PATCH 15/99] Correct declarations of utility methods --- components/nif/nifstream.cpp | 6 +++--- components/nif/nifstream.hpp | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/components/nif/nifstream.cpp b/components/nif/nifstream.cpp index 4ecb0e373..44be4b241 100644 --- a/components/nif/nifstream.cpp +++ b/components/nif/nifstream.cpp @@ -38,7 +38,7 @@ namespace Nif } // Convenience utility functions: get the versions of the currently read file - unsigned int NIFStream::getVersion() { return file->getVersion(); } - unsigned int NIFStream::getUserVersion() { return file->getBethVersion(); } - unsigned int NIFStream::getBethVersion() { return file->getBethVersion(); } + unsigned int NIFStream::getVersion() const { return file->getVersion(); } + unsigned int NIFStream::getUserVersion() const { return file->getBethVersion(); } + unsigned int NIFStream::getBethVersion() const { return file->getBethVersion(); } } diff --git a/components/nif/nifstream.hpp b/components/nif/nifstream.hpp index 62e334e01..c78377448 100644 --- a/components/nif/nifstream.hpp +++ b/components/nif/nifstream.hpp @@ -159,12 +159,12 @@ public: std::string getString(); - unsigned int getVersion(); - unsigned int getUserVersion(); - unsigned int getBethVersion(); + unsigned int getVersion() const; + unsigned int getUserVersion() const; + unsigned int getBethVersion() const; // Convert human-readable version numbers into a number that can be compared. - uint32_t generateVersion(uint8_t major, uint8_t minor, uint8_t patch, uint8_t rev) + static constexpr uint32_t generateVersion(uint8_t major, uint8_t minor, uint8_t patch, uint8_t rev) { return (major << 24) + (minor << 16) + (patch << 8) + rev; } From 216a5d27c6a14a7570291e23dea71dbce7337e4f Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 2 Feb 2020 15:12:53 +0300 Subject: [PATCH 16/99] Avoid working with AMBI subrecord in tools unnecessarily (bug #5269) --- CHANGELOG.md | 1 + apps/esmtool/record.cpp | 16 ++++++++++++---- apps/opencs/model/world/nestedcoladapterimp.cpp | 14 ++++++++++++++ components/esm/loadcell.cpp | 10 +++++++++- components/esm/loadcell.hpp | 12 ++++++++++++ 5 files changed, 48 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 11145e61b..1580be611 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -195,6 +195,7 @@ Bug #5249: Wandering NPCs start walking too soon after they hello Bug #5250: Creatures display shield ground mesh instead of shield body part Bug #5255: "GetTarget, player" doesn't return 1 during NPC hello + Bug #5269: Editor: Cell lighting in resaved cleaned content files is corrupted Feature #1774: Handle AvoidNode Feature #2229: Improve pathfinding AI Feature #3025: Analogue gamepad movement controls diff --git a/apps/esmtool/record.cpp b/apps/esmtool/record.cpp index b7cbd8f65..a3f98792c 100644 --- a/apps/esmtool/record.cpp +++ b/apps/esmtool/record.cpp @@ -534,10 +534,18 @@ void Record::print() if (mData.mData.mFlags & ESM::Cell::Interior && !(mData.mData.mFlags & ESM::Cell::QuasiEx)) { - std::cout << " Ambient Light Color: " << mData.mAmbi.mAmbient << std::endl; - std::cout << " Sunlight Color: " << mData.mAmbi.mSunlight << std::endl; - std::cout << " Fog Color: " << mData.mAmbi.mFog << std::endl; - std::cout << " Fog Density: " << mData.mAmbi.mFogDensity << std::endl; + if (mData.hasAmbient()) + { + // TODO: see if we can change the integer representation to something more sensible + std::cout << " Ambient Light Color: " << mData.mAmbi.mAmbient << std::endl; + std::cout << " Sunlight Color: " << mData.mAmbi.mSunlight << std::endl; + std::cout << " Fog Color: " << mData.mAmbi.mFog << std::endl; + std::cout << " Fog Density: " << mData.mAmbi.mFogDensity << std::endl; + } + else + { + std::cout << " No Ambient Information" << std::endl; + } std::cout << " Water Level: " << mData.mWater << std::endl; } else diff --git a/apps/opencs/model/world/nestedcoladapterimp.cpp b/apps/opencs/model/world/nestedcoladapterimp.cpp index 76338efe5..edf325b49 100644 --- a/apps/opencs/model/world/nestedcoladapterimp.cpp +++ b/apps/opencs/model/world/nestedcoladapterimp.cpp @@ -867,6 +867,8 @@ namespace CSMWorld switch (subColIndex) { case 0: return isInterior; + // While the ambient information is not necessarily valid if the subrecord wasn't loaded, + // the user should still be allowed to edit it case 1: return (isInterior && !behaveLikeExterior) ? cell.mAmbi.mAmbient : QVariant(QVariant::UserType); case 2: return (isInterior && !behaveLikeExterior) ? @@ -912,7 +914,10 @@ namespace CSMWorld case 1: { if (isInterior && !behaveLikeExterior) + { cell.mAmbi.mAmbient = static_cast(value.toInt()); + cell.setHasAmbient(true); + } else return; // return without saving break; @@ -920,7 +925,10 @@ namespace CSMWorld case 2: { if (isInterior && !behaveLikeExterior) + { cell.mAmbi.mSunlight = static_cast(value.toInt()); + cell.setHasAmbient(true); + } else return; // return without saving break; @@ -928,7 +936,10 @@ namespace CSMWorld case 3: { if (isInterior && !behaveLikeExterior) + { cell.mAmbi.mFog = static_cast(value.toInt()); + cell.setHasAmbient(true); + } else return; // return without saving break; @@ -936,7 +947,10 @@ namespace CSMWorld case 4: { if (isInterior && !behaveLikeExterior) + { cell.mAmbi.mFogDensity = value.toFloat(); + cell.setHasAmbient(true); + } else return; // return without saving break; diff --git a/components/esm/loadcell.cpp b/components/esm/loadcell.cpp index ec4155f59..5b259acef 100644 --- a/components/esm/loadcell.cpp +++ b/components/esm/loadcell.cpp @@ -110,6 +110,7 @@ namespace ESM void Cell::loadCell(ESMReader &esm, bool saveContext) { bool isLoaded = false; + mHasAmbi = false; while (!isLoaded && esm.hasMoreSubs()) { esm.getSubName(); @@ -127,6 +128,7 @@ namespace ESM break; case ESM::FourCC<'A','M','B','I'>::value: esm.getHT(mAmbi); + mHasAmbi = true; break; case ESM::FourCC<'R','G','N','N'>::value: mRegion = esm.getHString(); @@ -182,7 +184,12 @@ namespace ESM if (mData.mFlags & QuasiEx) esm.writeHNOCString("RGNN", mRegion); else - esm.writeHNT("AMBI", mAmbi, 16); + { + // Try to avoid saving ambient lighting information when it's unnecessary. + // This is to fix black lighting in resaved cell records that lack this information. + if (mHasAmbi) + esm.writeHNT("AMBI", mAmbi, 16); + } } else { @@ -272,6 +279,7 @@ namespace ESM mData.mX = 0; mData.mY = 0; + mHasAmbi = true; mAmbi.mAmbient = 0; mAmbi.mSunlight = 0; mAmbi.mFog = 0; diff --git a/components/esm/loadcell.hpp b/components/esm/loadcell.hpp index bc5016718..132c869ad 100644 --- a/components/esm/loadcell.hpp +++ b/components/esm/loadcell.hpp @@ -90,6 +90,7 @@ struct Cell Cell() : mName(""), mRegion(""), + mHasAmbi(true), mWater(0), mWaterInt(false), mMapColor(0), @@ -108,6 +109,7 @@ struct Cell CellId mCellId; AMBIstruct mAmbi; + bool mHasAmbi; float mWater; // Water level bool mWaterInt; @@ -152,6 +154,16 @@ struct Cell return ((mData.mFlags&HasWater) != 0) || isExterior(); } + bool hasAmbient() const + { + return mHasAmbi; + } + + void setHasAmbient(bool hasAmbi) + { + mHasAmbi = hasAmbi; + } + // Restore the given reader to the stored position. Will try to open // the file matching the stored file name. If you want to read from // somewhere other than the file system, you need to pre-open the From 349040ffb20b6511f22f1388d33c0b42e572683d Mon Sep 17 00:00:00 2001 From: elsid Date: Tue, 28 Jan 2020 22:24:51 +0100 Subject: [PATCH 17/99] Use status codes to handle navigator errors instead of exceptions For find path use case. --- apps/openmw/mwmechanics/pathfinding.cpp | 58 +++++++------ .../detournavigator/navigator.cpp | 37 +++++---- components/detournavigator/debug.hpp | 20 +++++ .../findrandompointaroundcircle.cpp | 3 +- components/detournavigator/findsmoothpath.hpp | 81 +++++++++---------- components/detournavigator/navigator.hpp | 8 +- components/detournavigator/status.hpp | 43 ++++++++++ 7 files changed, 163 insertions(+), 87 deletions(-) create mode 100644 components/detournavigator/status.hpp diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index f8736c062..a7bba5b63 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -341,21 +341,23 @@ namespace MWMechanics const osg::Vec3f& endPoint, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags, std::back_insert_iterator> out) { - try + const auto world = MWBase::Environment::get().getWorld(); + const auto stepSize = getPathStepSize(actor); + const auto navigator = world->getNavigator(); + const auto status = navigator->findPath(halfExtents, stepSize, startPoint, endPoint, flags, out); + + if (status == DetourNavigator::Status::NavMeshNotFound) + return false; + + if (status != DetourNavigator::Status::Success) { - const auto world = MWBase::Environment::get().getWorld(); - const auto stepSize = getPathStepSize(actor); - const auto navigator = world->getNavigator(); - return navigator->findPath(halfExtents, stepSize, startPoint, endPoint, flags, out).is_initialized(); - } - catch (const DetourNavigator::NavigatorException& exception) - { - Log(Debug::Debug) << "Build path by navigator exception: \"" << exception.what() + Log(Debug::Debug) << "Build path by navigator error: \"" << DetourNavigator::getMessage(status) << "\" for \"" << actor.getClass().getName(actor) << "\" (" << actor.getBase() << ") from " << startPoint << " to " << endPoint << " with flags (" << DetourNavigator::WriteFlags {flags} << ")"; - return true; } + + return true; } void PathFinder::buildPathByNavMeshToNextPoint(const MWWorld::ConstPtr& actor, const osg::Vec3f& halfExtents, @@ -370,26 +372,30 @@ namespace MWMechanics if (sqrDistanceIgnoreZ(mPath.front(), startPoint) <= 4 * stepSize * stepSize) return; - try + const auto navigator = MWBase::Environment::get().getWorld()->getNavigator(); + std::deque prePath; + auto prePathInserter = std::back_inserter(prePath); + const auto status = navigator->findPath(halfExtents, stepSize, startPoint, mPath.front(), flags, + prePathInserter); + + if (status == DetourNavigator::Status::NavMeshNotFound) + return; + + if (status != DetourNavigator::Status::Success) { - const auto navigator = MWBase::Environment::get().getWorld()->getNavigator(); - std::deque prePath; - navigator->findPath(halfExtents, stepSize, startPoint, mPath.front(), flags, std::back_inserter(prePath)); - - while (!prePath.empty() && sqrDistanceIgnoreZ(prePath.front(), startPoint) < stepSize * stepSize) - prePath.pop_front(); - - while (!prePath.empty() && sqrDistanceIgnoreZ(prePath.back(), mPath.front()) < stepSize * stepSize) - prePath.pop_back(); - - std::copy(prePath.rbegin(), prePath.rend(), std::front_inserter(mPath)); - } - catch (const DetourNavigator::NavigatorException& exception) - { - Log(Debug::Debug) << "Build path by navigator exception: \"" << exception.what() + Log(Debug::Debug) << "Build path by navigator error: \"" << DetourNavigator::getMessage(status) << "\" for \"" << actor.getClass().getName(actor) << "\" (" << actor.getBase() << ") from " << startPoint << " to " << mPath.front() << " with flags (" << DetourNavigator::WriteFlags {flags} << ")"; + return; } + + while (!prePath.empty() && sqrDistanceIgnoreZ(prePath.front(), startPoint) < stepSize * stepSize) + prePath.pop_front(); + + while (!prePath.empty() && sqrDistanceIgnoreZ(prePath.back(), mPath.front()) < stepSize * stepSize) + prePath.pop_back(); + + std::copy(prePath.rbegin(), prePath.rend(), std::front_inserter(mPath)); } } diff --git a/apps/openmw_test_suite/detournavigator/navigator.cpp b/apps/openmw_test_suite/detournavigator/navigator.cpp index f8aa8f535..df8be3781 100644 --- a/apps/openmw_test_suite/detournavigator/navigator.cpp +++ b/apps/openmw_test_suite/detournavigator/navigator.cpp @@ -73,14 +73,16 @@ namespace TEST_F(DetourNavigatorNavigatorTest, find_path_for_empty_should_return_empty) { - mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut); + EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), + Status::NavMeshNotFound); EXPECT_EQ(mPath, std::deque()); } TEST_F(DetourNavigatorNavigatorTest, find_path_for_existing_agent_with_no_navmesh_should_throw_exception) { mNavigator->addAgent(mAgentHalfExtents); - EXPECT_THROW(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), NavigatorException); + EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), + Status::StartPolygonNotFound); } TEST_F(DetourNavigatorNavigatorTest, add_agent_should_count_each_agent) @@ -88,7 +90,8 @@ namespace mNavigator->addAgent(mAgentHalfExtents); mNavigator->addAgent(mAgentHalfExtents); mNavigator->removeAgent(mAgentHalfExtents); - EXPECT_THROW(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), NavigatorException); + EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), + Status::StartPolygonNotFound); } TEST_F(DetourNavigatorNavigatorTest, update_then_find_path_should_return_path) @@ -108,7 +111,7 @@ namespace mNavigator->update(mPlayerPosition); mNavigator->wait(); - mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut); + EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), Status::Success); EXPECT_EQ(mPath, std::deque({ osg::Vec3f(-215, 215, 1.85963428020477294921875), @@ -158,7 +161,7 @@ namespace mNavigator->update(mPlayerPosition); mNavigator->wait(); - mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, std::back_inserter(mPath)); + EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), Status::Success); EXPECT_EQ(mPath, std::deque({ osg::Vec3f(-215, 215, 1.85963428020477294921875), @@ -191,7 +194,8 @@ namespace mNavigator->wait(); mPath.clear(); - mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, std::back_inserter(mPath)); + mOut = std::back_inserter(mPath); + EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), Status::Success); EXPECT_EQ(mPath, std::deque({ osg::Vec3f(-215, 215, 1.87826788425445556640625), @@ -242,7 +246,7 @@ namespace mNavigator->update(mPlayerPosition); mNavigator->wait(); - mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, std::back_inserter(mPath)); + EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), Status::Success); EXPECT_EQ(mPath, std::deque({ osg::Vec3f(-215, 215, 1.87826788425445556640625), @@ -277,7 +281,8 @@ namespace mNavigator->wait(); mPath.clear(); - mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut); + mOut = std::back_inserter(mPath); + EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), Status::Success); EXPECT_EQ(mPath, std::deque({ osg::Vec3f(-215, 215, 1.85963428020477294921875), @@ -334,7 +339,7 @@ namespace mNavigator->update(mPlayerPosition); mNavigator->wait(); - mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut); + EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), Status::Success); EXPECT_EQ(mPath, std::deque({ osg::Vec3f(-215, 215, 1.96328866481781005859375), @@ -390,7 +395,7 @@ namespace mNavigator->update(mPlayerPosition); mNavigator->wait(); - mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut); + EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), Status::Success); EXPECT_EQ(mPath, std::deque({ osg::Vec3f(-215, 215, 1.9393787384033203125), @@ -443,7 +448,7 @@ namespace mEnd.x() = 0; mEnd.z() = 300; - mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_swim, mOut); + EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_swim, mOut), Status::Success); EXPECT_EQ(mPath, std::deque({ osg::Vec3f(0, 215, 185.33331298828125), @@ -489,7 +494,8 @@ namespace mStart.x() = 0; mEnd.x() = 0; - mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_swim | Flag_walk, mOut); + EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_swim | Flag_walk, mOut), + Status::Success); EXPECT_EQ(mPath, std::deque({ osg::Vec3f(0, 215, -94.75363922119140625), @@ -535,7 +541,8 @@ namespace mStart.x() = 0; mEnd.x() = 0; - mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_swim | Flag_walk, mOut); + EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_swim | Flag_walk, mOut), + Status::Success); EXPECT_EQ(mPath, std::deque({ osg::Vec3f(0, 215, -94.75363922119140625), @@ -581,7 +588,7 @@ namespace mStart.x() = 0; mEnd.x() = 0; - mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut); + EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), Status::Success); EXPECT_EQ(mPath, std::deque({ osg::Vec3f(0, 215, -94.75363922119140625), @@ -630,7 +637,7 @@ namespace mNavigator->update(mPlayerPosition); mNavigator->wait(); - mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut); + EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mOut), Status::Success); EXPECT_EQ(mPath, std::deque({ osg::Vec3f(-215, 215, 1.85963428020477294921875), diff --git a/components/detournavigator/debug.hpp b/components/detournavigator/debug.hpp index 541680997..a17eec16a 100644 --- a/components/detournavigator/debug.hpp +++ b/components/detournavigator/debug.hpp @@ -2,6 +2,7 @@ #define OPENMW_COMPONENTS_DETOURNAVIGATOR_DEBUG_H #include "tilebounds.hpp" +#include "status.hpp" #include @@ -26,6 +27,25 @@ namespace DetourNavigator return stream << "TileBounds {" << value.mMin << ", " << value.mMax << "}"; } + inline std::ostream& operator <<(std::ostream& stream, Status value) + { +#define OPENMW_COMPONENTS_DETOURNAVIGATOR_DEBUG_STATUS_MESSAGE(name) \ + case Status::name: return stream << "DetourNavigator::Status::"#name; + switch (value) + { + OPENMW_COMPONENTS_DETOURNAVIGATOR_DEBUG_STATUS_MESSAGE(Success) + OPENMW_COMPONENTS_DETOURNAVIGATOR_DEBUG_STATUS_MESSAGE(NavMeshNotFound) + OPENMW_COMPONENTS_DETOURNAVIGATOR_DEBUG_STATUS_MESSAGE(StartPolygonNotFound) + OPENMW_COMPONENTS_DETOURNAVIGATOR_DEBUG_STATUS_MESSAGE(EndPolygonNotFound) + OPENMW_COMPONENTS_DETOURNAVIGATOR_DEBUG_STATUS_MESSAGE(MoveAlongSurfaceFailed) + OPENMW_COMPONENTS_DETOURNAVIGATOR_DEBUG_STATUS_MESSAGE(FindPathOverPolygonsFailed) + OPENMW_COMPONENTS_DETOURNAVIGATOR_DEBUG_STATUS_MESSAGE(GetPolyHeightFailed) + OPENMW_COMPONENTS_DETOURNAVIGATOR_DEBUG_STATUS_MESSAGE(InitNavMeshQueryFailed) + } +#undef OPENMW_COMPONENTS_DETOURNAVIGATOR_DEBUG_STATUS_MESSAGE + return stream << "DetourNavigator::Error::" << static_cast(value); + } + class RecastMesh; void writeToFile(const RecastMesh& recastMesh, const std::string& pathPrefix, const std::string& revision); diff --git a/components/detournavigator/findrandompointaroundcircle.cpp b/components/detournavigator/findrandompointaroundcircle.cpp index c894e6681..3888c59fe 100644 --- a/components/detournavigator/findrandompointaroundcircle.cpp +++ b/components/detournavigator/findrandompointaroundcircle.cpp @@ -14,7 +14,8 @@ namespace DetourNavigator const osg::Vec3f& start, const float maxRadius, const Flags includeFlags, const Settings& settings) { dtNavMeshQuery navMeshQuery; - initNavMeshQuery(navMeshQuery, navMesh, settings.mMaxNavMeshQueryNodes); + if (!initNavMeshQuery(navMeshQuery, navMesh, settings.mMaxNavMeshQueryNodes)) + return boost::optional(); dtQueryFilter queryFilter; queryFilter.setIncludeFlags(includeFlags); diff --git a/components/detournavigator/findsmoothpath.hpp b/components/detournavigator/findsmoothpath.hpp index e988dedb7..0f8f2c09a 100644 --- a/components/detournavigator/findsmoothpath.hpp +++ b/components/detournavigator/findsmoothpath.hpp @@ -7,6 +7,7 @@ #include "settings.hpp" #include "settingsutils.hpp" #include "debug.hpp" +#include "status.hpp" #include #include @@ -97,11 +98,10 @@ namespace DetourNavigator std::reference_wrapper mSettings; }; - inline void initNavMeshQuery(dtNavMeshQuery& value, const dtNavMesh& navMesh, const int maxNodes) + inline bool initNavMeshQuery(dtNavMeshQuery& value, const dtNavMesh& navMesh, const int maxNodes) { const auto status = value.init(&navMesh, maxNodes); - if (!dtStatusSucceed(status)) - throw NavigatorException("Failed to init navmesh query"); + return dtStatusSucceed(status); } struct MoveAlongSurfaceResult @@ -110,8 +110,8 @@ namespace DetourNavigator std::vector mVisited; }; - inline MoveAlongSurfaceResult moveAlongSurface(const dtNavMeshQuery& navMeshQuery, const dtPolyRef startRef, - const osg::Vec3f& startPos, const osg::Vec3f& endPos, const dtQueryFilter& filter, + inline boost::optional moveAlongSurface(const dtNavMeshQuery& navMeshQuery, + const dtPolyRef startRef, const osg::Vec3f& startPos, const osg::Vec3f& endPos, const dtQueryFilter& filter, const std::size_t maxVisitedSize) { MoveAlongSurfaceResult result; @@ -120,18 +120,14 @@ namespace DetourNavigator const auto status = navMeshQuery.moveAlongSurface(startRef, startPos.ptr(), endPos.ptr(), &filter, result.mResultPos.ptr(), result.mVisited.data(), &visitedNumber, static_cast(maxVisitedSize)); if (!dtStatusSucceed(status)) - { - std::ostringstream message; - message << "Failed to move along surface from " << startPos << " to " << endPos; - throw NavigatorException(message.str()); - } + return {}; assert(visitedNumber >= 0); assert(visitedNumber <= static_cast(maxVisitedSize)); result.mVisited.resize(static_cast(visitedNumber)); - return result; + return {std::move(result)}; } - inline std::vector findPath(const dtNavMeshQuery& navMeshQuery, const dtPolyRef startRef, + inline boost::optional> findPath(const dtNavMeshQuery& navMeshQuery, const dtPolyRef startRef, const dtPolyRef endRef, const osg::Vec3f& startPos, const osg::Vec3f& endPos, const dtQueryFilter& queryFilter, const std::size_t maxSize) { @@ -140,34 +136,26 @@ namespace DetourNavigator const auto status = navMeshQuery.findPath(startRef, endRef, startPos.ptr(), endPos.ptr(), &queryFilter, result.data(), &pathLen, static_cast(maxSize)); if (!dtStatusSucceed(status)) - { - std::ostringstream message; - message << "Failed to find path over polygons from " << startRef << " to " << endRef; - throw NavigatorException(message.str()); - } + return {}; assert(pathLen >= 0); assert(static_cast(pathLen) <= maxSize); result.resize(static_cast(pathLen)); - return result; + return {std::move(result)}; } - inline float getPolyHeight(const dtNavMeshQuery& navMeshQuery, const dtPolyRef ref, const osg::Vec3f& pos) + inline boost::optional getPolyHeight(const dtNavMeshQuery& navMeshQuery, const dtPolyRef ref, const osg::Vec3f& pos) { float result = 0.0f; const auto status = navMeshQuery.getPolyHeight(ref, pos.ptr(), &result); if (!dtStatusSucceed(status)) - { - std::ostringstream message; - message << "Failed to get polygon height ref=" << ref << " pos=" << pos; - throw NavigatorException(message.str()); - } + return {}; return result; } template - OutputIterator makeSmoothPath(const dtNavMesh& navMesh, const dtNavMeshQuery& navMeshQuery, + Status makeSmoothPath(const dtNavMesh& navMesh, const dtNavMeshQuery& navMeshQuery, const dtQueryFilter& filter, const osg::Vec3f& start, const osg::Vec3f& end, const float stepSize, - std::vector polygonPath, std::size_t maxSmoothPathSize, OutputIterator out) + std::vector polygonPath, std::size_t maxSmoothPathSize, OutputIterator& out) { // Iterate over the path to find smooth path on the detail mesh surface. osg::Vec3f iterPos; @@ -207,12 +195,15 @@ namespace DetourNavigator const osg::Vec3f moveTgt = iterPos + delta * len; const auto result = moveAlongSurface(navMeshQuery, polygonPath.front(), iterPos, moveTgt, filter, 16); - polygonPath = fixupCorridor(polygonPath, result.mVisited); + if (!result) + return Status::MoveAlongSurfaceFailed; + + polygonPath = fixupCorridor(polygonPath, result->mVisited); polygonPath = fixupShortcuts(polygonPath, navMeshQuery); float h = 0; - navMeshQuery.getPolyHeight(polygonPath.front(), result.mResultPos.ptr(), &h); - iterPos = result.mResultPos; + navMeshQuery.getPolyHeight(polygonPath.front(), result->mResultPos.ptr(), &h); + iterPos = result->mResultPos; iterPos.y() = h; // Handle end of path and off-mesh links when close enough. @@ -259,7 +250,12 @@ namespace DetourNavigator // Move position at the other side of the off-mesh link. iterPos = endPos; - iterPos.y() = getPolyHeight(navMeshQuery, polygonPath.front(), iterPos); + const auto height = getPolyHeight(navMeshQuery, polygonPath.front(), iterPos); + + if (!height) + return Status::GetPolyHeightFailed; + + iterPos.y() = *height; } } @@ -268,16 +264,17 @@ namespace DetourNavigator ++smoothPathSize; } - return out; + return Status::Success; } template - OutputIterator findSmoothPath(const dtNavMesh& navMesh, const osg::Vec3f& halfExtents, const float stepSize, + Status findSmoothPath(const dtNavMesh& navMesh, const osg::Vec3f& halfExtents, const float stepSize, const osg::Vec3f& start, const osg::Vec3f& end, const Flags includeFlags, - const Settings& settings, OutputIterator out) + const Settings& settings, OutputIterator& out) { dtNavMeshQuery navMeshQuery; - initNavMeshQuery(navMeshQuery, navMesh, settings.mMaxNavMeshQueryNodes); + if (!initNavMeshQuery(navMeshQuery, navMesh, settings.mMaxNavMeshQueryNodes)) + return Status::InitNavMeshQueryFailed; dtQueryFilter queryFilter; queryFilter.setIncludeFlags(includeFlags); @@ -293,7 +290,7 @@ namespace DetourNavigator } if (startRef == 0) - throw NavigatorException("Navmesh polygon for start point is not found"); + return Status::StartPolygonNotFound; dtPolyRef endRef = 0; osg::Vec3f endPolygonPosition; @@ -306,18 +303,20 @@ namespace DetourNavigator } if (endRef == 0) - throw NavigatorException("Navmesh polygon for end polygon is not found"); + return Status::EndPolygonNotFound; const auto polygonPath = findPath(navMeshQuery, startRef, endRef, start, end, queryFilter, settings.mMaxPolygonPathSize); - if (polygonPath.empty() || polygonPath.back() != endRef) - return out; + if (!polygonPath) + return Status::FindPathOverPolygonsFailed; - makeSmoothPath(navMesh, navMeshQuery, queryFilter, start, end, stepSize, std::move(polygonPath), - settings.mMaxSmoothPathSize, OutputTransformIterator(out, settings)); + if (polygonPath->empty() || polygonPath->back() != endRef) + return Status::Success; - return out; + auto outTransform = OutputTransformIterator(out, settings); + return makeSmoothPath(navMesh, navMeshQuery, queryFilter, start, end, stepSize, std::move(*polygonPath), + settings.mMaxSmoothPathSize, outTransform); } } diff --git a/components/detournavigator/navigator.hpp b/components/detournavigator/navigator.hpp index 1b7af7e8b..2265b1d39 100644 --- a/components/detournavigator/navigator.hpp +++ b/components/detournavigator/navigator.hpp @@ -1,4 +1,4 @@ -#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_NAVIGATOR_H +#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_NAVIGATOR_H #define OPENMW_COMPONENTS_DETOURNAVIGATOR_NAVIGATOR_H #include "findsmoothpath.hpp" @@ -159,8 +159,8 @@ namespace DetourNavigator * Equal to out if no path is found. */ template - boost::optional findPath(const osg::Vec3f& agentHalfExtents, const float stepSize, const osg::Vec3f& start, - const osg::Vec3f& end, const Flags includeFlags, OutputIterator out) const + Status findPath(const osg::Vec3f& agentHalfExtents, const float stepSize, const osg::Vec3f& start, + const osg::Vec3f& end, const Flags includeFlags, OutputIterator& out) const { static_assert( std::is_same< @@ -171,7 +171,7 @@ namespace DetourNavigator ); const auto navMesh = getNavMesh(agentHalfExtents); if (!navMesh) - return {}; + return Status::NavMeshNotFound; const auto settings = getSettings(); return findSmoothPath(navMesh->lockConst()->getImpl(), toNavMeshCoordinates(settings, agentHalfExtents), toNavMeshCoordinates(settings, stepSize), toNavMeshCoordinates(settings, start), diff --git a/components/detournavigator/status.hpp b/components/detournavigator/status.hpp new file mode 100644 index 000000000..3715489ac --- /dev/null +++ b/components/detournavigator/status.hpp @@ -0,0 +1,43 @@ +#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_STATUS_H +#define OPENMW_COMPONENTS_DETOURNAVIGATOR_STATUS_H + +namespace DetourNavigator +{ + enum class Status + { + Success, + NavMeshNotFound, + StartPolygonNotFound, + EndPolygonNotFound, + MoveAlongSurfaceFailed, + FindPathOverPolygonsFailed, + GetPolyHeightFailed, + InitNavMeshQueryFailed, + }; + + constexpr const char* getMessage(Status value) + { + switch (value) + { + case Status::Success: + return "success"; + case Status::NavMeshNotFound: + return "navmesh is not found"; + case Status::StartPolygonNotFound: + return "polygon for start position is not found on navmesh"; + case Status::EndPolygonNotFound: + return "polygon for end position is not found on navmesh"; + case Status::MoveAlongSurfaceFailed: + return "move along surface on navmesh is failed"; + case Status::FindPathOverPolygonsFailed: + return "path over navmesh polygons is not found"; + case Status::GetPolyHeightFailed: + return "failed to get polygon height"; + case Status::InitNavMeshQueryFailed: + return "failed to init navmesh query"; + } + return "unknown error"; + } +} + +#endif From 122dffe4c180e63bc3f46a5992bb97de51961af9 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Fri, 31 Jan 2020 02:12:23 +0300 Subject: [PATCH 18/99] Make uncapped Damage Fatigue optional (bug #5264) Cap Absorb Fatigue --- CHANGELOG.md | 1 + apps/launcher/advancedpage.cpp | 2 ++ apps/openmw/mwmechanics/spellcasting.cpp | 11 +++++++---- docs/source/reference/modding/settings/game.rst | 17 +++++++++++++++++ files/settings-default.cfg | 4 ++++ files/ui/advancedpage.ui | 10 ++++++++++ 6 files changed, 41 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index af7edad31..d6ee41084 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -196,6 +196,7 @@ Bug #5250: Creatures display shield ground mesh instead of shield body part Bug #5255: "GetTarget, player" doesn't return 1 during NPC hello Bug #5261: Creatures can sometimes become stuck playing idles and never wander again + Bug #5264: "Damage Fatigue" Magic Effect Can Bring Fatigue below 0 Bug #5269: Editor: Cell lighting in resaved cleaned content files is corrupted Feature #1774: Handle AvoidNode Feature #2229: Improve pathfinding AI diff --git a/apps/launcher/advancedpage.cpp b/apps/launcher/advancedpage.cpp index 6f2811e66..e1a5d28af 100644 --- a/apps/launcher/advancedpage.cpp +++ b/apps/launcher/advancedpage.cpp @@ -89,6 +89,7 @@ bool Launcher::AdvancedPage::loadSettings() loadSettingBool(weaponSheathingCheckBox, "weapon sheathing", "Game"); loadSettingBool(shieldSheathingCheckBox, "shield sheathing", "Game"); } + loadSettingBool(uncappedDamageFatigueCheckBox, "uncapped damage fatigue", "Game"); // Input Settings loadSettingBool(grabCursorCheckBox, "grab cursor", "Input"); @@ -152,6 +153,7 @@ void Launcher::AdvancedPage::saveSettings() saveSettingBool(animSourcesCheckBox, "use additional anim sources", "Game"); saveSettingBool(weaponSheathingCheckBox, "weapon sheathing", "Game"); saveSettingBool(shieldSheathingCheckBox, "shield sheathing", "Game"); + saveSettingBool(uncappedDamageFatigueCheckBox, "uncapped damage fatigue", "Game"); // Input Settings saveSettingBool(grabCursorCheckBox, "grab cursor", "Input"); diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 0b7c27b86..21fa7f369 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -1199,10 +1199,10 @@ namespace MWMechanics return false; } - void adjustDynamicStat(CreatureStats& creatureStats, int index, float magnitude) + void adjustDynamicStat(CreatureStats& creatureStats, int index, float magnitude, bool allowDecreaseBelowZero = false) { DynamicStat stat = creatureStats.getDynamic(index); - stat.setCurrent(stat.getCurrent() + magnitude, index == 2); + stat.setCurrent(stat.getCurrent() + magnitude, allowDecreaseBelowZero); creatureStats.setDynamic(index, stat); } @@ -1241,9 +1241,12 @@ namespace MWMechanics case ESM::MagicEffect::DamageMagicka: case ESM::MagicEffect::DamageFatigue: - adjustDynamicStat(creatureStats, effectKey.mId-ESM::MagicEffect::DamageHealth, -magnitude); + { + int index = effectKey.mId-ESM::MagicEffect::DamageHealth; + static const bool uncappedDamageFatigue = Settings::Manager::getBool("uncapped damage fatigue", "Game"); + adjustDynamicStat(creatureStats, index, -magnitude, index == 2 && uncappedDamageFatigue); break; - + } case ESM::MagicEffect::AbsorbHealth: if (magnitude > 0.f) receivedMagicDamage = true; diff --git a/docs/source/reference/modding/settings/game.rst b/docs/source/reference/modding/settings/game.rst index 9a3e9c84d..201704f35 100644 --- a/docs/source/reference/modding/settings/game.rst +++ b/docs/source/reference/modding/settings/game.rst @@ -298,3 +298,20 @@ A value of 0 means that you can only enchant one projectile. If you want to have Morrowind Code Patch-like count of projectiles being enchanted at once, set this value to 0.25 (i.e. 25% of the charge). This setting can only be configured by editing the settings configuration file. + +uncapped damage fatigue +----------------------- + +:Type: boolean +:Range: True/False +:Default: False + +There are four ways to decrease an actor's Fatigue stat in Morrowind gameplay mechanics: +Drain, Absorb, Damage Fatigue magic effects and hand-to-hand combat. +However, in Morrowind you can't knock down an actor with a Damage Fatigue spell or an Absorb Fatigue spell. +Morrowind Code Patch adds an option to make it possible for Damage Fatigue spells. This is the equivalent of that option. + +Setting the value of this setting to true will remove the 0 lower cap from the value, +allowing Damage Fatigue to reduce Fatigue to a value below zero. + +This setting can be controlled in Advanced tab of the launcher. diff --git a/files/settings-default.cfg b/files/settings-default.cfg index ecc6be72e..d3458d293 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -272,6 +272,10 @@ normalise race speed = false # A value of 0 means that you can only enchant one projectile. projectiles enchant multiplier = 0 +# Make Damage Fatigue magic effect uncapped like Drain Fatigue effect. +# This means that unlike Morrowind you will be able to knock down actors using this effect. +uncapped damage fatigue = false + [General] # Anisotropy reduces distortion in textures at low angles (e.g. 0 to 16). diff --git a/files/ui/advancedpage.ui b/files/ui/advancedpage.ui index 8748a6d0b..2ea5d777c 100644 --- a/files/ui/advancedpage.ui +++ b/files/ui/advancedpage.ui @@ -226,6 +226,16 @@ + + + + <html><head/><body><p>Make Damage Fatigue magic effect uncapped like Drain Fatigue effect.</p><p>This means that unlike Morrowind you will be able to knock down actors using this effect.</p></body></html> + + + Uncapped Damage Fatigue + + + From aadf13b123fa739138b49f86840b33ba347fac72 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 9 Feb 2020 15:08:44 +0300 Subject: [PATCH 19/99] Make Show fallback to global variables when sensible (bug #5278) --- CHANGELOG.md | 1 + apps/openmw/mwscript/miscextensions.cpp | 14 ++++---------- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index af7edad31..757ff0e35 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -197,6 +197,7 @@ Bug #5255: "GetTarget, player" doesn't return 1 during NPC hello Bug #5261: Creatures can sometimes become stuck playing idles and never wander again Bug #5269: Editor: Cell lighting in resaved cleaned content files is corrupted + Bug #5278: Console command Show doesn't fall back to global variable after local var not found Feature #1774: Handle AvoidNode Feature #2229: Improve pathfinding AI Feature #3025: Analogue gamepad movement controls diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index ccf285844..a237ee2a2 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -923,11 +923,7 @@ namespace MWScript if (!ptr.isEmpty()) { const std::string& script = ptr.getClass().getScript(ptr); - if (script.empty()) - { - output << ptr.getCellRef().getRefId() << " has no script " << std::endl; - } - else + if (!script.empty()) { const Compiler::Locals& locals = MWBase::Environment::get().getScriptManager()->getLocals(script); @@ -941,13 +937,11 @@ namespace MWScript case 'f': output << ptr.getCellRef().getRefId() << "." << var << ": " << ptr.getRefData().getLocals().getFloatVar(script, var); break; - default: - output << "unknown local '" << var << "' for '" << ptr.getCellRef().getRefId() << "'"; - break; + // Do nothing otherwise } } } - else + if (output.rdbuf()->in_avail() == 0) { MWBase::World *world = MWBase::Environment::get().getWorld(); char type = world->getGlobalVariableType (var); @@ -964,7 +958,7 @@ namespace MWScript output << runtime.getContext().getGlobalFloat (var); break; default: - output << "unknown global variable"; + output << "unknown variable"; } } runtime.getContext().report(output.str()); From 9b4be677f658d097a1453366f208c8b00bd9e3a4 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 9 Feb 2020 20:10:24 +0300 Subject: [PATCH 20/99] Make Show output format closer to vanilla --- apps/openmw/mwscript/miscextensions.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index a237ee2a2..06676d006 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -928,16 +928,18 @@ namespace MWScript const Compiler::Locals& locals = MWBase::Environment::get().getScriptManager()->getLocals(script); char type = locals.getType(var); + std::string refId = ptr.getCellRef().getRefId(); + if (refId.find(' ') != std::string::npos) + refId = '"' + refId + '"'; switch (type) { case 'l': case 's': - output << ptr.getCellRef().getRefId() << "." << var << ": " << ptr.getRefData().getLocals().getIntVar(script, var); + output << refId << "." << var << " = " << ptr.getRefData().getLocals().getIntVar(script, var); break; case 'f': - output << ptr.getCellRef().getRefId() << "." << var << ": " << ptr.getRefData().getLocals().getFloatVar(script, var); + output << refId << "." << var << " = " << ptr.getRefData().getLocals().getFloatVar(script, var); break; - // Do nothing otherwise } } } @@ -949,13 +951,13 @@ namespace MWScript switch (type) { case 's': - output << runtime.getContext().getGlobalShort (var); + output << var << " = " << runtime.getContext().getGlobalShort (var); break; case 'l': - output << runtime.getContext().getGlobalLong (var); + output << var << " = " << runtime.getContext().getGlobalLong (var); break; case 'f': - output << runtime.getContext().getGlobalFloat (var); + output << var << " = " << runtime.getContext().getGlobalFloat (var); break; default: output << "unknown variable"; From f4291ea94805273a676d6aa34d9b613a5e4e00c5 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 10 Feb 2020 22:31:20 +0400 Subject: [PATCH 21/99] Fix intersector usage (bug #5283) --- components/terrain/quadtreenode.cpp | 4 ++-- components/terrain/quadtreenode.hpp | 10 +++++++++- components/terrain/quadtreeworld.cpp | 14 ++++++++++---- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index 7583cd09f..a28554be9 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -150,12 +150,12 @@ void QuadTreeNode::traverseTo(ViewData* vd, float size, const osg::Vec2f& center } } -void QuadTreeNode::intersect(ViewData* vd, TerrainLineIntersector* intersector) +void QuadTreeNode::intersect(ViewData* vd, TerrainLineIntersector& intersector) { if (!hasValidBounds()) return; - if (!intersector->intersectAndClip(getBoundingBox())) + if (!intersector.intersectAndClip(getBoundingBox())) return; if (getNumChildren() == 0) diff --git a/components/terrain/quadtreenode.hpp b/components/terrain/quadtreenode.hpp index 9aaf1beab..4adbc6025 100644 --- a/components/terrain/quadtreenode.hpp +++ b/components/terrain/quadtreenode.hpp @@ -20,6 +20,14 @@ namespace Terrain _parent = intersector; } + TerrainLineIntersector(osgUtil::LineSegmentIntersector* intersector) : + osgUtil::LineSegmentIntersector(intersector->getStart(), intersector->getEnd()) + { + setPrecisionHint(intersector->getPrecisionHint()); + _intersectionLimit = intersector->getIntersectionLimit(); + _parent = intersector; + } + bool intersectAndClip(const osg::BoundingBox& bbInput) { osg::Vec3d s(_start), e(_end); @@ -98,7 +106,7 @@ namespace Terrain void traverseTo(ViewData* vd, float size, const osg::Vec2f& center); /// Adds all leaf nodes which intersect the line from start to end - void intersect(ViewData* vd, TerrainLineIntersector* intersector); + void intersect(ViewData* vd, TerrainLineIntersector& intersector); private: QuadTreeNode* mParent; diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index e8089e1c4..8dd3a0706 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -362,11 +362,17 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) if (!lineIntersector) throw std::runtime_error("Cannot update QuadTreeWorld: node visitor is not LineSegmentIntersector"); - osg::Matrix matrix = osg::Matrix::identity(); if (lineIntersector->getCoordinateFrame() == osgUtil::Intersector::CoordinateFrame::MODEL && iv->getModelMatrix() == 0) - matrix = lineIntersector->getTransformation(*iv, osgUtil::Intersector::CoordinateFrame::MODEL); - osg::ref_ptr terrainIntersector (new TerrainLineIntersector(lineIntersector, matrix)); - mRootNode->intersect(vd, terrainIntersector); + { + TerrainLineIntersector terrainIntersector(lineIntersector); + mRootNode->intersect(vd, terrainIntersector); + } + else + { + osg::Matrix matrix(lineIntersector->getTransformation(*iv, lineIntersector->getCoordinateFrame())); + TerrainLineIntersector terrainIntersector(lineIntersector, matrix); + mRootNode->intersect(vd, terrainIntersector); + } } } From 52945921a7b8a65bc6484cd700d2677841a2e09b Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 2 Feb 2020 20:49:39 +0100 Subject: [PATCH 22/99] Print ptr by betacomment --- apps/openmw/mwscript/miscextensions.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 06676d006..7d779e62f 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -1254,6 +1254,7 @@ namespace MWScript msg << "[Deleted]" << std::endl; msg << "RefID: " << ptr.getCellRef().getRefId() << std::endl; + msg << "Memory address: " << ptr.getBase() << std::endl; if (ptr.isInCell()) { From 0c92a567afef73a34c83c18ecbb3787b782b03b4 Mon Sep 17 00:00:00 2001 From: elsid Date: Thu, 6 Feb 2020 00:20:55 +0100 Subject: [PATCH 23/99] Use distance to position since last normal state in obstacle checker --- apps/openmw/mwmechanics/obstacle.cpp | 5 ++++- apps/openmw/mwmechanics/obstacle.hpp | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/obstacle.cpp b/apps/openmw/mwmechanics/obstacle.cpp index 6268eaddf..e30a2947f 100644 --- a/apps/openmw/mwmechanics/obstacle.cpp +++ b/apps/openmw/mwmechanics/obstacle.cpp @@ -120,6 +120,7 @@ namespace MWMechanics mWalkState = WalkState::Norm; mStateDuration = 0; mPrev = position; + mInitialDistance = (destination - position).length(); return; } @@ -129,10 +130,11 @@ namespace MWMechanics const float prevDistance = (destination - mPrev).length(); const float currentDistance = (destination - position).length(); const float movedDistance = prevDistance - currentDistance; + const float movedFromInitialDistance = mInitialDistance - currentDistance; mPrev = position; - if (movedDistance >= distSameSpot) + if (movedDistance >= distSameSpot && movedFromInitialDistance >= distSameSpot) { mWalkState = WalkState::Norm; mStateDuration = 0; @@ -143,6 +145,7 @@ namespace MWMechanics { mWalkState = WalkState::CheckStuck; mStateDuration = duration; + mInitialDistance = (destination - position).length(); return; } diff --git a/apps/openmw/mwmechanics/obstacle.hpp b/apps/openmw/mwmechanics/obstacle.hpp index 8314031ea..6c2197d81 100644 --- a/apps/openmw/mwmechanics/obstacle.hpp +++ b/apps/openmw/mwmechanics/obstacle.hpp @@ -54,6 +54,7 @@ namespace MWMechanics float mStateDuration; int mEvadeDirectionIndex; + float mInitialDistance = 0; void chooseEvasionDirection(); }; From 85414e23539b22f10dbc2fe1b6c09acad3e2d964 Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 2 Feb 2020 20:02:25 +0100 Subject: [PATCH 24/99] Check for line of sight for wander destination --- apps/openmw/mwbase/world.hpp | 2 ++ apps/openmw/mwmechanics/aiwander.cpp | 32 ++++++++++++++++++---------- apps/openmw/mwmechanics/aiwander.hpp | 1 - apps/openmw/mwworld/worldimp.cpp | 5 +++++ apps/openmw/mwworld/worldimp.hpp | 2 ++ 5 files changed, 30 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 0529140f4..dc92e26a4 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -313,6 +313,8 @@ namespace MWBase virtual bool castRay (float x1, float y1, float z1, float x2, float y2, float z2) = 0; + virtual bool castRay(const osg::Vec3f& from, const osg::Vec3f& to, int mask, const MWWorld::ConstPtr& ignore) = 0; + virtual void setActorCollisionMode(const MWWorld::Ptr& ptr, bool internal, bool external) = 0; virtual bool isActorCollisionEnabled(const MWWorld::Ptr& ptr) = 0; diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 442ba0499..f7023ed89 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -61,6 +61,26 @@ namespace MWMechanics rotation.makeRotate(randomDirection, osg::Vec3f(0.0, 0.0, 1.0)); return position + osg::Vec3f(distance, 0.0, 0.0) * rotation; } + + bool isDestinationHidden(const MWWorld::ConstPtr &actor, const osg::Vec3f& destination) + { + const auto position = actor.getRefData().getPosition().asVec3(); + const bool isWaterCreature = actor.getClass().isPureWaterCreature(actor); + const bool isFlyingCreature = actor.getClass().isPureFlyingCreature(actor); + const osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getPathfindingHalfExtents(actor); + osg::Vec3f direction = destination - position; + direction.normalize(); + const auto visibleDestination = ( + isWaterCreature || isFlyingCreature + ? destination + : destination + osg::Vec3f(0, 0, halfExtents.z()) + ) + direction * std::max(halfExtents.x(), std::max(halfExtents.y(), halfExtents.z())); + const int mask = MWPhysics::CollisionType_World + | MWPhysics::CollisionType_HeightMap + | MWPhysics::CollisionType_Door + | MWPhysics::CollisionType_Actor; + return MWBase::Environment::get().getWorld()->castRay(position, visibleDestination, mask, actor); + } } AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector& idle, bool repeat): @@ -328,7 +348,7 @@ namespace MWMechanics if (!isWaterCreature && destinationIsAtWater(actor, mDestination)) continue; - if ((isWaterCreature || isFlyingCreature) && destinationThroughGround(currentPosition, mDestination)) + if (isDestinationHidden(actor, mDestination)) continue; if (isWaterCreature || isFlyingCreature) @@ -357,16 +377,6 @@ namespace MWMechanics return MWBase::Environment::get().getWorld()->isUnderwater(actor.getCell(), positionBelowSurface); } - /* - * Returns true if the start to end point travels through a collision point (land). - */ - bool AiWander::destinationThroughGround(const osg::Vec3f& startPoint, const osg::Vec3f& destination) { - const int mask = MWPhysics::CollisionType_World | MWPhysics::CollisionType_HeightMap | MWPhysics::CollisionType_Door; - return MWBase::Environment::get().getWorld()->castRay(startPoint.x(), startPoint.y(), startPoint.z(), - destination.x(), destination.y(), destination.z(), - mask); - } - void AiWander::completeManualWalking(const MWWorld::Ptr &actor, AiWanderStorage &storage) { stopWalking(actor, storage); mObstacleCheck.clear(); diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index 38123a970..a6c2563fc 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -126,7 +126,6 @@ namespace MWMechanics bool isPackageCompleted(const MWWorld::Ptr& actor, AiWanderStorage& storage); void wanderNearStart(const MWWorld::Ptr &actor, AiWanderStorage &storage, int wanderDistance); bool destinationIsAtWater(const MWWorld::Ptr &actor, const osg::Vec3f& destination); - bool destinationThroughGround(const osg::Vec3f& startPoint, const osg::Vec3f& destination); void completeManualWalking(const MWWorld::Ptr &actor, AiWanderStorage &storage); int mDistance; // how far the actor can wander from the spawn point diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 71948119a..e76580465 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1640,6 +1640,11 @@ namespace MWWorld return result.mHit; } + bool World::castRay(const osg::Vec3f& from, const osg::Vec3f& to, int mask, const MWWorld::ConstPtr& ignore) + { + return mPhysics->castRay(from, to, ignore, std::vector(), mask).mHit; + } + bool World::rotateDoor(const Ptr door, MWWorld::DoorState state, float duration) { const ESM::Position& objPos = door.getRefData().getPosition(); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index ed622b5b8..0fcf02891 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -426,6 +426,8 @@ namespace MWWorld bool castRay (float x1, float y1, float z1, float x2, float y2, float z2) override; + bool castRay(const osg::Vec3f& from, const osg::Vec3f& to, int mask, const MWWorld::ConstPtr& ignore) override; + void setActorCollisionMode(const Ptr& ptr, bool internal, bool external) override; bool isActorCollisionEnabled(const Ptr& ptr) override; From 4a0c056489a3adb0a48d251e77b0820a0a2d591e Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 9 Feb 2020 18:24:08 +0100 Subject: [PATCH 25/99] Do not wander to occupied area by other actor --- apps/openmw/mwbase/world.hpp | 2 + apps/openmw/mwmechanics/aiwander.cpp | 11 +++ .../mwphysics/hasspherecollisioncallback.hpp | 72 +++++++++++++++++++ apps/openmw/mwphysics/physicssystem.cpp | 17 +++++ apps/openmw/mwphysics/physicssystem.hpp | 2 + apps/openmw/mwworld/worldimp.cpp | 5 ++ apps/openmw/mwworld/worldimp.hpp | 2 + 7 files changed, 111 insertions(+) create mode 100644 apps/openmw/mwphysics/hasspherecollisioncallback.hpp diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index dc92e26a4..31d4a1b3c 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -625,6 +625,8 @@ namespace MWBase virtual osg::Vec3f getPathfindingHalfExtents(const MWWorld::ConstPtr& actor) const = 0; virtual bool hasCollisionWithDoor(const MWWorld::ConstPtr& door, const osg::Vec3f& position, const osg::Vec3f& destination) const = 0; + + virtual bool isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius, const MWWorld::ConstPtr& ignore) const = 0; }; } diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index f7023ed89..f08c19bbd 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -81,6 +81,14 @@ namespace MWMechanics | MWPhysics::CollisionType_Actor; return MWBase::Environment::get().getWorld()->castRay(position, visibleDestination, mask, actor); } + + bool isAreaOccupiedByOtherActor(const MWWorld::ConstPtr &actor, const osg::Vec3f& destination) + { + const auto world = MWBase::Environment::get().getWorld(); + const osg::Vec3f halfExtents = world->getPathfindingHalfExtents(actor); + const auto maxHalfExtent = std::max(halfExtents.x(), std::max(halfExtents.y(), halfExtents.z())); + return world->isAreaOccupiedByOtherActor(destination, 2 * maxHalfExtent, actor); + } } AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector& idle, bool repeat): @@ -351,6 +359,9 @@ namespace MWMechanics if (isDestinationHidden(actor, mDestination)) continue; + if (isAreaOccupiedByOtherActor(actor, mDestination)) + continue; + if (isWaterCreature || isFlyingCreature) mPathFinder.buildStraightPath(mDestination); else diff --git a/apps/openmw/mwphysics/hasspherecollisioncallback.hpp b/apps/openmw/mwphysics/hasspherecollisioncallback.hpp new file mode 100644 index 000000000..58e7373e5 --- /dev/null +++ b/apps/openmw/mwphysics/hasspherecollisioncallback.hpp @@ -0,0 +1,72 @@ +#ifndef OPENMW_MWPHYSICS_HASSPHERECOLLISIONCALLBACK_H +#define OPENMW_MWPHYSICS_HASSPHERECOLLISIONCALLBACK_H + +#include +#include +#include +#include + +#include + +namespace MWPhysics +{ + // https://developer.mozilla.org/en-US/docs/Games/Techniques/3D_collision_detection + bool testAabbAgainstSphere(const btVector3& aabbMin, const btVector3& aabbMax, + const btVector3& position, const btScalar radius) + { + const btVector3 nearest( + std::max(aabbMin.x(), std::min(aabbMax.x(), position.x())), + std::max(aabbMin.y(), std::min(aabbMax.y(), position.y())), + std::max(aabbMin.z(), std::min(aabbMax.z(), position.z())) + ); + return nearest.distance(position) < radius; + } + + class HasSphereCollisionCallback final : public btBroadphaseAabbCallback + { + public: + HasSphereCollisionCallback(const btVector3& position, const btScalar radius, btCollisionObject* object, + const int mask, const int group) + : mPosition(position), + mRadius(radius), + mCollisionObject(object), + mCollisionFilterMask(mask), + mCollisionFilterGroup(group) + { + } + + bool process(const btBroadphaseProxy* proxy) final + { + if (mResult) + return false; + const auto collisionObject = static_cast(proxy->m_clientObject); + if (collisionObject == mCollisionObject) + return true; + if (needsCollision(*proxy)) + mResult = testAabbAgainstSphere(proxy->m_aabbMin, proxy->m_aabbMax, mPosition, mRadius); + return !mResult; + } + + bool getResult() const + { + return mResult; + } + + private: + btVector3 mPosition; + btScalar mRadius; + btCollisionObject* mCollisionObject; + int mCollisionFilterMask; + int mCollisionFilterGroup; + bool mResult = false; + + bool needsCollision(const btBroadphaseProxy& proxy) const + { + bool collides = (proxy.m_collisionFilterGroup & mCollisionFilterMask) != 0; + collides = collides && (mCollisionFilterGroup & proxy.m_collisionFilterMask); + return collides; + } + }; +} + +#endif diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 31325cf21..69177d95d 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -45,6 +45,7 @@ #include "trace.h" #include "object.hpp" #include "heightfield.hpp" +#include "hasspherecollisioncallback.hpp" namespace MWPhysics { @@ -1444,4 +1445,20 @@ namespace MWPhysics mCollisionWorld->addCollisionObject(mWaterCollisionObject.get(), CollisionType_Water, CollisionType_Actor); } + + bool PhysicsSystem::isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius, const MWWorld::ConstPtr& ignore) const + { + btCollisionObject* object = nullptr; + const auto it = mActors.find(ignore); + if (it != mActors.end()) + object = it->second->getCollisionObject(); + const auto bulletPosition = Misc::Convert::toBullet(position); + const auto aabbMin = bulletPosition - btVector3(radius, radius, radius); + const auto aabbMax = bulletPosition + btVector3(radius, radius, radius); + const int mask = MWPhysics::CollisionType_Actor; + const int group = 0xff; + HasSphereCollisionCallback callback(bulletPosition, radius, object, mask, group); + mCollisionWorld->getBroadphase()->aabbTest(aabbMin, aabbMax, callback); + return callback.getResult(); + } } diff --git a/apps/openmw/mwphysics/physicssystem.hpp b/apps/openmw/mwphysics/physicssystem.hpp index 364a59ab1..d74e2de16 100644 --- a/apps/openmw/mwphysics/physicssystem.hpp +++ b/apps/openmw/mwphysics/physicssystem.hpp @@ -187,6 +187,8 @@ namespace MWPhysics std::for_each(mAnimatedObjects.begin(), mAnimatedObjects.end(), function); } + bool isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius, const MWWorld::ConstPtr& ignore) const; + private: void updateWater(); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index e76580465..29d1fd696 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -3914,4 +3914,9 @@ namespace MWWorld btVector3 hitNormal; return btRayAabb(localFrom, localTo, aabbMin, aabbMax, hitDistance, hitNormal); } + + bool World::isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius, const MWWorld::ConstPtr& ignore) const + { + return mPhysics->isAreaOccupiedByOtherActor(position, radius, ignore); + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 0fcf02891..fd36e5ba2 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -728,6 +728,8 @@ namespace MWWorld osg::Vec3f getPathfindingHalfExtents(const MWWorld::ConstPtr& actor) const override; bool hasCollisionWithDoor(const MWWorld::ConstPtr& door, const osg::Vec3f& position, const osg::Vec3f& destination) const override; + + bool isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius, const MWWorld::ConstPtr& ignore) const override; }; } From 9404b1dd7218f8bf77ec562fc458dd96c9198fce Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 9 Feb 2020 18:37:24 +0100 Subject: [PATCH 26/99] Stop wandering when destination is hidden or occupied by other actor --- apps/openmw/mwmechanics/aiwander.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index f08c19bbd..ff213b219 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -293,6 +293,11 @@ namespace MWMechanics completeManualWalking(actor, storage); } + if (wanderState == AiWanderStorage::Wander_Walking + && (isDestinationHidden(actor, mPathFinder.getPath().back()) + || isAreaOccupiedByOtherActor(actor, mPathFinder.getPath().back()))) + completeManualWalking(actor, storage); + return false; // AiWander package not yet completed } From fcbd3b4324717126133e556a5b38e2cb79de0cb4 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Tue, 11 Feb 2020 11:58:02 +0300 Subject: [PATCH 27/99] Don't try to attach absent shield models to NPCs --- apps/openmw/mwrender/npcanimation.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 9633e9e1b..4dea64f79 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -1011,9 +1011,11 @@ void NpcAnimation::showCarriedLeft(bool show) if (!bodyparts.empty()) mesh = getShieldBodypartMesh(bodyparts, !mNpc->isMale()); } - if (addOrReplaceIndividualPart(ESM::PRT_Shield, MWWorld::InventoryStore::Slot_CarriedLeft, 1, - mesh, !iter->getClass().getEnchantment(*iter).empty(), &glowColor)) + if (mesh.empty() || addOrReplaceIndividualPart(ESM::PRT_Shield, MWWorld::InventoryStore::Slot_CarriedLeft, 1, + mesh, !iter->getClass().getEnchantment(*iter).empty(), &glowColor)) { + if (mesh.empty()) + reserveIndividualPart(ESM::PRT_Shield, MWWorld::InventoryStore::Slot_CarriedLeft, 1); if (iter->getTypeName() == typeid(ESM::Light).name() && mObjectParts[ESM::PRT_Shield]) addExtraLight(mObjectParts[ESM::PRT_Shield]->getNode()->asGroup(), iter->get()->mBase); } From 013953ec7f12133f1b265721f1ddcf32ecfa0e87 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Tue, 11 Feb 2020 12:33:22 +0300 Subject: [PATCH 28/99] Support Wander movement deceleration --- apps/openmw/mwmechanics/aiescort.hpp | 2 +- apps/openmw/mwmechanics/aifollow.hpp | 2 +- apps/openmw/mwmechanics/aipackage.hpp | 2 +- apps/openmw/mwmechanics/aitravel.hpp | 2 +- apps/openmw/mwmechanics/aiwander.hpp | 10 ++++++++++ 5 files changed, 14 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwmechanics/aiescort.hpp b/apps/openmw/mwmechanics/aiescort.hpp index f7761e58f..e4319b425 100644 --- a/apps/openmw/mwmechanics/aiescort.hpp +++ b/apps/openmw/mwmechanics/aiescort.hpp @@ -44,7 +44,7 @@ namespace MWMechanics void fastForward(const MWWorld::Ptr& actor, AiState& state); - virtual osg::Vec3f getDestination() { return osg::Vec3f(mX, mY, mZ); } + virtual osg::Vec3f getDestination() const { return osg::Vec3f(mX, mY, mZ); } private: std::string mCellId; diff --git a/apps/openmw/mwmechanics/aifollow.hpp b/apps/openmw/mwmechanics/aifollow.hpp index 7b5a9e63e..24263bbc0 100644 --- a/apps/openmw/mwmechanics/aifollow.hpp +++ b/apps/openmw/mwmechanics/aifollow.hpp @@ -76,7 +76,7 @@ namespace MWMechanics void fastForward(const MWWorld::Ptr& actor, AiState& state); - virtual osg::Vec3f getDestination() + virtual osg::Vec3f getDestination() const { MWWorld::Ptr target = getTarget(); if (target.isEmpty()) diff --git a/apps/openmw/mwmechanics/aipackage.hpp b/apps/openmw/mwmechanics/aipackage.hpp index b9b3baf64..ec0715e52 100644 --- a/apps/openmw/mwmechanics/aipackage.hpp +++ b/apps/openmw/mwmechanics/aipackage.hpp @@ -102,7 +102,7 @@ namespace MWMechanics /// Return true if this package should repeat. Currently only used for Wander packages. virtual bool getRepeat() const; - virtual osg::Vec3f getDestination() { return osg::Vec3f(0, 0, 0); } + virtual osg::Vec3f getDestination() const { return osg::Vec3f(0, 0, 0); } // Return true if any loaded actor with this AI package must be active. virtual bool alwaysActive() const { return false; } diff --git a/apps/openmw/mwmechanics/aitravel.hpp b/apps/openmw/mwmechanics/aitravel.hpp index a333c83fd..e7895462f 100644 --- a/apps/openmw/mwmechanics/aitravel.hpp +++ b/apps/openmw/mwmechanics/aitravel.hpp @@ -36,7 +36,7 @@ namespace MWMechanics virtual bool alwaysActive() const { return true; } - virtual osg::Vec3f getDestination() { return osg::Vec3f(mX, mY, mZ); } + virtual osg::Vec3f getDestination() const { return osg::Vec3f(mX, mY, mZ); } private: float mX; diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index 38123a970..767c8c2e3 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -97,6 +97,8 @@ namespace MWMechanics virtual int getTypeId() const; + virtual bool useVariableSpeed() const { return true;} + virtual void writeState(ESM::AiSequence::AiSequence &sequence) const; virtual void fastForward(const MWWorld::Ptr& actor, AiState& state); @@ -105,6 +107,14 @@ namespace MWMechanics osg::Vec3f getDestination(const MWWorld::Ptr& actor) const; + virtual osg::Vec3f getDestination() const + { + if (!mHasDestination) + return osg::Vec3f(0, 0, 0); + + return mDestination; + } + private: // NOTE: mDistance and mDuration must be set already void init(); From 61b60c8a9498a19e98211c2ac81b3c28e6302ea2 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Mon, 13 Jan 2020 20:12:44 +0300 Subject: [PATCH 29/99] Add an infinite fall failsafe (feature 1415) --- CHANGELOG.md | 1 + apps/openmw/mwworld/scene.cpp | 29 ++++++++++++++++++++++++++--- apps/openmw/mwworld/scene.hpp | 1 + 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 757ff0e35..48932d3ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -198,6 +198,7 @@ Bug #5261: Creatures can sometimes become stuck playing idles and never wander again Bug #5269: Editor: Cell lighting in resaved cleaned content files is corrupted Bug #5278: Console command Show doesn't fall back to global variable after local var not found + Feature #1415: Infinite fall failsafe Feature #1774: Handle AvoidNode Feature #2229: Improve pathfinding AI Feature #3025: Analogue gamepad movement controls diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 4de04251b..ca84cf8f7 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -33,6 +33,7 @@ #include "../mwphysics/object.hpp" #include "../mwphysics/heightfield.hpp" +#include "actionteleport.hpp" #include "player.hpp" #include "localscripts.hpp" #include "esmstore.hpp" @@ -255,12 +256,22 @@ namespace } } - struct AdjustPositionVisitor + struct PositionVisitor { + float mLowestPos = std::numeric_limits::max(); + bool operator() (const MWWorld::Ptr& ptr) { if (!ptr.getRefData().isDeleted() && ptr.getRefData().isEnabled()) + { + if (!ptr.getClass().isActor()) + { + float objectPosZ = ptr.getRefData().getPosition().pos[2]; + if (objectPosZ < mLowestPos) + mLowestPos = objectPosZ; + } ptr.getClass().adjustPosition (ptr, false); + } return true; } }; @@ -487,6 +498,16 @@ namespace MWWorld const auto player = MWBase::Environment::get().getWorld()->getPlayerPtr(); navigator->update(player.getRefData().getPosition().asVec3()); + const float fallThreshold = 90.f; + if (mCurrentCell && !mCurrentCell->isExterior() && pos.z() < mLowestPos - fallThreshold) + { + ESM::Position newPos; + std::string cellName = mCurrentCell->getCell()->mName; + MWBase::Environment::get().getWorld()->findInteriorPosition(cellName, newPos); + if (newPos.pos[2] >= mLowestPos - fallThreshold) + MWWorld::ActionTeleport(cellName, newPos, false).execute(player); + } + if (!mCurrentCell || !mCurrentCell->isExterior()) return; @@ -877,8 +898,10 @@ namespace MWWorld insertVisitor.insert([&] (const MWWorld::Ptr& ptr) { addObject(ptr, *mPhysics, mNavigator); }); // do adjustPosition (snapping actors to ground) after objects are loaded, so we don't depend on the loading order - AdjustPositionVisitor adjustPosVisitor; - cell.forEach (adjustPosVisitor); + // Also note the lowest object position in the cell to allow infinite fall fail safe to work + PositionVisitor posVisitor; + cell.forEach (posVisitor); + mLowestPos = posVisitor.mLowestPos; } void Scene::addObjectToScene (const Ptr& ptr) diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index da795f84b..c08c5c163 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -84,6 +84,7 @@ namespace MWWorld float mPredictionTime; osg::Vec3f mLastPlayerPos; + float mLowestPos; void insertCell (CellStore &cell, Loading::Listener* loadingListener, bool test = false); From 1e4565a15c05d7c2059842b1c0435f05f61d531d Mon Sep 17 00:00:00 2001 From: elsid Date: Tue, 11 Feb 2020 22:24:18 +0100 Subject: [PATCH 30/99] Avoid using temporary vector to get items owned by --- apps/openmw/mwworld/cellstore.hpp | 2 +- apps/openmw/mwworld/scene.cpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 23 +++++------------------ 3 files changed, 7 insertions(+), 20 deletions(-) diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index 4872625c1..9529de855 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -269,7 +269,7 @@ namespace MWWorld /// \attention This function also lists deleted (count 0) objects! /// \return Iteration completed? template - bool forEach (Visitor& visitor) + bool forEach (Visitor&& visitor) { if (mState != State_Loaded) return false; diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 4de04251b..36e14e4a6 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -330,7 +330,7 @@ namespace MWWorld const auto navigator = MWBase::Environment::get().getWorld()->getNavigator(); ListAndResetObjectsVisitor visitor; - (*iter)->forEach(visitor); + (*iter)->forEach(visitor); const auto world = MWBase::Environment::get().getWorld(); for (const auto& ptr : visitor.mObjects) { diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 71948119a..33feac4af 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2745,28 +2745,15 @@ namespace MWWorld } } - struct ListObjectsVisitor - { - std::vector mObjects; - - bool operator() (Ptr ptr) - { - if (ptr.getRefData().getBaseNode()) - mObjects.push_back(ptr); - return true; - } - }; - void World::getItemsOwnedBy (const MWWorld::ConstPtr& npc, std::vector& out) { for (CellStore* cellstore : mWorldScene->getActiveCells()) { - ListObjectsVisitor visitor; - cellstore->forEach(visitor); - - for (const Ptr &object : visitor.mObjects) - if (Misc::StringUtils::ciEqual(object.getCellRef().getOwner(), npc.getCellRef().getRefId())) - out.push_back(object); + cellstore->forEach([&] (const auto& ptr) { + if (ptr.getRefData().getBaseNode() && Misc::StringUtils::ciEqual(ptr.getCellRef().getOwner(), npc.getCellRef().getRefId())) + out.push_back(ptr); + return true; + }); } } From 807f550386a1eac6f7306589a4e4a01e783e65df Mon Sep 17 00:00:00 2001 From: Alexander Olofsson Date: Thu, 13 Feb 2020 19:15:33 +0100 Subject: [PATCH 31/99] Fix building mwrender/sky with OSG 3.6.5 OSG commit aff574b completely replaces the method of doing user-defined query geometry, removing support for modifying the default geometry. --- apps/openmw/mwrender/sky.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 3996f472c..e13f5a7b6 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -558,16 +558,22 @@ private: osg::ref_ptr oqn = new osg::OcclusionQueryNode; oqn->setQueriesEnabled(true); +#if OSG_VERSION_GREATER_OR_EQUAL(3, 6, 5) + // With OSG 3.6.5, the method of providing user defined query geometry has been completely replaced + osg::ref_ptr queryGeom = new osg::QueryGeometry(oqn->getName()); +#else + auto* queryGeom = oqn->getQueryGeometry(); +#endif + // Make it fast! A DYNAMIC query geometry means we can't break frame until the flare is rendered (which is rendered after all the other geometry, // so that would be pretty bad). STATIC should be safe, since our node's local bounds are static, thus computeBounds() which modifies the queryGeometry // is only called once. // Note the debug geometry setDebugDisplay(true) is always DYNAMIC and that can't be changed, not a big deal. - oqn->getQueryGeometry()->setDataVariance(osg::Object::STATIC); + queryGeom->setDataVariance(osg::Object::STATIC); // Set up the query geometry to match the actual sun's rendering shape. osg::OcclusionQueryNode wasn't originally intended to allow this, // normally it would automatically adjust the query geometry to match the sub graph's bounding box. The below hack is needed to // circumvent this. - osg::Geometry* queryGeom = oqn->getQueryGeometry(); queryGeom->setVertexArray(mGeom->getVertexArray()); queryGeom->setTexCoordArray(0, mGeom->getTexCoordArray(0), osg::Array::BIND_PER_VERTEX); queryGeom->removePrimitiveSet(0, oqn->getQueryGeometry()->getNumPrimitiveSets()); @@ -578,6 +584,10 @@ private: // Still need a proper bounding sphere. oqn->setInitialBound(queryGeom->getBound()); +#if OSG_VERSION_GREATER_OR_EQUAL(3, 6, 5) + oqn->setQueryGeometry(queryGeom.release()); +#endif + osg::StateSet* queryStateSet = new osg::StateSet; if (queryVisible) { From 7db58a893a1448b7229de5464696b3cc6eb7663c Mon Sep 17 00:00:00 2001 From: Alexander Olofsson Date: Thu, 13 Feb 2020 19:32:17 +0100 Subject: [PATCH 32/99] Ensure osg/Version is included --- apps/openmw/mwrender/sky.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index e13f5a7b6..7cf2af88d 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include From 9db0bbf25576b2622503ebf20a3b69fd8d2935ac Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Fri, 14 Feb 2020 15:26:46 +0300 Subject: [PATCH 33/99] Make infinite failsafe logic more forgiving --- apps/openmw/mwworld/scene.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 3d08121f6..7c46a2fc7 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -498,13 +498,13 @@ namespace MWWorld const auto player = MWBase::Environment::get().getWorld()->getPlayerPtr(); navigator->update(player.getRefData().getPosition().asVec3()); - const float fallThreshold = 90.f; + const float fallThreshold = 256.f; if (mCurrentCell && !mCurrentCell->isExterior() && pos.z() < mLowestPos - fallThreshold) { ESM::Position newPos; std::string cellName = mCurrentCell->getCell()->mName; MWBase::Environment::get().getWorld()->findInteriorPosition(cellName, newPos); - if (newPos.pos[2] >= mLowestPos - fallThreshold) + if (newPos.pos[2] >= mLowestPos) MWWorld::ActionTeleport(cellName, newPos, false).execute(player); } From 5de1e0fb0cf45fd056da73e0d06e0c9b78dddd36 Mon Sep 17 00:00:00 2001 From: Alexander Olofsson Date: Sat, 15 Feb 2020 03:12:36 +0100 Subject: [PATCH 34/99] Use queryGeom for all query geometry accesses --- apps/openmw/mwrender/sky.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 7cf2af88d..84d107815 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -577,7 +577,7 @@ private: // circumvent this. queryGeom->setVertexArray(mGeom->getVertexArray()); queryGeom->setTexCoordArray(0, mGeom->getTexCoordArray(0), osg::Array::BIND_PER_VERTEX); - queryGeom->removePrimitiveSet(0, oqn->getQueryGeometry()->getNumPrimitiveSets()); + queryGeom->removePrimitiveSet(0, queryGeom->getNumPrimitiveSets()); queryGeom->addPrimitiveSet(mGeom->getPrimitiveSet(0)); // Hack to disable unwanted awful code inside OcclusionQueryNode::computeBound. From 84979fa8b77632b5734f3df28d7b4dfb543d933a Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 12 Feb 2020 13:20:24 +0400 Subject: [PATCH 35/99] Move VisMask to components --- apps/opencs/CMakeLists.txt | 4 -- apps/opencs/view/render/cell.cpp | 22 ++++---- apps/opencs/view/render/cellarrow.cpp | 7 ++- apps/opencs/view/render/cellborder.cpp | 5 +- apps/opencs/view/render/cellmarker.cpp | 4 +- apps/opencs/view/render/cellwater.cpp | 4 +- apps/opencs/view/render/instancemode.cpp | 28 +++++----- .../view/render/instanceselectionmode.cpp | 8 +-- apps/opencs/view/render/mask.hpp | 35 ------------- apps/opencs/view/render/object.cpp | 8 +-- .../view/render/pagedworldspacewidget.cpp | 17 +++--- apps/opencs/view/render/pathgrid.cpp | 5 +- apps/opencs/view/render/pathgridmode.cpp | 22 ++++---- .../view/render/pathgridselectionmode.cpp | 6 +-- apps/opencs/view/render/scenewidget.cpp | 14 ++--- apps/opencs/view/render/selectionmode.hpp | 2 - apps/opencs/view/render/tagbase.cpp | 4 +- apps/opencs/view/render/tagbase.hpp | 8 +-- apps/opencs/view/render/terrainshapemode.cpp | 4 +- .../opencs/view/render/terraintexturemode.cpp | 4 +- .../view/render/unpagedworldspacewidget.cpp | 5 +- apps/opencs/view/render/worldspacewidget.cpp | 13 ++--- apps/opencs/view/render/worldspacewidget.hpp | 1 - apps/openmw/CMakeLists.txt | 2 +- apps/openmw/engine.cpp | 5 +- apps/openmw/mwclass/activator.cpp | 4 +- apps/openmw/mwclass/door.cpp | 4 +- apps/openmw/mwclass/static.cpp | 4 +- apps/openmw/mwgui/loadingscreen.cpp | 7 ++- apps/openmw/mwgui/windowmanagerimp.cpp | 7 ++- apps/openmw/mwmechanics/actors.cpp | 11 ++-- apps/openmw/mwrender/actoranimation.cpp | 7 ++- apps/openmw/mwrender/actorspaths.cpp | 4 +- apps/openmw/mwrender/animation.cpp | 10 ++-- apps/openmw/mwrender/bulletdebugdraw.cpp | 4 +- apps/openmw/mwrender/characterpreview.cpp | 12 ++--- apps/openmw/mwrender/effectmanager.cpp | 4 +- apps/openmw/mwrender/globalmap.cpp | 7 ++- apps/openmw/mwrender/localmap.cpp | 11 ++-- apps/openmw/mwrender/navmesh.cpp | 4 +- apps/openmw/mwrender/npcanimation.cpp | 4 +- apps/openmw/mwrender/objects.cpp | 9 ++-- apps/openmw/mwrender/pathgrid.cpp | 5 +- apps/openmw/mwrender/renderingmanager.cpp | 52 +++++++++---------- apps/openmw/mwrender/ripplesimulation.cpp | 5 +- apps/openmw/mwrender/sky.cpp | 24 ++++----- apps/openmw/mwrender/water.cpp | 28 +++++----- apps/openmw/mwworld/projectilemanager.cpp | 6 +-- apps/openmw/mwworld/worldimp.cpp | 4 +- components/CMakeLists.txt | 2 +- components/nifosg/controller.cpp | 3 +- components/nifosg/nifloader.cpp | 6 +-- components/resource/stats.cpp | 5 +- components/sceneutil/lightutil.cpp | 11 ++-- components/sceneutil/lightutil.hpp | 4 +- components/sceneutil/optimizer.hpp | 4 +- components/sceneutil/shadow.cpp | 3 +- .../sceneutil}/vismask.hpp | 22 ++++++-- components/terrain/cellborder.cpp | 9 ++-- components/terrain/cellborder.hpp | 2 +- components/terrain/quadtreeworld.cpp | 7 +-- components/terrain/quadtreeworld.hpp | 2 +- components/terrain/terraingrid.cpp | 4 +- components/terrain/terraingrid.hpp | 2 +- components/terrain/world.cpp | 11 ++-- components/terrain/world.hpp | 2 +- 66 files changed, 271 insertions(+), 302 deletions(-) delete mode 100644 apps/opencs/view/render/mask.hpp rename {apps/openmw/mwrender => components/sceneutil}/vismask.hpp (82%) diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index f34ad66fb..af8bf8d55 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -97,10 +97,6 @@ opencs_units_noqt (view/render cellarrow cellmarker cellborder pathgrid ) -opencs_hdrs_noqt (view/render - mask - ) - opencs_units (view/tools reportsubview reporttable searchsubview searchbox merge diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp index 056c50e45..48659865a 100644 --- a/apps/opencs/view/render/cell.cpp +++ b/apps/opencs/view/render/cell.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include "../../model/world/idtable.hpp" @@ -21,7 +22,6 @@ #include "cellborder.hpp" #include "cellarrow.hpp" #include "cellmarker.hpp" -#include "mask.hpp" #include "pathgrid.hpp" #include "terrainstorage.hpp" #include "object.hpp" @@ -92,7 +92,7 @@ bool CSVRender::Cell::addObjects (int start, int end) std::unique_ptr object (new Object (mData, mCellNode, id, false)); - if (mSubModeElementMask & Mask_Reference) + if (mSubModeElementMask & SceneUtil::Mask_EditorReference) object->setSubMode (mSubMode); mObjects.insert (std::make_pair (id, object.release())); @@ -134,7 +134,7 @@ void CSVRender::Cell::updateLand() else { mTerrain.reset(new Terrain::TerrainGrid(mCellNode, mCellNode, - mData.getResourceSystem().get(), mTerrainStorage, Mask_Terrain)); + mData.getResourceSystem().get(), mTerrainStorage)); } mTerrain->loadCell(esmLand.mX, esmLand.mY); @@ -434,7 +434,7 @@ void CSVRender::Cell::reloadAssets() void CSVRender::Cell::setSelection (int elementMask, Selection mode) { - if (elementMask & Mask_Reference) + if (elementMask & SceneUtil::Mask_EditorReference) { for (std::map::const_iterator iter (mObjects.begin()); iter!=mObjects.end(); ++iter) @@ -451,7 +451,7 @@ void CSVRender::Cell::setSelection (int elementMask, Selection mode) iter->second->setSelected (selected); } } - if (mPathgrid && elementMask & Mask_Pathgrid) + if (mPathgrid && elementMask & SceneUtil::Mask_Pathgrid) { // Only one pathgrid may be selected, so some operations will only have an effect // if the pathgrid is already focused @@ -546,12 +546,12 @@ std::vector > CSVRender::Cell::getSelection (un { std::vector > result; - if (elementMask & Mask_Reference) + if (elementMask & SceneUtil::Mask_EditorReference) for (std::map::const_iterator iter (mObjects.begin()); iter!=mObjects.end(); ++iter) if (iter->second->getSelected()) result.push_back (iter->second->getTag()); - if (mPathgrid && elementMask & Mask_Pathgrid) + if (mPathgrid && elementMask & SceneUtil::Mask_Pathgrid) if (mPathgrid->isSelected()) result.push_back(mPathgrid->getTag()); @@ -562,7 +562,7 @@ std::vector > CSVRender::Cell::getEdited (unsig { std::vector > result; - if (elementMask & Mask_Reference) + if (elementMask & SceneUtil::Mask_EditorReference) for (std::map::const_iterator iter (mObjects.begin()); iter!=mObjects.end(); ++iter) if (iter->second->isEdited()) @@ -576,7 +576,7 @@ void CSVRender::Cell::setSubMode (int subMode, unsigned int elementMask) mSubMode = subMode; mSubModeElementMask = elementMask; - if (elementMask & Mask_Reference) + if (elementMask & SceneUtil::Mask_EditorReference) for (std::map::const_iterator iter (mObjects.begin()); iter!=mObjects.end(); ++iter) iter->second->setSubMode (subMode); @@ -584,10 +584,10 @@ void CSVRender::Cell::setSubMode (int subMode, unsigned int elementMask) void CSVRender::Cell::reset (unsigned int elementMask) { - if (elementMask & Mask_Reference) + if (elementMask & SceneUtil::Mask_EditorReference) for (std::map::const_iterator iter (mObjects.begin()); iter!=mObjects.end(); ++iter) iter->second->reset(); - if (mPathgrid && elementMask & Mask_Pathgrid) + if (mPathgrid && elementMask & SceneUtil::Mask_Pathgrid) mPathgrid->resetIndicators(); } diff --git a/apps/opencs/view/render/cellarrow.cpp b/apps/opencs/view/render/cellarrow.cpp index b6fee1545..3fb4f711d 100644 --- a/apps/opencs/view/render/cellarrow.cpp +++ b/apps/opencs/view/render/cellarrow.cpp @@ -11,11 +11,10 @@ #include "../../model/prefs/shortcutmanager.hpp" #include - -#include "mask.hpp" +#include CSVRender::CellArrowTag::CellArrowTag (CellArrow *arrow) -: TagBase (Mask_CellArrow), mArrow (arrow) +: TagBase (SceneUtil::Mask_EditorCellArrow), mArrow (arrow) {} CSVRender::CellArrow *CSVRender::CellArrowTag::getCellArrow() const @@ -175,7 +174,7 @@ CSVRender::CellArrow::CellArrow (osg::Group *cellNode, Direction direction, mParentNode->addChild (mBaseNode); - mBaseNode->setNodeMask (Mask_CellArrow); + mBaseNode->setNodeMask (SceneUtil::Mask_EditorCellArrow); adjustTransform(); buildShape(); diff --git a/apps/opencs/view/render/cellborder.cpp b/apps/opencs/view/render/cellborder.cpp index 6073807ce..0789ee22a 100644 --- a/apps/opencs/view/render/cellborder.cpp +++ b/apps/opencs/view/render/cellborder.cpp @@ -7,8 +7,7 @@ #include #include - -#include "mask.hpp" +#include #include "../../model/world/cellcoordinates.hpp" @@ -20,7 +19,7 @@ CSVRender::CellBorder::CellBorder(osg::Group* cellNode, const CSMWorld::CellCoor : mParentNode(cellNode) { mBaseNode = new osg::PositionAttitudeTransform(); - mBaseNode->setNodeMask(Mask_CellBorder); + mBaseNode->setNodeMask(SceneUtil::Mask_EditorCellBorder); mBaseNode->setPosition(osg::Vec3f(coords.getX() * CellSize, coords.getY() * CellSize, 10)); mParentNode->addChild(mBaseNode); diff --git a/apps/opencs/view/render/cellmarker.cpp b/apps/opencs/view/render/cellmarker.cpp index 3de96ab02..e629aa827 100644 --- a/apps/opencs/view/render/cellmarker.cpp +++ b/apps/opencs/view/render/cellmarker.cpp @@ -8,7 +8,7 @@ #include CSVRender::CellMarkerTag::CellMarkerTag(CellMarker *marker) -: TagBase(Mask_CellMarker), mMarker(marker) +: TagBase(SceneUtil::Mask_EditorCellMarker), mMarker(marker) {} CSVRender::CellMarker *CSVRender::CellMarkerTag::getCellMarker() const @@ -79,7 +79,7 @@ CSVRender::CellMarker::CellMarker( mMarkerNode->getOrCreateStateSet()->setAttribute(mat); mMarkerNode->setUserData(new CellMarkerTag(this)); - mMarkerNode->setNodeMask(Mask_CellMarker); + mMarkerNode->setNodeMask(SceneUtil::Mask_EditorCellMarker); mCellNode->addChild(mMarkerNode); diff --git a/apps/opencs/view/render/cellwater.cpp b/apps/opencs/view/render/cellwater.cpp index 435178860..8edbc1cd1 100644 --- a/apps/opencs/view/render/cellwater.cpp +++ b/apps/opencs/view/render/cellwater.cpp @@ -11,12 +11,12 @@ #include #include #include +#include #include "../../model/world/cell.hpp" #include "../../model/world/cellcoordinates.hpp" #include "../../model/world/data.hpp" -#include "mask.hpp" namespace CSVRender { @@ -38,7 +38,7 @@ namespace CSVRender mWaterTransform->setPosition(osg::Vec3f(cellCoords.getX() * CellSize + CellSize / 2.f, cellCoords.getY() * CellSize + CellSize / 2.f, 0)); - mWaterTransform->setNodeMask(Mask_Water); + mWaterTransform->setNodeMask(SceneUtil::Mask_Water); mParentNode->addChild(mWaterTransform); mWaterNode = new osg::Geode(); diff --git a/apps/opencs/view/render/instancemode.cpp b/apps/opencs/view/render/instancemode.cpp index 0cf58038d..dd5dc3bac 100644 --- a/apps/opencs/view/render/instancemode.cpp +++ b/apps/opencs/view/render/instancemode.cpp @@ -14,7 +14,7 @@ #include "../widget/scenetoolbar.hpp" #include "../widget/scenetoolmode.hpp" -#include "mask.hpp" +#include #include "object.hpp" #include "worldspacewidget.hpp" @@ -90,7 +90,7 @@ osg::Vec3f CSVRender::InstanceMode::getScreenCoords(const osg::Vec3f& pos) } CSVRender::InstanceMode::InstanceMode (WorldspaceWidget *worldspaceWidget, QWidget *parent) -: EditMode (worldspaceWidget, QIcon (":scenetoolbar/editing-instance"), Mask_Reference | Mask_Terrain, "Instance editing", +: EditMode (worldspaceWidget, QIcon (":scenetoolbar/editing-instance"), SceneUtil::Mask_EditorReference | SceneUtil::Mask_Terrain, "Instance editing", parent), mSubMode (0), mSubModeId ("move"), mSelectionMode (0), mDragMode (DragMode_None), mDragAxis (-1), mLocked (false), mUnitScaleDist(1) { @@ -137,13 +137,13 @@ void CSVRender::InstanceMode::activate (CSVWidget::SceneToolbar *toolbar) std::string subMode = mSubMode->getCurrentId(); - getWorldspaceWidget().setSubMode (getSubModeFromId (subMode), Mask_Reference); + getWorldspaceWidget().setSubMode (getSubModeFromId (subMode), SceneUtil::Mask_EditorReference); } void CSVRender::InstanceMode::deactivate (CSVWidget::SceneToolbar *toolbar) { mDragMode = DragMode_None; - getWorldspaceWidget().reset (Mask_Reference); + getWorldspaceWidget().reset (SceneUtil::Mask_EditorReference); if (mSelectionMode) { @@ -196,7 +196,7 @@ void CSVRender::InstanceMode::secondaryEditPressed (const WorldspaceHitResult& h void CSVRender::InstanceMode::primarySelectPressed (const WorldspaceHitResult& hit) { - getWorldspaceWidget().clearSelection (Mask_Reference); + getWorldspaceWidget().clearSelection (SceneUtil::Mask_EditorReference); if (hit.tag) { @@ -231,13 +231,13 @@ bool CSVRender::InstanceMode::primaryEditStartDrag (const QPoint& pos) WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask()); - std::vector > selection = getWorldspaceWidget().getSelection (Mask_Reference); + std::vector > selection = getWorldspaceWidget().getSelection (SceneUtil::Mask_EditorReference); if (selection.empty()) { // Only change selection at the start of drag if no object is already selected if (hit.tag && CSMPrefs::get()["3D Scene Input"]["context-select"].isTrue()) { - getWorldspaceWidget().clearSelection (Mask_Reference); + getWorldspaceWidget().clearSelection (SceneUtil::Mask_EditorReference); if (CSVRender::ObjectTag *objectTag = dynamic_cast (hit.tag.get())) { CSVRender::Object* object = objectTag->mObject; @@ -245,7 +245,7 @@ bool CSVRender::InstanceMode::primaryEditStartDrag (const QPoint& pos) } } - selection = getWorldspaceWidget().getSelection (Mask_Reference); + selection = getWorldspaceWidget().getSelection (SceneUtil::Mask_EditorReference); if (selection.empty()) return false; } @@ -271,7 +271,7 @@ bool CSVRender::InstanceMode::primaryEditStartDrag (const QPoint& pos) mDragMode = DragMode_Scale; // Calculate scale factor - std::vector > editedSelection = getWorldspaceWidget().getEdited (Mask_Reference); + std::vector > editedSelection = getWorldspaceWidget().getEdited (SceneUtil::Mask_EditorReference); osg::Vec3f center = getScreenCoords(getSelectionCenter(editedSelection)); int widgetHeight = getWorldspaceWidget().height(); @@ -307,7 +307,7 @@ void CSVRender::InstanceMode::drag (const QPoint& pos, int diffX, int diffY, dou osg::Vec3f offset; osg::Quat rotation; - std::vector > selection = getWorldspaceWidget().getEdited (Mask_Reference); + std::vector > selection = getWorldspaceWidget().getEdited (SceneUtil::Mask_EditorReference); if (mDragMode == DragMode_Move) { @@ -464,7 +464,7 @@ void CSVRender::InstanceMode::drag (const QPoint& pos, int diffX, int diffY, dou void CSVRender::InstanceMode::dragCompleted(const QPoint& pos) { std::vector > selection = - getWorldspaceWidget().getEdited (Mask_Reference); + getWorldspaceWidget().getEdited (SceneUtil::Mask_EditorReference); QUndoStack& undoStack = getWorldspaceWidget().getDocument().getUndoStack(); @@ -496,7 +496,7 @@ void CSVRender::InstanceMode::dragCompleted(const QPoint& pos) void CSVRender::InstanceMode::dragAborted() { - getWorldspaceWidget().reset (Mask_Reference); + getWorldspaceWidget().reset (SceneUtil::Mask_EditorReference); mDragMode = DragMode_None; } @@ -515,7 +515,7 @@ void CSVRender::InstanceMode::dragWheel (int diff, double speedFactor) offset *= diff * speedFactor; std::vector > selection = - getWorldspaceWidget().getEdited (Mask_Reference); + getWorldspaceWidget().getEdited (SceneUtil::Mask_EditorReference); for (std::vector >::iterator iter (selection.begin()); iter!=selection.end(); ++iter) @@ -657,5 +657,5 @@ void CSVRender::InstanceMode::subModeChanged (const std::string& id) { mSubModeId = id; getWorldspaceWidget().abortDrag(); - getWorldspaceWidget().setSubMode (getSubModeFromId (id), Mask_Reference); + getWorldspaceWidget().setSubMode (getSubModeFromId (id), SceneUtil::Mask_EditorReference); } diff --git a/apps/opencs/view/render/instanceselectionmode.cpp b/apps/opencs/view/render/instanceselectionmode.cpp index bf8ede0eb..470f34e5d 100644 --- a/apps/opencs/view/render/instanceselectionmode.cpp +++ b/apps/opencs/view/render/instanceselectionmode.cpp @@ -6,13 +6,15 @@ #include "../../model/world/idtable.hpp" #include "../../model/world/commands.hpp" +#include + #include "worldspacewidget.hpp" #include "object.hpp" namespace CSVRender { InstanceSelectionMode::InstanceSelectionMode(CSVWidget::SceneToolbar* parent, WorldspaceWidget& worldspaceWidget) - : SelectionMode(parent, worldspaceWidget, Mask_Reference) + : SelectionMode(parent, worldspaceWidget, SceneUtil::Mask_EditorReference) { mSelectSame = new QAction("Extend selection to instances with same object ID", this); mDeleteSelection = new QAction("Delete selected instances", this); @@ -36,12 +38,12 @@ namespace CSVRender void InstanceSelectionMode::selectSame() { - getWorldspaceWidget().selectAllWithSameParentId(Mask_Reference); + getWorldspaceWidget().selectAllWithSameParentId(SceneUtil::Mask_EditorReference); } void InstanceSelectionMode::deleteSelection() { - std::vector > selection = getWorldspaceWidget().getSelection(Mask_Reference); + std::vector > selection = getWorldspaceWidget().getSelection(SceneUtil::Mask_EditorReference); CSMWorld::IdTable& referencesTable = dynamic_cast( *getWorldspaceWidget().getDocument().getData().getTableModel(CSMWorld::UniversalId::Type_References)); diff --git a/apps/opencs/view/render/mask.hpp b/apps/opencs/view/render/mask.hpp deleted file mode 100644 index 55b7c823f..000000000 --- a/apps/opencs/view/render/mask.hpp +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef CSV_RENDER_ELEMENTS_H -#define CSV_RENDER_ELEMENTS_H - -namespace CSVRender -{ - - /// Node masks used on the OSG scene graph in OpenMW-CS. - /// @note See the respective file in OpenMW (apps/openmw/mwrender/vismask.hpp) - /// for general usage hints about node masks. - /// @copydoc MWRender::VisMask - enum Mask - { - // internal use within NifLoader, do not change - Mask_UpdateVisitor = 0x1, - - // elements that are part of the actual scene - Mask_Reference = 0x2, - Mask_Pathgrid = 0x4, - Mask_Water = 0x8, - Mask_Fog = 0x10, - Mask_Terrain = 0x20, - - // used within models - Mask_ParticleSystem = 0x100, - - Mask_Lighting = 0x200, - - // control elements - Mask_CellMarker = 0x10000, - Mask_CellArrow = 0x20000, - Mask_CellBorder = 0x40000 - }; -} - -#endif diff --git a/apps/opencs/view/render/object.cpp b/apps/opencs/view/render/object.cpp index 484f1a48a..02f89b0ab 100644 --- a/apps/opencs/view/render/object.cpp +++ b/apps/opencs/view/render/object.cpp @@ -29,9 +29,9 @@ #include #include #include +#include #include "actor.hpp" -#include "mask.hpp" const float CSVRender::Object::MarkerShaftWidth = 30; @@ -58,7 +58,7 @@ namespace CSVRender::ObjectTag::ObjectTag (Object* object) -: TagBase (Mask_Reference), mObject (object) +: TagBase (SceneUtil::Mask_EditorReference), mObject (object) {} QString CSVRender::ObjectTag::getToolTip (bool hideBasics) const @@ -140,7 +140,7 @@ void CSVRender::Object::update() if (light) { bool isExterior = false; // FIXME - SceneUtil::addLight(mBaseNode, light, Mask_ParticleSystem, Mask_Lighting, isExterior); + SceneUtil::addLight(mBaseNode, light, isExterior); } } @@ -429,7 +429,7 @@ CSVRender::Object::Object (CSMWorld::Data& data, osg::Group* parentNode, parentNode->addChild (mRootNode); - mRootNode->setNodeMask(Mask_Reference); + mRootNode->setNodeMask(SceneUtil::Mask_EditorReference); if (referenceable) { diff --git a/apps/opencs/view/render/pagedworldspacewidget.cpp b/apps/opencs/view/render/pagedworldspacewidget.cpp index b5d9234e4..8fafa8459 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.cpp +++ b/apps/opencs/view/render/pagedworldspacewidget.cpp @@ -21,7 +21,6 @@ #include "../widget/scenetooltoggle2.hpp" #include "editmode.hpp" -#include "mask.hpp" #include "cameracontroller.hpp" #include "cellarrow.hpp" #include "terraintexturemode.hpp" @@ -127,8 +126,8 @@ void CSVRender::PagedWorldspaceWidget::addVisibilitySelectorButtons ( CSVWidget::SceneToolToggle2 *tool) { WorldspaceWidget::addVisibilitySelectorButtons (tool); - tool->addButton (Button_Terrain, Mask_Terrain, "Terrain"); - tool->addButton (Button_Fog, Mask_Fog, "Fog", "", true); + tool->addButton (Button_Terrain, SceneUtil::Mask_Terrain, "Terrain"); + //tool->addButton (Button_Fog, Mask_Fog, "Fog", "", true); } void CSVRender::PagedWorldspaceWidget::addEditModeSelectorButtons ( @@ -142,16 +141,16 @@ void CSVRender::PagedWorldspaceWidget::addEditModeSelectorButtons ( tool->addButton ( new TerrainTextureMode (this, mRootNode, tool), "terrain-texture"); tool->addButton ( - new EditMode (this, QIcon (":placeholder"), Mask_Reference, "Terrain vertex paint editing"), + new EditMode (this, QIcon (":placeholder"), SceneUtil::Mask_EditorReference, "Terrain vertex paint editing"), "terrain-vertex"); tool->addButton ( - new EditMode (this, QIcon (":placeholder"), Mask_Reference, "Terrain movement"), + new EditMode (this, QIcon (":placeholder"), SceneUtil::Mask_EditorReference, "Terrain movement"), "terrain-move"); } void CSVRender::PagedWorldspaceWidget::handleInteractionPress (const WorldspaceHitResult& hit, InteractionType type) { - if (hit.tag && hit.tag->getMask()==Mask_CellArrow) + if (hit.tag && hit.tag->getMask()==SceneUtil::Mask_EditorCellArrow) { if (CellArrowTag *cellArrowTag = dynamic_cast (hit.tag.get())) { @@ -874,9 +873,9 @@ CSVWidget::SceneToolToggle2 *CSVRender::PagedWorldspaceWidget::makeControlVisibi mControlElements = new CSVWidget::SceneToolToggle2 (parent, "Controls & Guides Visibility", ":scenetoolbar/scene-view-marker-c", ":scenetoolbar/scene-view-marker-"); - mControlElements->addButton (1, Mask_CellMarker, "Cell Marker"); - mControlElements->addButton (2, Mask_CellArrow, "Cell Arrows"); - mControlElements->addButton (4, Mask_CellBorder, "Cell Border"); + mControlElements->addButton (1, SceneUtil::Mask_EditorCellMarker, "Cell Marker"); + mControlElements->addButton (2, SceneUtil::Mask_EditorCellArrow, "Cell Arrows"); + mControlElements->addButton (4, SceneUtil::Mask_EditorCellBorder, "Cell Border"); mControlElements->setSelectionMask (0xffffffff); diff --git a/apps/opencs/view/render/pathgrid.cpp b/apps/opencs/view/render/pathgrid.cpp index d8acfe2e1..b2714014b 100644 --- a/apps/opencs/view/render/pathgrid.cpp +++ b/apps/opencs/view/render/pathgrid.cpp @@ -10,6 +10,7 @@ #include #include +#include #include "../../model/world/cell.hpp" #include "../../model/world/commands.hpp" @@ -31,7 +32,7 @@ namespace CSVRender }; PathgridTag::PathgridTag(Pathgrid* pathgrid) - : TagBase(Mask_Pathgrid), mPathgrid(pathgrid) + : TagBase(SceneUtil::Mask_Pathgrid), mPathgrid(pathgrid) { } @@ -70,7 +71,7 @@ namespace CSVRender mBaseNode->setPosition(osg::Vec3f(mCoords.getX() * CoordScalar, mCoords.getY() * CoordScalar, 0.f)); mBaseNode->setUserData(mTag); mBaseNode->setUpdateCallback(new PathgridNodeCallback()); - mBaseNode->setNodeMask(Mask_Pathgrid); + mBaseNode->setNodeMask(SceneUtil::Mask_Pathgrid); mParent->addChild(mBaseNode); mPathgridGeode = new osg::Geode(); diff --git a/apps/opencs/view/render/pathgridmode.cpp b/apps/opencs/view/render/pathgridmode.cpp index 8863ad235..33c1b8b42 100644 --- a/apps/opencs/view/render/pathgridmode.cpp +++ b/apps/opencs/view/render/pathgridmode.cpp @@ -4,6 +4,7 @@ #include #include +#include #include "../../model/prefs/state.hpp" @@ -15,7 +16,6 @@ #include "../widget/scenetoolbar.hpp" #include "cell.hpp" -#include "mask.hpp" #include "pathgrid.hpp" #include "pathgridselectionmode.hpp" #include "worldspacewidget.hpp" @@ -23,7 +23,7 @@ namespace CSVRender { PathgridMode::PathgridMode(WorldspaceWidget* worldspaceWidget, QWidget* parent) - : EditMode(worldspaceWidget, QIcon(":placeholder"), Mask_Pathgrid | Mask_Terrain | Mask_Reference, + : EditMode(worldspaceWidget, QIcon(":placeholder"), SceneUtil::Mask_Pathgrid | SceneUtil::Mask_Terrain | SceneUtil::Mask_EditorReference, getTooltip(), parent) , mDragMode(DragMode_None) , mFromNode(0) @@ -110,7 +110,7 @@ namespace CSVRender void PathgridMode::primarySelectPressed(const WorldspaceHitResult& hit) { - getWorldspaceWidget().clearSelection(Mask_Pathgrid); + getWorldspaceWidget().clearSelection(SceneUtil::Mask_Pathgrid); if (hit.tag) { @@ -131,7 +131,7 @@ namespace CSVRender { if (tag->getPathgrid()->getId() != mLastId) { - getWorldspaceWidget().clearSelection(Mask_Pathgrid); + getWorldspaceWidget().clearSelection(SceneUtil::Mask_Pathgrid); mLastId = tag->getPathgrid()->getId(); } @@ -142,12 +142,12 @@ namespace CSVRender } } - getWorldspaceWidget().clearSelection(Mask_Pathgrid); + getWorldspaceWidget().clearSelection(SceneUtil::Mask_Pathgrid); } bool PathgridMode::primaryEditStartDrag(const QPoint& pos) { - std::vector > selection = getWorldspaceWidget().getSelection (Mask_Pathgrid); + std::vector > selection = getWorldspaceWidget().getSelection (SceneUtil::Mask_Pathgrid); if (CSMPrefs::get()["3D Scene Input"]["context-select"].isTrue()) { @@ -156,7 +156,7 @@ namespace CSVRender if (dynamic_cast(hit.tag.get())) { primarySelectPressed(hit); - selection = getWorldspaceWidget().getSelection (Mask_Pathgrid); + selection = getWorldspaceWidget().getSelection (SceneUtil::Mask_Pathgrid); } } @@ -192,7 +192,7 @@ namespace CSVRender { if (mDragMode == DragMode_Move) { - std::vector > selection = getWorldspaceWidget().getSelection(Mask_Pathgrid); + std::vector > selection = getWorldspaceWidget().getSelection(SceneUtil::Mask_Pathgrid); for (std::vector >::iterator it = selection.begin(); it != selection.end(); ++it) { @@ -233,7 +233,7 @@ namespace CSVRender { if (mDragMode == DragMode_Move) { - std::vector > selection = getWorldspaceWidget().getSelection (Mask_Pathgrid); + std::vector > selection = getWorldspaceWidget().getSelection (SceneUtil::Mask_Pathgrid); for (std::vector >::iterator it = selection.begin(); it != selection.end(); ++it) { if (PathgridTag* tag = dynamic_cast(it->get())) @@ -272,11 +272,11 @@ namespace CSVRender } mDragMode = DragMode_None; - getWorldspaceWidget().reset(Mask_Pathgrid); + getWorldspaceWidget().reset(SceneUtil::Mask_Pathgrid); } void PathgridMode::dragAborted() { - getWorldspaceWidget().reset(Mask_Pathgrid); + getWorldspaceWidget().reset(SceneUtil::Mask_Pathgrid); } } diff --git a/apps/opencs/view/render/pathgridselectionmode.cpp b/apps/opencs/view/render/pathgridselectionmode.cpp index db41faf50..43050d52a 100644 --- a/apps/opencs/view/render/pathgridselectionmode.cpp +++ b/apps/opencs/view/render/pathgridselectionmode.cpp @@ -13,7 +13,7 @@ namespace CSVRender { PathgridSelectionMode::PathgridSelectionMode(CSVWidget::SceneToolbar* parent, WorldspaceWidget& worldspaceWidget) - : SelectionMode(parent, worldspaceWidget, Mask_Pathgrid) + : SelectionMode(parent, worldspaceWidget, SceneUtil::Mask_Pathgrid) { mRemoveSelectedNodes = new QAction("Remove selected nodes", this); mRemoveSelectedEdges = new QAction("Remove edges between selected nodes", this); @@ -37,7 +37,7 @@ namespace CSVRender void PathgridSelectionMode::removeSelectedNodes() { - std::vector > selection = getWorldspaceWidget().getSelection (Mask_Pathgrid); + std::vector > selection = getWorldspaceWidget().getSelection (SceneUtil::Mask_Pathgrid); for (std::vector >::iterator it = selection.begin(); it != selection.end(); ++it) { @@ -54,7 +54,7 @@ namespace CSVRender void PathgridSelectionMode::removeSelectedEdges() { - std::vector > selection = getWorldspaceWidget().getSelection (Mask_Pathgrid); + std::vector > selection = getWorldspaceWidget().getSelection (SceneUtil::Mask_Pathgrid); for (std::vector >::iterator it = selection.begin(); it != selection.end(); ++it) { diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index ed232722d..cd671f875 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include "../widget/scenetoolmode.hpp" @@ -25,7 +26,6 @@ #include "../../model/prefs/shortcuteventhandler.hpp" #include "lighting.hpp" -#include "mask.hpp" #include "cameracontroller.hpp" namespace CSVRender @@ -71,7 +71,7 @@ RenderWidget::RenderWidget(QWidget *parent, Qt::WindowFlags f) SceneUtil::LightManager* lightMgr = new SceneUtil::LightManager; lightMgr->setStartLight(1); - lightMgr->setLightingMask(Mask_Lighting); + lightMgr->setLightingMask(SceneUtil::Mask_Lighting); mRootNode = lightMgr; mView->getCamera()->getOrCreateStateSet()->setMode(GL_NORMALIZE, osg::StateAttribute::ON); @@ -88,7 +88,7 @@ RenderWidget::RenderWidget(QWidget *parent, Qt::WindowFlags f) // Add ability to signal osg to show its statistics for debugging purposes mView->addEventHandler(new osgViewer::StatsHandler); - mView->getCamera()->setCullMask(~(Mask_UpdateVisitor)); + mView->getCamera()->setCullMask(~(SceneUtil::Mask_UpdateVisitor)); viewer.addView(mView); viewer.setDone(false); @@ -122,7 +122,7 @@ void RenderWidget::flagAsModified() void RenderWidget::setVisibilityMask(int mask) { - mView->getCamera()->setCullMask(mask | Mask_ParticleSystem | Mask_Lighting); + mView->getCamera()->setCullMask(mask | SceneUtil::Mask_ParticleSystem | SceneUtil::Mask_Lighting); } osg::Camera *RenderWidget::getCamera() @@ -212,7 +212,7 @@ SceneWidget::SceneWidget(std::shared_ptr resourceSyste mOrbitCamControl = new OrbitCameraController(this); mCurrentCamControl = mFreeCamControl; - mOrbitCamControl->setPickingMask(Mask_Reference | Mask_Terrain); + mOrbitCamControl->setPickingMask(SceneUtil::Mask_EditorReference | SceneUtil::Mask_Terrain); mOrbitCamControl->setConstRoll( CSMPrefs::get()["3D Scene Input"]["navi-orbit-const-roll"].isTrue() ); @@ -221,7 +221,7 @@ SceneWidget::SceneWidget(std::shared_ptr resourceSyste setLighting(&mLightingDay); - mResourceSystem->getSceneManager()->setParticleSystemMask(Mask_ParticleSystem); + mResourceSystem->getSceneManager()->setParticleSystemMask(SceneUtil::Mask_ParticleSystem); // Recieve mouse move event even if mouse button is not pressed setMouseTracking(true); @@ -350,7 +350,7 @@ void SceneWidget::update(double dt) } else { - mCurrentCamControl->setup(mRootNode, Mask_Reference | Mask_Terrain, CameraController::WorldUp); + mCurrentCamControl->setup(mRootNode, SceneUtil::Mask_EditorReference | SceneUtil::Mask_Terrain, CameraController::WorldUp); mCamPositionSet = true; } } diff --git a/apps/opencs/view/render/selectionmode.hpp b/apps/opencs/view/render/selectionmode.hpp index f28888bfd..18c751290 100644 --- a/apps/opencs/view/render/selectionmode.hpp +++ b/apps/opencs/view/render/selectionmode.hpp @@ -3,8 +3,6 @@ #include "../widget/scenetoolmode.hpp" -#include "mask.hpp" - class QAction; namespace CSVRender diff --git a/apps/opencs/view/render/tagbase.cpp b/apps/opencs/view/render/tagbase.cpp index 3ddd68690..bdd648102 100644 --- a/apps/opencs/view/render/tagbase.cpp +++ b/apps/opencs/view/render/tagbase.cpp @@ -1,9 +1,9 @@ #include "tagbase.hpp" -CSVRender::TagBase::TagBase (Mask mask) : mMask (mask) {} +CSVRender::TagBase::TagBase (SceneUtil::VisMask mask) : mMask (mask) {} -CSVRender::Mask CSVRender::TagBase::getMask() const +SceneUtil::VisMask CSVRender::TagBase::getMask() const { return mMask; } diff --git a/apps/opencs/view/render/tagbase.hpp b/apps/opencs/view/render/tagbase.hpp index d1ecd2cfd..6a0bc4aef 100644 --- a/apps/opencs/view/render/tagbase.hpp +++ b/apps/opencs/view/render/tagbase.hpp @@ -5,19 +5,19 @@ #include -#include "mask.hpp" +#include namespace CSVRender { class TagBase : public osg::Referenced { - Mask mMask; + SceneUtil::VisMask mMask; public: - TagBase (Mask mask); + TagBase (SceneUtil::VisMask mask); - Mask getMask() const; + SceneUtil::VisMask getMask() const; virtual QString getToolTip (bool hideBasics) const; diff --git a/apps/opencs/view/render/terrainshapemode.cpp b/apps/opencs/view/render/terrainshapemode.cpp index 5423cd3dd..2a6d7f33f 100644 --- a/apps/opencs/view/render/terrainshapemode.cpp +++ b/apps/opencs/view/render/terrainshapemode.cpp @@ -17,6 +17,7 @@ #include #include +#include #include "../widget/brushshapes.hpp" #include "../widget/modebutton.hpp" @@ -38,13 +39,12 @@ #include "editmode.hpp" #include "pagedworldspacewidget.hpp" -#include "mask.hpp" #include "tagbase.hpp" #include "terrainselection.hpp" #include "worldspacewidget.hpp" CSVRender::TerrainShapeMode::TerrainShapeMode (WorldspaceWidget *worldspaceWidget, osg::Group* parentNode, QWidget *parent) -: EditMode (worldspaceWidget, QIcon {":scenetoolbar/editing-terrain-shape"}, Mask_Terrain | Mask_Reference, "Terrain land editing", parent), +: EditMode (worldspaceWidget, QIcon {":scenetoolbar/editing-terrain-shape"}, SceneUtil::Mask_Terrain | SceneUtil::Mask_EditorReference, "Terrain land editing", parent), mParentNode(parentNode) { } diff --git a/apps/opencs/view/render/terraintexturemode.cpp b/apps/opencs/view/render/terraintexturemode.cpp index 40dfc9474..efdb600b8 100644 --- a/apps/opencs/view/render/terraintexturemode.cpp +++ b/apps/opencs/view/render/terraintexturemode.cpp @@ -13,6 +13,7 @@ #include #include +#include #include "../widget/modebutton.hpp" #include "../widget/scenetoolbar.hpp" @@ -34,12 +35,11 @@ #include "editmode.hpp" #include "pagedworldspacewidget.hpp" -#include "mask.hpp" #include "object.hpp" // Something small needed regarding pointers from here () #include "worldspacewidget.hpp" CSVRender::TerrainTextureMode::TerrainTextureMode (WorldspaceWidget *worldspaceWidget, osg::Group* parentNode, QWidget *parent) -: EditMode (worldspaceWidget, QIcon {":scenetoolbar/editing-terrain-texture"}, Mask_Terrain | Mask_Reference, "Terrain texture editing", parent), +: EditMode (worldspaceWidget, QIcon {":scenetoolbar/editing-terrain-texture"}, SceneUtil::Mask_Terrain | SceneUtil::Mask_EditorReference, "Terrain texture editing", parent), mBrushTexture("L0#0"), mBrushSize(1), mBrushShape(0), diff --git a/apps/opencs/view/render/unpagedworldspacewidget.cpp b/apps/opencs/view/render/unpagedworldspacewidget.cpp index b1088aa60..5f5441b83 100644 --- a/apps/opencs/view/render/unpagedworldspacewidget.cpp +++ b/apps/opencs/view/render/unpagedworldspacewidget.cpp @@ -16,7 +16,6 @@ #include "../widget/scenetooltoggle2.hpp" #include "cameracontroller.hpp" -#include "mask.hpp" #include "tagbase.hpp" void CSVRender::UnpagedWorldspaceWidget::update() @@ -304,8 +303,8 @@ void CSVRender::UnpagedWorldspaceWidget::addVisibilitySelectorButtons ( CSVWidget::SceneToolToggle2 *tool) { WorldspaceWidget::addVisibilitySelectorButtons (tool); - tool->addButton (Button_Terrain, Mask_Terrain, "Terrain", "", true); - tool->addButton (Button_Fog, Mask_Fog, "Fog"); + tool->addButton (Button_Terrain, SceneUtil::Mask_Terrain, "Terrain", "", true); + //tool->addButton (Button_Fog, Mask_Fog, "Fog"); } std::string CSVRender::UnpagedWorldspaceWidget::getStartupInstruction() diff --git a/apps/opencs/view/render/worldspacewidget.cpp b/apps/opencs/view/render/worldspacewidget.cpp index 1eca61e73..77ecba918 100644 --- a/apps/opencs/view/render/worldspacewidget.cpp +++ b/apps/opencs/view/render/worldspacewidget.cpp @@ -26,8 +26,9 @@ #include "../widget/scenetooltoggle2.hpp" #include "../widget/scenetoolrun.hpp" +#include + #include "object.hpp" -#include "mask.hpp" #include "instancemode.hpp" #include "pathgridmode.hpp" #include "cameracontroller.hpp" @@ -138,7 +139,7 @@ void CSVRender::WorldspaceWidget::settingChanged (const CSMPrefs::Setting *setti { float alpha = setting->toDouble(); // getSelection is virtual, thus this can not be called from the constructor - auto selection = getSelection(Mask_Reference); + auto selection = getSelection(SceneUtil::Mask_EditorReference); for (osg::ref_ptr tag : selection) { if (auto objTag = dynamic_cast(tag.get())) @@ -345,7 +346,7 @@ unsigned int CSVRender::WorldspaceWidget::getVisibilityMask() const void CSVRender::WorldspaceWidget::setInteractionMask (unsigned int mask) { - mInteractionMask = mask | Mask_CellMarker | Mask_CellArrow; + mInteractionMask = mask | SceneUtil::Mask_EditorCellMarker | SceneUtil::Mask_EditorCellArrow; } unsigned int CSVRender::WorldspaceWidget::getInteractionMask() const @@ -361,9 +362,9 @@ void CSVRender::WorldspaceWidget::setEditLock (bool locked) void CSVRender::WorldspaceWidget::addVisibilitySelectorButtons ( CSVWidget::SceneToolToggle2 *tool) { - tool->addButton (Button_Reference, Mask_Reference, "Instances"); - tool->addButton (Button_Water, Mask_Water, "Water"); - tool->addButton (Button_Pathgrid, Mask_Pathgrid, "Pathgrid"); + tool->addButton (Button_Reference, SceneUtil::Mask_EditorReference, "Instances"); + tool->addButton (Button_Water, SceneUtil::Mask_Water, "Water"); + tool->addButton (Button_Pathgrid, SceneUtil::Mask_Pathgrid, "Pathgrid"); } void CSVRender::WorldspaceWidget::addEditModeSelectorButtons (CSVWidget::SceneToolMode *tool) diff --git a/apps/opencs/view/render/worldspacewidget.hpp b/apps/opencs/view/render/worldspacewidget.hpp index a80032b82..5ac63c673 100644 --- a/apps/opencs/view/render/worldspacewidget.hpp +++ b/apps/opencs/view/render/worldspacewidget.hpp @@ -8,7 +8,6 @@ #include "../../model/world/tablemimedata.hpp" #include "scenewidget.hpp" -#include "mask.hpp" namespace CSMPrefs { diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 55d6b1af9..14c271b66 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -18,7 +18,7 @@ set(GAME_HEADER source_group(game FILES ${GAME} ${GAME_HEADER}) add_openmw_dir (mwrender - actors objects renderingmanager animation rotatecontroller sky npcanimation vismask + actors objects renderingmanager animation rotatecontroller sky npcanimation creatureanimation effectmanager util renderinginterface pathgrid rendermode weaponanimation bulletdebugdraw globalmap characterpreview camera localmap water terrainstorage ripplesimulation renderbin actoranimation landmanager navmesh actorspaths diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 1299fde0d..57c69be05 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -26,6 +26,7 @@ #include +#include #include #include @@ -47,8 +48,6 @@ #include "mwworld/player.hpp" #include "mwworld/worldimp.hpp" -#include "mwrender/vismask.hpp" - #include "mwclass/classes.hpp" #include "mwdialogue/dialoguemanagerimp.hpp" @@ -544,7 +543,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) std::string myguiResources = (mResDir / "mygui").string(); osg::ref_ptr guiRoot = new osg::Group; guiRoot->setName("GUI Root"); - guiRoot->setNodeMask(MWRender::Mask_GUI); + guiRoot->setNodeMask(SceneUtil::Mask_GUI); rootNode->addChild(guiRoot); MWGui::WindowManager* window = new MWGui::WindowManager(mViewer, guiRoot, mResourceSystem.get(), mWorkQueue.get(), mCfgMgr.getLogPath().string() + std::string("/"), myguiResources, diff --git a/apps/openmw/mwclass/activator.cpp b/apps/openmw/mwclass/activator.cpp index c54b1c369..3f4e13c1c 100644 --- a/apps/openmw/mwclass/activator.cpp +++ b/apps/openmw/mwclass/activator.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" @@ -19,7 +20,6 @@ #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" -#include "../mwrender/vismask.hpp" #include "../mwgui/tooltips.hpp" @@ -34,7 +34,7 @@ namespace MWClass if (!model.empty()) { renderingInterface.getObjects().insertModel(ptr, model, true); - ptr.getRefData().getBaseNode()->setNodeMask(MWRender::Mask_Static); + ptr.getRefData().getBaseNode()->setNodeMask(SceneUtil::Mask_Static); } } diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index cd2039869..6a0be8631 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -25,7 +26,6 @@ #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" #include "../mwrender/animation.hpp" -#include "../mwrender/vismask.hpp" #include "../mwmechanics/actorutil.hpp" @@ -58,7 +58,7 @@ namespace MWClass if (!model.empty()) { renderingInterface.getObjects().insertModel(ptr, model, true); - ptr.getRefData().getBaseNode()->setNodeMask(MWRender::Mask_Static); + ptr.getRefData().getBaseNode()->setNodeMask(SceneUtil::Mask_Static); } } diff --git a/apps/openmw/mwclass/static.cpp b/apps/openmw/mwclass/static.cpp index 5551b3d73..2cb00c497 100644 --- a/apps/openmw/mwclass/static.cpp +++ b/apps/openmw/mwclass/static.cpp @@ -2,6 +2,7 @@ #include #include +#include #include "../mwworld/ptr.hpp" #include "../mwphysics/physicssystem.hpp" @@ -9,7 +10,6 @@ #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" -#include "../mwrender/vismask.hpp" namespace MWClass { @@ -19,7 +19,7 @@ namespace MWClass if (!model.empty()) { renderingInterface.getObjects().insertModel(ptr, model); - ptr.getRefData().getBaseNode()->setNodeMask(MWRender::Mask_Static); + ptr.getRefData().getBaseNode()->setNodeMask(SceneUtil::Mask_Static); } } diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index 54382ab4d..ddaa9063a 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -14,14 +14,13 @@ #include #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/statemanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/inputmanager.hpp" -#include "../mwrender/vismask.hpp" - #include "backgroundimage.hpp" namespace MWGui @@ -335,8 +334,8 @@ namespace MWGui // Turn off rendering except the GUI int oldUpdateMask = mViewer->getUpdateVisitor()->getTraversalMask(); int oldCullMask = mViewer->getCamera()->getCullMask(); - mViewer->getUpdateVisitor()->setTraversalMask(MWRender::Mask_GUI|MWRender::Mask_PreCompile); - mViewer->getCamera()->setCullMask(MWRender::Mask_GUI|MWRender::Mask_PreCompile); + mViewer->getUpdateVisitor()->setTraversalMask(SceneUtil::Mask_GUI|SceneUtil::Mask_PreCompile); + mViewer->getCamera()->setCullMask(SceneUtil::Mask_GUI|SceneUtil::Mask_PreCompile); MWBase::Environment::get().getInputManager()->update(0, true, true); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 0524945fd..752738b9c 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -30,6 +30,7 @@ #include #include +#include #include #include @@ -50,8 +51,6 @@ #include "../mwbase/soundmanager.hpp" #include "../mwbase/world.hpp" -#include "../mwrender/vismask.hpp" - #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" #include "../mwworld/cellstore.hpp" @@ -1883,8 +1882,8 @@ namespace MWGui // Turn off all rendering except for the GUI int oldUpdateMask = mViewer->getUpdateVisitor()->getTraversalMask(); int oldCullMask = mViewer->getCamera()->getCullMask(); - mViewer->getUpdateVisitor()->setTraversalMask(MWRender::Mask_GUI); - mViewer->getCamera()->setCullMask(MWRender::Mask_GUI); + mViewer->getUpdateVisitor()->setTraversalMask(SceneUtil::Mask_GUI); + mViewer->getCamera()->setCullMask(SceneUtil::Mask_GUI); MyGUI::IntSize screenSize = MyGUI::RenderManager::getInstance().getViewSize(); sizeVideo(screenSize.width, screenSize.height); diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index c264d63df..550caf9d1 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -24,8 +25,6 @@ #include "../mwmechanics/aibreathe.hpp" -#include "../mwrender/vismask.hpp" - #include "spellcasting.hpp" #include "steering.hpp" #include "npcstats.hpp" @@ -1414,11 +1413,11 @@ namespace MWMechanics const float dist = (player.getRefData().getPosition().asVec3() - ptr.getRefData().getPosition().asVec3()).length(); if (dist > mActorsProcessingRange) { - ptr.getRefData().getBaseNode()->setNodeMask(0); + ptr.getRefData().getBaseNode()->setNodeMask(SceneUtil::Mask_Disabled); return; } else - ptr.getRefData().getBaseNode()->setNodeMask(MWRender::Mask_Actor); + ptr.getRefData().getBaseNode()->setNodeMask(SceneUtil::Mask_Actor); // Fade away actors on large distance (>90% of actor's processing distance) float visibilityRatio = 1.0; @@ -1742,12 +1741,12 @@ namespace MWMechanics if (!inRange) { - iter->first.getRefData().getBaseNode()->setNodeMask(0); + iter->first.getRefData().getBaseNode()->setNodeMask(SceneUtil::Mask_Disabled); world->setActorCollisionMode(iter->first, false, false); continue; } else if (!isPlayer) - iter->first.getRefData().getBaseNode()->setNodeMask(MWRender::Mask_Actor); + iter->first.getRefData().getBaseNode()->setNodeMask(SceneUtil::Mask_Actor); const bool isDead = iter->first.getClass().getCreatureStats(iter->first).isDead(); if (!isDead && iter->first.getClass().getCreatureStats(iter->first).isParalyzed()) diff --git a/apps/openmw/mwrender/actoranimation.cpp b/apps/openmw/mwrender/actoranimation.cpp index fcffe220b..1c54f0684 100644 --- a/apps/openmw/mwrender/actoranimation.cpp +++ b/apps/openmw/mwrender/actoranimation.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include @@ -31,8 +32,6 @@ #include "../mwmechanics/actorutil.hpp" #include "../mwmechanics/weapontype.hpp" -#include "vismask.hpp" - namespace MWRender { @@ -367,7 +366,7 @@ void ActorAnimation::updateHolsteredWeapon(bool showHolsteredWeapons) // Otherwise add the enchanted glow to it. if (!showHolsteredWeapons) { - weaponNode->setNodeMask(0); + weaponNode->setNodeMask(SceneUtil::Mask_Disabled); } else { @@ -541,7 +540,7 @@ void ActorAnimation::addHiddenItemLight(const MWWorld::ConstPtr& item, const ESM bool exterior = mPtr.isInCell() && mPtr.getCell()->getCell()->isExterior(); osg::Vec4f ambient(1,1,1,1); - osg::ref_ptr lightSource = SceneUtil::createLightSource(esmLight, Mask_Lighting, exterior, ambient); + osg::ref_ptr lightSource = SceneUtil::createLightSource(esmLight, exterior, ambient); mInsert->addChild(lightSource); diff --git a/apps/openmw/mwrender/actorspaths.cpp b/apps/openmw/mwrender/actorspaths.cpp index 35b255355..ec90949bc 100644 --- a/apps/openmw/mwrender/actorspaths.cpp +++ b/apps/openmw/mwrender/actorspaths.cpp @@ -1,7 +1,7 @@ #include "actorspaths.hpp" -#include "vismask.hpp" #include +#include #include @@ -43,7 +43,7 @@ namespace MWRender const auto newGroup = SceneUtil::createAgentPathGroup(path, halfExtents, start, end, settings); if (newGroup) { - newGroup->setNodeMask(Mask_Debug); + newGroup->setNodeMask(SceneUtil::Mask_Debug); mRootNode->addChild(newGroup); mGroups[actor] = newGroup; } diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 3505ea261..0f7548f05 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -44,7 +45,6 @@ #include "../mwmechanics/character.hpp" // FIXME: for MWMechanics::Priority -#include "vismask.hpp" #include "util.hpp" #include "rotatecontroller.hpp" @@ -578,7 +578,7 @@ namespace MWRender else { // Hide effect immediately - node->setNodeMask(0); + node->setNodeMask(SceneUtil::Mask_Disabled); mFinished = true; } } @@ -1595,7 +1595,7 @@ namespace MWRender { bool exterior = mPtr.isInCell() && mPtr.getCell()->getCell()->isExterior(); - SceneUtil::addLight(parent, esmLight, Mask_ParticleSystem, Mask_Lighting, exterior); + SceneUtil::addLight(parent, esmLight, exterior); } void Animation::addEffect (const std::string& model, int effectId, bool loop, const std::string& bonename, const std::string& texture) @@ -1647,7 +1647,7 @@ namespace MWRender // FreezeOnCull doesn't work so well with effect particles, that tend to have moving emitters SceneUtil::DisableFreezeOnCullVisitor disableFreezeOnCullVisitor; node->accept(disableFreezeOnCullVisitor); - node->setNodeMask(Mask_Effect); + node->setNodeMask(SceneUtil::Mask_Effect); params.mMaxControllerLength = findMaxLengthVisitor.getMaxLength(); params.mLoop = loop; @@ -1806,7 +1806,7 @@ namespace MWRender SceneUtil::configureLight(light, radius, isExterior); mGlowLight = new SceneUtil::LightSource; - mGlowLight->setNodeMask(Mask_Lighting); + mGlowLight->setNodeMask(SceneUtil::Mask_Lighting); mInsert->addChild(mGlowLight); mGlowLight->setLight(light); } diff --git a/apps/openmw/mwrender/bulletdebugdraw.cpp b/apps/openmw/mwrender/bulletdebugdraw.cpp index 4cf76e473..9883d9fe3 100644 --- a/apps/openmw/mwrender/bulletdebugdraw.cpp +++ b/apps/openmw/mwrender/bulletdebugdraw.cpp @@ -7,7 +7,7 @@ #include -#include "vismask.hpp" +#include namespace { @@ -34,7 +34,7 @@ void DebugDrawer::createGeometry() if (!mGeometry) { mGeometry = new osg::Geometry; - mGeometry->setNodeMask(Mask_Debug); + mGeometry->setNodeMask(SceneUtil::Mask_Debug); mVertices = new osg::Vec3Array; diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index b2552e598..c74c70b55 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -27,7 +28,6 @@ #include "../mwmechanics/weapontype.hpp" #include "npcanimation.hpp" -#include "vismask.hpp" namespace MWRender { @@ -61,7 +61,7 @@ namespace MWRender } else { - node->setNodeMask(0); + node->setNodeMask(SceneUtil::Mask_Disabled); } } @@ -138,9 +138,9 @@ namespace MWRender mCamera->attach(osg::Camera::COLOR_BUFFER, mTexture); mCamera->setName("CharacterPreview"); mCamera->setComputeNearFarMode(osg::Camera::COMPUTE_NEAR_FAR_USING_BOUNDING_VOLUMES); - mCamera->setCullMask(~(Mask_UpdateVisitor)); + mCamera->setCullMask(~(SceneUtil::Mask_UpdateVisitor)); - mCamera->setNodeMask(Mask_RenderToTexture); + mCamera->setNodeMask(SceneUtil::Mask_RenderToTexture); osg::ref_ptr lightManager = new SceneUtil::LightManager; lightManager->setStartLight(1); @@ -255,7 +255,7 @@ namespace MWRender void CharacterPreview::redraw() { - mCamera->setNodeMask(Mask_RenderToTexture); + mCamera->setNodeMask(SceneUtil::Mask_RenderToTexture); mDrawOnceCallback->redrawNextFrame(); } @@ -364,7 +364,7 @@ namespace MWRender visitor.setTraversalNumber(mDrawOnceCallback->getLastRenderedFrame()); osg::Node::NodeMask nodeMask = mCamera->getNodeMask(); - mCamera->setNodeMask(~0); + mCamera->setNodeMask(SceneUtil::Mask_Default); mCamera->accept(visitor); mCamera->setNodeMask(nodeMask); diff --git a/apps/openmw/mwrender/effectmanager.cpp b/apps/openmw/mwrender/effectmanager.cpp index 3e785a769..450cb20f5 100644 --- a/apps/openmw/mwrender/effectmanager.cpp +++ b/apps/openmw/mwrender/effectmanager.cpp @@ -6,9 +6,9 @@ #include #include +#include #include "animation.hpp" -#include "vismask.hpp" #include "util.hpp" namespace MWRender @@ -29,7 +29,7 @@ void EffectManager::addEffect(const std::string &model, const std::string& textu { osg::ref_ptr node = mResourceSystem->getSceneManager()->getInstance(model); - node->setNodeMask(Mask_Effect); + node->setNodeMask(SceneUtil::Mask_Effect); Effect effect; effect.mAnimTime.reset(new EffectAnimationTime); diff --git a/apps/openmw/mwrender/globalmap.cpp b/apps/openmw/mwrender/globalmap.cpp index 72e8679eb..4813de398 100644 --- a/apps/openmw/mwrender/globalmap.cpp +++ b/apps/openmw/mwrender/globalmap.cpp @@ -16,6 +16,7 @@ #include #include +#include #include @@ -24,8 +25,6 @@ #include "../mwworld/esmstore.hpp" -#include "vismask.hpp" - namespace { @@ -76,7 +75,7 @@ namespace { if (mParent->copyResult(static_cast(node), nv->getTraversalNumber())) { - node->setNodeMask(0); + node->setNodeMask(SceneUtil::Mask_Disabled); mParent->markForRemoval(static_cast(node)); } return; @@ -288,7 +287,7 @@ namespace MWRender float srcLeft, float srcTop, float srcRight, float srcBottom) { osg::ref_ptr camera (new osg::Camera); - camera->setNodeMask(Mask_RenderToTexture); + camera->setNodeMask(SceneUtil::Mask_RenderToTexture); camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF); camera->setViewMatrix(osg::Matrix::identity()); camera->setProjectionMatrix(osg::Matrix::identity()); diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index f4a54eb98..7bd202e7e 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include "../mwbase/environment.hpp" @@ -25,8 +26,6 @@ #include "../mwworld/cellstore.hpp" -#include "vismask.hpp" - namespace { @@ -42,7 +41,7 @@ namespace virtual void operator()(osg::Node* node, osg::NodeVisitor*) { if (mRendered) - node->setNodeMask(0); + node->setNodeMask(SceneUtil::Mask_Disabled); if (!mRendered) { @@ -178,8 +177,8 @@ osg::ref_ptr LocalMap::createOrthographicCamera(float x, float y, f camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); camera->setRenderOrder(osg::Camera::PRE_RENDER); - camera->setCullMask(Mask_Scene | Mask_SimpleWater | Mask_Terrain | Mask_Object | Mask_Static); - camera->setNodeMask(Mask_RenderToTexture); + camera->setCullMask(SceneUtil::Mask_Scene | SceneUtil::Mask_SimpleWater | SceneUtil::Mask_Terrain | SceneUtil::Mask_Object | SceneUtil::Mask_Static); + camera->setNodeMask(SceneUtil::Mask_RenderToTexture); osg::ref_ptr stateset = new osg::StateSet; stateset->setAttribute(new osg::PolygonMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::FILL), osg::StateAttribute::OVERRIDE); @@ -376,7 +375,7 @@ void LocalMap::requestExteriorMap(const MWWorld::CellStore* cell) void LocalMap::requestInteriorMap(const MWWorld::CellStore* cell) { osg::ComputeBoundsVisitor computeBoundsVisitor; - computeBoundsVisitor.setTraversalMask(Mask_Scene | Mask_Terrain | Mask_Object | Mask_Static); + computeBoundsVisitor.setTraversalMask(SceneUtil::Mask_Scene | SceneUtil::Mask_Terrain | SceneUtil::Mask_Object | SceneUtil::Mask_Static); mSceneRoot->accept(computeBoundsVisitor); osg::BoundingBox bounds = computeBoundsVisitor.getBoundingBox(); diff --git a/apps/openmw/mwrender/navmesh.cpp b/apps/openmw/mwrender/navmesh.cpp index bfc80914a..3a80ac8ca 100644 --- a/apps/openmw/mwrender/navmesh.cpp +++ b/apps/openmw/mwrender/navmesh.cpp @@ -1,7 +1,7 @@ #include "navmesh.hpp" -#include "vismask.hpp" #include +#include #include @@ -45,7 +45,7 @@ namespace MWRender mGroup = SceneUtil::createNavMeshGroup(navMesh, settings); if (mGroup) { - mGroup->setNodeMask(Mask_Debug); + mGroup->setNodeMask(SceneUtil::Mask_Debug); mRootNode->addChild(mGroup); } } diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 9633e9e1b..edaf633c6 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -43,7 +44,6 @@ #include "camera.hpp" #include "rotatecontroller.hpp" #include "renderbin.hpp" -#include "vismask.hpp" namespace { @@ -535,7 +535,7 @@ void NpcAnimation::updateNpcBase() addAnimSource(smodel, smodel); - mObjectRoot->setNodeMask(Mask_FirstPerson); + mObjectRoot->setNodeMask(SceneUtil::Mask_FirstPerson); mObjectRoot->addCullCallback(new OverrideFieldOfViewCallback(mFirstPersonFieldOfView)); } diff --git a/apps/openmw/mwrender/objects.cpp b/apps/openmw/mwrender/objects.cpp index ec1c4397b..83fd807dc 100644 --- a/apps/openmw/mwrender/objects.cpp +++ b/apps/openmw/mwrender/objects.cpp @@ -5,6 +5,7 @@ #include #include +#include #include "../mwworld/ptr.hpp" #include "../mwworld/class.hpp" @@ -12,8 +13,6 @@ #include "animation.hpp" #include "npcanimation.hpp" #include "creatureanimation.hpp" -#include "vismask.hpp" - namespace MWRender { @@ -71,7 +70,7 @@ void Objects::insertBegin(const MWWorld::Ptr& ptr) void Objects::insertModel(const MWWorld::Ptr &ptr, const std::string &mesh, bool animated, bool allowLight) { insertBegin(ptr); - ptr.getRefData().getBaseNode()->setNodeMask(Mask_Object); + ptr.getRefData().getBaseNode()->setNodeMask(SceneUtil::Mask_Object); osg::ref_ptr anim (new ObjectAnimation(ptr, mesh, mResourceSystem, animated, allowLight)); @@ -81,7 +80,7 @@ void Objects::insertModel(const MWWorld::Ptr &ptr, const std::string &mesh, bool void Objects::insertCreature(const MWWorld::Ptr &ptr, const std::string &mesh, bool weaponsShields) { insertBegin(ptr); - ptr.getRefData().getBaseNode()->setNodeMask(Mask_Actor); + ptr.getRefData().getBaseNode()->setNodeMask(SceneUtil::Mask_Actor); // CreatureAnimation osg::ref_ptr anim; @@ -98,7 +97,7 @@ void Objects::insertCreature(const MWWorld::Ptr &ptr, const std::string &mesh, b void Objects::insertNPC(const MWWorld::Ptr &ptr) { insertBegin(ptr); - ptr.getRefData().getBaseNode()->setNodeMask(Mask_Actor); + ptr.getRefData().getBaseNode()->setNodeMask(SceneUtil::Mask_Actor); osg::ref_ptr anim (new NpcAnimation(ptr, osg::ref_ptr(ptr.getRefData().getBaseNode()), mResourceSystem)); diff --git a/apps/openmw/mwrender/pathgrid.cpp b/apps/openmw/mwrender/pathgrid.cpp index 797794457..a2c5a1f46 100644 --- a/apps/openmw/mwrender/pathgrid.cpp +++ b/apps/openmw/mwrender/pathgrid.cpp @@ -8,6 +8,7 @@ #include #include +#include #include "../mwbase/world.hpp" // these includes can be removed once the static-hack is gone #include "../mwbase/environment.hpp" @@ -17,8 +18,6 @@ #include "../mwmechanics/pathfinding.hpp" #include "../mwmechanics/coordinateconverter.hpp" -#include "vismask.hpp" - namespace MWRender { @@ -73,7 +72,7 @@ void Pathgrid::togglePathgrid() { // add path grid meshes to already loaded cells mPathGridRoot = new osg::Group; - mPathGridRoot->setNodeMask(Mask_Debug); + mPathGridRoot->setNodeMask(SceneUtil::Mask_Pathgrid); mRootNode->addChild(mPathGridRoot); for(const MWWorld::CellStore* cell : mActiveCells) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 175f141ba..39318be30 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -40,6 +40,7 @@ #include #include #include +#include #include #include @@ -58,7 +59,6 @@ #include "sky.hpp" #include "effectmanager.hpp" #include "npcanimation.hpp" -#include "vismask.hpp" #include "pathgrid.hpp" #include "camera.hpp" #include "water.hpp" @@ -215,7 +215,7 @@ namespace MWRender , mFieldOfViewOverride(0.f) , mBorders(false) { - resourceSystem->getSceneManager()->setParticleSystemMask(MWRender::Mask_ParticleSystem); + resourceSystem->getSceneManager()->setParticleSystemMask(SceneUtil::Mask_ParticleSystem); resourceSystem->getSceneManager()->setShaderPath(resourcePath + "/shaders"); resourceSystem->getSceneManager()->setForceShaders(Settings::Manager::getBool("force shaders", "Shaders") || Settings::Manager::getBool("enable shadows", "Shadows")); // Shadows have problems with fixed-function mode // FIXME: calling dummy method because terrain needs to know whether lighting is clamped @@ -227,21 +227,21 @@ namespace MWRender resourceSystem->getSceneManager()->setSpecularMapPattern(Settings::Manager::getString("specular map pattern", "Shaders")); osg::ref_ptr sceneRoot = new SceneUtil::LightManager; - sceneRoot->setLightingMask(Mask_Lighting); + sceneRoot->setLightingMask(SceneUtil::Mask_Lighting); mSceneRoot = sceneRoot; sceneRoot->setStartLight(1); - int shadowCastingTraversalMask = Mask_Scene; + int shadowCastingTraversalMask = SceneUtil::Mask_Scene; if (Settings::Manager::getBool("actor shadows", "Shadows")) - shadowCastingTraversalMask |= Mask_Actor; + shadowCastingTraversalMask |= SceneUtil::Mask_Actor; if (Settings::Manager::getBool("player shadows", "Shadows")) - shadowCastingTraversalMask |= Mask_Player; + shadowCastingTraversalMask |= SceneUtil::Mask_Player; if (Settings::Manager::getBool("terrain shadows", "Shadows")) - shadowCastingTraversalMask |= Mask_Terrain; + shadowCastingTraversalMask |= SceneUtil::Mask_Terrain; int indoorShadowCastingTraversalMask = shadowCastingTraversalMask; if (Settings::Manager::getBool("object shadows", "Shadows")) - shadowCastingTraversalMask |= (Mask_Object|Mask_Static); + shadowCastingTraversalMask |= (SceneUtil::Mask_Object|SceneUtil::Mask_Static); mShadowManager.reset(new SceneUtil::ShadowManager(sceneRoot, mRootNode, shadowCastingTraversalMask, indoorShadowCastingTraversalMask, mResourceSystem->getSceneManager()->getShaderManager())); @@ -305,11 +305,10 @@ namespace MWRender float maxCompGeometrySize = Settings::Manager::getFloat("max composite geometry size", "Terrain"); maxCompGeometrySize = std::max(maxCompGeometrySize, 1.f); mTerrain.reset(new Terrain::QuadTreeWorld( - sceneRoot, mRootNode, mResourceSystem, mTerrainStorage, Mask_Terrain, Mask_PreCompile, Mask_Debug, - compMapResolution, compMapLevel, lodFactor, vertexLodMod, maxCompGeometrySize)); + sceneRoot, mRootNode, mResourceSystem, mTerrainStorage, compMapResolution, compMapLevel, lodFactor, vertexLodMod, maxCompGeometrySize)); } else - mTerrain.reset(new Terrain::TerrainGrid(sceneRoot, mRootNode, mResourceSystem, mTerrainStorage, Mask_Terrain, Mask_PreCompile, Mask_Debug)); + mTerrain.reset(new Terrain::TerrainGrid(sceneRoot, mRootNode, mResourceSystem, mTerrainStorage)); mTerrain->setTargetFrameRate(Settings::Manager::getFloat("target framerate", "Cells")); mTerrain->setWorkQueue(mWorkQueue.get()); @@ -319,7 +318,7 @@ namespace MWRender mViewer->setLightingMode(osgViewer::View::NO_LIGHT); osg::ref_ptr source = new osg::LightSource; - source->setNodeMask(Mask_Lighting); + source->setNodeMask(SceneUtil::Mask_Lighting); mSunLight = new osg::Light; source->setLight(mSunLight); mSunLight->setDiffuse(osg::Vec4f(0,0,0,1)); @@ -338,7 +337,7 @@ namespace MWRender defaultMat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 0.f)); sceneRoot->getOrCreateStateSet()->setAttribute(defaultMat); - sceneRoot->setNodeMask(Mask_Scene); + sceneRoot->setNodeMask(SceneUtil::Mask_Scene); sceneRoot->setName("Scene Root"); mSky.reset(new SkyManager(sceneRoot, resourceSystem->getSceneManager())); @@ -366,7 +365,7 @@ namespace MWRender mViewer->getCamera()->setComputeNearFarMode(osg::Camera::DO_NOT_COMPUTE_NEAR_FAR); mViewer->getCamera()->setCullingMode(cullingMode); - mViewer->getCamera()->setCullMask(~(Mask_UpdateVisitor|Mask_SimpleWater)); + mViewer->getCamera()->setCullMask(~(SceneUtil::Mask_UpdateVisitor|SceneUtil::Mask_SimpleWater)); mNearClip = Settings::Manager::getFloat("near clip", "Camera"); mViewDistance = Settings::Manager::getFloat("viewing distance", "Camera"); @@ -568,12 +567,12 @@ namespace MWRender else if (mode == Render_Scene) { int mask = mViewer->getCamera()->getCullMask(); - bool enabled = mask&Mask_Scene; + bool enabled = mask & SceneUtil::Mask_Scene; enabled = !enabled; if (enabled) - mask |= Mask_Scene; + mask |= SceneUtil::Mask_Scene; else - mask &= ~Mask_Scene; + mask &= ~SceneUtil::Mask_Scene; mViewer->getCamera()->setCullMask(mask); return enabled; } @@ -841,7 +840,7 @@ namespace MWRender int maskBackup = mPlayerAnimation->getObjectRoot()->getNodeMask(); if (mCamera->isFirstPerson()) - mPlayerAnimation->getObjectRoot()->setNodeMask(0); + mPlayerAnimation->getObjectRoot()->setNodeMask(SceneUtil::Mask_Disabled); for (int i = 0; i < 6; ++i) // for each cubemap side { @@ -915,7 +914,7 @@ namespace MWRender void RenderingManager::renderCameraToImage(osg::Camera *camera, osg::Image *image, int w, int h) { - camera->setNodeMask(Mask_RenderToTexture); + camera->setNodeMask(SceneUtil::Mask_RenderToTexture); camera->attach(osg::Camera::COLOR_BUFFER, image); camera->setRenderOrder(osg::Camera::PRE_RENDER); camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF); @@ -968,7 +967,7 @@ namespace MWRender rttCamera->addChild(mWater->getReflectionCamera()); rttCamera->addChild(mWater->getRefractionCamera()); - rttCamera->setCullMask(mViewer->getCamera()->getCullMask() & (~Mask_GUI)); + rttCamera->setCullMask(mViewer->getCamera()->getCullMask() & (~SceneUtil::Mask_GUI)); rttCamera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); @@ -981,7 +980,7 @@ namespace MWRender return osg::Vec4f(); osg::ComputeBoundsVisitor computeBoundsVisitor; - computeBoundsVisitor.setTraversalMask(~(Mask_ParticleSystem|Mask_Effect)); + computeBoundsVisitor.setTraversalMask(~(SceneUtil::Mask_ParticleSystem|SceneUtil::Mask_Effect)); ptr.getRefData().getBaseNode()->accept(computeBoundsVisitor); osg::Matrix viewProj = mViewer->getCamera()->getViewMatrix() * mViewer->getCamera()->getProjectionMatrix(); @@ -1053,12 +1052,11 @@ namespace MWRender mIntersectionVisitor->setTraversalNumber(mViewer->getFrameStamp()->getFrameNumber()); mIntersectionVisitor->setIntersector(intersector); - int mask = ~0; - mask &= ~(Mask_RenderToTexture|Mask_Sky|Mask_Debug|Mask_Effect|Mask_Water|Mask_SimpleWater); + int mask = ~(SceneUtil::Mask_RenderToTexture|SceneUtil::Mask_Sky|SceneUtil::Mask_Pathgrid|SceneUtil::Mask_Debug|SceneUtil::Mask_Effect|SceneUtil::Mask_Water|SceneUtil::Mask_SimpleWater); if (ignorePlayer) - mask &= ~(Mask_Player); + mask &= ~(SceneUtil::Mask_Player); if (ignoreActors) - mask &= ~(Mask_Actor|Mask_Player); + mask &= ~(SceneUtil::Mask_Actor|SceneUtil::Mask_Player); mIntersectionVisitor->setTraversalMask(mask); return mIntersectionVisitor; @@ -1138,7 +1136,7 @@ namespace MWRender if (!mPlayerNode) { mPlayerNode = new SceneUtil::PositionAttitudeTransform; - mPlayerNode->setNodeMask(Mask_Player); + mPlayerNode->setNodeMask(SceneUtil::Mask_Player); mPlayerNode->setName("Player Root"); mSceneRoot->addChild(mPlayerNode); } @@ -1382,7 +1380,7 @@ namespace MWRender osg::ref_ptr node = mResourceSystem->getSceneManager()->getTemplate(modelName); osg::ComputeBoundsVisitor computeBoundsVisitor; - computeBoundsVisitor.setTraversalMask(~(MWRender::Mask_ParticleSystem|MWRender::Mask_Effect)); + computeBoundsVisitor.setTraversalMask(~(SceneUtil::Mask_ParticleSystem|SceneUtil::Mask_Effect)); const_cast(node.get())->accept(computeBoundsVisitor); osg::BoundingBox bounds = computeBoundsVisitor.getBoundingBox(); diff --git a/apps/openmw/mwrender/ripplesimulation.cpp b/apps/openmw/mwrender/ripplesimulation.cpp index f7feb267a..6597dde24 100644 --- a/apps/openmw/mwrender/ripplesimulation.cpp +++ b/apps/openmw/mwrender/ripplesimulation.cpp @@ -16,8 +16,7 @@ #include #include #include - -#include "vismask.hpp" +#include #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" @@ -104,7 +103,7 @@ RippleSimulation::RippleSimulation(osg::Group *parent, Resource::ResourceSystem* mParticleNode->setName("Ripple Root"); mParticleNode->addChild(updater); mParticleNode->addChild(mParticleSystem); - mParticleNode->setNodeMask(Mask_Water); + mParticleNode->setNodeMask(SceneUtil::Mask_Water); createWaterRippleStateSet(resourceSystem, mParticleNode); diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 3996f472c..51577ce41 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -42,12 +42,12 @@ #include #include #include +#include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" -#include "vismask.hpp" #include "renderbin.hpp" namespace @@ -453,7 +453,7 @@ public: void setVisible(bool visible) { - mTransform->setNodeMask(visible ? mVisibleMask : 0); + mTransform->setNodeMask(visible ? mVisibleMask : SceneUtil::Mask_Disabled); } protected: @@ -469,7 +469,7 @@ class Sun : public CelestialBody { public: Sun(osg::Group* parentNode, Resource::ImageManager& imageManager) - : CelestialBody(parentNode, 1.0f, 1, Mask_Sun) + : CelestialBody(parentNode, 1.0f, 1, SceneUtil::Mask_Sun) , mUpdater(new Updater) { mTransform->addUpdateCallback(mUpdater); @@ -644,7 +644,7 @@ private: camera->setProjectionMatrix(osg::Matrix::identity()); camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF); // add to skyRoot instead? camera->setViewMatrix(osg::Matrix::identity()); - camera->setClearMask(0); + camera->setClearMask(SceneUtil::Mask_Disabled); camera->setRenderOrder(osg::Camera::NESTED_RENDER); camera->setAllowEventFocus(false); @@ -1134,7 +1134,7 @@ SkyManager::SkyManager(osg::Group* parentNode, Resource::SceneManager* sceneMana skyroot->getOrCreateStateSet()->setAttributeAndModes(new osg::Program(), osg::StateAttribute::OVERRIDE|osg::StateAttribute::PROTECTED|osg::StateAttribute::ON); SceneUtil::ShadowManager::disableShadowsForStateSet(skyroot->getOrCreateStateSet()); - skyroot->setNodeMask(Mask_Sky); + skyroot->setNodeMask(SceneUtil::Mask_Sky); parentNode->addChild(skyroot); mRootNode = skyroot; @@ -1166,7 +1166,7 @@ void SkyManager::create() mAtmosphereDay->addUpdateCallback(mAtmosphereUpdater); mAtmosphereNightNode = new osg::PositionAttitudeTransform; - mAtmosphereNightNode->setNodeMask(0); + mAtmosphereNightNode->setNodeMask(SceneUtil::Mask_Disabled); mEarlyRenderBinRoot->addChild(mAtmosphereNightNode); osg::ref_ptr atmosphereNight; @@ -1199,7 +1199,7 @@ void SkyManager::create() mCloudUpdater2 = new CloudUpdater; mCloudUpdater2->setOpacity(0.f); mCloudMesh2->addUpdateCallback(mCloudUpdater2); - mCloudMesh2->setNodeMask(0); + mCloudMesh2->setNodeMask(SceneUtil::Mask_Disabled); osg::ref_ptr depth = new osg::Depth; depth->setWriteMask(false); @@ -1522,7 +1522,7 @@ void SkyManager::createRain() mRainFader = new RainFader(&mWeatherAlpha); mRainNode->addUpdateCallback(mRainFader); mRainNode->addCullCallback(mUnderwaterSwitch); - mRainNode->setNodeMask(Mask_WeatherParticles); + mRainNode->setNodeMask(SceneUtil::Mask_WeatherParticles); mRootNode->addChild(mRainNode); } @@ -1625,7 +1625,7 @@ void SkyManager::setEnabled(bool enabled) if (enabled && !mCreated) create(); - mRootNode->setNodeMask(enabled ? Mask_Sky : 0); + mRootNode->setNodeMask(enabled ? SceneUtil::Mask_Sky : SceneUtil::Mask_Disabled); mEnabled = enabled; } @@ -1718,7 +1718,7 @@ void SkyManager::setWeather(const WeatherResult& weather) { mParticleNode = new osg::PositionAttitudeTransform; mParticleNode->addCullCallback(mUnderwaterSwitch); - mParticleNode->setNodeMask(Mask_WeatherParticles); + mParticleNode->setNodeMask(SceneUtil::Mask_WeatherParticles); mRootNode->addChild(mParticleNode); } @@ -1788,7 +1788,7 @@ void SkyManager::setWeather(const WeatherResult& weather) mCloudUpdater->setOpacity((1.f-mCloudBlendFactor)); mCloudUpdater2->setOpacity(mCloudBlendFactor); - mCloudMesh2->setNodeMask(mCloudBlendFactor > 0.f ? ~0 : 0); + mCloudMesh2->setNodeMask(mCloudBlendFactor > 0.f ? SceneUtil::Mask_Default : SceneUtil::Mask_Disabled); } if (mCloudColour != weather.mFogColor) @@ -1833,7 +1833,7 @@ void SkyManager::setWeather(const WeatherResult& weather) mAtmosphereNightUpdater->setFade(mStarsOpacity); } - mAtmosphereNightNode->setNodeMask(weather.mNight ? ~0 : 0); + mAtmosphereNightNode->setNodeMask(weather.mNight ? SceneUtil::Mask_Default : SceneUtil::Mask_Disabled); if (mRainFader) mRainFader->setAlpha(weather.mEffectFade * 0.6); // * Rain_Threshold? diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index 6d230d36e..2b307055d 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -27,6 +27,7 @@ #include #include +#include #include @@ -40,7 +41,6 @@ #include "../mwworld/cellstore.hpp" -#include "vismask.hpp" #include "ripplesimulation.hpp" #include "renderbin.hpp" #include "util.hpp" @@ -243,8 +243,8 @@ public: setName("RefractionCamera"); setCullCallback(new InheritViewPointCallback); - setCullMask(Mask_Effect|Mask_Scene|Mask_Object|Mask_Static|Mask_Terrain|Mask_Actor|Mask_ParticleSystem|Mask_Sky|Mask_Sun|Mask_Player|Mask_Lighting); - setNodeMask(Mask_RenderToTexture); + setCullMask(SceneUtil::Mask_Effect|SceneUtil::Mask_Scene|SceneUtil::Mask_Object|SceneUtil::Mask_Static|SceneUtil::Mask_Terrain|SceneUtil::Mask_Actor|SceneUtil::Mask_ParticleSystem|SceneUtil::Mask_Sky|SceneUtil::Mask_Sun|SceneUtil::Mask_Player|SceneUtil::Mask_Lighting); + setNodeMask(SceneUtil::Mask_RenderToTexture); setViewport(0, 0, rttSize, rttSize); // No need for Update traversal since the scene is already updated as part of the main scene graph @@ -337,7 +337,7 @@ public: setCullCallback(new InheritViewPointCallback); setInterior(isInterior); - setNodeMask(Mask_RenderToTexture); + setNodeMask(SceneUtil::Mask_RenderToTexture); unsigned int rttSize = Settings::Manager::getInt("rtt size", "Water"); setViewport(0, 0, rttSize, rttSize); @@ -372,11 +372,11 @@ public: int reflectionDetail = Settings::Manager::getInt("reflection detail", "Water"); reflectionDetail = std::min(4, std::max(isInterior ? 2 : 0, reflectionDetail)); unsigned int extraMask = 0; - if(reflectionDetail >= 1) extraMask |= Mask_Terrain; - if(reflectionDetail >= 2) extraMask |= Mask_Static; - if(reflectionDetail >= 3) extraMask |= Mask_Effect|Mask_ParticleSystem|Mask_Object; - if(reflectionDetail >= 4) extraMask |= Mask_Player|Mask_Actor; - setCullMask(Mask_Scene|Mask_Sky|Mask_Lighting|extraMask); + if(reflectionDetail >= 1) extraMask |= SceneUtil::Mask_Terrain; + if(reflectionDetail >= 2) extraMask |= SceneUtil::Mask_Static; + if(reflectionDetail >= 3) extraMask |= SceneUtil::Mask_Effect|SceneUtil::Mask_ParticleSystem|SceneUtil::Mask_Object; + if(reflectionDetail >= 4) extraMask |= SceneUtil::Mask_Player|SceneUtil::Mask_Actor; + setCullMask(SceneUtil::Mask_Scene|SceneUtil::Mask_Sky|SceneUtil::Mask_Lighting|extraMask); } void setWaterLevel(float waterLevel) @@ -441,7 +441,7 @@ Water::Water(osg::Group *parent, osg::Group* sceneRoot, Resource::ResourceSystem mWaterGeom = SceneUtil::createWaterGeometry(Constants::CellSizeInUnits*150, 40, 900); mWaterGeom->setDrawCallback(new DepthClampCallback); - mWaterGeom->setNodeMask(Mask_Water); + mWaterGeom->setNodeMask(SceneUtil::Mask_Water); mWaterNode = new osg::PositionAttitudeTransform; mWaterNode->setName("Water Root"); @@ -451,7 +451,7 @@ Water::Water(osg::Group *parent, osg::Group* sceneRoot, Resource::ResourceSystem // simple water fallback for the local map osg::ref_ptr geom2 (osg::clone(mWaterGeom.get(), osg::CopyOp::DEEP_COPY_NODES)); createSimpleWaterStateSet(geom2, Fallback::Map::getFloat("Water_Map_Alpha")); - geom2->setNodeMask(Mask_SimpleWater); + geom2->setNodeMask(SceneUtil::Mask_SimpleWater); mWaterNode->addChild(geom2); mSceneRoot->addChild(mWaterNode); @@ -706,11 +706,11 @@ void Water::update(float dt) void Water::updateVisible() { bool visible = mEnabled && mToggled; - mWaterNode->setNodeMask(visible ? ~0 : 0); + mWaterNode->setNodeMask(visible ? SceneUtil::Mask_Default : SceneUtil::Mask_Disabled); if (mRefraction) - mRefraction->setNodeMask(visible ? Mask_RenderToTexture : 0); + mRefraction->setNodeMask(visible ? SceneUtil::Mask_RenderToTexture : SceneUtil::Mask_Disabled); if (mReflection) - mReflection->setNodeMask(visible ? Mask_RenderToTexture : 0); + mReflection->setNodeMask(visible ? SceneUtil::Mask_RenderToTexture : SceneUtil::Mask_Disabled); } bool Water::toggle() diff --git a/apps/openmw/mwworld/projectilemanager.cpp b/apps/openmw/mwworld/projectilemanager.cpp index 6004a34a6..73f8ac667 100644 --- a/apps/openmw/mwworld/projectilemanager.cpp +++ b/apps/openmw/mwworld/projectilemanager.cpp @@ -16,6 +16,7 @@ #include #include +#include #include #include "../mwworld/manualref.hpp" @@ -35,7 +36,6 @@ #include "../mwmechanics/weapontype.hpp" #include "../mwrender/animation.hpp" -#include "../mwrender/vismask.hpp" #include "../mwrender/renderingmanager.hpp" #include "../mwrender/util.hpp" @@ -188,7 +188,7 @@ namespace MWWorld bool rotate, bool createLight, osg::Vec4 lightDiffuseColor, std::string texture) { state.mNode = new osg::PositionAttitudeTransform; - state.mNode->setNodeMask(MWRender::Mask_Effect); + state.mNode->setNodeMask(SceneUtil::Mask_Effect); state.mNode->setPosition(pos); state.mNode->setAttitude(orient); @@ -228,7 +228,7 @@ namespace MWWorld projectileLight->setPosition(osg::Vec4(pos, 1.0)); SceneUtil::LightSource* projectileLightSource = new SceneUtil::LightSource; - projectileLightSource->setNodeMask(MWRender::Mask_Lighting); + projectileLightSource->setNodeMask(SceneUtil::Mask_Lighting); projectileLightSource->setRadius(66.f); state.mNode->addChild(projectileLightSource); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 71948119a..d5f849048 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -23,6 +23,7 @@ #include #include +#include #include #include @@ -46,7 +47,6 @@ #include "../mwrender/npcanimation.hpp" #include "../mwrender/renderingmanager.hpp" #include "../mwrender/camera.hpp" -#include "../mwrender/vismask.hpp" #include "../mwscript/globalscripts.hpp" @@ -2265,7 +2265,7 @@ namespace MWWorld { // Adjust position so the location we wanted ends up in the middle of the object bounding box osg::ComputeBoundsVisitor computeBounds; - computeBounds.setTraversalMask(~MWRender::Mask_ParticleSystem); + computeBounds.setTraversalMask(~SceneUtil::Mask_ParticleSystem); dropped.getRefData().getBaseNode()->accept(computeBounds); osg::BoundingBox bounds = computeBounds.getBoundingBox(); if (bounds.valid()) diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 45526a5af..2f6266dff 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -51,7 +51,7 @@ add_component_dir (shader add_component_dir (sceneutil clone attach visitor util statesetupdater controller skeleton riggeometry morphgeometry lightcontroller lightmanager lightutil positionattitudetransform workqueue unrefqueue pathgridutil waterutil writescene serialize optimizer - actorutil detourdebugdraw navmesh agentpath shadow mwshadowtechnique + actorutil detourdebugdraw navmesh agentpath shadow mwshadowtechnique vismask ) add_component_dir (nif diff --git a/components/nifosg/controller.cpp b/components/nifosg/controller.cpp index 1842e0017..4b87ba1a3 100644 --- a/components/nifosg/controller.cpp +++ b/components/nifosg/controller.cpp @@ -10,6 +10,7 @@ #include #include +#include #include "userdata.hpp" @@ -304,7 +305,7 @@ void VisController::operator() (osg::Node* node, osg::NodeVisitor* nv) { bool vis = calculate(getInputValue(nv)); // Leave 0x1 enabled for UpdateVisitor, so we can make ourselves visible again in the future from this update callback - node->setNodeMask(vis ? ~0 : 0x1); + node->setNodeMask(vis ? ~SceneUtil::Mask_Default : SceneUtil::Mask_UpdateVisitor); } traverse(node, nv); } diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index ec63dc606..602f1fa9a 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -16,6 +16,7 @@ #include #include #include +#include // particle #include @@ -167,7 +168,6 @@ namespace namespace NifOsg { - bool Loader::sShowMarkers = false; void Loader::setShowMarkers(bool show) @@ -553,7 +553,7 @@ namespace NifOsg { skipMeshes = true; // Leave mask for UpdateVisitor enabled - node->setNodeMask(0x1); + node->setNodeMask(SceneUtil::Mask_UpdateVisitor); } // We can skip creating meshes for hidden nodes if they don't have a VisController that @@ -568,7 +568,7 @@ namespace NifOsg skipMeshes = true; // skip child meshes, but still create the child node hierarchy for animating collision shapes // now hide this node, but leave the mask for UpdateVisitor enabled so that KeyframeController works - node->setNodeMask(0x1); + node->setNodeMask(SceneUtil::Mask_UpdateVisitor); } if ((skipMeshes || hasMarkers) && isAnimated) // make sure the empty node is not optimized away so the physicssystem can find it. diff --git a/components/resource/stats.cpp b/components/resource/stats.cpp index 51497cd27..1535bdbf6 100644 --- a/components/resource/stats.cpp +++ b/components/resource/stats.cpp @@ -12,6 +12,7 @@ #include #include +#include namespace Resource { @@ -103,14 +104,14 @@ void StatsHandler::toggle(osgViewer::ViewerBase *viewer) if (!_statsType) { - _camera->setNodeMask(0); + _camera->setNodeMask(SceneUtil::Mask_Disabled); _switch->setAllChildrenOff(); viewer->getViewerStats()->collectStats("resource", false); } else { - _camera->setNodeMask(0xffffffff); + _camera->setNodeMask(SceneUtil::Mask_Default); _switch->setSingleChildOn(_resourceStatsChildNum); viewer->getViewerStats()->collectStats("resource", true); diff --git a/components/sceneutil/lightutil.cpp b/components/sceneutil/lightutil.cpp index e9be05908..c90fa8923 100644 --- a/components/sceneutil/lightutil.cpp +++ b/components/sceneutil/lightutil.cpp @@ -11,6 +11,7 @@ #include "lightcontroller.hpp" #include "util.hpp" #include "visitor.hpp" +#include "vismask.hpp" #include "positionattitudetransform.hpp" namespace SceneUtil @@ -58,7 +59,7 @@ namespace SceneUtil light->setQuadraticAttenuation(quadraticAttenuation); } - void addLight (osg::Group* node, const ESM::Light* esmLight, unsigned int partsysMask, unsigned int lightMask, bool isExterior) + void addLight (osg::Group* node, const ESM::Light* esmLight, bool isExterior) { SceneUtil::FindByNameVisitor visitor("AttachLight"); node->accept(visitor); @@ -71,7 +72,7 @@ namespace SceneUtil else { osg::ComputeBoundsVisitor computeBound; - computeBound.setTraversalMask(~partsysMask); + computeBound.setTraversalMask(~SceneUtil::Mask_ParticleSystem); // We want the bounds of all children of the node, ignoring the node's local transformation // So do a traverse(), not accept() computeBound.traverse(*node); @@ -85,15 +86,15 @@ namespace SceneUtil attachTo = trans; } - osg::ref_ptr lightSource = createLightSource(esmLight, lightMask, isExterior); + osg::ref_ptr lightSource = createLightSource(esmLight, isExterior); attachTo->addChild(lightSource); } - osg::ref_ptr createLightSource(const ESM::Light* esmLight, unsigned int lightMask, bool isExterior, const osg::Vec4f& ambient) + osg::ref_ptr createLightSource(const ESM::Light* esmLight, bool isExterior, const osg::Vec4f& ambient) { osg::ref_ptr lightSource (new SceneUtil::LightSource); osg::ref_ptr light (new osg::Light); - lightSource->setNodeMask(lightMask); + lightSource->setNodeMask(SceneUtil::Mask_Lighting); float radius = esmLight->mData.mRadius; lightSource->setRadius(radius); diff --git a/components/sceneutil/lightutil.hpp b/components/sceneutil/lightutil.hpp index 7096c38b2..f72cf9f19 100644 --- a/components/sceneutil/lightutil.hpp +++ b/components/sceneutil/lightutil.hpp @@ -32,14 +32,14 @@ namespace SceneUtil /// @param partsysMask Node mask to ignore when computing the sub graph's bounding box. /// @param lightMask Mask to assign to the newly created LightSource. /// @param isExterior Is the light outside? May be used for deciding which attenuation settings to use. - void addLight (osg::Group* node, const ESM::Light* esmLight, unsigned int partsysMask, unsigned int lightMask, bool isExterior); + void addLight (osg::Group* node, const ESM::Light* esmLight, bool isExterior); /// @brief Convert an ESM::Light to a SceneUtil::LightSource, and return it. /// @param esmLight The light definition coming from the game files containing radius, color, flicker, etc. /// @param lightMask Mask to assign to the newly created LightSource. /// @param isExterior Is the light outside? May be used for deciding which attenuation settings to use. /// @param ambient Ambient component of the light. - osg::ref_ptr createLightSource (const ESM::Light* esmLight, unsigned int lightMask, bool isExterior, const osg::Vec4f& ambient=osg::Vec4f(0,0,0,1)); + osg::ref_ptr createLightSource (const ESM::Light* esmLight, bool isExterior, const osg::Vec4f& ambient=osg::Vec4f(0,0,0,1)); } diff --git a/components/sceneutil/optimizer.hpp b/components/sceneutil/optimizer.hpp index 9974e7097..9b3dc47f9 100644 --- a/components/sceneutil/optimizer.hpp +++ b/components/sceneutil/optimizer.hpp @@ -22,6 +22,8 @@ #include #include +#include + //#include #include @@ -42,7 +44,7 @@ class BaseOptimizerVisitor : public osg::NodeVisitor _optimizer(optimizer), _operationType(operation) { - setNodeMaskOverride(0xffffffff); + setNodeMaskOverride(SceneUtil::Mask_Default); } inline bool isOperationPermissibleForObject(const osg::StateSet* object) const; diff --git a/components/sceneutil/shadow.cpp b/components/sceneutil/shadow.cpp index 6b88adaab..035c19a5f 100644 --- a/components/sceneutil/shadow.cpp +++ b/components/sceneutil/shadow.cpp @@ -2,6 +2,7 @@ #include +#include #include namespace SceneUtil @@ -21,7 +22,7 @@ namespace SceneUtil mShadowTechnique->enableShadows(); mShadowSettings->setLightNum(0); - mShadowSettings->setReceivesShadowTraversalMask(~0u); + mShadowSettings->setReceivesShadowTraversalMask(SceneUtil::Mask_Default); int numberOfShadowMapsPerLight = Settings::Manager::getInt("number of shadow maps", "Shadows"); numberOfShadowMapsPerLight = std::max(1, std::min(numberOfShadowMapsPerLight, 8)); diff --git a/apps/openmw/mwrender/vismask.hpp b/components/sceneutil/vismask.hpp similarity index 82% rename from apps/openmw/mwrender/vismask.hpp rename to components/sceneutil/vismask.hpp index f9f9dc74c..e9c35922c 100644 --- a/apps/openmw/mwrender/vismask.hpp +++ b/components/sceneutil/vismask.hpp @@ -1,7 +1,7 @@ -#ifndef OPENMW_MWRENDER_VISMASK_H -#define OPENMW_MWRENDER_VISMASK_H +#ifndef OPENMW_COMPONENTS_SCENEUTIL_VISMASK_H +#define OPENMW_COMPONENTS_SCENEUTIL_VISMASK_H -namespace MWRender +namespace SceneUtil { /// Node masks used for controlling visibility of game objects. @@ -21,6 +21,8 @@ namespace MWRender /// compatibility if the enumeration values were to be changed. Feel free to change them when it makes sense. enum VisMask { + Mask_Disabled = 0, // For hidden nodes + Mask_UpdateVisitor = 0x1, // reserved for separating UpdateVisitors from CullVisitors // child of Scene @@ -53,7 +55,19 @@ namespace MWRender Mask_PreCompile = (1<<18), // Set on a camera's cull mask to enable the LightManager - Mask_Lighting = (1<<19) + Mask_Lighting = (1<<19), + + // For pathgrid nodes debugging + Mask_Pathgrid = (1<<20), + + // Editor control elements + Mask_EditorCellMarker = (1<<21), + Mask_EditorCellArrow = (1<<22), + Mask_EditorCellBorder = (1<<23), + Mask_EditorReference = (1<<24), + + // Default mask for OSG nodes + Mask_Default = 0xffffffff }; } diff --git a/components/terrain/cellborder.cpp b/components/terrain/cellborder.cpp index 6eabadf92..d6ecd5b5a 100644 --- a/components/terrain/cellborder.cpp +++ b/components/terrain/cellborder.cpp @@ -4,16 +4,17 @@ #include #include +#include + #include "world.hpp" #include "../esm/loadland.hpp" namespace Terrain { -CellBorder::CellBorder(Terrain::World *world, osg::Group *root, int borderMask): +CellBorder::CellBorder(Terrain::World *world, osg::Group *root): mWorld(world), - mRoot(root), - mBorderMask(borderMask) + mRoot(root) { } @@ -69,7 +70,7 @@ void CellBorder::createCellBorderGeometry(int x, int y) polygonmode->setMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE); stateSet->setAttributeAndModes(polygonmode,osg::StateAttribute::ON); - borderGeode->setNodeMask(mBorderMask); + borderGeode->setNodeMask(SceneUtil::Mask_Debug); mRoot->addChild(borderGeode); diff --git a/components/terrain/cellborder.hpp b/components/terrain/cellborder.hpp index 908cdea09..7770204e1 100644 --- a/components/terrain/cellborder.hpp +++ b/components/terrain/cellborder.hpp @@ -16,7 +16,7 @@ namespace Terrain public: typedef std::map, osg::ref_ptr > CellGrid; - CellBorder(Terrain::World *world, osg::Group *root, int borderMask); + CellBorder(Terrain::World *world, osg::Group *root); void createCellBorderGeometry(int x, int y); void destroyCellBorderGeometry(int x, int y); diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 8dd3a0706..f998b7877 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -6,6 +6,7 @@ #include #include +#include #include "quadtreenode.hpp" #include "storage.hpp" @@ -217,8 +218,8 @@ private: osg::ref_ptr mLodCallback; }; -QuadTreeWorld::QuadTreeWorld(osg::Group *parent, osg::Group *compileRoot, Resource::ResourceSystem *resourceSystem, Storage *storage, int nodeMask, int preCompileMask, int borderMask, int compMapResolution, float compMapLevel, float lodFactor, int vertexLodMod, float maxCompGeometrySize) - : TerrainGrid(parent, compileRoot, resourceSystem, storage, nodeMask, preCompileMask, borderMask) +QuadTreeWorld::QuadTreeWorld(osg::Group *parent, osg::Group *compileRoot, Resource::ResourceSystem *resourceSystem, Storage *storage, int compMapResolution, float compMapLevel, float lodFactor, int vertexLodMod, float maxCompGeometrySize) + : TerrainGrid(parent, compileRoot, resourceSystem, storage) , mViewDataMap(new ViewDataMap) , mQuadTreeBuilt(false) , mLodFactor(lodFactor) @@ -425,7 +426,7 @@ void QuadTreeWorld::enable(bool enabled) } if (mRootNode) - mRootNode->setNodeMask(enabled ? ~0 : 0); + mRootNode->setNodeMask(enabled ? SceneUtil::Mask_Default : SceneUtil::Mask_Disabled); } void QuadTreeWorld::cacheCell(View *view, int x, int y) diff --git a/components/terrain/quadtreeworld.hpp b/components/terrain/quadtreeworld.hpp index bcb671ee1..2bfd9f896 100644 --- a/components/terrain/quadtreeworld.hpp +++ b/components/terrain/quadtreeworld.hpp @@ -21,7 +21,7 @@ namespace Terrain class QuadTreeWorld : public TerrainGrid // note: derived from TerrainGrid is only to render default cells (see loadCell) { public: - QuadTreeWorld(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, Storage* storage, int nodeMask, int preCompileMask, int borderMask, int compMapResolution, float comMapLevel, float lodFactor, int vertexLodMod, float maxCompGeometrySize); + QuadTreeWorld(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, Storage* storage, int compMapResolution, float comMapLevel, float lodFactor, int vertexLodMod, float maxCompGeometrySize); ~QuadTreeWorld(); diff --git a/components/terrain/terraingrid.cpp b/components/terrain/terraingrid.cpp index 7310846c2..f7a7644fd 100644 --- a/components/terrain/terraingrid.cpp +++ b/components/terrain/terraingrid.cpp @@ -18,8 +18,8 @@ public: virtual void reset() {} }; -TerrainGrid::TerrainGrid(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, Storage* storage, int nodeMask, int preCompileMask, int borderMask) - : Terrain::World(parent, compileRoot, resourceSystem, storage, nodeMask, preCompileMask, borderMask) +TerrainGrid::TerrainGrid(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, Storage* storage) + : Terrain::World(parent, compileRoot, resourceSystem, storage) , mNumSplits(4) { } diff --git a/components/terrain/terraingrid.hpp b/components/terrain/terraingrid.hpp index eb30fb97d..3764eb986 100644 --- a/components/terrain/terraingrid.hpp +++ b/components/terrain/terraingrid.hpp @@ -14,7 +14,7 @@ namespace Terrain class TerrainGrid : public Terrain::World { public: - TerrainGrid(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, Storage* storage, int nodeMask, int preCompileMask=~0, int borderMask=0); + TerrainGrid(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, Storage* storage); ~TerrainGrid(); virtual void cacheCell(View* view, int x, int y); diff --git a/components/terrain/world.cpp b/components/terrain/world.cpp index da3bdb5c2..010ae9568 100644 --- a/components/terrain/world.cpp +++ b/components/terrain/world.cpp @@ -5,6 +5,7 @@ #include #include +#include #include "storage.hpp" #include "texturemanager.hpp" @@ -14,14 +15,14 @@ namespace Terrain { -World::World(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, Storage* storage, int nodeMask, int preCompileMask, int borderMask) +World::World(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, Storage* storage) : mStorage(storage) , mParent(parent) , mResourceSystem(resourceSystem) , mBorderVisible(false) { mTerrainRoot = new osg::Group; - mTerrainRoot->setNodeMask(nodeMask); + mTerrainRoot->setNodeMask(SceneUtil::Mask_Terrain); mTerrainRoot->getOrCreateStateSet()->setRenderingHint(osg::StateSet::OPAQUE_BIN); osg::ref_ptr material (new osg::Material); material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE); @@ -34,8 +35,8 @@ World::World(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSyst compositeCam->setProjectionMatrix(osg::Matrix::identity()); compositeCam->setViewMatrix(osg::Matrix::identity()); compositeCam->setReferenceFrame(osg::Camera::ABSOLUTE_RF); - compositeCam->setClearMask(0); - compositeCam->setNodeMask(preCompileMask); + compositeCam->setClearMask(SceneUtil::Mask_Disabled); + compositeCam->setNodeMask(SceneUtil::Mask_PreCompile); mCompositeMapCamera = compositeCam; compileRoot->addChild(compositeCam); @@ -47,7 +48,7 @@ World::World(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSyst mTextureManager.reset(new TextureManager(mResourceSystem->getSceneManager())); mChunkManager.reset(new ChunkManager(mStorage, mResourceSystem->getSceneManager(), mTextureManager.get(), mCompositeMapRenderer)); - mCellBorder.reset(new CellBorder(this,mTerrainRoot.get(),borderMask)); + mCellBorder.reset(new CellBorder(this,mTerrainRoot.get())); mResourceSystem->addResourceManager(mChunkManager.get()); mResourceSystem->addResourceManager(mTextureManager.get()); diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index 0402b8197..8929e0f6b 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -63,7 +63,7 @@ namespace Terrain /// @param storage Storage instance to get terrain data from (heights, normals, colors, textures..) /// @param nodeMask mask for the terrain root /// @param preCompileMask mask for pre compiling textures - World(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, Storage* storage, int nodeMask, int preCompileMask, int borderMask); + World(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, Storage* storage); virtual ~World(); /// Set a WorkQueue to delete objects in the background thread. From 650f429ff503984394b554bf291c11fff0a71015 Mon Sep 17 00:00:00 2001 From: elsid Date: Thu, 13 Feb 2020 21:36:56 +0100 Subject: [PATCH 36/99] Add final modifier to fix warnings --- apps/openmw/mwgui/backgroundimage.hpp | 6 +-- apps/openmw/mwgui/bookpage.cpp | 32 ++++++------- apps/openmw/mwgui/controllers.hpp | 6 +-- apps/openmw/mwgui/cursor.hpp | 16 +++---- apps/openmw/mwgui/itemchargeview.hpp | 8 ++-- apps/openmw/mwgui/itemview.hpp | 8 ++-- apps/openmw/mwgui/itemwidget.hpp | 2 +- apps/openmw/mwgui/mapwindow.cpp | 6 +-- apps/openmw/mwgui/resourceskin.hpp | 4 +- apps/openmw/mwgui/spellview.hpp | 8 ++-- apps/openmw/mwgui/widgets.hpp | 24 +++++----- components/myguiplatform/additivelayer.hpp | 4 +- components/myguiplatform/scalinglayer.hpp | 12 ++--- components/widgets/box.hpp | 52 +++++++++++----------- components/widgets/imagebutton.hpp | 16 +++---- components/widgets/list.hpp | 4 +- components/widgets/numericeditbox.hpp | 10 ++--- components/widgets/sharedstatebutton.hpp | 14 +++--- components/widgets/windowcaption.hpp | 10 ++--- 19 files changed, 121 insertions(+), 121 deletions(-) diff --git a/apps/openmw/mwgui/backgroundimage.hpp b/apps/openmw/mwgui/backgroundimage.hpp index 3db5bab16..3ea8b3bba 100644 --- a/apps/openmw/mwgui/backgroundimage.hpp +++ b/apps/openmw/mwgui/backgroundimage.hpp @@ -9,7 +9,7 @@ namespace MWGui /** * @brief A variant of MyGUI::ImageBox with aspect ratio correction using black bars */ - class BackgroundImage : public MyGUI::ImageBox + class BackgroundImage final : public MyGUI::ImageBox { MYGUI_RTTI_DERIVED(BackgroundImage) @@ -22,8 +22,8 @@ namespace MWGui */ void setBackgroundImage (const std::string& image, bool fixedRatio=true, bool stretch=true); - virtual void setSize (const MyGUI::IntSize &_value); - virtual void setCoord (const MyGUI::IntCoord &_value); + void setSize (const MyGUI::IntSize &_value) final; + void setCoord (const MyGUI::IntCoord &_value) final; private: MyGUI::ImageBox* mChild; diff --git a/apps/openmw/mwgui/bookpage.cpp b/apps/openmw/mwgui/bookpage.cpp index 8f8ca4bb5..5724defcd 100644 --- a/apps/openmw/mwgui/bookpage.cpp +++ b/apps/openmw/mwgui/bookpage.cpp @@ -817,7 +817,7 @@ namespace }; } -class PageDisplay : public MyGUI::ISubWidgetText +class PageDisplay final : public MyGUI::ISubWidgetText { MYGUI_RTTI_DERIVED(PageDisplay) protected: @@ -1140,7 +1140,7 @@ public: i->second->createDrawItem (mNode); } - void setVisible (bool newVisible) + void setVisible (bool newVisible) final { if (mVisible == newVisible) return; @@ -1162,7 +1162,7 @@ public: } } - void createDrawItem(MyGUI::ITexture* texture, MyGUI::ILayerNode* node) + void createDrawItem(MyGUI::ITexture* texture, MyGUI::ILayerNode* node) final { mNode = node; @@ -1230,9 +1230,9 @@ public: // ISubWidget should not necessarily be a drawitem // in this case, it is not... - void doRender() { } + void doRender() final { } - void _updateView () + void _updateView () final { _checkMargin(); @@ -1241,7 +1241,7 @@ public: mNode->outOfDate (i->second->mRenderItem); } - void _correctView() + void _correctView() final { _checkMargin (); @@ -1251,7 +1251,7 @@ public: } - void destroyDrawItem() + void destroyDrawItem() final { for (ActiveTextFormats::iterator i = mActiveTextFormats.begin (); i != mActiveTextFormats.end (); ++i) i->second->destroyDrawItem (mNode); @@ -1261,7 +1261,7 @@ public: }; -class BookPageImpl : public BookPage +class BookPageImpl final : public BookPage { MYGUI_RTTI_DERIVED(BookPage) public: @@ -1271,24 +1271,24 @@ public: { } - void showPage (TypesetBook::Ptr book, size_t page) + void showPage (TypesetBook::Ptr book, size_t page) final { mPageDisplay->showPage (book, page); } - void adviseLinkClicked (std::function linkClicked) + void adviseLinkClicked (std::function linkClicked) final { mPageDisplay->mLinkClicked = linkClicked; } - void unadviseLinkClicked () + void unadviseLinkClicked () final { mPageDisplay->mLinkClicked = std::function (); } protected: - virtual void initialiseOverride() + void initialiseOverride() final { Base::initialiseOverride(); @@ -1302,24 +1302,24 @@ protected: } } - void onMouseLostFocus(Widget* _new) + void onMouseLostFocus(Widget* _new) final { // NOTE: MyGUI also fires eventMouseLostFocus for widgets that are about to be destroyed (if they had focus). // Child widgets may already be destroyed! So be careful. mPageDisplay->onMouseLostFocus (); } - void onMouseMove(int left, int top) + void onMouseMove(int left, int top) final { mPageDisplay->onMouseMove (left, top); } - void onMouseButtonPressed (int left, int top, MyGUI::MouseButton id) + void onMouseButtonPressed (int left, int top, MyGUI::MouseButton id) final { mPageDisplay->onMouseButtonPressed (left, top, id); } - void onMouseButtonReleased(int left, int top, MyGUI::MouseButton id) + void onMouseButtonReleased(int left, int top, MyGUI::MouseButton id) final { mPageDisplay->onMouseButtonReleased (left, top, id); } diff --git a/apps/openmw/mwgui/controllers.hpp b/apps/openmw/mwgui/controllers.hpp index b75fe79ab..bd9646ec2 100644 --- a/apps/openmw/mwgui/controllers.hpp +++ b/apps/openmw/mwgui/controllers.hpp @@ -14,14 +14,14 @@ namespace MWGui namespace Controllers { /// Automatically positions a widget below the mouse cursor. - class ControllerFollowMouse : + class ControllerFollowMouse final : public MyGUI::ControllerItem { MYGUI_RTTI_DERIVED( ControllerFollowMouse ) private: - bool addTime(MyGUI::Widget* _widget, float _time); - void prepareItem(MyGUI::Widget* _widget); + bool addTime(MyGUI::Widget* _widget, float _time) final; + void prepareItem(MyGUI::Widget* _widget) final; }; } } diff --git a/apps/openmw/mwgui/cursor.hpp b/apps/openmw/mwgui/cursor.hpp index 4e3eb9097..ef5099ef8 100644 --- a/apps/openmw/mwgui/cursor.hpp +++ b/apps/openmw/mwgui/cursor.hpp @@ -11,7 +11,7 @@ namespace MWGui /// ResourceImageSetPointer that we need. /// \example MyGUI::FactoryManager::getInstance().registerFactory("Resource", "ResourceImageSetPointer"); /// MyGUI::ResourceManager::getInstance().load("core.xml"); - class ResourceImageSetPointerFix : + class ResourceImageSetPointerFix final : public MyGUI::IPointer { MYGUI_RTTI_DERIVED( ResourceImageSetPointerFix ) @@ -20,17 +20,17 @@ namespace MWGui ResourceImageSetPointerFix(); virtual ~ResourceImageSetPointerFix(); - virtual void deserialization(MyGUI::xml::ElementPtr _node, MyGUI::Version _version); + void deserialization(MyGUI::xml::ElementPtr _node, MyGUI::Version _version) final; - virtual void setImage(MyGUI::ImageBox* _image); - virtual void setPosition(MyGUI::ImageBox* _image, const MyGUI::IntPoint& _point); + void setImage(MyGUI::ImageBox* _image) final; + void setPosition(MyGUI::ImageBox* _image, const MyGUI::IntPoint& _point) final; //and now for the whole point of this class, allow us to get //the hot spot, the image and the size of the cursor. - virtual MyGUI::ResourceImageSetPtr getImageSet(); - virtual MyGUI::IntPoint getHotSpot(); - virtual MyGUI::IntSize getSize(); - virtual int getRotation(); + MyGUI::ResourceImageSetPtr getImageSet(); + MyGUI::IntPoint getHotSpot(); + MyGUI::IntSize getSize(); + int getRotation(); private: MyGUI::IntPoint mPoint; diff --git a/apps/openmw/mwgui/itemchargeview.hpp b/apps/openmw/mwgui/itemchargeview.hpp index 956272aec..2522f55d1 100644 --- a/apps/openmw/mwgui/itemchargeview.hpp +++ b/apps/openmw/mwgui/itemchargeview.hpp @@ -21,7 +21,7 @@ namespace MWGui class ItemModel; class ItemWidget; - class ItemChargeView : public MyGUI::Widget + class ItemChargeView final : public MyGUI::Widget { MYGUI_RTTI_DERIVED(ItemChargeView) public: @@ -36,7 +36,7 @@ namespace MWGui /// Register needed components with MyGUI's factory manager static void registerComponents(); - virtual void initialiseOverride(); + void initialiseOverride() final; /// Takes ownership of \a model void setModel(ItemModel* model); @@ -47,8 +47,8 @@ namespace MWGui void layoutWidgets(); void resetScrollbars(); - virtual void setSize(const MyGUI::IntSize& value); - virtual void setCoord(const MyGUI::IntCoord& value); + void setSize(const MyGUI::IntSize& value) final; + void setCoord(const MyGUI::IntCoord& value) final; MyGUI::delegates::CMultiDelegate2 eventItemClicked; diff --git a/apps/openmw/mwgui/itemview.hpp b/apps/openmw/mwgui/itemview.hpp index fa6ef29f9..a5e537aa0 100644 --- a/apps/openmw/mwgui/itemview.hpp +++ b/apps/openmw/mwgui/itemview.hpp @@ -8,7 +8,7 @@ namespace MWGui { - class ItemView : public MyGUI::Widget + class ItemView final : public MyGUI::Widget { MYGUI_RTTI_DERIVED(ItemView) public: @@ -33,12 +33,12 @@ namespace MWGui void resetScrollBars(); private: - virtual void initialiseOverride(); + void initialiseOverride() final; void layoutWidgets(); - virtual void setSize(const MyGUI::IntSize& _value); - virtual void setCoord(const MyGUI::IntCoord& _value); + void setSize(const MyGUI::IntSize& _value) final; + void setCoord(const MyGUI::IntCoord& _value) final; void onSelectedItem (MyGUI::Widget* sender); void onSelectedBackground (MyGUI::Widget* sender); diff --git a/apps/openmw/mwgui/itemwidget.hpp b/apps/openmw/mwgui/itemwidget.hpp index 0cb22c7f7..748a44445 100644 --- a/apps/openmw/mwgui/itemwidget.hpp +++ b/apps/openmw/mwgui/itemwidget.hpp @@ -41,7 +41,7 @@ namespace MWGui void setFrame (const std::string& frame, const MyGUI::IntCoord& coord); protected: - virtual void initialiseOverride(); + void initialiseOverride() final; MyGUI::ImageBox* mItem; MyGUI::ImageBox* mItemShadow; diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index 02d2be577..bc65ee021 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -54,7 +54,7 @@ namespace /// @brief A widget that changes its color when hovered. - class MarkerWidget: public MyGUI::Widget + class MarkerWidget final : public MyGUI::Widget { MYGUI_RTTI_DERIVED(MarkerWidget) @@ -74,12 +74,12 @@ namespace MyGUI::Colour mNormalColour; MyGUI::Colour mHoverColour; - void onMouseLostFocus(MyGUI::Widget* _new) + void onMouseLostFocus(MyGUI::Widget* _new) final { setColour(mNormalColour); } - void onMouseSetFocus(MyGUI::Widget* _old) + void onMouseSetFocus(MyGUI::Widget* _old) final { setColour(mHoverColour); } diff --git a/apps/openmw/mwgui/resourceskin.hpp b/apps/openmw/mwgui/resourceskin.hpp index 5a1f05c05..bdf0d2f0b 100644 --- a/apps/openmw/mwgui/resourceskin.hpp +++ b/apps/openmw/mwgui/resourceskin.hpp @@ -5,12 +5,12 @@ namespace MWGui { - class AutoSizedResourceSkin : public MyGUI::ResourceSkin + class AutoSizedResourceSkin final : public MyGUI::ResourceSkin { MYGUI_RTTI_DERIVED( AutoSizedResourceSkin ) public: - virtual void deserialization(MyGUI::xml::ElementPtr _node, MyGUI::Version _version); + void deserialization(MyGUI::xml::ElementPtr _node, MyGUI::Version _version) final; }; } diff --git a/apps/openmw/mwgui/spellview.hpp b/apps/openmw/mwgui/spellview.hpp index 66113d869..a387cac39 100644 --- a/apps/openmw/mwgui/spellview.hpp +++ b/apps/openmw/mwgui/spellview.hpp @@ -19,7 +19,7 @@ namespace MWGui class SpellModel; ///@brief Displays a SpellModel in a list widget - class SpellView : public MyGUI::Widget + class SpellView final : public MyGUI::Widget { MYGUI_RTTI_DERIVED(SpellView) public: @@ -47,10 +47,10 @@ namespace MWGui /// Fired when a spell was clicked EventHandle_ModelIndex eventSpellClicked; - virtual void initialiseOverride(); + void initialiseOverride() final; - virtual void setSize(const MyGUI::IntSize& _value); - virtual void setCoord(const MyGUI::IntCoord& _value); + void setSize(const MyGUI::IntSize& _value) final; + void setCoord(const MyGUI::IntCoord& _value) final; void resetScrollbars(); diff --git a/apps/openmw/mwgui/widgets.hpp b/apps/openmw/mwgui/widgets.hpp index 4b42b888a..ff3b2311a 100644 --- a/apps/openmw/mwgui/widgets.hpp +++ b/apps/openmw/mwgui/widgets.hpp @@ -90,7 +90,7 @@ namespace MWGui typedef std::vector SpellEffectList; - class MWSkill : public MyGUI::Widget + class MWSkill final : public MyGUI::Widget { MYGUI_RTTI_DERIVED( MWSkill ) public: @@ -116,7 +116,7 @@ namespace MWGui protected: virtual ~MWSkill(); - virtual void initialiseOverride(); + void initialiseOverride() final; void onClicked(MyGUI::Widget* _sender); @@ -131,7 +131,7 @@ namespace MWGui }; typedef MWSkill* MWSkillPtr; - class MWAttribute : public MyGUI::Widget + class MWAttribute final : public MyGUI::Widget { MYGUI_RTTI_DERIVED( MWAttribute ) public: @@ -156,7 +156,7 @@ namespace MWGui protected: virtual ~MWAttribute(); - virtual void initialiseOverride(); + void initialiseOverride() final; void onClicked(MyGUI::Widget* _sender); @@ -175,7 +175,7 @@ namespace MWGui * @todo remove this class and use MWEffectList instead */ class MWSpellEffect; - class MWSpell : public MyGUI::Widget + class MWSpell final : public MyGUI::Widget { MYGUI_RTTI_DERIVED( MWSpell ) public: @@ -199,7 +199,7 @@ namespace MWGui protected: virtual ~MWSpell(); - virtual void initialiseOverride(); + void initialiseOverride() final; private: void updateWidgets(); @@ -209,7 +209,7 @@ namespace MWGui }; typedef MWSpell* MWSpellPtr; - class MWEffectList : public MyGUI::Widget + class MWEffectList final : public MyGUI::Widget { MYGUI_RTTI_DERIVED( MWEffectList ) public: @@ -241,7 +241,7 @@ namespace MWGui protected: virtual ~MWEffectList(); - virtual void initialiseOverride(); + void initialiseOverride() final; private: void updateWidgets(); @@ -250,7 +250,7 @@ namespace MWGui }; typedef MWEffectList* MWEffectListPtr; - class MWSpellEffect : public MyGUI::Widget + class MWSpellEffect final : public MyGUI::Widget { MYGUI_RTTI_DERIVED( MWSpellEffect ) public: @@ -265,7 +265,7 @@ namespace MWGui protected: virtual ~MWSpellEffect(); - virtual void initialiseOverride(); + void initialiseOverride() final; private: static const int sIconOffset = 24; @@ -279,7 +279,7 @@ namespace MWGui }; typedef MWSpellEffect* MWSpellEffectPtr; - class MWDynamicStat : public MyGUI::Widget + class MWDynamicStat final : public MyGUI::Widget { MYGUI_RTTI_DERIVED( MWDynamicStat ) public: @@ -294,7 +294,7 @@ namespace MWGui protected: virtual ~MWDynamicStat(); - virtual void initialiseOverride(); + void initialiseOverride() final; private: diff --git a/components/myguiplatform/additivelayer.hpp b/components/myguiplatform/additivelayer.hpp index f3d47bc82..f0cfc1b41 100644 --- a/components/myguiplatform/additivelayer.hpp +++ b/components/myguiplatform/additivelayer.hpp @@ -14,7 +14,7 @@ namespace osgMyGUI { /// @brief A Layer rendering with additive blend mode. - class AdditiveLayer : public MyGUI::OverlappedLayer + class AdditiveLayer final : public MyGUI::OverlappedLayer { public: MYGUI_RTTI_DERIVED( AdditiveLayer ) @@ -22,7 +22,7 @@ namespace osgMyGUI AdditiveLayer(); ~AdditiveLayer(); - virtual void renderToTarget(MyGUI::IRenderTarget* _target, bool _update); + void renderToTarget(MyGUI::IRenderTarget* _target, bool _update) final; private: osg::ref_ptr mStateSet; diff --git a/components/myguiplatform/scalinglayer.hpp b/components/myguiplatform/scalinglayer.hpp index 3ee1489f1..7ce5f84f7 100644 --- a/components/myguiplatform/scalinglayer.hpp +++ b/components/myguiplatform/scalinglayer.hpp @@ -8,18 +8,18 @@ namespace osgMyGUI ///@brief A Layer that lays out and renders widgets in screen-relative coordinates. The "Size" property determines the size of the virtual screen, /// which is then upscaled to the real screen size during rendering. The aspect ratio is kept intact, adding blanks to the sides when necessary. - class ScalingLayer : public MyGUI::OverlappedLayer + class ScalingLayer final : public MyGUI::OverlappedLayer { public: MYGUI_RTTI_DERIVED(ScalingLayer) - virtual void deserialization(MyGUI::xml::ElementPtr _node, MyGUI::Version _version); + void deserialization(MyGUI::xml::ElementPtr _node, MyGUI::Version _version) final; - virtual MyGUI::ILayerItem* getLayerItemByPoint(int _left, int _top) const; - virtual MyGUI::IntPoint getPosition(int _left, int _top) const; - virtual void renderToTarget(MyGUI::IRenderTarget* _target, bool _update); + MyGUI::ILayerItem* getLayerItemByPoint(int _left, int _top) const final; + MyGUI::IntPoint getPosition(int _left, int _top) const final; + void renderToTarget(MyGUI::IRenderTarget* _target, bool _update) final; - virtual void resizeView(const MyGUI::IntSize& _viewSize); + void resizeView(const MyGUI::IntSize& _viewSize) final; private: void screenToLayerCoords(int& _left, int& _top) const; diff --git a/components/widgets/box.hpp b/components/widgets/box.hpp index 2d9b497db..59bb7f88b 100644 --- a/components/widgets/box.hpp +++ b/components/widgets/box.hpp @@ -44,11 +44,11 @@ namespace Gui MYGUI_RTTI_DERIVED( AutoSizedTextBox ) public: - virtual MyGUI::IntSize getRequestedSize(); - virtual void setCaption(const MyGUI::UString& _value); + MyGUI::IntSize getRequestedSize() final; + void setCaption(const MyGUI::UString& _value) final; protected: - virtual void setPropertyOverride(const std::string& _key, const std::string& _value); + void setPropertyOverride(const std::string& _key, const std::string& _value) final; std::string mFontSize; }; @@ -58,14 +58,14 @@ namespace Gui public: - virtual MyGUI::IntSize getRequestedSize(); - virtual void setCaption(const MyGUI::UString& _value); + MyGUI::IntSize getRequestedSize() final; + void setCaption(const MyGUI::UString& _value) final; - virtual void initialiseOverride(); + void initialiseOverride() final; protected: - virtual void setPropertyOverride(const std::string& _key, const std::string& _value); - virtual int getWidth(); + void setPropertyOverride(const std::string& _key, const std::string& _value) final; + int getWidth(); std::string mFontSize; bool mShrink = false; bool mWasResized = false; @@ -77,11 +77,11 @@ namespace Gui MYGUI_RTTI_DERIVED( AutoSizedButton ) public: - virtual MyGUI::IntSize getRequestedSize(); - virtual void setCaption(const MyGUI::UString& _value); + MyGUI::IntSize getRequestedSize() final; + void setCaption(const MyGUI::UString& _value) final; protected: - virtual void setPropertyOverride(const std::string& _key, const std::string& _value); + void setPropertyOverride(const std::string& _key, const std::string& _value) final; std::string mFontSize; }; @@ -114,7 +114,7 @@ namespace Gui public: Spacer(); - virtual MyGUI::IntSize getRequestedSize() { return MyGUI::IntSize(0,0); } + MyGUI::IntSize getRequestedSize() final { return MyGUI::IntSize(0,0); } }; class HBox : public Box, public MyGUI::Widget @@ -122,18 +122,18 @@ namespace Gui MYGUI_RTTI_DERIVED( HBox ) public: - virtual void setSize (const MyGUI::IntSize &_value); - virtual void setCoord (const MyGUI::IntCoord &_value); + void setSize (const MyGUI::IntSize &_value) final; + void setCoord (const MyGUI::IntCoord &_value) final; protected: - virtual void initialiseOverride(); + void initialiseOverride() final; - virtual void align(); - virtual MyGUI::IntSize getRequestedSize(); + void align() final; + MyGUI::IntSize getRequestedSize() final; - virtual void setPropertyOverride(const std::string& _key, const std::string& _value); + void setPropertyOverride(const std::string& _key, const std::string& _value) final; - virtual void onWidgetCreated(MyGUI::Widget* _widget); + void onWidgetCreated(MyGUI::Widget* _widget) final; }; class VBox : public Box, public MyGUI::Widget @@ -141,18 +141,18 @@ namespace Gui MYGUI_RTTI_DERIVED( VBox) public: - virtual void setSize (const MyGUI::IntSize &_value); - virtual void setCoord (const MyGUI::IntCoord &_value); + void setSize (const MyGUI::IntSize &_value) final; + void setCoord (const MyGUI::IntCoord &_value) final; protected: - virtual void initialiseOverride(); + void initialiseOverride() final; - virtual void align(); - virtual MyGUI::IntSize getRequestedSize(); + void align() final; + MyGUI::IntSize getRequestedSize() final; - virtual void setPropertyOverride(const std::string& _key, const std::string& _value); + void setPropertyOverride(const std::string& _key, const std::string& _value) final; - virtual void onWidgetCreated(MyGUI::Widget* _widget); + void onWidgetCreated(MyGUI::Widget* _widget) final; }; } diff --git a/components/widgets/imagebutton.hpp b/components/widgets/imagebutton.hpp index bb2baa91f..160f24e99 100644 --- a/components/widgets/imagebutton.hpp +++ b/components/widgets/imagebutton.hpp @@ -9,7 +9,7 @@ namespace Gui /** * @brief allows using different image textures depending on the button state */ - class ImageButton : public MyGUI::ImageBox + class ImageButton final : public MyGUI::ImageBox { MYGUI_RTTI_DERIVED(ImageButton) @@ -31,13 +31,13 @@ namespace Gui static bool sDefaultNeedKeyFocus; protected: - virtual void setPropertyOverride(const std::string& _key, const std::string& _value); - virtual void onMouseLostFocus(MyGUI::Widget* _new); - virtual void onMouseSetFocus(MyGUI::Widget* _old); - virtual void onMouseButtonPressed(int _left, int _top, MyGUI::MouseButton _id); - virtual void onMouseButtonReleased(int _left, int _top, MyGUI::MouseButton _id); - virtual void onKeySetFocus(MyGUI::Widget* _old); - virtual void onKeyLostFocus(MyGUI::Widget* _new); + void setPropertyOverride(const std::string& _key, const std::string& _value) final; + void onMouseLostFocus(MyGUI::Widget* _new) final; + void onMouseSetFocus(MyGUI::Widget* _old) final; + void onMouseButtonPressed(int _left, int _top, MyGUI::MouseButton _id) final; + void onMouseButtonReleased(int _left, int _top, MyGUI::MouseButton _id) final; + void onKeySetFocus(MyGUI::Widget* _old) final; + void onKeyLostFocus(MyGUI::Widget* _new) final; std::string mImageHighlighted; std::string mImageNormal; diff --git a/components/widgets/list.hpp b/components/widgets/list.hpp index cc7b75c2f..604d5dada 100644 --- a/components/widgets/list.hpp +++ b/components/widgets/list.hpp @@ -48,10 +48,10 @@ namespace Gui void scrollToTop(); - virtual void setPropertyOverride(const std::string& _key, const std::string& _value); + void setPropertyOverride(const std::string& _key, const std::string& _value) final; protected: - void initialiseOverride(); + void initialiseOverride() final; void redraw(bool scrollbarShown = false); diff --git a/components/widgets/numericeditbox.hpp b/components/widgets/numericeditbox.hpp index 137583d37..ff16424d0 100644 --- a/components/widgets/numericeditbox.hpp +++ b/components/widgets/numericeditbox.hpp @@ -11,7 +11,7 @@ namespace Gui /** * @brief A variant of the EditBox that only allows integer inputs */ - class NumericEditBox : public FontWrapper + class NumericEditBox final : public FontWrapper { MYGUI_RTTI_DERIVED(NumericEditBox) @@ -22,8 +22,8 @@ namespace Gui { } - void initialiseOverride(); - void shutdownOverride(); + void initialiseOverride() final; + void shutdownOverride() final; typedef MyGUI::delegates::CMultiDelegate1 EventHandle_ValueChanged; EventHandle_ValueChanged eventValueChanged; @@ -36,8 +36,8 @@ namespace Gui void setMaxValue(int maxValue); private: void onEditTextChange(MyGUI::EditBox* sender); - void onKeyLostFocus(MyGUI::Widget* _new); - void onKeyButtonPressed(MyGUI::KeyCode key, MyGUI::Char character); + void onKeyLostFocus(MyGUI::Widget* _new) final; + void onKeyButtonPressed(MyGUI::KeyCode key, MyGUI::Char character) final; int mValue; diff --git a/components/widgets/sharedstatebutton.hpp b/components/widgets/sharedstatebutton.hpp index 414349396..859543d20 100644 --- a/components/widgets/sharedstatebutton.hpp +++ b/components/widgets/sharedstatebutton.hpp @@ -13,7 +13,7 @@ namespace Gui typedef std::vector ButtonGroup; /// @brief A button that applies its own state changes to other widgets, to do this you define it as part of a ButtonGroup. - class SharedStateButton : public FontWrapper + class SharedStateButton final : public FontWrapper { MYGUI_RTTI_DERIVED(SharedStateButton) @@ -23,13 +23,13 @@ namespace Gui protected: void updateButtonState(); - virtual void onMouseButtonPressed(int _left, int _top, MyGUI::MouseButton _id); - virtual void onMouseButtonReleased(int _left, int _top, MyGUI::MouseButton _id); - virtual void onMouseSetFocus(MyGUI::Widget* _old); - virtual void onMouseLostFocus(MyGUI::Widget* _new); - virtual void baseUpdateEnable(); + void onMouseButtonPressed(int _left, int _top, MyGUI::MouseButton _id) final; + void onMouseButtonReleased(int _left, int _top, MyGUI::MouseButton _id) final; + void onMouseSetFocus(MyGUI::Widget* _old) final; + void onMouseLostFocus(MyGUI::Widget* _new) final; + void baseUpdateEnable() final; - virtual void shutdownOverride(); + void shutdownOverride() final; bool _setState(const std::string &_value); diff --git a/components/widgets/windowcaption.hpp b/components/widgets/windowcaption.hpp index b45da2d1c..f8c1a310c 100644 --- a/components/widgets/windowcaption.hpp +++ b/components/widgets/windowcaption.hpp @@ -8,17 +8,17 @@ namespace Gui /// Window caption that automatically adjusts "Left" and "Right" widgets in its skin /// based on the text size of the caption in the middle - class WindowCaption : public MyGUI::EditBox + class WindowCaption final : public MyGUI::EditBox { MYGUI_RTTI_DERIVED(WindowCaption) public: WindowCaption(); - virtual void setCaption(const MyGUI::UString &_value); - virtual void initialiseOverride(); + void setCaption(const MyGUI::UString &_value) final; + void initialiseOverride() final; - virtual void setSize(const MyGUI::IntSize& _value); - virtual void setCoord(const MyGUI::IntCoord& _value); + void setSize(const MyGUI::IntSize& _value) final; + void setCoord(const MyGUI::IntCoord& _value) final; private: MyGUI::Widget* mLeft; From 7b5ec76db4e89fca7a7b652ea4d830edacde573a Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 19 Feb 2020 11:50:59 +0400 Subject: [PATCH 37/99] Fix regression in the NiVisController --- components/nifosg/controller.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/nifosg/controller.cpp b/components/nifosg/controller.cpp index 4b87ba1a3..934e9b565 100644 --- a/components/nifosg/controller.cpp +++ b/components/nifosg/controller.cpp @@ -305,7 +305,7 @@ void VisController::operator() (osg::Node* node, osg::NodeVisitor* nv) { bool vis = calculate(getInputValue(nv)); // Leave 0x1 enabled for UpdateVisitor, so we can make ourselves visible again in the future from this update callback - node->setNodeMask(vis ? ~SceneUtil::Mask_Default : SceneUtil::Mask_UpdateVisitor); + node->setNodeMask(vis ? SceneUtil::Mask_Default : SceneUtil::Mask_UpdateVisitor); } traverse(node, nv); } From 77d6fe46ca845e2c816012a19e07a53a24787494 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 19 Feb 2020 12:14:21 +0400 Subject: [PATCH 38/99] Handle NiCollisionSwitch node --- components/nif/niffile.cpp | 1 + components/nif/node.hpp | 3 ++- components/nif/record.hpp | 1 + components/nifbullet/bulletnifloader.cpp | 4 ++++ components/nifosg/nifloader.cpp | 22 ++++++++++++++++++++++ 5 files changed, 30 insertions(+), 1 deletion(-) diff --git a/components/nif/niffile.cpp b/components/nif/niffile.cpp index 87bafa6b1..2070ee850 100644 --- a/components/nif/niffile.cpp +++ b/components/nif/niffile.cpp @@ -48,6 +48,7 @@ static std::map makeFactory() newFactory.insert(makeEntry("NiSwitchNode", &construct , RC_NiSwitchNode )); newFactory.insert(makeEntry("NiLODNode", &construct , RC_NiLODNode )); newFactory.insert(makeEntry("AvoidNode", &construct , RC_AvoidNode )); + newFactory.insert(makeEntry("NiCollisionSwitch", &construct , RC_NiCollisionSwitch )); newFactory.insert(makeEntry("NiBSParticleNode", &construct , RC_NiBSParticleNode )); newFactory.insert(makeEntry("NiBSAnimationNode", &construct , RC_NiBSAnimationNode )); newFactory.insert(makeEntry("NiBillboardNode", &construct , RC_NiBillboardNode )); diff --git a/components/nif/node.hpp b/components/nif/node.hpp index de4bfb22e..4c52cd158 100644 --- a/components/nif/node.hpp +++ b/components/nif/node.hpp @@ -84,7 +84,8 @@ struct NiNode : Node enum Flags { Flag_Hidden = 0x0001, Flag_MeshCollision = 0x0002, - Flag_BBoxCollision = 0x0004 + Flag_BBoxCollision = 0x0004, + Flag_ActiveCollision = 0x0020 }; enum BSAnimFlags { AnimFlag_AutoPlay = 0x0020 diff --git a/components/nif/record.hpp b/components/nif/record.hpp index 67ffbc574..074dea9cf 100644 --- a/components/nif/record.hpp +++ b/components/nif/record.hpp @@ -40,6 +40,7 @@ enum RecordType RC_NiLODNode, RC_NiBillboardNode, RC_AvoidNode, + RC_NiCollisionSwitch, RC_NiTriShape, RC_NiTriStrips, RC_NiRotatingParticles, diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index 91f849421..dafee1b49 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -254,6 +254,10 @@ bool BulletNifLoader::hasAutoGeneratedCollision(const Nif::Node* rootNode) void BulletNifLoader::handleNode(const std::string& fileName, const Nif::Node *node, int flags, bool isCollisionNode, bool isAnimated, bool autogenerated, bool avoid) { + // TODO: allow on-the fly collision switching via toggling this flag + if (node->recType == Nif::RC_NiCollisionSwitch && !(node->flags & Nif::NiNode::Flag_ActiveCollision)) + return; + // Accumulate the flags from all the child nodes. This works for all // the flags we currently use, at least. flags |= node->flags; diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 602f1fa9a..76fd4b656 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -168,6 +168,20 @@ namespace namespace NifOsg { + class CollisionSwitch : public osg::Group + { + public: + CollisionSwitch(bool enabled) : osg::Group() + { + setEnabled(enabled); + } + + void setEnabled(bool enabled) + { + setNodeMask(enabled ? SceneUtil::Mask_Default : SceneUtil::Mask_Effect); + } + }; + bool Loader::sShowMarkers = false; void Loader::setShowMarkers(bool show) @@ -460,6 +474,14 @@ namespace NifOsg case Nif::RC_NiBillboardNode: dataVariance = osg::Object::DYNAMIC; break; + case Nif::RC_NiCollisionSwitch: + { + bool enabled = nifNode->flags & Nif::NiNode::Flag_ActiveCollision; + node = new CollisionSwitch(enabled); + dataVariance = osg::Object::STATIC; + + break; + } default: // The Root node can be created as a Group if no transformation is required. // This takes advantage of the fact root nodes can't have additional controllers From 198a75b8e6959980a1d3a629fa89f097b042bc0f Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Wed, 19 Feb 2020 11:44:55 +0200 Subject: [PATCH 39/99] Delete selected instances with a hotkey --- CHANGELOG.md | 1 + CHANGELOG_PR.md | 1 + apps/opencs/model/prefs/state.cpp | 1 + apps/opencs/view/render/instancemode.cpp | 22 ++++++++++++++++++++++ apps/opencs/view/render/instancemode.hpp | 1 + 5 files changed, 26 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 33378f9db..3702b88ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -252,6 +252,7 @@ Feature #5146: Safe Dispose corpse Feature #5147: Show spell magicka cost in spell buying window Feature #5170: Editor: Land shape editing, land selection + Feature #5172: Editor: Delete instances/references with keypress in scene window Feature #5193: Weapon sheathing Feature #5219: Impelement TestCells console command Feature #5224: Handle NiKeyframeController for NiTriShape diff --git a/CHANGELOG_PR.md b/CHANGELOG_PR.md index 526e09f14..f462da32c 100644 --- a/CHANGELOG_PR.md +++ b/CHANGELOG_PR.md @@ -42,6 +42,7 @@ New Editor Features: - "Faction Ranks" table for "Faction" records (#4209) - Changes to height editing can be cancelled without changes to data (press esc to cancel) (#4840) - Land heightmap/shape editing and vertex selection (#5170) +- Deleting instances with a keypress (#5172) Bug Fixes: - The Mouse Wheel can now be used for key bindings (#2679) diff --git a/apps/opencs/model/prefs/state.cpp b/apps/opencs/model/prefs/state.cpp index 1bf5752f0..442e707bf 100644 --- a/apps/opencs/model/prefs/state.cpp +++ b/apps/opencs/model/prefs/state.cpp @@ -355,6 +355,7 @@ void CSMPrefs::State::declare() declareShortcut ("scene-select-secondary", "Secondary Select", QKeySequence(Qt::ControlModifier | (int)Qt::MiddleButton)); declareModifier ("scene-speed-modifier", "Speed Modifier", Qt::Key_Shift); + declareShortcut ("scene-delete", "Delete Instance", QKeySequence(Qt::Key_Delete)); declareShortcut ("scene-load-cam-cell", "Load Camera Cell", QKeySequence(Qt::KeypadModifier | Qt::Key_5)); declareShortcut ("scene-load-cam-eastcell", "Load East Cell", QKeySequence(Qt::KeypadModifier | Qt::Key_6)); declareShortcut ("scene-load-cam-northcell", "Load North Cell", QKeySequence(Qt::KeypadModifier | Qt::Key_8)); diff --git a/apps/opencs/view/render/instancemode.cpp b/apps/opencs/view/render/instancemode.cpp index dd5dc3bac..92f5cbb2d 100644 --- a/apps/opencs/view/render/instancemode.cpp +++ b/apps/opencs/view/render/instancemode.cpp @@ -10,6 +10,7 @@ #include "../../model/world/idtree.hpp" #include "../../model/world/commands.hpp" #include "../../model/world/commandmacro.hpp" +#include "../../model/prefs/shortcut.hpp" #include "../widget/scenetoolbar.hpp" #include "../widget/scenetoolmode.hpp" @@ -96,6 +97,9 @@ CSVRender::InstanceMode::InstanceMode (WorldspaceWidget *worldspaceWidget, QWidg { connect(this, SIGNAL(requestFocus(const std::string&)), worldspaceWidget, SIGNAL(requestFocus(const std::string&))); + + CSMPrefs::Shortcut* deleteShortcut = new CSMPrefs::Shortcut("scene-delete", worldspaceWidget); + connect(deleteShortcut, SIGNAL(activated(bool)), this, SLOT(deleteSelectedInstances(bool))); } void CSVRender::InstanceMode::activate (CSVWidget::SceneToolbar *toolbar) @@ -659,3 +663,21 @@ void CSVRender::InstanceMode::subModeChanged (const std::string& id) getWorldspaceWidget().abortDrag(); getWorldspaceWidget().setSubMode (getSubModeFromId (id), SceneUtil::Mask_EditorReference); } + +void CSVRender::InstanceMode::deleteSelectedInstances(bool active) +{ + std::vector > selection = getWorldspaceWidget().getSelection (SceneUtil::Mask_EditorReference); + if (selection.empty()) return; + + CSMDoc::Document& document = getWorldspaceWidget().getDocument(); + CSMWorld::IdTable& referencesTable = dynamic_cast ( + *document.getData().getTableModel (CSMWorld::UniversalId::Type_References)); + QUndoStack& undoStack = document.getUndoStack(); + + CSMWorld::CommandMacro macro (undoStack, "Delete Instances"); + for(osg::ref_ptr tag: selection) + if (CSVRender::ObjectTag *objectTag = dynamic_cast (tag.get())) + macro.push(new CSMWorld::DeleteCommand(referencesTable, objectTag->mObject->getReferenceId())); + + getWorldspaceWidget().clearSelection (SceneUtil::Mask_EditorReference); +} diff --git a/apps/opencs/view/render/instancemode.hpp b/apps/opencs/view/render/instancemode.hpp index 6ddaa254f..23df3f37d 100644 --- a/apps/opencs/view/render/instancemode.hpp +++ b/apps/opencs/view/render/instancemode.hpp @@ -92,6 +92,7 @@ namespace CSVRender private slots: void subModeChanged (const std::string& id); + void deleteSelectedInstances(bool active); }; } From 3787625e61d326b2e032ed69bfc470afb21ba0b3 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Wed, 19 Feb 2020 23:26:42 +0300 Subject: [PATCH 40/99] Fix water ripple cleanup (bug #5246) --- CHANGELOG.md | 1 + apps/openmw/mwrender/renderingmanager.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 33378f9db..e10bad7b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -192,6 +192,7 @@ Bug #5239: OpenMW-CS does not support non-ASCII characters in path names Bug #5241: On-self absorb spells cannot be detected Bug #5242: ExplodeSpell behavior differs from Cast behavior + Bug #5246: Water ripples persist after cell change Bug #5249: Wandering NPCs start walking too soon after they hello Bug #5250: Creatures display shield ground mesh instead of shield body part Bug #5255: "GetTarget, player" doesn't return 1 during NPC hello diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 39318be30..1f53ff3d6 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -1106,6 +1106,7 @@ namespace MWRender void RenderingManager::notifyWorldSpaceChanged() { mEffectManager->clear(); + mWater->clearRipples(); } void RenderingManager::clear() From f9f0299c274a605fbbfd713cdf167a9ce58d1d23 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Fri, 21 Feb 2020 00:05:13 +0300 Subject: [PATCH 41/99] Make ModCurrentFatigue KO the actor when necessary (bug #4523) --- CHANGELOG.md | 1 + apps/openmw/mwscript/statsextensions.cpp | 11 +++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e10bad7b8..1d4d075ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,7 @@ Bug #4411: Reloading a saved game while falling prevents damage in some cases Bug #4449: Value returned by GetWindSpeed is incorrect Bug #4456: AiActivate should not be cancelled after target activation + Bug #4523: "player->ModCurrentFatigue -0.001" in global script does not cause the running player to fall Bug #4540: Rain delay when exiting water Bug #4594: Actors without AI packages don't use Hello dialogue Bug #4598: Script parser does not support non-ASCII characters diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index af3ab4af5..45eafa960 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -291,8 +291,15 @@ namespace MWScript MWMechanics::DynamicStat stat (ptr.getClass().getCreatureStats (ptr) .getDynamic (mIndex)); - // for fatigue, a negative current value is allowed and means the actor will be knocked down - bool allowDecreaseBelowZero = (mIndex == 2); + bool allowDecreaseBelowZero = false; + if (mIndex == 2) // Fatigue-specific logic + { + // For fatigue, a negative current value is allowed and means the actor will be knocked down + allowDecreaseBelowZero = true; + // Knock down the actor immediately if a non-positive new value is the case + if (diff + current <= 0.f) + ptr.getClass().getCreatureStats(ptr).setKnockedDown(true); + } stat.setCurrent (diff + current, allowDecreaseBelowZero); ptr.getClass().getCreatureStats (ptr).setDynamic (mIndex, stat); From 776c0857fabb4434bc9f29f9cb1dae8b10cb3f88 Mon Sep 17 00:00:00 2001 From: Alexander Olofsson Date: Fri, 21 Feb 2020 07:51:50 +0100 Subject: [PATCH 42/99] Always use ref_ptr for query geometry --- apps/openmw/mwrender/sky.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 84d107815..10fc630bd 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -563,7 +563,7 @@ private: // With OSG 3.6.5, the method of providing user defined query geometry has been completely replaced osg::ref_ptr queryGeom = new osg::QueryGeometry(oqn->getName()); #else - auto* queryGeom = oqn->getQueryGeometry(); + osg::ref_ptr queryGeom = oqn->getQueryGeometry(); #endif // Make it fast! A DYNAMIC query geometry means we can't break frame until the flare is rendered (which is rendered after all the other geometry, From cfa877b109e03dccb68e4936b4ccfe528a16b9dd Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Wed, 19 Feb 2020 22:41:22 +0300 Subject: [PATCH 43/99] Make PCSkipEquip and OnPCEquip behavior vanilla-like (bug #4141) --- CHANGELOG.md | 1 + apps/openmw/mwgui/inventorywindow.cpp | 53 ++++++++++++--------------- apps/openmw/mwgui/inventorywindow.hpp | 2 - 3 files changed, 24 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 33378f9db..995cf7185 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ Bug #3977: Non-ASCII characters in object ID's are not supported Bug #4009: Launcher does not show data files on the first run after installing Bug #4077: Enchanted items are not recharged if they are not in the player's inventory + Bug #4141: PCSkipEquip isn't set to 1 when reading books/scrolls Bug #4202: Open .omwaddon files without needing toopen openmw-cs first Bug #4240: Ash storm origin coordinates and hand shielding animation behavior are incorrect Bug #4262: Rain settings are hardcoded diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 77ecaca34..d09a9b4cc 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -21,12 +21,10 @@ #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" -#include "../mwbase/scriptmanager.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/class.hpp" #include "../mwworld/actionequip.hpp" -#include "../mwscript/interpretercontext.hpp" #include "../mwmechanics/actorutil.hpp" #include "../mwmechanics/creaturestats.hpp" @@ -507,6 +505,16 @@ namespace MWGui void InventoryWindow::useItem(const MWWorld::Ptr &ptr, bool force) { const std::string& script = ptr.getClass().getScript(ptr); + if (!script.empty()) + { + // Don't try to equip the item if PCSkipEquip is set to 1 + if (ptr.getRefData().getLocals().getIntVar(script, "pcskipequip") == 1) + { + ptr.getRefData().getLocals().setVarByInt(script, "onpcequip", 1); + return; + } + ptr.getRefData().getLocals().setVarByInt(script, "onpcequip", 0); + } MWWorld::Ptr player = MWMechanics::getPlayer(); @@ -526,43 +534,28 @@ namespace MWGui if (canEquip.first == 0) { - /// If PCSkipEquip is set, set OnPCEquip to 1 and don't message anything - if (!script.empty() && ptr.getRefData().getLocals().getIntVar(script, "pcskipequip") == 1) - ptr.getRefData().getLocals().setVarByInt(script, "onpcequip", 1); - else - MWBase::Environment::get().getWindowManager()->messageBox(canEquip.second); + MWBase::Environment::get().getWindowManager()->messageBox(canEquip.second); updateItemView(); return; } } } - // If the item has a script, set its OnPcEquip to 1 - if (!script.empty() - // Another morrowind oddity: when an item has skipped equipping and pcskipequip is reset to 0 afterwards, - // the next time it is equipped will work normally, but will not set onpcequip - && (ptr != mSkippedToEquip || ptr.getRefData().getLocals().getIntVar(script, "pcskipequip") == 1)) - ptr.getRefData().getLocals().setVarByInt(script, "onpcequip", 1); - - // Give the script a chance to run once before we do anything else - // this is important when setting pcskipequip as a reaction to onpcequip being set (bk_treasuryreport does this) - if (!force && !script.empty() && MWBase::Environment::get().getWorld()->getScriptsEnabled()) + // If the item has a script, set OnPCEquip or PCSkipEquip to 1 + if (!script.empty()) { - MWScript::InterpreterContext interpreterContext (&ptr.getRefData().getLocals(), ptr); - MWBase::Environment::get().getScriptManager()->run (script, interpreterContext); + // Ingredients, books and repair hammers must not have OnPCEquip set to 1 here + const std::string& type = ptr.getTypeName(); + bool isBook = type == typeid(ESM::Book).name(); + if (!isBook && type != typeid(ESM::Ingredient).name() && type != typeid(ESM::Repair).name()) + ptr.getRefData().getLocals().setVarByInt(script, "onpcequip", 1); + // Books must have PCSkipEquip set to 1 instead + else if (isBook) + ptr.getRefData().getLocals().setVarByInt(script, "pcskipequip", 1); } - mSkippedToEquip = MWWorld::Ptr(); - if (ptr.getRefData().getCount()) // make sure the item is still there, the script might have removed it - { - if (script.empty() || ptr.getRefData().getLocals().getIntVar(script, "pcskipequip") == 0) - { - std::shared_ptr action = ptr.getClass().use(ptr, force); - action->execute(player); - } - else - mSkippedToEquip = ptr; - } + std::shared_ptr action = ptr.getClass().use(ptr, force); + action->execute(player); if (isVisible()) { diff --git a/apps/openmw/mwgui/inventorywindow.hpp b/apps/openmw/mwgui/inventorywindow.hpp index 8d0a86bbf..271cd4a18 100644 --- a/apps/openmw/mwgui/inventorywindow.hpp +++ b/apps/openmw/mwgui/inventorywindow.hpp @@ -93,8 +93,6 @@ namespace MWGui MyGUI::Button* mFilterMagic; MyGUI::Button* mFilterMisc; - MWWorld::Ptr mSkippedToEquip; - GuiMode mGuiMode; int mLastXSize; From 1da4b31047af1356bc3a5c4491e53714a6973005 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Mon, 24 Feb 2020 02:16:09 +0300 Subject: [PATCH 44/99] Switch torches to shields for hostile NPCs (bug #5300) --- CHANGELOG.md | 1 + apps/openmw/mwmechanics/actors.cpp | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cf28d3da6..75acc0339 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -202,6 +202,7 @@ Bug #5264: "Damage Fatigue" Magic Effect Can Bring Fatigue below 0 Bug #5269: Editor: Cell lighting in resaved cleaned content files is corrupted Bug #5278: Console command Show doesn't fall back to global variable after local var not found + Bug #5300: NPCs don't switch from torch to shield when starting combat Feature #1415: Infinite fall failsafe Feature #1774: Handle AvoidNode Feature #2229: Improve pathfinding AI diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 550caf9d1..bda78c0b5 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1234,6 +1234,11 @@ namespace MWMechanics if (heldIter != inventoryStore.end() && heldIter->getTypeName() != typeid(ESM::Light).name()) inventoryStore.unequipItem(*heldIter, ptr); } + else if (heldIter == inventoryStore.end() || heldIter->getTypeName() == typeid(ESM::Light).name()) + { + // For hostile NPCs, see if they have anything better to equip first + inventoryStore.autoEquip(ptr); + } heldIter = inventoryStore.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); From 78d58a344d70e4f5ea56d02a3bd4a55742ac8090 Mon Sep 17 00:00:00 2001 From: Frederic Chardon Date: Mon, 24 Feb 2020 18:14:40 +0000 Subject: [PATCH 45/99] Item search in inventory (feature #4831) --- CHANGELOG.md | 1 + apps/openmw/mwgui/companionwindow.cpp | 8 ++++++++ apps/openmw/mwgui/companionwindow.hpp | 2 ++ apps/openmw/mwgui/inventorywindow.cpp | 15 ++++++++++++++- apps/openmw/mwgui/inventorywindow.hpp | 3 +++ apps/openmw/mwgui/sortfilteritemmodel.cpp | 9 +++++++++ apps/openmw/mwgui/sortfilteritemmodel.hpp | 3 +++ apps/openmw/mwgui/tradewindow.cpp | 11 +++++++++-- apps/openmw/mwgui/tradewindow.hpp | 3 +++ files/mygui/openmw_companion_window.layout | 8 +++++++- files/mygui/openmw_inventory_window.layout | 4 ++++ files/mygui/openmw_trade_window.layout | 4 ++++ 12 files changed, 67 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d4d075ca..a64ddfbb5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -225,6 +225,7 @@ Feature #4730: Native animated containers support Feature #4784: Launcher: Duplicate Content Lists Feature #4812: Support NiSwitchNode + Feature #4831: Item search in the player's inventory Feature #4836: Daytime node switch Feature #4840: Editor: Transient terrain change support Feature #4859: Make water reflections more configurable diff --git a/apps/openmw/mwgui/companionwindow.cpp b/apps/openmw/mwgui/companionwindow.cpp index b2639e938..acbe2ca52 100644 --- a/apps/openmw/mwgui/companionwindow.cpp +++ b/apps/openmw/mwgui/companionwindow.cpp @@ -46,9 +46,11 @@ CompanionWindow::CompanionWindow(DragAndDrop *dragAndDrop, MessageBoxManager* ma getWidget(mCloseButton, "CloseButton"); getWidget(mProfitLabel, "ProfitLabel"); getWidget(mEncumbranceBar, "EncumbranceBar"); + getWidget(mFilterEdit, "FilterEdit"); getWidget(mItemView, "ItemView"); mItemView->eventBackgroundClicked += MyGUI::newDelegate(this, &CompanionWindow::onBackgroundSelected); mItemView->eventItemClicked += MyGUI::newDelegate(this, &CompanionWindow::onItemSelected); + mFilterEdit->eventEditTextChange += MyGUI::newDelegate(this, &CompanionWindow::onNameFilterChanged); mCloseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &CompanionWindow::onCloseButtonClicked); @@ -92,6 +94,12 @@ void CompanionWindow::onItemSelected(int index) dragItem (nullptr, count); } +void CompanionWindow::onNameFilterChanged(MyGUI::EditBox* _sender) + { + mSortModel->setNameFilter(_sender->getCaption()); + mItemView->update(); + } + void CompanionWindow::dragItem(MyGUI::Widget* sender, int count) { mDragAndDrop->startDrag(mSelectedItem, mSortModel, mModel, mItemView, count); diff --git a/apps/openmw/mwgui/companionwindow.hpp b/apps/openmw/mwgui/companionwindow.hpp index 8ca350617..442ce57da 100644 --- a/apps/openmw/mwgui/companionwindow.hpp +++ b/apps/openmw/mwgui/companionwindow.hpp @@ -39,11 +39,13 @@ namespace MWGui DragAndDrop* mDragAndDrop; MyGUI::Button* mCloseButton; + MyGUI::EditBox* mFilterEdit; MyGUI::TextBox* mProfitLabel; Widgets::MWDynamicStat* mEncumbranceBar; MessageBoxManager* mMessageBoxManager; void onItemSelected(int index); + void onNameFilterChanged(MyGUI::EditBox* _sender); void onBackgroundSelected(); void dragItem(MyGUI::Widget* sender, int count); diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 77ecaca34..f00dfebb7 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include @@ -90,6 +91,7 @@ namespace MWGui getWidget(mLeftPane, "LeftPane"); getWidget(mRightPane, "RightPane"); getWidget(mArmorRating, "ArmorRating"); + getWidget(mFilterEdit, "FilterEdit"); mAvatarImage->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryWindow::onAvatarClicked); mAvatarImage->setRenderItemTexture(mPreviewTexture.get()); @@ -104,6 +106,7 @@ namespace MWGui mFilterApparel->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryWindow::onFilterChanged); mFilterMagic->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryWindow::onFilterChanged); mFilterMisc->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryWindow::onFilterChanged); + mFilterEdit->eventEditTextChange += MyGUI::newDelegate(this, &InventoryWindow::onNameFilterChanged); mFilterAll->setStateSelected(true); @@ -388,6 +391,11 @@ namespace MWGui void InventoryWindow::onOpen() { + // Reset the filter focus when opening the window + MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget(); + if (focus == mFilterEdit) + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(nullptr); + if (!mPtr.isEmpty()) { updateEncumbranceBar(); @@ -465,6 +473,12 @@ namespace MWGui width*mScaleFactor/float(mPreview->getTextureWidth()), height*mScaleFactor/float(mPreview->getTextureHeight()))); } + void InventoryWindow::onNameFilterChanged(MyGUI::EditBox* _sender) + { + mSortModel->setNameFilter(_sender->getCaption()); + mItemView->update(); + } + void InventoryWindow::onFilterChanged(MyGUI::Widget* _sender) { if (_sender == mFilterAll) @@ -477,7 +491,6 @@ namespace MWGui mSortModel->setCategory(SortFilterItemModel::Category_Magic); else if (_sender == mFilterMisc) mSortModel->setCategory(SortFilterItemModel::Category_Misc); - mFilterAll->setStateSelected(false); mFilterWeapon->setStateSelected(false); mFilterApparel->setStateSelected(false); diff --git a/apps/openmw/mwgui/inventorywindow.hpp b/apps/openmw/mwgui/inventorywindow.hpp index 8d0a86bbf..103de45b0 100644 --- a/apps/openmw/mwgui/inventorywindow.hpp +++ b/apps/openmw/mwgui/inventorywindow.hpp @@ -92,6 +92,8 @@ namespace MWGui MyGUI::Button* mFilterApparel; MyGUI::Button* mFilterMagic; MyGUI::Button* mFilterMisc; + + MyGUI::EditBox* mFilterEdit; MWWorld::Ptr mSkippedToEquip; @@ -121,6 +123,7 @@ namespace MWGui void onWindowResize(MyGUI::Window* _sender); void onFilterChanged(MyGUI::Widget* _sender); + void onNameFilterChanged(MyGUI::EditBox* _sender); void onAvatarClicked(MyGUI::Widget* _sender); void onPinToggled(); diff --git a/apps/openmw/mwgui/sortfilteritemmodel.cpp b/apps/openmw/mwgui/sortfilteritemmodel.cpp index 23f8a121b..88ae5fd1b 100644 --- a/apps/openmw/mwgui/sortfilteritemmodel.cpp +++ b/apps/openmw/mwgui/sortfilteritemmodel.cpp @@ -250,6 +250,10 @@ namespace MWGui return false; } + std::string compare = Misc::StringUtils::lowerCase(item.mBase.getClass().getName(item.mBase)); + if(compare.find(mNameFilter) == std::string::npos) + return false; + return true; } @@ -277,6 +281,11 @@ namespace MWGui mFilter = filter; } + void SortFilterItemModel::setNameFilter (const std::string& filter) + { + mNameFilter = Misc::StringUtils::lowerCase(filter); + } + void SortFilterItemModel::update() { mSourceModel->update(); diff --git a/apps/openmw/mwgui/sortfilteritemmodel.hpp b/apps/openmw/mwgui/sortfilteritemmodel.hpp index 98da8d8c9..6e400ddc9 100644 --- a/apps/openmw/mwgui/sortfilteritemmodel.hpp +++ b/apps/openmw/mwgui/sortfilteritemmodel.hpp @@ -25,6 +25,7 @@ namespace MWGui void setCategory (int category); void setFilter (int filter); + void setNameFilter (const std::string& filter); /// Use ItemStack::Type for sorting? void setSortByType(bool sort) { mSortByType = sort; } @@ -57,6 +58,8 @@ namespace MWGui int mCategory; int mFilter; bool mSortByType; + + std::string mNameFilter; // filter by item name }; } diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index 0c9b31b65..b102b13ce 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -69,6 +69,7 @@ namespace MWGui getWidget(mTotalBalance, "TotalBalance"); getWidget(mTotalBalanceLabel, "TotalBalanceLabel"); getWidget(mBottomPane, "BottomPane"); + getWidget(mFilterEdit, "FilterEdit"); getWidget(mItemView, "ItemView"); mItemView->eventItemClicked += MyGUI::newDelegate(this, &TradeWindow::onItemSelected); @@ -80,6 +81,7 @@ namespace MWGui mFilterApparel->eventMouseButtonClick += MyGUI::newDelegate(this, &TradeWindow::onFilterChanged); mFilterMagic->eventMouseButtonClick += MyGUI::newDelegate(this, &TradeWindow::onFilterChanged); mFilterMisc->eventMouseButtonClick += MyGUI::newDelegate(this, &TradeWindow::onFilterChanged); + mFilterEdit->eventEditTextChange += MyGUI::newDelegate(this, &TradeWindow::onNameFilterChanged); mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &TradeWindow::onCancelButtonClicked); mOfferButton->eventMouseButtonClick += MyGUI::newDelegate(this, &TradeWindow::onOfferButtonClicked); @@ -135,8 +137,7 @@ namespace MWGui setTitle(actor.getClass().getName(actor)); onFilterChanged(mFilterAll); - - MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mTotalBalance); + mFilterEdit->setCaption(""); } void TradeWindow::onFrame(float dt) @@ -144,6 +145,12 @@ namespace MWGui checkReferenceAvailable(); } + void TradeWindow::onNameFilterChanged(MyGUI::EditBox* _sender) + { + mSortModel->setNameFilter(_sender->getCaption()); + mItemView->update(); + } + void TradeWindow::onFilterChanged(MyGUI::Widget* _sender) { if (_sender == mFilterAll) diff --git a/apps/openmw/mwgui/tradewindow.hpp b/apps/openmw/mwgui/tradewindow.hpp index 514d24022..0730df04f 100644 --- a/apps/openmw/mwgui/tradewindow.hpp +++ b/apps/openmw/mwgui/tradewindow.hpp @@ -59,6 +59,8 @@ namespace MWGui MyGUI::Button* mFilterMagic; MyGUI::Button* mFilterMisc; + MyGUI::EditBox* mFilterEdit; + MyGUI::Button* mIncreaseButton; MyGUI::Button* mDecreaseButton; MyGUI::TextBox* mTotalBalanceLabel; @@ -86,6 +88,7 @@ namespace MWGui void sellItem (MyGUI::Widget* sender, int count); void onFilterChanged(MyGUI::Widget* _sender); + void onNameFilterChanged(MyGUI::EditBox* _sender); void onOfferButtonClicked(MyGUI::Widget* _sender); void onAccept(MyGUI::EditBox* sender); void onCancelButtonClicked(MyGUI::Widget* _sender); diff --git a/files/mygui/openmw_companion_window.layout b/files/mygui/openmw_companion_window.layout index 1266da397..0b82a6cf3 100644 --- a/files/mygui/openmw_companion_window.layout +++ b/files/mygui/openmw_companion_window.layout @@ -4,8 +4,14 @@ + + + + + + - + diff --git a/files/mygui/openmw_inventory_window.layout b/files/mygui/openmw_inventory_window.layout index 8f5b4c146..1b75c064b 100644 --- a/files/mygui/openmw_inventory_window.layout +++ b/files/mygui/openmw_inventory_window.layout @@ -50,6 +50,10 @@ + + + + diff --git a/files/mygui/openmw_trade_window.layout b/files/mygui/openmw_trade_window.layout index 30e22302d..270649ea8 100644 --- a/files/mygui/openmw_trade_window.layout +++ b/files/mygui/openmw_trade_window.layout @@ -27,6 +27,10 @@ + + + + From 21c49ca1b087b9396c84eaf49a765fa9a68e33c8 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Wed, 22 Jan 2020 23:39:12 +0300 Subject: [PATCH 46/99] Make Wizard less shy (bug #4493) --- CHANGELOG.md | 1 + apps/wizard/installationpage.cpp | 8 ++++++++ apps/wizard/unshield/unshieldworker.cpp | 4 +++- 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0419d70c7..2e4118950 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,7 @@ Bug #4411: Reloading a saved game while falling prevents damage in some cases Bug #4449: Value returned by GetWindSpeed is incorrect Bug #4456: AiActivate should not be cancelled after target activation + Bug #4493: If the setup doesn't find what it is expecting, it fails silently and displays the requester again instead of letting the user know what wasn't found. Bug #4523: "player->ModCurrentFatigue -0.001" in global script does not cause the running player to fall Bug #4540: Rain delay when exiting water Bug #4594: Actors without AI packages don't use Hello dialogue diff --git a/apps/wizard/installationpage.cpp b/apps/wizard/installationpage.cpp index 3deb30f25..9c90b0bbf 100644 --- a/apps/wizard/installationpage.cpp +++ b/apps/wizard/installationpage.cpp @@ -154,6 +154,14 @@ void Wizard::InstallationPage::showFileDialog(Wizard::Component component) name = QLatin1String("Bloodmoon"); break; } + logTextEdit->appendHtml(tr("

Attempting to install component %1.

").arg(name)); + mWizard->addLogText(tr("Attempting to install component %1.").arg(name)); + + QMessageBox msgBox; + msgBox.setWindowTitle(tr("%1 Installation").arg(name)); + msgBox.setIcon(QMessageBox::Information); + msgBox.setText(QObject::tr("Select a valid %1 installation media.
Hint: make sure that it contains at least one .cab file.").arg(name)); + msgBox.exec(); QString path = QFileDialog::getExistingDirectory(this, tr("Select %1 installation media").arg(name), diff --git a/apps/wizard/unshield/unshieldworker.cpp b/apps/wizard/unshield/unshieldworker.cpp index 96e959983..f84658bf3 100644 --- a/apps/wizard/unshield/unshieldworker.cpp +++ b/apps/wizard/unshield/unshieldworker.cpp @@ -493,7 +493,9 @@ bool Wizard::UnshieldWorker::setupComponent(Component component) } - if (!found) { + if (!found) + { + emit textChanged(tr("Failed to find a valid archive containing %1.bsa! Retrying.").arg(name)); QReadLocker readLock(&mLock); emit requestFileDialog(component); mWait.wait(&mLock); From 8c177f0306c361f02bde0ec086870f86ce6e1e4d Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 25 Feb 2020 11:09:36 +0400 Subject: [PATCH 47/99] Take in account transformations of NiCollisionSwitch --- components/nifosg/nifloader.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 76fd4b656..b70391a58 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -168,10 +168,10 @@ namespace namespace NifOsg { - class CollisionSwitch : public osg::Group + class CollisionSwitch : public osg::MatrixTransform { public: - CollisionSwitch(bool enabled) : osg::Group() + CollisionSwitch(const osg::Matrixf& transformations, bool enabled) : osg::MatrixTransform(transformations) { setEnabled(enabled); } @@ -477,7 +477,7 @@ namespace NifOsg case Nif::RC_NiCollisionSwitch: { bool enabled = nifNode->flags & Nif::NiNode::Flag_ActiveCollision; - node = new CollisionSwitch(enabled); + node = new CollisionSwitch(nifNode->trafo.toMatrix(), enabled); dataVariance = osg::Object::STATIC; break; From d44dcc3242532efc2be37eb26a12c1da276391db Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Tue, 25 Feb 2020 16:46:53 +0300 Subject: [PATCH 48/99] Revert infinite fall failsafe addition --- CHANGELOG.md | 1 - apps/openmw/mwworld/scene.cpp | 23 ----------------------- apps/openmw/mwworld/scene.hpp | 1 - 3 files changed, 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e3c2ab85e..77c06bd2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -203,7 +203,6 @@ Bug #5269: Editor: Cell lighting in resaved cleaned content files is corrupted Bug #5278: Console command Show doesn't fall back to global variable after local var not found Bug #5300: NPCs don't switch from torch to shield when starting combat - Feature #1415: Infinite fall failsafe Feature #1774: Handle AvoidNode Feature #2229: Improve pathfinding AI Feature #3025: Analogue gamepad movement controls diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 7c46a2fc7..a366f9a75 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -33,7 +33,6 @@ #include "../mwphysics/object.hpp" #include "../mwphysics/heightfield.hpp" -#include "actionteleport.hpp" #include "player.hpp" #include "localscripts.hpp" #include "esmstore.hpp" @@ -258,20 +257,10 @@ namespace struct PositionVisitor { - float mLowestPos = std::numeric_limits::max(); - bool operator() (const MWWorld::Ptr& ptr) { if (!ptr.getRefData().isDeleted() && ptr.getRefData().isEnabled()) - { - if (!ptr.getClass().isActor()) - { - float objectPosZ = ptr.getRefData().getPosition().pos[2]; - if (objectPosZ < mLowestPos) - mLowestPos = objectPosZ; - } ptr.getClass().adjustPosition (ptr, false); - } return true; } }; @@ -498,16 +487,6 @@ namespace MWWorld const auto player = MWBase::Environment::get().getWorld()->getPlayerPtr(); navigator->update(player.getRefData().getPosition().asVec3()); - const float fallThreshold = 256.f; - if (mCurrentCell && !mCurrentCell->isExterior() && pos.z() < mLowestPos - fallThreshold) - { - ESM::Position newPos; - std::string cellName = mCurrentCell->getCell()->mName; - MWBase::Environment::get().getWorld()->findInteriorPosition(cellName, newPos); - if (newPos.pos[2] >= mLowestPos) - MWWorld::ActionTeleport(cellName, newPos, false).execute(player); - } - if (!mCurrentCell || !mCurrentCell->isExterior()) return; @@ -898,10 +877,8 @@ namespace MWWorld insertVisitor.insert([&] (const MWWorld::Ptr& ptr) { addObject(ptr, *mPhysics, mNavigator); }); // do adjustPosition (snapping actors to ground) after objects are loaded, so we don't depend on the loading order - // Also note the lowest object position in the cell to allow infinite fall fail safe to work PositionVisitor posVisitor; cell.forEach (posVisitor); - mLowestPos = posVisitor.mLowestPos; } void Scene::addObjectToScene (const Ptr& ptr) diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index c08c5c163..da795f84b 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -84,7 +84,6 @@ namespace MWWorld float mPredictionTime; osg::Vec3f mLastPlayerPos; - float mLowestPos; void insertCell (CellStore &cell, Loading::Listener* loadingListener, bool test = false); From 4b17d5da439fca7c3312b2d5c7d2459604d5147b Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Tue, 25 Feb 2020 15:21:06 +0300 Subject: [PATCH 49/99] Make search fields behave more consistently --- apps/openmw/mwgui/companionwindow.cpp | 4 ++++ apps/openmw/mwgui/inventorywindow.cpp | 4 ++++ apps/openmw/mwgui/keyboardnavigation.cpp | 3 --- apps/openmw/mwgui/spellwindow.cpp | 2 +- apps/openmw/mwgui/tradewindow.cpp | 2 ++ 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwgui/companionwindow.cpp b/apps/openmw/mwgui/companionwindow.cpp index acbe2ca52..426894451 100644 --- a/apps/openmw/mwgui/companionwindow.cpp +++ b/apps/openmw/mwgui/companionwindow.cpp @@ -48,6 +48,9 @@ CompanionWindow::CompanionWindow(DragAndDrop *dragAndDrop, MessageBoxManager* ma getWidget(mEncumbranceBar, "EncumbranceBar"); getWidget(mFilterEdit, "FilterEdit"); getWidget(mItemView, "ItemView"); + + mFilterEdit->setUserString("AcceptTab", "true"); + mItemView->eventBackgroundClicked += MyGUI::newDelegate(this, &CompanionWindow::onBackgroundSelected); mItemView->eventItemClicked += MyGUI::newDelegate(this, &CompanionWindow::onItemSelected); mFilterEdit->eventEditTextChange += MyGUI::newDelegate(this, &CompanionWindow::onNameFilterChanged); @@ -121,6 +124,7 @@ void CompanionWindow::setPtr(const MWWorld::Ptr& npc) mModel = new CompanionItemModel(npc); mSortModel = new SortFilterItemModel(mModel); + mFilterEdit->setCaption(std::string()); mItemView->setModel(mSortModel); mItemView->resetScrollBars(); diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 77a14101a..6c85fe016 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -91,6 +91,8 @@ namespace MWGui getWidget(mArmorRating, "ArmorRating"); getWidget(mFilterEdit, "FilterEdit"); + mFilterEdit->setUserString("AcceptTab", "true"); + mAvatarImage->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryWindow::onAvatarClicked); mAvatarImage->setRenderItemTexture(mPreviewTexture.get()); mAvatarImage->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 0.f, 1.f, 1.f)); @@ -134,6 +136,8 @@ namespace MWGui else mSortModel = new SortFilterItemModel(mTradeModel); + mSortModel->setNameFilter(mFilterEdit->getCaption()); + mItemView->setModel(mSortModel); mFilterAll->setStateSelected(true); diff --git a/apps/openmw/mwgui/keyboardnavigation.cpp b/apps/openmw/mwgui/keyboardnavigation.cpp index 7355dc1f4..3f98b8607 100644 --- a/apps/openmw/mwgui/keyboardnavigation.cpp +++ b/apps/openmw/mwgui/keyboardnavigation.cpp @@ -16,9 +16,6 @@ namespace MWGui bool shouldAcceptKeyFocus(MyGUI::Widget* w) { - if (w && w->getUserString("IgnoreTabKey") == "y") - return false; - return w && !w->castType(false) && w->getInheritedEnabled() && w->getInheritedVisible() && w->getVisible() && w->getEnabled(); } diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index d0be05dfb..406ff1222 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -45,7 +45,7 @@ namespace MWGui getWidget(mEffectBox, "EffectsBox"); getWidget(mFilterEdit, "FilterEdit"); - mFilterEdit->setUserString("IgnoreTabKey", "y"); + mFilterEdit->setUserString("AcceptTab", "true"); mSpellView->eventSpellClicked += MyGUI::newDelegate(this, &SpellWindow::onModelIndexSelected); mFilterEdit->eventEditTextChange += MyGUI::newDelegate(this, &SpellWindow::onFilterChanged); diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index b102b13ce..9d977e603 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -71,6 +71,8 @@ namespace MWGui getWidget(mBottomPane, "BottomPane"); getWidget(mFilterEdit, "FilterEdit"); + mFilterEdit->setUserString("AcceptTab", "true"); + getWidget(mItemView, "ItemView"); mItemView->eventItemClicked += MyGUI::newDelegate(this, &TradeWindow::onItemSelected); From 54334932d91738a50a4acdc1fb53ee79924d22c1 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Mon, 2 Mar 2020 01:48:18 +0300 Subject: [PATCH 50/99] Move user string assignment to layout files --- apps/openmw/mwgui/companionwindow.cpp | 3 --- apps/openmw/mwgui/inventorywindow.cpp | 2 -- apps/openmw/mwgui/spellwindow.cpp | 2 -- apps/openmw/mwgui/tradewindow.cpp | 2 -- files/mygui/openmw_companion_window.layout | 1 + files/mygui/openmw_inventory_window.layout | 3 ++- files/mygui/openmw_spell_window.layout | 1 + files/mygui/openmw_trade_window.layout | 1 + 8 files changed, 5 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwgui/companionwindow.cpp b/apps/openmw/mwgui/companionwindow.cpp index 426894451..c069ae885 100644 --- a/apps/openmw/mwgui/companionwindow.cpp +++ b/apps/openmw/mwgui/companionwindow.cpp @@ -48,9 +48,6 @@ CompanionWindow::CompanionWindow(DragAndDrop *dragAndDrop, MessageBoxManager* ma getWidget(mEncumbranceBar, "EncumbranceBar"); getWidget(mFilterEdit, "FilterEdit"); getWidget(mItemView, "ItemView"); - - mFilterEdit->setUserString("AcceptTab", "true"); - mItemView->eventBackgroundClicked += MyGUI::newDelegate(this, &CompanionWindow::onBackgroundSelected); mItemView->eventItemClicked += MyGUI::newDelegate(this, &CompanionWindow::onItemSelected); mFilterEdit->eventEditTextChange += MyGUI::newDelegate(this, &CompanionWindow::onNameFilterChanged); diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 6c85fe016..653f03153 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -91,8 +91,6 @@ namespace MWGui getWidget(mArmorRating, "ArmorRating"); getWidget(mFilterEdit, "FilterEdit"); - mFilterEdit->setUserString("AcceptTab", "true"); - mAvatarImage->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryWindow::onAvatarClicked); mAvatarImage->setRenderItemTexture(mPreviewTexture.get()); mAvatarImage->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 0.f, 1.f, 1.f)); diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index 406ff1222..41be4f3a8 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -45,8 +45,6 @@ namespace MWGui getWidget(mEffectBox, "EffectsBox"); getWidget(mFilterEdit, "FilterEdit"); - mFilterEdit->setUserString("AcceptTab", "true"); - mSpellView->eventSpellClicked += MyGUI::newDelegate(this, &SpellWindow::onModelIndexSelected); mFilterEdit->eventEditTextChange += MyGUI::newDelegate(this, &SpellWindow::onFilterChanged); deleteButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellWindow::onDeleteClicked); diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index 9d977e603..b102b13ce 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -71,8 +71,6 @@ namespace MWGui getWidget(mBottomPane, "BottomPane"); getWidget(mFilterEdit, "FilterEdit"); - mFilterEdit->setUserString("AcceptTab", "true"); - getWidget(mItemView, "ItemView"); mItemView->eventItemClicked += MyGUI::newDelegate(this, &TradeWindow::onItemSelected); diff --git a/files/mygui/openmw_companion_window.layout b/files/mygui/openmw_companion_window.layout index 0b82a6cf3..dd1fa8447 100644 --- a/files/mygui/openmw_companion_window.layout +++ b/files/mygui/openmw_companion_window.layout @@ -8,6 +8,7 @@ + diff --git a/files/mygui/openmw_inventory_window.layout b/files/mygui/openmw_inventory_window.layout index 1b75c064b..a555c9403 100644 --- a/files/mygui/openmw_inventory_window.layout +++ b/files/mygui/openmw_inventory_window.layout @@ -52,7 +52,8 @@ - + + diff --git a/files/mygui/openmw_spell_window.layout b/files/mygui/openmw_spell_window.layout index 67a9346f4..a3bf10684 100644 --- a/files/mygui/openmw_spell_window.layout +++ b/files/mygui/openmw_spell_window.layout @@ -24,6 +24,7 @@ + diff --git a/files/mygui/openmw_trade_window.layout b/files/mygui/openmw_trade_window.layout index 270649ea8..44f6597fd 100644 --- a/files/mygui/openmw_trade_window.layout +++ b/files/mygui/openmw_trade_window.layout @@ -30,6 +30,7 @@ + From 19a7245251bc76b7ac0b243210e1fe3b5971c3de Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Mon, 2 Mar 2020 04:03:36 +0300 Subject: [PATCH 51/99] Add bump mapping support --- components/nif/property.cpp | 6 ++---- components/nif/property.hpp | 3 +++ components/nifosg/nifloader.cpp | 6 +++++- components/shader/shadervisitor.cpp | 10 +++++++++- files/shaders/objects_fragment.glsl | 21 ++++++++++++++++----- files/shaders/objects_vertex.glsl | 8 ++++++++ 6 files changed, 43 insertions(+), 11 deletions(-) diff --git a/components/nif/property.cpp b/components/nif/property.cpp index de30e7da9..71db7458d 100644 --- a/components/nif/property.cpp +++ b/components/nif/property.cpp @@ -46,12 +46,10 @@ void NiTexturingProperty::read(NIFStream *nif) for (unsigned int i = 0; i < numTextures; i++) { textures[i].read(nif); - // Ignore these at the moment if (i == 5 && textures[5].inUse) // Bump map settings { - /*float lumaScale =*/ nif->getFloat(); - /*float lumaOffset =*/ nif->getFloat(); - /*const Vector4 *lumaMatrix =*/ nif->getVector4(); + envMapLumaBias = nif->getVector2(); + bumpMapMatrix = nif->getVector4(); } } } diff --git a/components/nif/property.hpp b/components/nif/property.hpp index a3f399b83..c72dbf6ba 100644 --- a/components/nif/property.hpp +++ b/components/nif/property.hpp @@ -93,6 +93,9 @@ public: std::vector textures; + osg::Vec2f envMapLumaBias; + osg::Vec4f bumpMapMatrix; + void read(NIFStream *nif); void post(NIFFile *nif); }; diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index b70391a58..644e82484 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -1536,6 +1536,10 @@ namespace NifOsg { // Set this texture to Off by default since we can't render it with the fixed-function pipeline stateset->setTextureMode(texUnit, GL_TEXTURE_2D, osg::StateAttribute::OFF); + osg::Matrix2 bumpMapMatrix(texprop->bumpMapMatrix.x(), texprop->bumpMapMatrix.y(), + texprop->bumpMapMatrix.z(), texprop->bumpMapMatrix.w()); + stateset->addUniform(new osg::Uniform("bumpMapMatrix", bumpMapMatrix)); + stateset->addUniform(new osg::Uniform("envMapLumaBias", texprop->envMapLumaBias)); } else if (i == Nif::NiTexturingProperty::DecalTexture) { @@ -1559,7 +1563,7 @@ namespace NifOsg texture2d->setName("diffuseMap"); break; case Nif::NiTexturingProperty::BumpTexture: - texture2d->setName("normalMap"); + texture2d->setName("bumpMap"); break; case Nif::NiTexturingProperty::GlowTexture: texture2d->setName("emissiveMap"); diff --git a/components/shader/shadervisitor.cpp b/components/shader/shadervisitor.cpp index ae2da0947..31338da8b 100644 --- a/components/shader/shadervisitor.cpp +++ b/components/shader/shadervisitor.cpp @@ -75,7 +75,7 @@ namespace Shader return newStateSet.get(); } - const char* defaultTextures[] = { "diffuseMap", "normalMap", "emissiveMap", "darkMap", "detailMap", "envMap", "specularMap", "decalMap" }; + const char* defaultTextures[] = { "diffuseMap", "normalMap", "emissiveMap", "darkMap", "detailMap", "envMap", "specularMap", "decalMap", "bumpMap" }; bool isTextureNameRecognized(const std::string& name) { for (unsigned int i=0; isetTextureMode(unit, GL_TEXTURE_2D, osg::StateAttribute::ON); + } } else Log(Debug::Error) << "ShaderVisitor encountered unknown texture " << texture; diff --git a/files/shaders/objects_fragment.glsl b/files/shaders/objects_fragment.glsl index eb605d421..f6f3bc792 100644 --- a/files/shaders/objects_fragment.glsl +++ b/files/shaders/objects_fragment.glsl @@ -42,6 +42,13 @@ uniform sampler2D specularMap; varying vec2 specularMapUV; #endif +#if @bumpMap +uniform sampler2D bumpMap; +varying vec2 bumpMapUV; +uniform vec2 envMapLumaBias; +uniform mat2 bumpMapMatrix; +#endif + varying float depth; #define PER_PIXEL_LIGHTING (@normalMap || @forcePPL) @@ -132,20 +139,24 @@ void main() gl_FragData[0].xyz += texture2D(emissiveMap, emissiveMapUV).xyz; #endif - #if @envMap + vec2 texCoordGen = envMapUV; + float envLuma = 1.0; #if @normalMap // if using normal map + env map, take advantage of per-pixel normals for texCoordGen vec3 viewVec = normalize(passViewPos.xyz); vec3 r = reflect( viewVec, viewNormal ); float m = 2.0 * sqrt( r.x*r.x + r.y*r.y + (r.z+1.0)*(r.z+1.0) ); - vec2 texCoordGen = vec2(r.x/m + 0.5, r.y/m + 0.5); - gl_FragData[0].xyz += texture2D(envMap, texCoordGen).xyz * envMapColor.xyz; -#else - gl_FragData[0].xyz += texture2D(envMap, envMapUV).xyz * envMapColor.xyz; + texCoordGen = vec2(r.x/m + 0.5, r.y/m + 0.5); #endif +#if @bumpMap + vec4 bumpTex = texture2D(bumpMap, bumpMapUV); + texCoordGen += bumpTex.rg * bumpMapMatrix; + envLuma = clamp(bumpTex.b * envMapLumaBias.x + envMapLumaBias.y, 0.0, 1.0); +#endif + gl_FragData[0].xyz += texture2D(envMap, texCoordGen).xyz * envMapColor.xyz * envLuma; #endif #if @specularMap diff --git a/files/shaders/objects_vertex.glsl b/files/shaders/objects_vertex.glsl index dcb241274..ec5449b87 100644 --- a/files/shaders/objects_vertex.glsl +++ b/files/shaders/objects_vertex.glsl @@ -29,6 +29,10 @@ varying vec4 passTangent; varying vec2 envMapUV; #endif +#if @bumpMap +varying vec2 bumpMapUV; +#endif + #if @specularMap varying vec2 specularMapUV; #endif @@ -91,6 +95,10 @@ void main(void) passTangent = gl_MultiTexCoord7.xyzw; #endif +#if @bumpMap + bumpMapUV = (gl_TextureMatrix[@bumpMapUV] * gl_MultiTexCoord@bumpMapUV).xy; +#endif + #if @specularMap specularMapUV = (gl_TextureMatrix[@specularMapUV] * gl_MultiTexCoord@specularMapUV).xy; #endif From 29e6c6baaed616571dd6b7434ed5e9ce1a971d1f Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Tue, 3 Mar 2020 17:52:37 +0300 Subject: [PATCH 52/99] Update documentation --- .../convert-bump-mapped-mods.rst | 197 ++++++++---------- .../texture-modding/texture-basics.rst | 16 +- 2 files changed, 89 insertions(+), 124 deletions(-) diff --git a/docs/source/reference/modding/texture-modding/convert-bump-mapped-mods.rst b/docs/source/reference/modding/texture-modding/convert-bump-mapped-mods.rst index d87e4d244..5a77e81ce 100644 --- a/docs/source/reference/modding/texture-modding/convert-bump-mapped-mods.rst +++ b/docs/source/reference/modding/texture-modding/convert-bump-mapped-mods.rst @@ -3,59 +3,59 @@ Normal maps from Morrowind to OpenMW ==================================== - `General introduction to normal map conversion`_ - - `Normal Mapping in OpenMW`_ - - `Activating normal mapping shaders in OpenMW`_ - - `Normal mapping in Morrowind with Morrowind Code Patch`_ - - `Normal mapping in Morrowind with MGE XE`_ + - `OpenMW normal-mapping`_ + - `Activating normal-mapping shaders in OpenMW`_ + - `Morrowind bump-mapping`_ + - `MGE XE normal-mapping`_ - `Converting PeterBitt's Scamp Replacer`_ (Mod made for the MGE XE PBR prototype) - `Tutorial - MGE`_ -- `Converting Lougian's Hlaalu Bump mapped`_ (MCP's fake bump map function, part 1: *without* custom models) - - `Tutorial - MCP, Part 1`_ -- `Converting Apel's Various Things - Sacks`_ (MCP's fake bump map function, part 2: *with* custom models) - - `Tutorial - MCP, Part 2`_ +- `Converting Lougian's Hlaalu Bump mapped`_ (Morrowind's bump-mapping, part 1: *without* custom models) + - `Tutorial - Morrowind, Part 1`_ +- `Converting Apel's Various Things - Sacks`_ (Morrowind's bump-mapping, part 2: *with* custom models) + - `Tutorial - Morrowind, Part 2`_ General introduction to normal map conversion ------------------------------------------------ -:Authors: Joakim (Lysol) Berg -:Updated: 2016-11-11 +:Authors: Joakim (Lysol) Berg, Alexei (Capo) Dobrohotov +:Updated: 2020-03-03 -This page has general information and tutorials on how normal mapping works in OpenMW and how you can make mods using -the old fake normal mapping technique (such as `Netch Bump mapped`_ and `Hlaalu Bump mapped`_, and maybe the most -(in)famous one to give shiny rocks in OpenMW, the mod `On the Rocks`_!, featured in MGSO and Morrowind Rebirth) work in OpenMW. +This page has general information and tutorials on how normal-mapping works in OpenMW and how you can make mods using +the old environment-mapped bump-mapping technique (such as `Netch Bump mapped`_ and `Hlaalu Bump mapped`_, and maybe the most +(in)famous one to previously give shiny rocks in OpenMW, the mod `On the Rocks`_!, featured in MGSO and Morrowind Rebirth) work better in OpenMW. *Note:* The conversion made in the `Converting Apel's Various Things - Sacks`_-part of this tutorial require the use of the application NifSkope_. -*Another note:* I will use the terms bump mapping and normal mapping simultaneously. -Normal mapping is one form of bump mapping. In other words, normal mapping is bump mapping, -but bump mapping isn't necessarily normal mapping. -There are several techniques for bump mapping, and normal mapping is the most common one today. +*Another note:* I will use the terms bump-mapping and normal-mapping simultaneously. +Normal-mapping is one form of bump-mapping. In other words, normal-mapping is bump-mapping, +but bump-mapping isn't necessarily normal-mapping. +There are several techniques for bump-mapping, and normal-mapping is the most common one today. So let's get on with it. -Normal Mapping in OpenMW +OpenMW normal-mapping ************************ -Normal mapping in OpenMW works in a very simple way: The engine just looks for a texture with a *_n.dds* suffix, +Normal-mapping in OpenMW works in a very simple way: The engine just looks for a texture with a *_n.dds* suffix, and you're done. -So to expand on this a bit, let's take a look at how a model seeks for textures. +So to expand on this a bit, let's take a look at how a model looks up textures. -Let us assume we have the model *example.nif*. In this model file, -there should be a tag (NiSourceTexture) that states what texture it should use and where to find it. Typically, -it will point to something like *exampletexture_01.dds*. This texture is supposed to be located directly in the -Textures folder since it does not state anything else. If the model is a custom made one, modders tend to group -their textures in separate folders, just to easily keep track of them. -It might be something like *./Textures/moddername/exampletexture_02.dds*. +Let us assume we have the model *example.nif*. In this model file, +there should be a tag (NiTexturingProperty) that states what textures it should use and where to find them. Typically, +the model's base (diffuse) texture reference will point to something named like *exampletexture_01.dds*. This texture is supposed to be located directly in the +Textures folder since it does not state anything else. +Modders tend to group textures for custom-made models in dedicated folders to keep track of them easily, +so it might be something like *./Textures/moddername/exampletexture_02.dds*. -When OpenMW finally adds normal mapping, it simply takes the NiSourceTexture file path, e.g., -*exampletexture_01.dds*, and looks for a *exampletexture_01_n.dds*. If it can't find this file, no normal mapping is added. -If it *does* find this file, the model will use this texture as a normal map. Simple. +OpenMW will pick the diffuse map file path from the mesh, e.g. +*exampletexture_01.dds*, and look up a texture named *exampletexture_01_n.dds*. +That file will be the normal map if it's present. Simple. -Activating normal mapping shaders in OpenMW +Activating normal-mapping shaders in OpenMW ******************************************* -Before normal (and specular and parallax) maps will show up in OpenMW, you'll need to activate them in the +Before normal (and specular and parallax) maps can show up in OpenMW, their auto-detection needs to be turned on in settings.cfg_-file. Add these rows where it would make sense: :: @@ -64,66 +64,46 @@ settings.cfg_-file. Add these rows where it would make sense: auto use object normal maps = true auto use terrain normal maps = true -And while we're at it, why not activate specular maps too just for the sake of it? - -:: - auto use object specular maps = true auto use terrain specular maps = true -Lastly, if you want really nice lights in OpenMW, add these rows: +See OpenMW's wiki page about `texture modding`_ to read more about it. -:: - - force shaders = true - clamp lighting = false - -See OpenMW's wiki page about `texture modding`_ to read further about this. - -Normal mapping in Morrowind with Morrowind Code Patch +Morrowind bump-mapping ***************************************************** **Conversion difficulty:** *Varies. Sometimes quick and easy, sometimes time-consuming and hard.* -You might have bumped (pun intended) on a few bump-mapped texture packs for Morrowind that require the -Morrowind Code Patch (MCP). You might even be thinking: Why doesn't OpenMW just support these instead of reinventing -the wheel? I know it sounds strange, but it will make sense. Here's how MCP handles normal maps: +You might have bumped (pun intended) on a few bump-mapped texture packs for Morrowind that require the +Morrowind Code Patch (MCP). OpenMW does support the legacy bump-maps, +but bump-mapped models may look brighter than intended if they were designed with Morrowind Code Patch in mind +because MCP can optionally apply lighting after environment maps are processed which makes bump-mapped models look a bit better, +and can make use of the gloss map channel in the bump map. It also makes skinned mesh bump-mapping possible. +We chose not to replicate the difference in environment map handling, but gloss map channel as well as skinned mesh bump-mapping are supported. -Morrowind does not recognize normal maps (they weren't really a "thing" yet in 2002), so even if you have a normal map, -Morrowind will not load and display it. MCP has a clever way to solve this issue, by using something Morrowind *does* support, -namely environment maps. You could add a tag for an environment map and then add a normal map as the environment map, -but you'd end up with a shiny ugly model in the game. MCP solves this by turning down the brightness of the environment maps, -making the model look *kind of* as if it had a normal map applied to it. -I say kind of because it does not really look as good as normal mapping usually does. It was a hacky way to do it, -but it was the only way at the time, and therefore the best way. +Technically you aren't supposed to convert bump maps because they shouldn't be normal maps that are supported by OpenMW as well, +but artists may use actual normal maps as bump maps either because they look better in vanilla... or because they're lazy. +In this case you can benefit from OpenMW's normal-mapping support by using these bump maps the way normal maps are used. +This means that you will have to drop the bump-mapping references from the model and sometimes rename the texture. -The biggest problem with this is not that it doesn't look as good as it could – no, -the biggest problem in my opinion is that it requires you to state the file paths for your normal map textures *in the models*! -For buildings, which often use several textures for one single model file, it could take *ages* to do this, -and you had to do it for dozens of model files too. You also had to ship your texture pack with model files, -making your mod bigger in file size. - -These are basically the reasons why OpenMW does not support fake bump maps like MCP does. -It is just a really bad way to enhance your models, all the more when you have the possibility to do it in a better way. - -Normal mapping in Morrowind with MGE XE +MGE XE normal-mapping *************************************** **Conversion difficulty:** *Easy* -The most recent feature on this topic is that the Morrowind Graphics Extender (MGE) finally started to support real -normal mapping in an experimental version available here: `MGE XE`_ (you can't use MGE with OpenMW!). -Not only this but it also adds full support for physically based rendering (PBR), -making it one step ahead of OpenMW in terms of texturing techniques. However, -OpenMW will probably have this feature in the future too – and let's hope that OpenMW and MGE will handle PBR in a +The most recent feature on this topic is that the Morrowind Graphics Extender (MGE) finally started to support real +normal-mapping in an experimental version available here: `MGE XE`_ (you can't use MGE with OpenMW!). +Not only this but it also adds full support for physically based rendering (PBR), +making it one step ahead of OpenMW in terms of texturing techniques. However, +OpenMW will probably have this feature in the future too – and let's hope that OpenMW and MGE will handle PBR in a similar fashion in the future so that mods can be used for both MGE and OpenMW without any hassle. -I haven't researched that much on the MGE variant yet but it does support real implementation of normal mapping, -making it really easy to convert mods made for MGE into OpenMW (I'm only talking about the normal map textures though). -There's some kind of text file if I understood it correctly that MGE uses to find the normal map. -OpenMW does not need this, you just have to make sure the normal map has the same name as the diffuse texture but with +I haven't researched that much on the MGE variant yet but it does support real implementation of normal-mapping, +making it really easy to convert mods made for MGE into OpenMW (I'm only talking about the normal map textures though). +There's some kind of text file if I understood it correctly that MGE uses to find the normal map. +OpenMW does not need this, you just have to make sure the normal map has the same name as the diffuse texture but with the correct suffix after. Now, on to the tutorials. @@ -135,20 +115,20 @@ Converting PeterBitt's Scamp Replacer :Authors: Joakim (Lysol) Berg :Updated: 2016-11-11 -So, let's say you've found out that PeterBitt_ makes awesome models and textures featuring physically based rendering -(PBR) and normal maps. Let's say that you tried to run his `PBR Scamp Replacer`_ in OpenMW and that you were greatly -disappointed when the normal map didn't seem to work. Lastly, let's say you came here, looking for some answers. +So, let's say you've found out that PeterBitt_ makes awesome models and textures featuring physically based rendering +(PBR) and normal maps. Let's say that you tried to run his `PBR Scamp Replacer`_ in OpenMW and that you were greatly +disappointed when the normal map didn't seem to work. Lastly, let's say you came here, looking for some answers. Am I right? Great. Because you've come to the right place! -*A quick note before we begin*: Please note that you can only use the normal map texture and not the rest of the materials, -since PBR isn't implemented in OpenMW yet. Sometimes PBR textures can look dull without all of the texture files, +*A quick note before we begin*: Please note that you can only use the normal map texture and not the rest of the materials, +since PBR isn't implemented in OpenMW yet. Sometimes PBR textures can look dull without all of the texture files, so have that in mind. Tutorial - MGE ************** -In this tutorial, I will use PeterBitt's `PBR Scamp Replacer`_ as an example, -but any mod featuring PBR that requires the PBR version of MGE will do, +In this tutorial, I will use PeterBitt's `PBR Scamp Replacer`_ as an example, +but any mod featuring PBR that requires the PBR version of MGE will do, provided it also includes a normal map (which it probably does). So, follow these steps: @@ -163,67 +143,67 @@ So, follow these steps: #. Rename your newly extracted file (``tx_Scamp_normals.dds``) to ``tx_Scamp_n.dds`` (which is exactly the same name as the diffuse texture file, except for the added *_n* suffix before the filename extention). #. You're actually done! -So as you might notice, converting these mods is very simple and takes just a couple of minutes. +So as you might notice, converting these mods is very simple and takes just a couple of minutes. It's more or less just a matter of renaming and moving a few files. -I totally recommend you to also try this on PeterBitt's Nix Hound replacer and Flash3113's various replacers. +I totally recommend you to also try this on PeterBitt's Nix Hound replacer and Flash3113's various replacers. It should be the same principle to get those to work. -And let's hope that some one implements PBR shaders to OpenMW too, +And let's hope that some one implements PBR shaders to OpenMW too, so that we can use all the material files of these mods in the future. Converting Lougian's Hlaalu Bump mapped --------------------------------------- -**Mod made for MCP's fake bump function, without custom models** +**Mod made for Morrowind's bump-mapping, without custom models** -:Authors: Joakim (Lysol) Berg -:Updated: 2016-11-11 +:Authors: Joakim (Lysol) Berg, Alexei (Capo) Dobrohotov +:Updated: 2020-03-03 -Converting textures made for the Morrowind Code Patch (MCP) fake bump mapping can be really easy or a real pain, -depending on a few circumstances. In this tutorial, we will look at a very easy, +Converting normal maps made for the Morrowind's bump-mapping can be really easy or a real pain, +depending on a few circumstances. In this tutorial, we will look at a very easy, although in some cases a bit time-consuming, example. -Tutorial - MCP, Part 1 +Tutorial - Morrowind, Part 1 ********************** We will be converting a quite popular texture replacer of the Hlaalu architecture, namely Lougian's `Hlaalu Bump mapped`_. -Since this is just a texture pack and not a model replacer, -we can convert the mod in a few minutes by just renaming a few dozen files and by *not* extracting the included model +Since this is just a texture pack and not a model replacer, +we can convert the mod in a few minutes by just renaming a few dozen files and by *not* extracting the included model (``.nif``) files when installing the mod. #. Download Lougian's `Hlaalu Bump mapped`_. #. Install the mod by extracting the ``./Textures`` folder to a data folder the way you usually install mods (**Pro tip**: Install using OpenMW's `Multiple data folders`_ function!). - - Again, yes, *only* the ``./Textures`` folder. Do *not* extract the Meshes folder. They are only there to make the MCP hack work, which is not of any interest to us. + - Again, yes, *only* the ``./Textures`` folder. Do not extract the Meshes folder. They are there to make Morrowind bump-mapping work. #. Go to your new texture folder. If you installed the mod like I recommended, you won't have any trouble finding the files. If you instead placed all your files in Morrowinds main Data Files folder (sigh), you need to check with the mod's .rar file to see what files you should look for. Because you'll be scrolling through a lot of files. #. Find all the textures related to the texture pack in the Textures folder and take note of all the ones that ends with a *_nm.dds*. #. The *_nm.dds* files are normal map files. OpenMW's standard format is to have the normal maps with a *_n.dds* instead. Rename all the normal map textures to only have a *_n.dds* instead of the *_nm.dds*. - As a nice bonus to this tutorial, this pack actually included one specularity texture too. We should use it of course. It's the one called "``tx_glass_amber_02_reflection.dds``". For OpenMW to recognize this file and use it as a specular map, you need to change the *_reflection.dds* part to *_spec.dds*, resulting in the name ``tx_glass_amber_01_spec.dds``. #. That should be it. Really simple, but I do know that it takes a few minutes to rename all those files. -Now – if the mod you want to change includes custom made models it gets a bit more complicated I'm afraid. +Now – if the mod you want to change includes custom made models it gets a bit more complicated I'm afraid. But that is for the next tutorial. Converting Apel's Various Things - Sacks ---------------------------------------- -**Mod made for MCP's fake bump function, with custom models** +**Mod made for Morrowind bump-mapping, with custom models** -:Authors: Joakim (Lysol) Berg -:Updated: 2016-11-09 +:Authors: Joakim (Lysol) Berg, Alexei (Capostrophic) Dobrohotov +:Updated: 2020-03-03 -In part one of this tutorial, we converted a mod that only included modified Morrowind model (``.nif``) -files so that the normal maps could be loaded in Morrowind with MCP. -We ignored those model files since they are not needed with OpenMW. In this tutorial however, -we will convert a mod that includes new, custom made models. In other words, we cannot just ignore those files this time. +In part one of this tutorial, we converted a mod that only included modified Morrowind model (``.nif``) +files so that the bump maps could be loaded as normal maps. +We ignored those model files since they are not needed with OpenMW. In this tutorial however, +we will convert a mod that includes new, custom-made models. In other words, we cannot just ignore those files this time. -Tutorial - MCP, Part 2 +Tutorial - Morrowind, Part 2 ********************** -The sacks included in Apel's `Various Things - Sacks`_ come in two versions – Without bump mapping, and with bump mapping. -Since we want the glory of normal mapping in our OpenMW setup, we will go with the bump-mapped version. +The sacks included in Apel's `Various Things - Sacks`_ come in two versions – without bump-mapping, and with bump-mapping. +Since we want the glory of normal-mapping in our OpenMW setup, we will go with the bump-mapped version. #. Start by downloading Apel's `Various Things - Sacks`_ from Nexus. #. Once downloaded, install it the way you'd normally install your mods (**Pro tip**: Install using OpenMW's `Multiple data folders`_ function!). -#. Now, if you ran the mod right away, your sacks will be made out of lead_. This is because the normal map is loaded as an environment map which MCP fixes so that it looks less shiny. We don't use MCP, so therefore, it looks kind of like the shack was made out of lead. +#. Now, if you ran the mod right away, your sacks may look... wetter than expected. This is because the mod assumes you have the MCP feature which makes the sacks less shiny enabled. Which you can't have enabled. #. We need to fix this by removing some tags in the model files. You need to download NifSkope_ for this, which, again, only have binaries available for Windows. #. Go the place where you installed the mod and go to ``./Meshes/o/`` to find the model files. - If you installed the mod like I suggested, finding the files will be easy as a pie, but if you installed it by dropping everything into your main Morrowind Data Files folder, then you'll have to scroll a lot to find them. Check the mod's zip file for the file names of the models if this is the case. The same thing applies to when fixing the textures. @@ -232,14 +212,14 @@ Since we want the glory of normal mapping in our OpenMW setup, we will go with t - NiSourceTexture with the value that appears to be a normal map file, in this mod, they have the suffix *_nm.dds*. #. Remove all these tags by selecting them one at a time and press right click>Block>Remove Branch. (Ctrl-Del) #. Repeat this on all the affected models. -#. If you launch OpenMW now, you'll `no longer have shiny models`_. But one thing is missing. Can you see it? It's actually hard to spot on still pictures, but we have no normal maps here. +#. If you launch OpenMW now, you'll `no longer have wet models`_. But one thing is missing. Can you see it? It's actually hard to spot on still pictures, but we have no normal maps here. #. Now, go back to the root of where you installed the mod. Now go to ``./Textures/`` and you'll find the texture files in question. #. OpenMW detects normal maps if they have the same name as the base diffuse texture, but with a *_n.dds* suffix. In this mod, the normal maps has a suffix of *_nm.dds*. Change all the files that ends with *_nm.dds* to instead end with *_n.dds*. #. Finally, `we are done`_! -Since these models have one or two textures applied to them, the fix was not that time-consuming. The process continues to work for more complex models that use more textures, but looking through each category for texture effects and normal mapped textures rapidly becomes tedious. Luckily, NifSkope provides a feature to do the same automatically. +Since these models have one or two textures applied to them, the fix was not that time-consuming. The process continues to work for more complex models that use more textures, but looking through each category for texture effects and normal mapped textures rapidly becomes tedious. Luckily, NifSkope provides a feature to do the same automatically. -Rightclick in NifSkope to access the *Spells* dropdown menu, also available via the top bar, hover over the *Blocks* section, and `choose the action to Remove by ID`_. You can then input the RegEx expression ``^NiTextureEffect`` (directing it to remove any block whose name starts with "NiTextureEffect") to automatically remove all texture effect blocks within the NIF. This also has the helpful side effect of listing `all the blocks within the NIF in the bottom section`_, allowing you to additionally root out any blocks referencing *_nm.dds* textures without having to painstakingly open each category. +Right-click in NifSkope to access the *Spells* dropdown menu, also available via the top bar, hover over the *Blocks* section, and `choose the action to Remove by ID`_. You can then input the RegEx expression ``^NiTextureEffect`` (directing it to remove any block whose name starts with "NiTextureEffect") to automatically remove all texture effect blocks within the NIF. This also has the helpful side effect of listing `all the blocks within the NIF in the bottom section`_, allowing you to additionally root out any blocks referencing *_nm.dds* textures without having to painstakingly open each category. .. _`Netch Bump mapped`: https://www.nexusmods.com/morrowind/mods/42851/? .. _`Hlaalu Bump mapped`: https://www.nexusmods.com/morrowind/mods/42396/? @@ -251,10 +231,9 @@ Rightclick in NifSkope to access the *Spells* dropdown menu, also available via .. _settings.cfg: https://wiki.openmw.org/index.php?title=Settings .. _`Multiple data folders`: https://wiki.openmw.org/index.php?title=Mod_installation .. _`Various Things - Sacks`: https://www.nexusmods.com/morrowind/mods/42558/? -.. _Lead: https://imgur.com/bwpcYlc .. _NifSkope: https://wiki.openmw.org/index.php?title=Tools#NifSkope .. _Blocks: https://imgur.com/VmQC0WG -.. _`no longer have shiny models`: https://imgur.com/vu1k7n1 +.. _`no longer have wet models`: https://imgur.com/vu1k7n1 .. _`we are done`: https://imgur.com/yyZxlTw .. _`choose the action to Remove by ID`: https://imgur.com/a/qs2t0tC .. _`all the blocks within the NIF in the bottom section`: https://imgur.com/a/UFFNyWt diff --git a/docs/source/reference/modding/texture-modding/texture-basics.rst b/docs/source/reference/modding/texture-modding/texture-basics.rst index 51523af3f..65e71834c 100644 --- a/docs/source/reference/modding/texture-modding/texture-basics.rst +++ b/docs/source/reference/modding/texture-modding/texture-basics.rst @@ -23,18 +23,6 @@ To plug in a normal map, you name the normal map as the diffuse texture but with OpenMW will then recognise the file and load it as a normal map, provided you have set up your settings file correctly. See the section `Automatic use`_ further down below for detailed information. -.. note:: - While the original Morrowind engine does support the loading of a BumpTexture slot in the NIF, - it will not display it as a normal map. Morrowind Code Patch (MCP) - added a way to hack normal maps into the engine by first enabling the engine to load the BumpTexture slot as an - environment map and then turn down the brightness of the environment map. - This will imitate how a real normal map shader would display a normal map, but it will not look exactly the same. - OpenMW uses standard normal mapping, which achieves much better results. - Unfortunately, this difference can result in incompatibilities. - Some mods - (e.g. `Redoran Bump Mapped `_) - look much darker compared to the vanilla engine and will have to be recalibrated. - Specular Mapping ################ @@ -43,14 +31,12 @@ The alpha channel specifies shininess in range [0, 255]. If a specular map is used, it will override the shininess and specular color set in the NiMaterialProperty / osg::Material. -NIF files do not support specular maps. +Morrowind format NIF files do not support normal maps or specular maps. In order to use them anyway, see the next section. Automatic Use ############# -In addition to editing mesh files, -there is another way of plugging in these texture maps. Simply create the textures with appropriate naming convention (e.g. when the base texture is called foo.dds, the normal map would have to be called foo_n.dds). From 6999f1fd28e4bfba2ae7a17a384e59cf346fdfdd Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Tue, 3 Mar 2020 20:08:59 +0300 Subject: [PATCH 53/99] Add an option to apply lighting to environment maps --- apps/opencs/model/world/data.cpp | 1 + apps/openmw/mwrender/renderingmanager.cpp | 1 + components/nifosg/nifloader.cpp | 28 +++++++++-- .../reference/modding/settings/shaders.rst | 11 +++++ .../convert-bump-mapped-mods.rst | 19 ++++--- files/settings-default.cfg | 4 ++ files/shaders/objects_fragment.glsl | 49 +++++++++++-------- 7 files changed, 81 insertions(+), 32 deletions(-) diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 0753569bf..f609e6999 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -81,6 +81,7 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, bool fsStrict, const Files::Pat Shader::ShaderManager::DefineMap shadowDefines = SceneUtil::ShadowManager::getShadowsDisabledDefines(); defines["forcePPL"] = "0"; defines["clamp"] = "1"; + defines["preLightEnv"] = "0"; for (const auto& define : shadowDefines) defines[define.first] = define.second; mResourceSystem->getSceneManager()->getShaderManager().setGlobalDefines(defines); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 1f53ff3d6..dd570f558 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -253,6 +253,7 @@ namespace MWRender globalDefines["forcePPL"] = Settings::Manager::getBool("force per pixel lighting", "Shaders") ? "1" : "0"; globalDefines["clamp"] = Settings::Manager::getBool("clamp lighting", "Shaders") ? "1" : "0"; + globalDefines["preLightEnv"] = Settings::Manager::getBool("apply lighting to environment maps", "Shaders") ? "1" : "0"; // It is unnecessary to stop/start the viewer as no frames are being rendered yet. mResourceSystem->getSceneManager()->getShaderManager().setGlobalDefines(globalDefines); diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 644e82484..91232d03a 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -41,6 +41,7 @@ #include #include #include +#include #include "particle.hpp" #include "userdata.hpp" @@ -427,12 +428,29 @@ namespace NifOsg texture2d->setWrap(osg::Texture::WRAP_S, wrapS ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE); texture2d->setWrap(osg::Texture::WRAP_T, wrapT ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE); + static const bool preLightEnv = Settings::Manager::getBool("apply lighting to environment maps", "Shaders"); osg::ref_ptr texEnv = new osg::TexEnvCombine; - texEnv->setCombine_Alpha(osg::TexEnvCombine::REPLACE); - texEnv->setSource0_Alpha(osg::TexEnvCombine::PREVIOUS); - texEnv->setCombine_RGB(osg::TexEnvCombine::ADD); - texEnv->setSource0_RGB(osg::TexEnvCombine::PREVIOUS); - texEnv->setSource1_RGB(osg::TexEnvCombine::TEXTURE); + if (!preLightEnv) + { + texEnv->setCombine_Alpha(osg::TexEnvCombine::REPLACE); + texEnv->setSource0_Alpha(osg::TexEnvCombine::PREVIOUS); + texEnv->setCombine_RGB(osg::TexEnvCombine::ADD); + texEnv->setSource0_RGB(osg::TexEnvCombine::PREVIOUS); + texEnv->setSource1_RGB(osg::TexEnvCombine::TEXTURE); + } + else + { + texEnv->setCombine_RGB(osg::TexEnvCombine::INTERPOLATE); + texEnv->setSource0_RGB(osg::TexEnvCombine::TEXTURE); + texEnv->setOperand0_RGB(osg::TexEnvCombine::SRC_COLOR); + texEnv->setSource1_RGB(osg::TexEnvCombine::PREVIOUS); + texEnv->setOperand1_RGB(osg::TexEnvCombine::SRC_COLOR); + texEnv->setSource2_RGB(osg::TexEnvCombine::TEXTURE); + texEnv->setOperand2_RGB(osg::TexEnvCombine::SRC_ALPHA); + texEnv->setCombine_Alpha(osg::TexEnvCombine::REPLACE); + texEnv->setSource0_Alpha(osg::TexEnvCombine::PREVIOUS); + texEnv->setOperand0_Alpha(osg::TexEnvCombine::SRC_ALPHA); + } int texUnit = 3; // FIXME diff --git a/docs/source/reference/modding/settings/shaders.rst b/docs/source/reference/modding/settings/shaders.rst index b36f64285..8f1040225 100644 --- a/docs/source/reference/modding/settings/shaders.rst +++ b/docs/source/reference/modding/settings/shaders.rst @@ -124,3 +124,14 @@ terrain specular map pattern :Default: _diffusespec The filename pattern to probe for when detecting terrain specular maps (see 'auto use terrain specular maps') + +apply lighting to environment maps +---------------------------------- + +:Type: boolean +:Range: True/False +:Default: False + +Normally environment map reflections aren't affected by lighting, which makes environment-mapped (and thus bump-mapped objects) glow in the dark. +Morrowind Code Patch includes an option to remedy that by doing environment-mapping before applying lighting, this is the equivalent of that option. +This also affects fixed function pipeline rendering. diff --git a/docs/source/reference/modding/texture-modding/convert-bump-mapped-mods.rst b/docs/source/reference/modding/texture-modding/convert-bump-mapped-mods.rst index 5a77e81ce..0ad35d7a5 100644 --- a/docs/source/reference/modding/texture-modding/convert-bump-mapped-mods.rst +++ b/docs/source/reference/modding/texture-modding/convert-bump-mapped-mods.rst @@ -75,13 +75,18 @@ Morrowind bump-mapping **Conversion difficulty:** *Varies. Sometimes quick and easy, sometimes time-consuming and hard.* -You might have bumped (pun intended) on a few bump-mapped texture packs for Morrowind that require the -Morrowind Code Patch (MCP). OpenMW does support the legacy bump-maps, -but bump-mapped models may look brighter than intended if they were designed with Morrowind Code Patch in mind -because MCP can optionally apply lighting after environment maps are processed which makes bump-mapped models look a bit better, -and can make use of the gloss map channel in the bump map. It also makes skinned mesh bump-mapping possible. -We chose not to replicate the difference in environment map handling, but gloss map channel as well as skinned mesh bump-mapping are supported. +You might have bumped (pun intended) on a few bump-mapped texture packs for Morrowind that require +Morrowind Code Patch (MCP). OpenMW supports them, and like MCP can optionally apply lighting after environment maps +are processed which makes bump-mapped models look a bit better, +can make use of the gloss map channel in the bump map and can apply bump-mapping to skinned models. +Add this to settings.cfg_-file: +:: + + [Shaders] + apply lighting to environment maps = true + +But sometimes you may want them to look a bit better than in vanilla. Technically you aren't supposed to convert bump maps because they shouldn't be normal maps that are supported by OpenMW as well, but artists may use actual normal maps as bump maps either because they look better in vanilla... or because they're lazy. In this case you can benefit from OpenMW's normal-mapping support by using these bump maps the way normal maps are used. @@ -203,7 +208,7 @@ Since we want the glory of normal-mapping in our OpenMW setup, we will go with t #. Start by downloading Apel's `Various Things - Sacks`_ from Nexus. #. Once downloaded, install it the way you'd normally install your mods (**Pro tip**: Install using OpenMW's `Multiple data folders`_ function!). -#. Now, if you ran the mod right away, your sacks may look... wetter than expected. This is because the mod assumes you have the MCP feature which makes the sacks less shiny enabled. Which you can't have enabled. +#. Now, if you ran the mod right away, your sacks may look... wetter than expected. This is because the mod assumes you have the MCP feature which makes the sacks less shiny enabled. You can have its equivalent enabled to make the sacks look like in Morrowind with MCP, or you may proceed on the tutorial. #. We need to fix this by removing some tags in the model files. You need to download NifSkope_ for this, which, again, only have binaries available for Windows. #. Go the place where you installed the mod and go to ``./Meshes/o/`` to find the model files. - If you installed the mod like I suggested, finding the files will be easy as a pie, but if you installed it by dropping everything into your main Morrowind Data Files folder, then you'll have to scroll a lot to find them. Check the mod's zip file for the file names of the models if this is the case. The same thing applies to when fixing the textures. diff --git a/files/settings-default.cfg b/files/settings-default.cfg index d3458d293..b7e0eb6c2 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -346,6 +346,10 @@ specular map pattern = _spec # The filename pattern to probe for when detecting terrain specular maps (see 'auto use terrain specular maps') terrain specular map pattern = _diffusespec +# Apply lighting to environment map reflections on the rendered objects like in Morrowind Code Patch. +# Also affects fixed function pipeline. +apply lighting to environment maps = false + [Input] # Capture control of the cursor prevent movement outside the window. diff --git a/files/shaders/objects_fragment.glsl b/files/shaders/objects_fragment.glsl index f6f3bc792..a222d9c7b 100644 --- a/files/shaders/objects_fragment.glsl +++ b/files/shaders/objects_fragment.glsl @@ -119,6 +119,31 @@ void main() #if @decalMap vec4 decalTex = texture2D(decalMap, decalMapUV); gl_FragData[0].xyz = mix(gl_FragData[0].xyz, decalTex.xyz, decalTex.a); +#endif + +#if @envMap + + vec2 envTexCoordGen = envMapUV; + float envLuma = 1.0; + +#if @normalMap + // if using normal map + env map, take advantage of per-pixel normals for envTexCoordGen + vec3 viewVec = normalize(passViewPos.xyz); + vec3 r = reflect( viewVec, viewNormal ); + float m = 2.0 * sqrt( r.x*r.x + r.y*r.y + (r.z+1.0)*(r.z+1.0) ); + envTexCoordGen = vec2(r.x/m + 0.5, r.y/m + 0.5); +#endif + +#if @bumpMap + vec4 bumpTex = texture2D(bumpMap, bumpMapUV); + envTexCoordGen += bumpTex.rg * bumpMapMatrix; + envLuma = clamp(bumpTex.b * envMapLumaBias.x + envMapLumaBias.y, 0.0, 1.0); +#endif + +#if @preLightEnv + gl_FragData[0].xyz += texture2D(envMap, envTexCoordGen).xyz * envMapColor.xyz * envLuma; +#endif + #endif float shadowing = unshadowedLightRatio(depth); @@ -135,30 +160,14 @@ void main() gl_FragData[0] *= doLighting(passViewPos, normalize(viewNormal), passColor, shadowing); #endif +#if @envMap && !@preLightEnv + gl_FragData[0].xyz += texture2D(envMap, envTexCoordGen).xyz * envMapColor.xyz * envLuma; +#endif + #if @emissiveMap gl_FragData[0].xyz += texture2D(emissiveMap, emissiveMapUV).xyz; #endif -#if @envMap - vec2 texCoordGen = envMapUV; - float envLuma = 1.0; - -#if @normalMap - // if using normal map + env map, take advantage of per-pixel normals for texCoordGen - vec3 viewVec = normalize(passViewPos.xyz); - vec3 r = reflect( viewVec, viewNormal ); - float m = 2.0 * sqrt( r.x*r.x + r.y*r.y + (r.z+1.0)*(r.z+1.0) ); - texCoordGen = vec2(r.x/m + 0.5, r.y/m + 0.5); -#endif - -#if @bumpMap - vec4 bumpTex = texture2D(bumpMap, bumpMapUV); - texCoordGen += bumpTex.rg * bumpMapMatrix; - envLuma = clamp(bumpTex.b * envMapLumaBias.x + envMapLumaBias.y, 0.0, 1.0); -#endif - gl_FragData[0].xyz += texture2D(envMap, texCoordGen).xyz * envMapColor.xyz * envLuma; -#endif - #if @specularMap vec4 specTex = texture2D(specularMap, specularMapUV); float shininess = specTex.a * 255; From ed95a7919939eb605f9a84551d96bf1df8edd131 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Tue, 3 Mar 2020 20:38:28 +0300 Subject: [PATCH 54/99] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3bfd756b5..0b51de66f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -261,6 +261,7 @@ Feature #5193: Weapon sheathing Feature #5219: Impelement TestCells console command Feature #5224: Handle NiKeyframeController for NiTriShape + Feature #5304: Morrowind-style bump-mapping Task #4686: Upgrade media decoder to a more current FFmpeg API Task #4695: Optimize Distant Terrain memory consumption Task #4789: Optimize cell transitions From 2b3ecff0041e1e6a08634b46853e3b8669175532 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Tue, 3 Mar 2020 21:50:50 +0300 Subject: [PATCH 55/99] Remove broken fixed function pipeline support Added by mistake --- components/nifosg/nifloader.cpp | 28 ++++--------------- .../reference/modding/settings/shaders.rst | 2 +- files/settings-default.cfg | 2 +- 3 files changed, 7 insertions(+), 25 deletions(-) diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 91232d03a..644e82484 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -41,7 +41,6 @@ #include #include #include -#include #include "particle.hpp" #include "userdata.hpp" @@ -428,29 +427,12 @@ namespace NifOsg texture2d->setWrap(osg::Texture::WRAP_S, wrapS ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE); texture2d->setWrap(osg::Texture::WRAP_T, wrapT ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE); - static const bool preLightEnv = Settings::Manager::getBool("apply lighting to environment maps", "Shaders"); osg::ref_ptr texEnv = new osg::TexEnvCombine; - if (!preLightEnv) - { - texEnv->setCombine_Alpha(osg::TexEnvCombine::REPLACE); - texEnv->setSource0_Alpha(osg::TexEnvCombine::PREVIOUS); - texEnv->setCombine_RGB(osg::TexEnvCombine::ADD); - texEnv->setSource0_RGB(osg::TexEnvCombine::PREVIOUS); - texEnv->setSource1_RGB(osg::TexEnvCombine::TEXTURE); - } - else - { - texEnv->setCombine_RGB(osg::TexEnvCombine::INTERPOLATE); - texEnv->setSource0_RGB(osg::TexEnvCombine::TEXTURE); - texEnv->setOperand0_RGB(osg::TexEnvCombine::SRC_COLOR); - texEnv->setSource1_RGB(osg::TexEnvCombine::PREVIOUS); - texEnv->setOperand1_RGB(osg::TexEnvCombine::SRC_COLOR); - texEnv->setSource2_RGB(osg::TexEnvCombine::TEXTURE); - texEnv->setOperand2_RGB(osg::TexEnvCombine::SRC_ALPHA); - texEnv->setCombine_Alpha(osg::TexEnvCombine::REPLACE); - texEnv->setSource0_Alpha(osg::TexEnvCombine::PREVIOUS); - texEnv->setOperand0_Alpha(osg::TexEnvCombine::SRC_ALPHA); - } + texEnv->setCombine_Alpha(osg::TexEnvCombine::REPLACE); + texEnv->setSource0_Alpha(osg::TexEnvCombine::PREVIOUS); + texEnv->setCombine_RGB(osg::TexEnvCombine::ADD); + texEnv->setSource0_RGB(osg::TexEnvCombine::PREVIOUS); + texEnv->setSource1_RGB(osg::TexEnvCombine::TEXTURE); int texUnit = 3; // FIXME diff --git a/docs/source/reference/modding/settings/shaders.rst b/docs/source/reference/modding/settings/shaders.rst index 8f1040225..eaea53972 100644 --- a/docs/source/reference/modding/settings/shaders.rst +++ b/docs/source/reference/modding/settings/shaders.rst @@ -134,4 +134,4 @@ apply lighting to environment maps Normally environment map reflections aren't affected by lighting, which makes environment-mapped (and thus bump-mapped objects) glow in the dark. Morrowind Code Patch includes an option to remedy that by doing environment-mapping before applying lighting, this is the equivalent of that option. -This also affects fixed function pipeline rendering. +Has no effect in fixed function pipeline. diff --git a/files/settings-default.cfg b/files/settings-default.cfg index b7e0eb6c2..523ffc2f6 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -347,7 +347,7 @@ specular map pattern = _spec terrain specular map pattern = _diffusespec # Apply lighting to environment map reflections on the rendered objects like in Morrowind Code Patch. -# Also affects fixed function pipeline. +# Requires shaders to work. apply lighting to environment maps = false [Input] From 4455c978df4224dd5b5810dda9138a1b12196207 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Wed, 4 Mar 2020 01:00:09 +0300 Subject: [PATCH 56/99] Explain the global shader define default values in editor --- apps/opencs/model/world/data.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index f609e6999..23ea1a5a9 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -79,9 +79,9 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, bool fsStrict, const Files::Pat Shader::ShaderManager::DefineMap defines = mResourceSystem->getSceneManager()->getShaderManager().getGlobalDefines(); Shader::ShaderManager::DefineMap shadowDefines = SceneUtil::ShadowManager::getShadowsDisabledDefines(); - defines["forcePPL"] = "0"; - defines["clamp"] = "1"; - defines["preLightEnv"] = "0"; + defines["forcePPL"] = "0"; // Don't force per-pixel lighting + defines["clamp"] = "1"; // Clamp lighting + defines["preLightEnv"] = "0"; // Apply environment maps after lighting like Morrowind for (const auto& define : shadowDefines) defines[define.first] = define.second; mResourceSystem->getSceneManager()->getShaderManager().setGlobalDefines(defines); From 624a9ac353fb9b24631997b2749527c244ff6258 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Wed, 4 Mar 2020 14:06:22 +0300 Subject: [PATCH 57/99] Force shaders for objects affected by new envmap option --- components/shader/shadervisitor.cpp | 7 +++++++ docs/source/reference/modding/settings/shaders.rst | 2 +- files/settings-default.cfg | 4 ++-- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/components/shader/shadervisitor.cpp b/components/shader/shadervisitor.cpp index 31338da8b..d090c3397 100644 --- a/components/shader/shadervisitor.cpp +++ b/components/shader/shadervisitor.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include "shadermanager.hpp" @@ -138,6 +139,12 @@ namespace Shader // Bump maps are off by default as well writableStateSet->setTextureMode(unit, GL_TEXTURE_2D, osg::StateAttribute::ON); } + else if (texName == "envMap") + { + static const bool preLightEnv = Settings::Manager::getBool("apply lighting to environment maps", "Shaders"); + if (preLightEnv) + mRequirements.back().mShaderRequired = true; + } } else Log(Debug::Error) << "ShaderVisitor encountered unknown texture " << texture; diff --git a/docs/source/reference/modding/settings/shaders.rst b/docs/source/reference/modding/settings/shaders.rst index eaea53972..bc676d22f 100644 --- a/docs/source/reference/modding/settings/shaders.rst +++ b/docs/source/reference/modding/settings/shaders.rst @@ -134,4 +134,4 @@ apply lighting to environment maps Normally environment map reflections aren't affected by lighting, which makes environment-mapped (and thus bump-mapped objects) glow in the dark. Morrowind Code Patch includes an option to remedy that by doing environment-mapping before applying lighting, this is the equivalent of that option. -Has no effect in fixed function pipeline. +Affected objects will use shaders. diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 523ffc2f6..c6f7abfba 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -346,8 +346,8 @@ specular map pattern = _spec # The filename pattern to probe for when detecting terrain specular maps (see 'auto use terrain specular maps') terrain specular map pattern = _diffusespec -# Apply lighting to environment map reflections on the rendered objects like in Morrowind Code Patch. -# Requires shaders to work. +# Apply lighting to reflections on the environment-mapped objects like in Morrowind Code Patch. +# Affected objects use shaders. apply lighting to environment maps = false [Input] From 5770227e378c6eaa8879a85127a19cecbd4b267e Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Thu, 5 Mar 2020 13:15:18 +0300 Subject: [PATCH 58/99] Avoid using auto-detected normal maps that are bump map namesakes --- components/shader/shadervisitor.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/components/shader/shadervisitor.cpp b/components/shader/shadervisitor.cpp index d090c3397..7fb5d53f5 100644 --- a/components/shader/shadervisitor.cpp +++ b/components/shader/shadervisitor.cpp @@ -96,6 +96,7 @@ namespace Shader const osg::Texture* diffuseMap = nullptr; const osg::Texture* normalMap = nullptr; const osg::Texture* specularMap = nullptr; + const osg::Texture* bumpMap = nullptr; for(unsigned int unit=0;unitgetTextureAttribute(unit, osg::StateAttribute::TEXTURE); @@ -133,6 +134,7 @@ namespace Shader specularMap = texture; else if (texName == "bumpMap") { + bumpMap = texture; mRequirements.back().mShaderRequired = true; if (!writableStateSet) writableStateSet = getWritableStateSet(node); @@ -173,8 +175,11 @@ namespace Shader image = mImageManager.getImage(normalMapFileName); } } + // Avoid using the auto-detected normal map if it's already being used as a bump map. + // It's probably not an actual normal map. + bool hasNamesakeBumpMap = image && bumpMap && bumpMap->getImage(0) && image->getFileName() == bumpMap->getImage(0)->getFileName(); - if (image) + if (!hasNamesakeBumpMap && image) { osg::ref_ptr normalMapTex (new osg::Texture2D(image)); normalMapTex->setTextureSize(image->s(), image->t()); From 8e1e4d675785a3645c84b31afaafd4f9813d6b89 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 7 Mar 2020 11:37:02 +0400 Subject: [PATCH 59/99] Do not copy global map texture during savegame loading (bug #5308) --- CHANGELOG.md | 1 + apps/openmw/mwrender/globalmap.cpp | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3bfd756b5..ec280599c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -204,6 +204,7 @@ Bug #5269: Editor: Cell lighting in resaved cleaned content files is corrupted Bug #5278: Console command Show doesn't fall back to global variable after local var not found Bug #5300: NPCs don't switch from torch to shield when starting combat + Bug #5308: World map copying makes save loading much slower Feature #1774: Handle AvoidNode Feature #2229: Improve pathfinding AI Feature #3025: Analogue gamepad movement controls diff --git a/apps/openmw/mwrender/globalmap.cpp b/apps/openmw/mwrender/globalmap.cpp index 4813de398..d51791da6 100644 --- a/apps/openmw/mwrender/globalmap.cpp +++ b/apps/openmw/mwrender/globalmap.cpp @@ -457,7 +457,7 @@ namespace MWRender if (map.mImageData.empty()) return; - Files::IMemStream istream(&map.mImageData[0], map.mImageData.size()); + Files::IMemStream istream(map.mImageData.data(), map.mImageData.size()); osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension("png"); if (!readerwriter) @@ -522,7 +522,7 @@ namespace MWRender if (srcBox == destBox && imageWidth == mWidth && imageHeight == mHeight) { - mOverlayImage->copySubImage(0, 0, 0, image); + mOverlayImage = image; requestOverlayTextureUpdate(0, 0, mWidth, mHeight, texture, true, false); } From 9f27a0d095784955f536fdc399780a3d258d895a Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 10 Mar 2020 10:28:23 +0400 Subject: [PATCH 60/99] Keep an AiWanderStorage when cloning an actor (bug #5267) --- apps/openmw/mwmechanics/aisequence.cpp | 4 ++++ apps/openmw/mwmechanics/aistate.hpp | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 5760069e7..5f3931fcf 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -28,6 +28,10 @@ void AiSequence::copy (const AiSequence& sequence) for (std::list::const_iterator iter (sequence.mPackages.begin()); iter!=sequence.mPackages.end(); ++iter) mPackages.push_back ((*iter)->clone()); + + // We need to keep an AiWander storage, if present - it has a state machine. + // Not sure about another temporary storages + sequence.mAiState.copy(mAiState); } AiSequence::AiSequence() : mDone (false), mRepeat(false), mLastAiPackage(-1) {} diff --git a/apps/openmw/mwmechanics/aistate.hpp b/apps/openmw/mwmechanics/aistate.hpp index 949fb74dd..976e21c65 100644 --- a/apps/openmw/mwmechanics/aistate.hpp +++ b/apps/openmw/mwmechanics/aistate.hpp @@ -38,6 +38,14 @@ namespace MWMechanics //return a reference to the (new allocated) object return *result; } + + template< class Derived > + void copy(DerivedClassStorage& destination) const + { + Derived* result = dynamic_cast(mStorage); + if (result != nullptr) + destination.store(*result); + } template< class Derived > void store( const Derived& payload ) From aef6cd7006d1cf737b6c1ed0b66944de8b4b3caa Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Tue, 10 Mar 2020 22:44:50 +0300 Subject: [PATCH 61/99] Fix handling of empty strings in NIF string tables --- apps/openmw_test_suite/nifloader/testbulletnifloader.cpp | 2 +- components/nif/niffile.hpp | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp b/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp index 3044bf270..36f251246 100644 --- a/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp +++ b/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp @@ -266,7 +266,7 @@ namespace MOCK_CONST_METHOD0(numRecords, std::size_t ()); MOCK_CONST_METHOD1(getRoot, Nif::Record* (std::size_t)); MOCK_CONST_METHOD0(numRoots, std::size_t ()); - MOCK_CONST_METHOD1(getString, std::string (std::size_t)); + MOCK_CONST_METHOD1(getString, std::string (uint32_t)); MOCK_METHOD1(setUseSkinning, void (bool)); MOCK_CONST_METHOD0(getUseSkinning, bool ()); MOCK_CONST_METHOD0(getFilename, std::string ()); diff --git a/components/nif/niffile.hpp b/components/nif/niffile.hpp index e63a7ce73..4d5620a37 100644 --- a/components/nif/niffile.hpp +++ b/components/nif/niffile.hpp @@ -26,7 +26,7 @@ struct File virtual size_t numRoots() const = 0; - virtual std::string getString(size_t index) const = 0; + virtual std::string getString(uint32_t index) const = 0; virtual void setUseSkinning(bool skinning) = 0; @@ -129,8 +129,10 @@ public: size_t numRoots() const override { return roots.size(); } /// Get a given string from the file's string table - std::string getString(size_t index) const override + std::string getString(uint32_t index) const override { + if (index == std::numeric_limits::max()) + return std::string(); return strings.at(index); } From d3244dc7772d11041314554e5862c0dfb6cfb265 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Tue, 10 Mar 2020 22:50:12 +0300 Subject: [PATCH 62/99] Don't save space in compressed BSA hash generation --- components/bsa/compressedbsafile.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/components/bsa/compressedbsafile.cpp b/components/bsa/compressedbsafile.cpp index ba96ee8de..d0bebe3c1 100644 --- a/components/bsa/compressedbsafile.cpp +++ b/components/bsa/compressedbsafile.cpp @@ -422,7 +422,8 @@ void CompressedBSAFile::convertCompressedSizesToUncompressed() std::uint64_t CompressedBSAFile::generateHash(std::string stem, std::string extension) const { size_t len = stem.length(); - if (len == 0) return 0; + if (len == 0) + return 0; std::uint64_t hash = 0; unsigned int hash2 = 0; Misc::StringUtils::lowerCaseInPlace(stem); @@ -434,12 +435,19 @@ std::uint64_t CompressedBSAFile::generateHash(std::string stem, std::string exte for (const char &c : extension) hash = hash * 0x1003f + c; } - for (size_t i = 1; i < len-2 && len > 3; i++) - hash2 = hash2 * 0x1003f + stem[i]; + if (len >= 4) + { + for (size_t i = 1; i < len-2; i++) + hash2 = hash2 * 0x1003f + stem[i]; + } hash = (hash + hash2) << 32; hash2 = (stem[0] << 24) | (len << 16); - if (len >= 3) hash2 |= stem[len-2] << 8; - if (len >= 2) hash2 |= stem[len-1]; + if (len >= 2) + { + if (len >= 3) + hash2 |= stem[len-2] << 8; + hash2 |= stem[len-1]; + } if (!extension.empty()) { if (extension == ".kf") hash2 |= 0x80; From c1f1e367fa11ebc3011a3d136cf58e211a10f9a1 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Wed, 11 Mar 2020 20:49:53 +0300 Subject: [PATCH 63/99] Accept an extra numerical argument in GetItemCount --- components/compiler/extensions0.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index 5bec1bca1..cfa8c4667 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -116,7 +116,7 @@ namespace Compiler void registerExtensions (Extensions& extensions) { extensions.registerInstruction ("additem", "clX", opcodeAddItem, opcodeAddItemExplicit); - extensions.registerFunction ("getitemcount", 'l', "c", opcodeGetItemCount, + extensions.registerFunction ("getitemcount", 'l', "cX", opcodeGetItemCount, opcodeGetItemCountExplicit); extensions.registerInstruction ("removeitem", "clX", opcodeRemoveItem, opcodeRemoveItemExplicit); From 7ae7cb181d5623235f1f7edb483ac5c58d6934a3 Mon Sep 17 00:00:00 2001 From: elsid Date: Wed, 27 Nov 2019 23:45:01 +0100 Subject: [PATCH 64/99] Support recast mesh rendering --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwrender/recastmesh.cpp | 92 +++++++++++++++++++ apps/openmw/mwrender/recastmesh.hpp | 53 +++++++++++ apps/openmw/mwrender/renderingmanager.cpp | 15 +++ apps/openmw/mwrender/renderingmanager.hpp | 4 + apps/openmw/mwrender/rendermode.hpp | 1 + apps/openmw/mwscript/docs/vmformat.txt | 3 +- apps/openmw/mwscript/miscextensions.cpp | 15 +++ .../detournavigator/navmeshtilescache.cpp | 42 +++++---- .../detournavigator/recastmeshbuilder.cpp | 32 ++++--- components/CMakeLists.txt | 2 +- components/compiler/extensions0.cpp | 1 + components/compiler/opcodes.hpp | 1 + .../cachedrecastmeshmanager.cpp | 5 +- .../cachedrecastmeshmanager.hpp | 2 +- components/detournavigator/navigator.hpp | 4 + components/detournavigator/navigatorimpl.cpp | 5 + components/detournavigator/navigatorimpl.hpp | 2 + components/detournavigator/navigatorstub.hpp | 5 + components/detournavigator/navmeshmanager.cpp | 11 +++ components/detournavigator/navmeshmanager.hpp | 3 + components/detournavigator/recastmesh.cpp | 8 +- components/detournavigator/recastmesh.hpp | 16 +++- .../detournavigator/recastmeshbuilder.cpp | 5 +- .../detournavigator/recastmeshbuilder.hpp | 2 +- .../detournavigator/recastmeshmanager.cpp | 24 ++--- .../detournavigator/recastmeshmanager.hpp | 6 +- .../detournavigator/recastmeshtiles.hpp | 15 +++ .../tilecachedrecastmeshmanager.cpp | 11 ++- .../tilecachedrecastmeshmanager.hpp | 1 + components/sceneutil/detourdebugdraw.cpp | 9 +- components/sceneutil/recastmesh.cpp | 48 ++++++++++ components/sceneutil/recastmesh.hpp | 23 +++++ .../reference/modding/settings/navigator.rst | 14 +++ files/settings-default.cfg | 3 + 35 files changed, 418 insertions(+), 67 deletions(-) create mode 100644 apps/openmw/mwrender/recastmesh.cpp create mode 100644 apps/openmw/mwrender/recastmesh.hpp create mode 100644 components/detournavigator/recastmeshtiles.hpp create mode 100644 components/sceneutil/recastmesh.cpp create mode 100644 components/sceneutil/recastmesh.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 14c271b66..2bc85e062 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -21,7 +21,7 @@ add_openmw_dir (mwrender actors objects renderingmanager animation rotatecontroller sky npcanimation creatureanimation effectmanager util renderinginterface pathgrid rendermode weaponanimation bulletdebugdraw globalmap characterpreview camera localmap water terrainstorage ripplesimulation - renderbin actoranimation landmanager navmesh actorspaths + renderbin actoranimation landmanager navmesh actorspaths recastmesh ) add_openmw_dir (mwinput diff --git a/apps/openmw/mwrender/recastmesh.cpp b/apps/openmw/mwrender/recastmesh.cpp new file mode 100644 index 000000000..5aec174df --- /dev/null +++ b/apps/openmw/mwrender/recastmesh.cpp @@ -0,0 +1,92 @@ +#include "recastmesh.hpp" + +#include +#include +#include + +#include + +namespace MWRender +{ + RecastMesh::RecastMesh(const osg::ref_ptr& root, bool enabled) + : mRootNode(root) + , mEnabled(enabled) + { + } + + RecastMesh::~RecastMesh() + { + if (mEnabled) + disable(); + } + + bool RecastMesh::toggle() + { + if (mEnabled) + disable(); + else + enable(); + + return mEnabled; + } + + void RecastMesh::update(const DetourNavigator::RecastMeshTiles& tiles, const DetourNavigator::Settings& settings) + { + if (!mEnabled) + return; + + for (auto it = mGroups.begin(); it != mGroups.end();) + { + const auto tile = tiles.find(it->first); + if (tile == tiles.end()) + { + mRootNode->removeChild(it->second.mValue); + it = mGroups.erase(it); + continue; + } + + if (it->second.mGeneration != tile->second->getGeneration() + || it->second.mRevision != tile->second->getRevision()) + { + const auto group = SceneUtil::createRecastMeshGroup(*tile->second, settings); + group->setNodeMask(SceneUtil::Mask_Debug); + mRootNode->removeChild(it->second.mValue); + mRootNode->addChild(group); + it->second.mValue = group; + it->second.mGeneration = tile->second->getGeneration(); + it->second.mRevision = tile->second->getRevision(); + continue; + } + + ++it; + } + + for (const auto& tile : tiles) + { + if (mGroups.count(tile.first)) + continue; + const auto group = SceneUtil::createRecastMeshGroup(*tile.second, settings); + group->setNodeMask(SceneUtil::Mask_Debug); + mGroups.emplace(tile.first, Group {tile.second->getGeneration(), tile.second->getRevision(), group}); + mRootNode->addChild(group); + } + } + + void RecastMesh::reset() + { + std::for_each(mGroups.begin(), mGroups.end(), [&] (const auto& v) { mRootNode->removeChild(v.second.mValue); }); + mGroups.clear(); + } + + void RecastMesh::enable() + { + std::for_each(mGroups.begin(), mGroups.end(), [&] (const auto& v) { mRootNode->addChild(v.second.mValue); }); + mEnabled = true; + } + + void RecastMesh::disable() + { + std::for_each(mGroups.begin(), mGroups.end(), [&] (const auto& v) { mRootNode->removeChild(v.second.mValue); }); + mEnabled = false; + } +} diff --git a/apps/openmw/mwrender/recastmesh.hpp b/apps/openmw/mwrender/recastmesh.hpp new file mode 100644 index 000000000..729438dbe --- /dev/null +++ b/apps/openmw/mwrender/recastmesh.hpp @@ -0,0 +1,53 @@ +#ifndef OPENMW_MWRENDER_RECASTMESH_H +#define OPENMW_MWRENDER_RECASTMESH_H + +#include + +#include + +#include + +namespace osg +{ + class Group; + class Geometry; +} + +namespace MWRender +{ + class RecastMesh + { + public: + RecastMesh(const osg::ref_ptr& root, bool enabled); + ~RecastMesh(); + + bool toggle(); + + void update(const DetourNavigator::RecastMeshTiles& recastMeshTiles, const DetourNavigator::Settings& settings); + + void reset(); + + void enable(); + + void disable(); + + bool isEnabled() const + { + return mEnabled; + } + + private: + struct Group + { + std::size_t mGeneration; + std::size_t mRevision; + osg::ref_ptr mValue; + }; + + osg::ref_ptr mRootNode; + bool mEnabled; + std::map mGroups; + }; +} + +#endif diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index dd570f558..4d8db6cc4 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -66,6 +66,7 @@ #include "util.hpp" #include "navmesh.hpp" #include "actorspaths.hpp" +#include "recastmesh.hpp" namespace { @@ -260,6 +261,7 @@ namespace MWRender mNavMesh.reset(new NavMesh(mRootNode, Settings::Manager::getBool("enable nav mesh render", "Navigator"))); mActorsPaths.reset(new ActorsPaths(mRootNode, Settings::Manager::getBool("enable agents paths render", "Navigator"))); + mRecastMesh.reset(new RecastMesh(mRootNode, Settings::Manager::getBool("enable recast mesh render", "Navigator"))); mPathgrid.reset(new Pathgrid(mRootNode)); mObjects.reset(new Objects(mResourceSystem, sceneRoot, mUnrefQueue.get())); @@ -585,6 +587,10 @@ namespace MWRender { return mActorsPaths->toggle(); } + else if (mode == Render_RecastMesh) + { + return mRecastMesh->toggle(); + } return false; } @@ -651,6 +657,7 @@ namespace MWRender } updateNavMesh(); + updateRecastMesh(); mCamera->update(dt, paused); @@ -1463,4 +1470,12 @@ namespace MWRender } } } + + void RenderingManager::updateRecastMesh() + { + if (!mRecastMesh->isEnabled()) + return; + + mRecastMesh->update(mNavigator.getRecastMeshTiles(), mNavigator.getSettings()); + } } diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 22d2cf28c..d4b0b1840 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -82,6 +82,7 @@ namespace MWRender class LandManager; class NavMesh; class ActorsPaths; + class RecastMesh; class RenderingManager : public MWRender::RenderingInterface { @@ -246,6 +247,8 @@ namespace MWRender void updateNavMesh(); + void updateRecastMesh(); + osg::ref_ptr getIntersectionVisitor(osgUtil::Intersector* intersector, bool ignorePlayer, bool ignoreActors); osg::ref_ptr mIntersectionVisitor; @@ -264,6 +267,7 @@ namespace MWRender std::unique_ptr mNavMesh; std::size_t mNavMeshNumber = 0; std::unique_ptr mActorsPaths; + std::unique_ptr mRecastMesh; std::unique_ptr mPathgrid; std::unique_ptr mObjects; std::unique_ptr mWater; diff --git a/apps/openmw/mwrender/rendermode.hpp b/apps/openmw/mwrender/rendermode.hpp index 077710f4f..3ba2f9596 100644 --- a/apps/openmw/mwrender/rendermode.hpp +++ b/apps/openmw/mwrender/rendermode.hpp @@ -13,6 +13,7 @@ namespace MWRender Render_Scene, Render_NavMesh, Render_ActorsPaths, + Render_RecastMesh, }; } diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 6795a058f..5a333a5b7 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -463,5 +463,6 @@ op 0x200030c: RepairedOnMe op 0x200030d: RepairedOnMe, explicit op 0x200030e: TestCells op 0x200030f: TestInteriorCells +op 0x2000310: ToggleRecastMesh -opcodes 0x2000310-0x3ffffff unused +opcodes 0x2000311-0x3ffffff unused diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 7d779e62f..b7b91cf5c 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -1440,6 +1440,20 @@ namespace MWScript } }; + class OpToggleRecastMesh : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + bool enabled = + MWBase::Environment::get().getWorld()->toggleRenderMode (MWRender::Render_RecastMesh); + + runtime.getContext().report (enabled ? + "Recast Mesh Rendering -> On" : "Recast Mesh Rendering -> Off"); + } + }; + void installOpcodes (Interpreter::Interpreter& interpreter) { interpreter.installSegment5 (Compiler::Misc::opcodeXBox, new OpXBox); @@ -1545,6 +1559,7 @@ namespace MWScript interpreter.installSegment5 (Compiler::Misc::opcodeSetNavMeshNumberToRender, new OpSetNavMeshNumberToRender); interpreter.installSegment5 (Compiler::Misc::opcodeRepairedOnMe, new OpRepairedOnMe); interpreter.installSegment5 (Compiler::Misc::opcodeRepairedOnMeExplicit, new OpRepairedOnMe); + interpreter.installSegment5 (Compiler::Misc::opcodeToggleRecastMesh, new OpToggleRecastMesh); } } } diff --git a/apps/openmw_test_suite/detournavigator/navmeshtilescache.cpp b/apps/openmw_test_suite/detournavigator/navmeshtilescache.cpp index a3a1816ad..e8e7820d9 100644 --- a/apps/openmw_test_suite/detournavigator/navmeshtilescache.cpp +++ b/apps/openmw_test_suite/detournavigator/navmeshtilescache.cpp @@ -25,12 +25,15 @@ namespace { const osg::Vec3f mAgentHalfExtents {1, 2, 3}; const TilePosition mTilePosition {0, 0}; + const std::size_t mGeneration = 0; + const std::size_t mRevision = 0; const std::vector mIndices {{0, 1, 2}}; const std::vector mVertices {{0, 0, 0, 1, 0, 0, 1, 1, 0}}; const std::vector mAreaTypes {1, AreaType_ground}; const std::vector mWater {}; const std::size_t mTrianglesPerChunk {1}; - const RecastMesh mRecastMesh {mIndices, mVertices, mAreaTypes, mWater, mTrianglesPerChunk}; + const RecastMesh mRecastMesh {mGeneration, mRevision, mIndices, mVertices, + mAreaTypes, mWater, mTrianglesPerChunk}; const std::vector mOffMeshConnections {}; unsigned char* const mData = reinterpret_cast(dtAlloc(1, DT_ALLOC_PERM)); NavMeshData mNavMeshData {mData, 1}; @@ -128,7 +131,8 @@ namespace const std::size_t maxSize = 1; NavMeshTilesCache cache(maxSize); const std::vector water {1, RecastMesh::Water {1, btTransform::getIdentity()}}; - const RecastMesh unexistentRecastMesh {mIndices, mVertices, mAreaTypes, water, mTrianglesPerChunk}; + const RecastMesh unexistentRecastMesh {mGeneration, mRevision, mIndices, mVertices, + mAreaTypes, water, mTrianglesPerChunk}; cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, std::move(mNavMeshData)); EXPECT_FALSE(cache.get(mAgentHalfExtents, mTilePosition, unexistentRecastMesh, mOffMeshConnections)); @@ -142,7 +146,8 @@ namespace NavMeshTilesCache cache(maxSize); const std::vector water {1, RecastMesh::Water {1, btTransform::getIdentity()}}; - const RecastMesh anotherRecastMesh {mIndices, mVertices, mAreaTypes, water, mTrianglesPerChunk}; + const RecastMesh anotherRecastMesh {mGeneration, mRevision, mIndices, mVertices, + mAreaTypes, water, mTrianglesPerChunk}; const auto anotherData = reinterpret_cast(dtAlloc(1, DT_ALLOC_PERM)); NavMeshData anotherNavMeshData {anotherData, 1}; @@ -162,7 +167,8 @@ namespace NavMeshTilesCache cache(maxSize); const std::vector water {1, RecastMesh::Water {1, btTransform::getIdentity()}}; - const RecastMesh anotherRecastMesh {mIndices, mVertices, mAreaTypes, water, mTrianglesPerChunk}; + const RecastMesh anotherRecastMesh {mGeneration, mRevision, mIndices, mVertices, + mAreaTypes, water, mTrianglesPerChunk}; const auto anotherData = reinterpret_cast(dtAlloc(1, DT_ALLOC_PERM)); NavMeshData anotherNavMeshData {anotherData, 1}; @@ -180,14 +186,14 @@ namespace NavMeshTilesCache cache(maxSize); const std::vector leastRecentlySetWater {1, RecastMesh::Water {1, btTransform::getIdentity()}}; - const RecastMesh leastRecentlySetRecastMesh {mIndices, mVertices, mAreaTypes, leastRecentlySetWater, - mTrianglesPerChunk}; + const RecastMesh leastRecentlySetRecastMesh {mGeneration, mRevision, mIndices, mVertices, + mAreaTypes, leastRecentlySetWater, mTrianglesPerChunk}; const auto leastRecentlySetData = reinterpret_cast(dtAlloc(1, DT_ALLOC_PERM)); NavMeshData leastRecentlySetNavMeshData {leastRecentlySetData, 1}; const std::vector mostRecentlySetWater {1, RecastMesh::Water {2, btTransform::getIdentity()}}; - const RecastMesh mostRecentlySetRecastMesh {mIndices, mVertices, mAreaTypes, mostRecentlySetWater, - mTrianglesPerChunk}; + const RecastMesh mostRecentlySetRecastMesh {mGeneration, mRevision, mIndices, mVertices, + mAreaTypes, mostRecentlySetWater, mTrianglesPerChunk}; const auto mostRecentlySetData = reinterpret_cast(dtAlloc(1, DT_ALLOC_PERM)); NavMeshData mostRecentlySetNavMeshData {mostRecentlySetData, 1}; @@ -212,14 +218,14 @@ namespace NavMeshTilesCache cache(maxSize); const std::vector leastRecentlyUsedWater {1, RecastMesh::Water {1, btTransform::getIdentity()}}; - const RecastMesh leastRecentlyUsedRecastMesh {mIndices, mVertices, mAreaTypes, leastRecentlyUsedWater, - mTrianglesPerChunk}; + const RecastMesh leastRecentlyUsedRecastMesh {mGeneration, mRevision, mIndices, mVertices, + mAreaTypes, leastRecentlyUsedWater, mTrianglesPerChunk}; const auto leastRecentlyUsedData = reinterpret_cast(dtAlloc(1, DT_ALLOC_PERM)); NavMeshData leastRecentlyUsedNavMeshData {leastRecentlyUsedData, 1}; const std::vector mostRecentlyUsedWater {1, RecastMesh::Water {2, btTransform::getIdentity()}}; - const RecastMesh mostRecentlyUsedRecastMesh {mIndices, mVertices, mAreaTypes, mostRecentlyUsedWater, - mTrianglesPerChunk}; + const RecastMesh mostRecentlyUsedRecastMesh {mGeneration, mRevision, mIndices, mVertices, + mAreaTypes, mostRecentlyUsedWater, mTrianglesPerChunk}; const auto mostRecentlyUsedData = reinterpret_cast(dtAlloc(1, DT_ALLOC_PERM)); NavMeshData mostRecentlyUsedNavMeshData {mostRecentlyUsedData, 1}; @@ -256,7 +262,7 @@ namespace NavMeshTilesCache cache(maxSize); const std::vector water {1, RecastMesh::Water {1, btTransform::getIdentity()}}; - const RecastMesh tooLargeRecastMesh {mIndices, mVertices, mAreaTypes, water, mTrianglesPerChunk}; + const RecastMesh tooLargeRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, water, mTrianglesPerChunk}; const auto tooLargeData = reinterpret_cast(dtAlloc(2, DT_ALLOC_PERM)); NavMeshData tooLargeNavMeshData {tooLargeData, 2}; @@ -275,12 +281,13 @@ namespace NavMeshTilesCache cache(maxSize); const std::vector anotherWater {1, RecastMesh::Water {1, btTransform::getIdentity()}}; - const RecastMesh anotherRecastMesh {mIndices, mVertices, mAreaTypes, anotherWater, mTrianglesPerChunk}; + const RecastMesh anotherRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, anotherWater, mTrianglesPerChunk}; const auto anotherData = reinterpret_cast(dtAlloc(1, DT_ALLOC_PERM)); NavMeshData anotherNavMeshData {anotherData, 1}; const std::vector tooLargeWater {1, RecastMesh::Water {2, btTransform::getIdentity()}}; - const RecastMesh tooLargeRecastMesh {mIndices, mVertices, mAreaTypes, tooLargeWater, mTrianglesPerChunk}; + const RecastMesh tooLargeRecastMesh {mGeneration, mRevision, mIndices, mVertices, + mAreaTypes, tooLargeWater, mTrianglesPerChunk}; const auto tooLargeData = reinterpret_cast(dtAlloc(2, DT_ALLOC_PERM)); NavMeshData tooLargeNavMeshData {tooLargeData, 2}; @@ -303,7 +310,8 @@ namespace NavMeshTilesCache cache(maxSize); const std::vector water {1, RecastMesh::Water {1, btTransform::getIdentity()}}; - const RecastMesh anotherRecastMesh {mIndices, mVertices, mAreaTypes, water, mTrianglesPerChunk}; + const RecastMesh anotherRecastMesh {mGeneration, mRevision, mIndices, mVertices, + mAreaTypes, water, mTrianglesPerChunk}; const auto anotherData = reinterpret_cast(dtAlloc(1, DT_ALLOC_PERM)); NavMeshData anotherNavMeshData {anotherData, 1}; @@ -326,7 +334,7 @@ namespace NavMeshTilesCache cache(maxSize); const std::vector water {1, RecastMesh::Water {1, btTransform::getIdentity()}}; - const RecastMesh anotherRecastMesh {mIndices, mVertices, mAreaTypes, water, mTrianglesPerChunk}; + const RecastMesh anotherRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, water, mTrianglesPerChunk}; const auto anotherData = reinterpret_cast(dtAlloc(1, DT_ALLOC_PERM)); NavMeshData anotherNavMeshData {anotherData, 1}; diff --git a/apps/openmw_test_suite/detournavigator/recastmeshbuilder.cpp b/apps/openmw_test_suite/detournavigator/recastmeshbuilder.cpp index 60b754915..af6797cf0 100644 --- a/apps/openmw_test_suite/detournavigator/recastmeshbuilder.cpp +++ b/apps/openmw_test_suite/detournavigator/recastmeshbuilder.cpp @@ -30,6 +30,8 @@ namespace { Settings mSettings; TileBounds mBounds; + const std::size_t mGeneration = 0; + const std::size_t mRevision = 0; DetourNavigatorRecastMeshBuilderTest() { @@ -45,7 +47,7 @@ namespace TEST_F(DetourNavigatorRecastMeshBuilderTest, create_for_empty_should_return_empty) { RecastMeshBuilder builder(mSettings, mBounds); - const auto recastMesh = builder.create(); + const auto recastMesh = builder.create(mGeneration, mRevision); EXPECT_EQ(recastMesh->getVertices(), std::vector()); EXPECT_EQ(recastMesh->getIndices(), std::vector()); EXPECT_EQ(recastMesh->getAreaTypes(), std::vector()); @@ -59,7 +61,7 @@ namespace RecastMeshBuilder builder(mSettings, mBounds); builder.addObject(static_cast(shape), btTransform::getIdentity(), AreaType_ground); - const auto recastMesh = builder.create(); + const auto recastMesh = builder.create(mGeneration, mRevision); EXPECT_EQ(recastMesh->getVertices(), std::vector({ 1, 0, -1, -1, 0, 1, @@ -80,7 +82,7 @@ namespace btTransform(btMatrix3x3::getIdentity().scaled(btVector3(1, 2, 3)), btVector3(1, 2, 3)), AreaType_ground ); - const auto recastMesh = builder.create(); + const auto recastMesh = builder.create(mGeneration, mRevision); EXPECT_EQ(recastMesh->getVertices(), std::vector({ 2, 3, 0, 0, 3, 4, @@ -96,7 +98,7 @@ namespace btHeightfieldTerrainShape shape(2, 2, heightfieldData.data(), 1, 0, 0, 2, PHY_FLOAT, false); RecastMeshBuilder builder(mSettings, mBounds); builder.addObject(static_cast(shape), btTransform::getIdentity(), AreaType_ground); - const auto recastMesh = builder.create(); + const auto recastMesh = builder.create(mGeneration, mRevision); EXPECT_EQ(recastMesh->getVertices(), std::vector({ -0.5, 0, -0.5, -0.5, 0, 0.5, @@ -114,7 +116,7 @@ namespace btBoxShape shape(btVector3(1, 1, 2)); RecastMeshBuilder builder(mSettings, mBounds); builder.addObject(static_cast(shape), btTransform::getIdentity(), AreaType_ground); - const auto recastMesh = builder.create(); + const auto recastMesh = builder.create(mGeneration, mRevision); EXPECT_EQ(recastMesh->getVertices(), std::vector({ 1, 2, 1, -1, 2, 1, @@ -161,7 +163,7 @@ namespace btTransform::getIdentity(), AreaType_ground ); - const auto recastMesh = builder.create(); + const auto recastMesh = builder.create(mGeneration, mRevision); EXPECT_EQ(recastMesh->getVertices(), std::vector({ 1, 0, -1, -1, 0, 1, @@ -210,7 +212,7 @@ namespace btTransform(btMatrix3x3::getIdentity().scaled(btVector3(1, 2, 3)), btVector3(1, 2, 3)), AreaType_ground ); - const auto recastMesh = builder.create(); + const auto recastMesh = builder.create(mGeneration, mRevision); EXPECT_EQ(recastMesh->getVertices(), std::vector({ 2, 3, 0, 0, 3, 4, @@ -234,7 +236,7 @@ namespace btTransform(btMatrix3x3::getIdentity().scaled(btVector3(1, 2, 3)), btVector3(1, 2, 3)), AreaType_ground ); - const auto recastMesh = builder.create(); + const auto recastMesh = builder.create(mGeneration, mRevision); EXPECT_EQ(recastMesh->getVertices(), std::vector({ 3, 12, 2, 1, 12, 10, @@ -256,7 +258,7 @@ namespace btTransform::getIdentity(), AreaType_ground ); - const auto recastMesh = builder.create(); + const auto recastMesh = builder.create(mGeneration, mRevision); EXPECT_EQ(recastMesh->getVertices(), std::vector({ 1, 0, -1, -1, 0, 1, @@ -284,7 +286,7 @@ namespace btTransform::getIdentity(), AreaType_ground ); - const auto recastMesh = builder.create(); + const auto recastMesh = builder.create(mGeneration, mRevision); EXPECT_EQ(recastMesh->getVertices(), std::vector({ -0.2f, 0, -0.3f, -0.3f, 0, -0.2f, @@ -309,7 +311,7 @@ namespace static_cast(-osg::PI_4))), AreaType_ground ); - const auto recastMesh = builder.create(); + const auto recastMesh = builder.create(mGeneration, mRevision); EXPECT_EQ(recastMesh->getVertices(), std::vector({ 0, -0.70710659027099609375, -3.535533905029296875, 0, 0.707107067108154296875, -3.535533905029296875, @@ -334,7 +336,7 @@ namespace static_cast(osg::PI_4))), AreaType_ground ); - const auto recastMesh = builder.create(); + const auto recastMesh = builder.create(mGeneration, mRevision); EXPECT_EQ(recastMesh->getVertices(), std::vector({ -3.535533905029296875, -0.70710659027099609375, 0, -3.535533905029296875, 0.707107067108154296875, 0, @@ -359,7 +361,7 @@ namespace static_cast(osg::PI_4))), AreaType_ground ); - const auto recastMesh = builder.create(); + const auto recastMesh = builder.create(mGeneration, mRevision); EXPECT_EQ(recastMesh->getVertices(), std::vector({ 1.41421353816986083984375, 0, 1.1920928955078125e-07, -1.41421353816986083984375, 0, -1.1920928955078125e-07, @@ -388,7 +390,7 @@ namespace btTransform::getIdentity(), AreaType_null ); - const auto recastMesh = builder.create(); + const auto recastMesh = builder.create(mGeneration, mRevision); EXPECT_EQ(recastMesh->getVertices(), std::vector({ 1, 0, -1, -1, 0, 1, @@ -405,7 +407,7 @@ namespace { RecastMeshBuilder builder(mSettings, mBounds); builder.addWater(1000, btTransform(btMatrix3x3::getIdentity(), btVector3(100, 200, 300))); - const auto recastMesh = builder.create(); + const auto recastMesh = builder.create(mGeneration, mRevision); EXPECT_EQ(recastMesh->getWater(), std::vector({ RecastMesh::Water {1000, btTransform(btMatrix3x3::getIdentity(), btVector3(100, 200, 300))} })); diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 2f6266dff..8d644c6de 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -51,7 +51,7 @@ add_component_dir (shader add_component_dir (sceneutil clone attach visitor util statesetupdater controller skeleton riggeometry morphgeometry lightcontroller lightmanager lightutil positionattitudetransform workqueue unrefqueue pathgridutil waterutil writescene serialize optimizer - actorutil detourdebugdraw navmesh agentpath shadow mwshadowtechnique vismask + actorutil detourdebugdraw navmesh agentpath shadow mwshadowtechnique vismask recastmesh ) add_component_dir (nif diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index cfa8c4667..24578b238 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -327,6 +327,7 @@ namespace Compiler extensions.registerInstruction ("toggleactorspaths", "", opcodeToggleActorsPaths); extensions.registerInstruction ("setnavmeshnumber", "l", opcodeSetNavMeshNumberToRender); extensions.registerFunction ("repairedonme", 'l', "S", opcodeRepairedOnMe, opcodeRepairedOnMeExplicit); + extensions.registerInstruction ("togglerecastmesh", "", opcodeToggleRecastMesh); } } diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index 5fa8cc170..d3446e688 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -304,6 +304,7 @@ namespace Compiler const int opcodeSetNavMeshNumberToRender = 0x200030a; const int opcodeRepairedOnMe = 0x200030c; const int opcodeRepairedOnMeExplicit = 0x200030d; + const int opcodeToggleRecastMesh = 0x2000310; } namespace Sky diff --git a/components/detournavigator/cachedrecastmeshmanager.cpp b/components/detournavigator/cachedrecastmeshmanager.cpp index 5e145486b..da0f6c874 100644 --- a/components/detournavigator/cachedrecastmeshmanager.cpp +++ b/components/detournavigator/cachedrecastmeshmanager.cpp @@ -3,8 +3,9 @@ namespace DetourNavigator { - CachedRecastMeshManager::CachedRecastMeshManager(const Settings& settings, const TileBounds& bounds) - : mImpl(settings, bounds) + CachedRecastMeshManager::CachedRecastMeshManager(const Settings& settings, const TileBounds& bounds, + std::size_t generation) + : mImpl(settings, bounds, generation) {} bool CachedRecastMeshManager::addObject(const ObjectId id, const btCollisionShape& shape, diff --git a/components/detournavigator/cachedrecastmeshmanager.hpp b/components/detournavigator/cachedrecastmeshmanager.hpp index 528e8dabc..5efb315e8 100644 --- a/components/detournavigator/cachedrecastmeshmanager.hpp +++ b/components/detournavigator/cachedrecastmeshmanager.hpp @@ -10,7 +10,7 @@ namespace DetourNavigator class CachedRecastMeshManager { public: - CachedRecastMeshManager(const Settings& settings, const TileBounds& bounds); + CachedRecastMeshManager(const Settings& settings, const TileBounds& bounds, std::size_t generation); bool addObject(const ObjectId id, const btCollisionShape& shape, const btTransform& transform, const AreaType areaType); diff --git a/components/detournavigator/navigator.hpp b/components/detournavigator/navigator.hpp index 2265b1d39..99f1e258d 100644 --- a/components/detournavigator/navigator.hpp +++ b/components/detournavigator/navigator.hpp @@ -6,6 +6,8 @@ #include "settings.hpp" #include "objectid.hpp" #include "navmeshcacheitem.hpp" +#include "recastmesh.hpp" +#include "recastmeshtiles.hpp" namespace DetourNavigator { @@ -204,6 +206,8 @@ namespace DetourNavigator */ boost::optional findRandomPointAroundCircle(const osg::Vec3f& agentHalfExtents, const osg::Vec3f& start, const float maxRadius, const Flags includeFlags) const; + + virtual RecastMeshTiles getRecastMeshTiles() = 0; }; } diff --git a/components/detournavigator/navigatorimpl.cpp b/components/detournavigator/navigatorimpl.cpp index b743f26b2..3ecfd8b51 100644 --- a/components/detournavigator/navigatorimpl.cpp +++ b/components/detournavigator/navigatorimpl.cpp @@ -143,6 +143,11 @@ namespace DetourNavigator mNavMeshManager.reportStats(frameNumber, stats); } + RecastMeshTiles NavigatorImpl::getRecastMeshTiles() + { + return mNavMeshManager.getRecastMeshTiles(); + } + void NavigatorImpl::updateAvoidShapeId(const ObjectId id, const ObjectId avoidId) { updateId(id, avoidId, mWaterIds); diff --git a/components/detournavigator/navigatorimpl.hpp b/components/detournavigator/navigatorimpl.hpp index b6b3b1b7f..be291f501 100644 --- a/components/detournavigator/navigatorimpl.hpp +++ b/components/detournavigator/navigatorimpl.hpp @@ -50,6 +50,8 @@ namespace DetourNavigator void reportStats(unsigned int frameNumber, osg::Stats& stats) const override; + RecastMeshTiles getRecastMeshTiles() override; + private: Settings mSettings; NavMeshManager mNavMeshManager; diff --git a/components/detournavigator/navigatorstub.hpp b/components/detournavigator/navigatorstub.hpp index 885482a45..ee23e67be 100644 --- a/components/detournavigator/navigatorstub.hpp +++ b/components/detournavigator/navigatorstub.hpp @@ -81,6 +81,11 @@ namespace DetourNavigator void reportStats(unsigned int /*frameNumber*/, osg::Stats& /*stats*/) const override {} + RecastMeshTiles getRecastMeshTiles() override + { + return {}; + } + private: Settings mDefaultSettings {}; SharedNavMeshCacheItem mEmptyNavMeshCacheItem; diff --git a/components/detournavigator/navmeshmanager.cpp b/components/detournavigator/navmeshmanager.cpp index 4bd797326..9e8619469 100644 --- a/components/detournavigator/navmeshmanager.cpp +++ b/components/detournavigator/navmeshmanager.cpp @@ -220,6 +220,17 @@ namespace DetourNavigator mAsyncNavMeshUpdater.reportStats(frameNumber, stats); } + RecastMeshTiles NavMeshManager::getRecastMeshTiles() + { + std::vector tiles; + mRecastMeshManager.forEachTilePosition( + [&tiles] (const TilePosition& tile) { tiles.push_back(tile); }); + RecastMeshTiles result; + std::transform(tiles.begin(), tiles.end(), std::inserter(result, result.end()), + [this] (const TilePosition& tile) { return std::make_pair(tile, mRecastMeshManager.getMesh(tile)); }); + return result; + } + void NavMeshManager::addChangedTiles(const btCollisionShape& shape, const btTransform& transform, const ChangeType changeType) { diff --git a/components/detournavigator/navmeshmanager.hpp b/components/detournavigator/navmeshmanager.hpp index 3ef898b05..a6bdca09b 100644 --- a/components/detournavigator/navmeshmanager.hpp +++ b/components/detournavigator/navmeshmanager.hpp @@ -5,6 +5,7 @@ #include "cachedrecastmeshmanager.hpp" #include "offmeshconnectionsmanager.hpp" #include "sharednavmesh.hpp" +#include "recastmeshtiles.hpp" #include @@ -52,6 +53,8 @@ namespace DetourNavigator void reportStats(unsigned int frameNumber, osg::Stats& stats) const; + RecastMeshTiles getRecastMeshTiles(); + private: const Settings& mSettings; TileCachedRecastMeshManager mRecastMeshManager; diff --git a/components/detournavigator/recastmesh.cpp b/components/detournavigator/recastmesh.cpp index 476799c1f..dc56f7b93 100644 --- a/components/detournavigator/recastmesh.cpp +++ b/components/detournavigator/recastmesh.cpp @@ -5,9 +5,11 @@ namespace DetourNavigator { - RecastMesh::RecastMesh(std::vector indices, std::vector vertices, std::vector areaTypes, - std::vector water, const std::size_t trianglesPerChunk) - : mIndices(std::move(indices)) + RecastMesh::RecastMesh(std::size_t generation, std::size_t revision, std::vector indices, std::vector vertices, + std::vector areaTypes, std::vector water, const std::size_t trianglesPerChunk) + : mGeneration(generation) + , mRevision(revision) + , mIndices(std::move(indices)) , mVertices(std::move(vertices)) , mAreaTypes(std::move(areaTypes)) , mWater(std::move(water)) diff --git a/components/detournavigator/recastmesh.hpp b/components/detournavigator/recastmesh.hpp index 47d5f7963..f3259903f 100644 --- a/components/detournavigator/recastmesh.hpp +++ b/components/detournavigator/recastmesh.hpp @@ -24,8 +24,18 @@ namespace DetourNavigator btTransform mTransform; }; - RecastMesh(std::vector indices, std::vector vertices, std::vector areaTypes, - std::vector water, const std::size_t trianglesPerChunk); + RecastMesh(std::size_t generation, std::size_t revision, std::vector indices, std::vector vertices, + std::vector areaTypes, std::vector water, const std::size_t trianglesPerChunk); + + std::size_t getGeneration() const + { + return mGeneration; + } + + std::size_t getRevision() const + { + return mRevision; + } const std::vector& getIndices() const { @@ -68,6 +78,8 @@ namespace DetourNavigator } private: + std::size_t mGeneration; + std::size_t mRevision; std::vector mIndices; std::vector mVertices; std::vector mAreaTypes; diff --git a/components/detournavigator/recastmeshbuilder.cpp b/components/detournavigator/recastmeshbuilder.cpp index 5d8a07055..d96ba2f29 100644 --- a/components/detournavigator/recastmeshbuilder.cpp +++ b/components/detournavigator/recastmeshbuilder.cpp @@ -112,9 +112,10 @@ namespace DetourNavigator mWater.push_back(RecastMesh::Water {cellSize, transform}); } - std::shared_ptr RecastMeshBuilder::create() const + std::shared_ptr RecastMeshBuilder::create(std::size_t generation, std::size_t revision) const { - return std::make_shared(mIndices, mVertices, mAreaTypes, mWater, mSettings.get().mTrianglesPerChunk); + return std::make_shared(generation, revision, mIndices, mVertices, mAreaTypes, + mWater, mSettings.get().mTrianglesPerChunk); } void RecastMeshBuilder::reset() diff --git a/components/detournavigator/recastmeshbuilder.hpp b/components/detournavigator/recastmeshbuilder.hpp index 070b9c67d..d28558d0f 100644 --- a/components/detournavigator/recastmeshbuilder.hpp +++ b/components/detournavigator/recastmeshbuilder.hpp @@ -34,7 +34,7 @@ namespace DetourNavigator void addWater(const int mCellSize, const btTransform& transform); - std::shared_ptr create() const; + std::shared_ptr create(std::size_t generation, std::size_t revision) const; void reset(); diff --git a/components/detournavigator/recastmeshmanager.cpp b/components/detournavigator/recastmeshmanager.cpp index 39afdf56e..05f250596 100644 --- a/components/detournavigator/recastmeshmanager.cpp +++ b/components/detournavigator/recastmeshmanager.cpp @@ -4,8 +4,8 @@ namespace DetourNavigator { - RecastMeshManager::RecastMeshManager(const Settings& settings, const TileBounds& bounds) - : mShouldRebuild(false) + RecastMeshManager::RecastMeshManager(const Settings& settings, const TileBounds& bounds, std::size_t generation) + : mGeneration(generation) , mMeshBuilder(settings, bounds) { } @@ -19,8 +19,8 @@ namespace DetourNavigator mObjectsOrder.erase(iterator); return false; } - mShouldRebuild = true; - return mShouldRebuild; + ++mRevision; + return true; } bool RecastMeshManager::updateObject(const ObjectId id, const btTransform& transform, const AreaType areaType) @@ -30,8 +30,8 @@ namespace DetourNavigator return false; if (!object->second->update(transform, areaType)) return false; - mShouldRebuild = true; - return mShouldRebuild; + ++mRevision; + return true; } boost::optional RecastMeshManager::removeObject(const ObjectId id) @@ -42,7 +42,7 @@ namespace DetourNavigator const RemovedRecastMeshObject result {object->second->getShape(), object->second->getTransform()}; mObjectsOrder.erase(object->second); mObjects.erase(object); - mShouldRebuild = true; + ++mRevision; return result; } @@ -55,7 +55,7 @@ namespace DetourNavigator mWaterOrder.erase(iterator); return false; } - mShouldRebuild = true; + ++mRevision; return true; } @@ -64,7 +64,7 @@ namespace DetourNavigator const auto water = mWater.find(cellPosition); if (water == mWater.end()) return boost::none; - mShouldRebuild = true; + ++mRevision; const auto result = *water->second; mWaterOrder.erase(water->second); mWater.erase(water); @@ -74,7 +74,7 @@ namespace DetourNavigator std::shared_ptr RecastMeshManager::getMesh() { rebuild(); - return mMeshBuilder.create(); + return mMeshBuilder.create(mGeneration, mLastBuildRevision); } bool RecastMeshManager::isEmpty() const @@ -84,13 +84,13 @@ namespace DetourNavigator void RecastMeshManager::rebuild() { - if (!mShouldRebuild) + if (mLastBuildRevision == mRevision) return; mMeshBuilder.reset(); for (const auto& v : mWaterOrder) mMeshBuilder.addWater(v.mCellSize, v.mTransform); for (const auto& v : mObjectsOrder) mMeshBuilder.addObject(v.getShape(), v.getTransform(), v.getAreaType()); - mShouldRebuild = false; + mLastBuildRevision = mRevision; } } diff --git a/components/detournavigator/recastmeshmanager.hpp b/components/detournavigator/recastmeshmanager.hpp index a6f86bfcf..f1870ec6b 100644 --- a/components/detournavigator/recastmeshmanager.hpp +++ b/components/detournavigator/recastmeshmanager.hpp @@ -34,7 +34,7 @@ namespace DetourNavigator btTransform mTransform; }; - RecastMeshManager(const Settings& settings, const TileBounds& bounds); + RecastMeshManager(const Settings& settings, const TileBounds& bounds, std::size_t generation); bool addObject(const ObjectId id, const btCollisionShape& shape, const btTransform& transform, const AreaType areaType); @@ -52,7 +52,9 @@ namespace DetourNavigator bool isEmpty() const; private: - bool mShouldRebuild; + std::size_t mRevision = 0; + std::size_t mLastBuildRevision = 0; + std::size_t mGeneration; RecastMeshBuilder mMeshBuilder; std::list mObjectsOrder; std::unordered_map::iterator> mObjects; diff --git a/components/detournavigator/recastmeshtiles.hpp b/components/detournavigator/recastmeshtiles.hpp new file mode 100644 index 000000000..68e30ba63 --- /dev/null +++ b/components/detournavigator/recastmeshtiles.hpp @@ -0,0 +1,15 @@ +#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_RECASTMESHTILE_H +#define OPENMW_COMPONENTS_DETOURNAVIGATOR_RECASTMESHTILE_H + +#include "tileposition.hpp" +#include "recastmesh.hpp" + +#include +#include + +namespace DetourNavigator +{ + using RecastMeshTiles = std::map>; +} + +#endif diff --git a/components/detournavigator/tilecachedrecastmeshmanager.cpp b/components/detournavigator/tilecachedrecastmeshmanager.cpp index 0cb1cfb80..bbdbd410a 100644 --- a/components/detournavigator/tilecachedrecastmeshmanager.cpp +++ b/components/detournavigator/tilecachedrecastmeshmanager.cpp @@ -121,7 +121,7 @@ namespace DetourNavigator tileBounds.mMin -= osg::Vec2f(border, border); tileBounds.mMax += osg::Vec2f(border, border); tile = tiles->insert(std::make_pair(tilePosition, - CachedRecastMeshManager(mSettings, tileBounds))).first; + CachedRecastMeshManager(mSettings, tileBounds, mTilesGeneration))).first; } if (tile->second.addWater(cellPosition, cellSize, transform)) { @@ -151,7 +151,10 @@ namespace DetourNavigator continue; const auto tileResult = tile->second.removeWater(cellPosition); if (tile->second.isEmpty()) + { tiles->erase(tile); + ++mTilesGeneration; + } if (tileResult && !result) result = tileResult; } @@ -189,7 +192,8 @@ namespace DetourNavigator auto tileBounds = makeTileBounds(mSettings, tilePosition); tileBounds.mMin -= osg::Vec2f(border, border); tileBounds.mMax += osg::Vec2f(border, border); - tile = tiles.insert(std::make_pair(tilePosition, CachedRecastMeshManager(mSettings, tileBounds))).first; + tile = tiles.insert(std::make_pair( + tilePosition, CachedRecastMeshManager(mSettings, tileBounds, mTilesGeneration))).first; } return tile->second.addObject(id, shape, transform, areaType); } @@ -209,7 +213,10 @@ namespace DetourNavigator return boost::optional(); const auto tileResult = tile->second.removeObject(id); if (tile->second.isEmpty()) + { tiles.erase(tile); + ++mTilesGeneration; + } return tileResult; } } diff --git a/components/detournavigator/tilecachedrecastmeshmanager.hpp b/components/detournavigator/tilecachedrecastmeshmanager.hpp index a3d0ae1e5..4351c86bb 100644 --- a/components/detournavigator/tilecachedrecastmeshmanager.hpp +++ b/components/detournavigator/tilecachedrecastmeshmanager.hpp @@ -48,6 +48,7 @@ namespace DetourNavigator std::unordered_map> mObjectsTilesPositions; std::map> mWaterTilesPositions; std::size_t mRevision = 0; + std::size_t mTilesGeneration = 0; bool addTile(const ObjectId id, const btCollisionShape& shape, const btTransform& transform, const AreaType areaType, const TilePosition& tilePosition, float border, diff --git a/components/sceneutil/detourdebugdraw.cpp b/components/sceneutil/detourdebugdraw.cpp index ea8a4c2f6..b9c2fecef 100644 --- a/components/sceneutil/detourdebugdraw.cpp +++ b/components/sceneutil/detourdebugdraw.cpp @@ -50,10 +50,8 @@ namespace SceneUtil mDepthMask = state; } - void DebugDraw::texture(bool state) + void DebugDraw::texture(bool) { - if (state) - throw std::logic_error("DebugDraw does not support textures (at " __FILE__ ":" OPENMW_LINE_STRING ")"); } void DebugDraw::begin(osg::PrimitiveSet::Mode mode, float size) @@ -85,9 +83,10 @@ namespace SceneUtil vertex(pos[0], pos[1], pos[2], color, uv[0], uv[1]); } - void DebugDraw::vertex(const float, const float, const float, unsigned, const float, const float) + void DebugDraw::vertex(const float x, const float y, const float z, unsigned color, const float, const float) { - throw std::logic_error("Not implemented (at " __FILE__ ":" OPENMW_LINE_STRING ")"); + addVertex(osg::Vec3f(x, y, z)); + addColor(SceneUtil::colourFromRGBA(color)); } void DebugDraw::end() diff --git a/components/sceneutil/recastmesh.cpp b/components/sceneutil/recastmesh.cpp new file mode 100644 index 000000000..2716f4683 --- /dev/null +++ b/components/sceneutil/recastmesh.cpp @@ -0,0 +1,48 @@ +#include "navmesh.hpp" +#include "detourdebugdraw.hpp" + +#include +#include + +#include + +#include + +namespace +{ + std::vector calculateNormals(const std::vector& vertices, const std::vector& indices) + { + std::vector result(indices.size()); + for (std::size_t i = 0, n = indices.size(); i < n; i += 3) + { + const float* v0_ptr = &vertices[indices[i] * 3]; + const float* v1_ptr = &vertices[indices[i + 1] * 3]; + const float* v2_ptr = &vertices[indices[i + 2] * 3]; + const osg::Vec3f v0(v0_ptr[0], v0_ptr[1], v0_ptr[2]); + const osg::Vec3f v1(v1_ptr[0], v1_ptr[1], v1_ptr[2]); + const osg::Vec3f v2(v2_ptr[0], v2_ptr[1], v2_ptr[2]); + const osg::Vec3f e0 = v1 - v0; + const osg::Vec3f e1 = v2 - v0; + osg::Vec3f normal = e0 ^ e1; + normal.normalize(); + for (std::size_t j = 0; j < 3; ++j) + result[i + j] = normal[j]; + } + return result; + } +} + +namespace SceneUtil +{ + osg::ref_ptr createRecastMeshGroup(const DetourNavigator::RecastMesh& recastMesh, + const DetourNavigator::Settings& settings) + { + const osg::ref_ptr group(new osg::Group); + DebugDraw debugDraw(*group, osg::Vec3f(0, 0, 0), 1.0f / settings.mRecastScaleFactor); + const auto normals = calculateNormals(recastMesh.getVertices(), recastMesh.getIndices()); + const auto texScale = 1.0f / (settings.mCellSize * 10.0f); + duDebugDrawTriMesh(&debugDraw, recastMesh.getVertices().data(), recastMesh.getVerticesCount(), + recastMesh.getIndices().data(), normals.data(), recastMesh.getTrianglesCount(), nullptr, texScale); + return group; + } +} diff --git a/components/sceneutil/recastmesh.hpp b/components/sceneutil/recastmesh.hpp new file mode 100644 index 000000000..ee5d9865e --- /dev/null +++ b/components/sceneutil/recastmesh.hpp @@ -0,0 +1,23 @@ +#ifndef OPENMW_COMPONENTS_SCENEUTIL_RECASTMESH_H +#define OPENMW_COMPONENTS_SCENEUTIL_RECASTMESH_H + +#include + +namespace osg +{ + class Group; +} + +namespace DetourNavigator +{ + class RecastMesh; + struct Settings; +} + +namespace SceneUtil +{ + osg::ref_ptr createRecastMeshGroup(const DetourNavigator::RecastMesh& recastMesh, + const DetourNavigator::Settings& settings); +} + +#endif diff --git a/docs/source/reference/modding/settings/navigator.rst b/docs/source/reference/modding/settings/navigator.rst index 083048332..c7817b6e8 100644 --- a/docs/source/reference/modding/settings/navigator.rst +++ b/docs/source/reference/modding/settings/navigator.rst @@ -166,6 +166,20 @@ Make visible all NPC's and creaure's plans where they are going. Works even if Navigator is disabled. Potentially decreases performance. +enable recast mesh render +---------------------- + +:Type: boolean +:Range: True/False +:Default: False + +Render recast mesh that is built as set of culled tiles from physical mesh. +Should show similar mesh to physical one. +Little difference can be a result of floating point error. +Absent pieces usually mean a bug in recast mesh tiles building. +Allows to do in-game debug. +Potentially decreases performance. + Expert settings *************** diff --git a/files/settings-default.cfg b/files/settings-default.cfg index c6f7abfba..293f8ffaf 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -748,6 +748,9 @@ enable nav mesh render = false # Render agents paths (true, false) enable agents paths render = false +# Render recast mesh (true, false) +enable recast mesh render = false + # Max number of navmesh tiles (value >= 0) max tiles number = 512 From 5168f2059f892baa6cefc1c26b1c896a1d4f8f29 Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 30 Nov 2019 13:52:17 +0100 Subject: [PATCH 65/99] Compare revisions by equality to support overflow --- apps/openmw/mwrender/navmesh.cpp | 2 +- components/detournavigator/navmeshmanager.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/navmesh.cpp b/apps/openmw/mwrender/navmesh.cpp index 3a80ac8ca..7aade0c23 100644 --- a/apps/openmw/mwrender/navmesh.cpp +++ b/apps/openmw/mwrender/navmesh.cpp @@ -34,7 +34,7 @@ namespace MWRender void NavMesh::update(const dtNavMesh& navMesh, const std::size_t id, const std::size_t generation, const std::size_t revision, const DetourNavigator::Settings& settings) { - if (!mEnabled || (mGroup && mId == id && mGeneration >= generation && mRevision >= revision)) + if (!mEnabled || (mGroup && mId == id && mGeneration == generation && mRevision == revision)) return; mId = id; diff --git a/components/detournavigator/navmeshmanager.cpp b/components/detournavigator/navmeshmanager.cpp index 9e8619469..a769981d3 100644 --- a/components/detournavigator/navmeshmanager.cpp +++ b/components/detournavigator/navmeshmanager.cpp @@ -147,7 +147,7 @@ namespace DetourNavigator const auto playerTile = getTilePosition(mSettings, toNavMeshCoordinates(mSettings, playerPosition)); auto& lastRevision = mLastRecastMeshManagerRevision[agentHalfExtents]; auto lastPlayerTile = mPlayerTile.find(agentHalfExtents); - if (lastRevision >= mRecastMeshManager.getRevision() && lastPlayerTile != mPlayerTile.end() + if (lastRevision == mRecastMeshManager.getRevision() && lastPlayerTile != mPlayerTile.end() && lastPlayerTile->second == playerTile) return; lastRevision = mRecastMeshManager.getRevision(); From dea2018d9ffdc53d52bd482556686f6b9b6f3c10 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 17 Mar 2020 14:15:19 +0400 Subject: [PATCH 66/99] Do not use dynamic casts when using ObjectState --- apps/openmw/mwclass/container.cpp | 13 ++--- apps/openmw/mwclass/creature.cpp | 16 ++---- apps/openmw/mwclass/creaturelevlist.cpp | 14 ++--- apps/openmw/mwclass/door.cpp | 12 ++-- apps/openmw/mwclass/npc.cpp | 20 +++---- apps/openmw/mwmechanics/npcstats.cpp | 9 +++ apps/openmw/mwmechanics/npcstats.hpp | 2 + components/esm/containerstate.hpp | 9 +++ components/esm/creaturelevliststate.hpp | 9 +++ components/esm/creaturestate.hpp | 9 +++ components/esm/doorstate.hpp | 9 +++ components/esm/npcstate.hpp | 9 +++ components/esm/objectstate.cpp | 74 +++++++++++++++++++++++++ components/esm/objectstate.hpp | 20 +++++++ 14 files changed, 182 insertions(+), 43 deletions(-) diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index 369077d6c..3adeb19d9 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -317,7 +317,6 @@ namespace MWClass { if (!state.mHasCustomState) return; - const ESM::ContainerState& state2 = dynamic_cast (state); if (!ptr.getRefData().getCustomData()) { @@ -326,21 +325,21 @@ namespace MWClass ptr.getRefData().setCustomData (data.release()); } - dynamic_cast (*ptr.getRefData().getCustomData()).mContainerStore. - readState (state2.mInventory); + ContainerCustomData& customData = ptr.getRefData().getCustomData()->asContainerCustomData(); + const ESM::ContainerState& containerState = state.asContainerState(); + customData.mContainerStore.readState (containerState.mInventory); } void Container::writeAdditionalState (const MWWorld::ConstPtr& ptr, ESM::ObjectState& state) const { - ESM::ContainerState& state2 = dynamic_cast (state); - if (!ptr.getRefData().getCustomData()) { state.mHasCustomState = false; return; } - dynamic_cast (*ptr.getRefData().getCustomData()).mContainerStore. - writeState (state2.mInventory); + const ContainerCustomData& customData = ptr.getRefData().getCustomData()->asContainerCustomData(); + ESM::ContainerState& containerState = state.asContainerState(); + customData.mContainerStore.writeState (containerState.mInventory); } } diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 9d5bd14e4..d1a439528 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -776,8 +776,6 @@ namespace MWClass if (!state.mHasCustomState) return; - const ESM::CreatureState& state2 = dynamic_cast (state); - if (state.mVersion > 0) { if (!ptr.getRefData().getCustomData()) @@ -797,16 +795,14 @@ namespace MWClass ensureCustomData(ptr); // in openmw 0.30 savegames not all state was saved yet, so need to load it regardless. CreatureCustomData& customData = ptr.getRefData().getCustomData()->asCreatureCustomData(); - - customData.mContainerStore->readState (state2.mInventory); - customData.mCreatureStats.readState (state2.mCreatureStats); + const ESM::CreatureState& creatureState = state.asCreatureState(); + customData.mContainerStore->readState (creatureState.mInventory); + customData.mCreatureStats.readState (creatureState.mCreatureStats); } void Creature::writeAdditionalState (const MWWorld::ConstPtr& ptr, ESM::ObjectState& state) const { - ESM::CreatureState& state2 = dynamic_cast (state); - if (!ptr.getRefData().getCustomData()) { state.mHasCustomState = false; @@ -814,9 +810,9 @@ namespace MWClass } const CreatureCustomData& customData = ptr.getRefData().getCustomData()->asCreatureCustomData(); - - customData.mContainerStore->writeState (state2.mInventory); - customData.mCreatureStats.writeState (state2.mCreatureStats); + ESM::CreatureState& creatureState = state.asCreatureState(); + customData.mContainerStore->writeState (creatureState.mInventory); + customData.mCreatureStats.writeState (creatureState.mCreatureStats); } int Creature::getBaseGold(const MWWorld::ConstPtr& ptr) const diff --git a/apps/openmw/mwclass/creaturelevlist.cpp b/apps/openmw/mwclass/creaturelevlist.cpp index 1dab9e483..1f47b483f 100644 --- a/apps/openmw/mwclass/creaturelevlist.cpp +++ b/apps/openmw/mwclass/creaturelevlist.cpp @@ -151,19 +151,16 @@ namespace MWClass if (!state.mHasCustomState) return; - const ESM::CreatureLevListState& state2 = dynamic_cast (state); - ensureCustomData(ptr); CreatureLevListCustomData& customData = ptr.getRefData().getCustomData()->asCreatureLevListCustomData(); - customData.mSpawnActorId = state2.mSpawnActorId; - customData.mSpawn = state2.mSpawn; + const ESM::CreatureLevListState& levListState = state.asCreatureLevListState(); + customData.mSpawnActorId = levListState.mSpawnActorId; + customData.mSpawn = levListState.mSpawn; } void CreatureLevList::writeAdditionalState (const MWWorld::ConstPtr& ptr, ESM::ObjectState& state) const { - ESM::CreatureLevListState& state2 = dynamic_cast (state); - if (!ptr.getRefData().getCustomData()) { state.mHasCustomState = false; @@ -171,7 +168,8 @@ namespace MWClass } const CreatureLevListCustomData& customData = ptr.getRefData().getCustomData()->asCreatureLevListCustomData(); - state2.mSpawnActorId = customData.mSpawnActorId; - state2.mSpawn = customData.mSpawn; + ESM::CreatureLevListState& levListState = state.asCreatureLevListState(); + levListState.mSpawnActorId = customData.mSpawnActorId; + levListState.mSpawn = customData.mSpawn; } } diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index 6a0be8631..7d1c1d38a 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -370,11 +370,11 @@ namespace MWClass { if (!state.mHasCustomState) return; + ensureCustomData(ptr); DoorCustomData& customData = ptr.getRefData().getCustomData()->asDoorCustomData(); - - const ESM::DoorState& state2 = dynamic_cast(state); - customData.mDoorState = static_cast(state2.mDoorState); + const ESM::DoorState& doorState = state.asDoorState(); + customData.mDoorState = MWWorld::DoorState(doorState.mDoorState); } void Door::writeAdditionalState (const MWWorld::ConstPtr& ptr, ESM::ObjectState& state) const @@ -384,10 +384,10 @@ namespace MWClass state.mHasCustomState = false; return; } - const DoorCustomData& customData = ptr.getRefData().getCustomData()->asDoorCustomData(); - ESM::DoorState& state2 = dynamic_cast(state); - state2.mDoorState = static_cast(customData.mDoorState); + const DoorCustomData& customData = ptr.getRefData().getCustomData()->asDoorCustomData(); + ESM::DoorState& doorState = state.asDoorState(); + doorState.mDoorState = int(customData.mDoorState); } } diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 4f1a996e7..297471e07 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -1299,8 +1299,6 @@ namespace MWClass if (!state.mHasCustomState) return; - const ESM::NpcState& state2 = dynamic_cast (state); - if (state.mVersion > 0) { if (!ptr.getRefData().getCustomData()) @@ -1314,17 +1312,15 @@ namespace MWClass ensureCustomData(ptr); // in openmw 0.30 savegames not all state was saved yet, so need to load it regardless. NpcCustomData& customData = ptr.getRefData().getCustomData()->asNpcCustomData(); - - customData.mInventoryStore.readState (state2.mInventory); - customData.mNpcStats.readState (state2.mNpcStats); - static_cast (customData.mNpcStats).readState (state2.mCreatureStats); + const ESM::NpcState& npcState = state.asNpcState(); + customData.mInventoryStore.readState (npcState.mInventory); + customData.mNpcStats.readState (npcState.mNpcStats); + customData.mNpcStats.readState (npcState.mCreatureStats); } void Npc::writeAdditionalState (const MWWorld::ConstPtr& ptr, ESM::ObjectState& state) const { - ESM::NpcState& state2 = dynamic_cast (state); - if (!ptr.getRefData().getCustomData()) { state.mHasCustomState = false; @@ -1332,10 +1328,10 @@ namespace MWClass } const NpcCustomData& customData = ptr.getRefData().getCustomData()->asNpcCustomData(); - - customData.mInventoryStore.writeState (state2.mInventory); - customData.mNpcStats.writeState (state2.mNpcStats); - static_cast (customData.mNpcStats).writeState (state2.mCreatureStats); + ESM::NpcState& npcState = state.asNpcState(); + customData.mInventoryStore.writeState (npcState.mInventory); + customData.mNpcStats.writeState (npcState.mNpcStats); + customData.mNpcStats.writeState (npcState.mCreatureStats); } int Npc::getBaseGold(const MWWorld::ConstPtr& ptr) const diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index aa6cd142b..ee48ea7d5 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -455,6 +455,11 @@ void MWMechanics::NpcStats::setTimeToStartDrowning(float time) mTimeToStartDrowning=time; } +void MWMechanics::NpcStats::writeState (ESM::CreatureStats& state) const +{ + CreatureStats::writeState(state); +} + void MWMechanics::NpcStats::writeState (ESM::NpcStats& state) const { for (std::map::const_iterator iter (mFactionRank.begin()); @@ -494,6 +499,10 @@ void MWMechanics::NpcStats::writeState (ESM::NpcStats& state) const state.mTimeToStartDrowning = mTimeToStartDrowning; } +void MWMechanics::NpcStats::readState (const ESM::CreatureStats& state) +{ + CreatureStats::readState(state); +} void MWMechanics::NpcStats::readState (const ESM::NpcStats& state) { diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp index 47784ac1d..9bd8e20ad 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -127,8 +127,10 @@ namespace MWMechanics /// @param time value from [0,20] void setTimeToStartDrowning(float time); + void writeState (ESM::CreatureStats& state) const; void writeState (ESM::NpcStats& state) const; + void readState (const ESM::CreatureStats& state); void readState (const ESM::NpcStats& state); }; } diff --git a/components/esm/containerstate.hpp b/components/esm/containerstate.hpp index 1ecf2b46e..33818d4f1 100644 --- a/components/esm/containerstate.hpp +++ b/components/esm/containerstate.hpp @@ -14,6 +14,15 @@ namespace ESM virtual void load (ESMReader &esm); virtual void save (ESMWriter &esm, bool inInventory = false) const; + + virtual ContainerState& asContainerState() + { + return *this; + } + virtual const ContainerState& asContainerState() const + { + return *this; + } }; } diff --git a/components/esm/creaturelevliststate.hpp b/components/esm/creaturelevliststate.hpp index da64cd7c2..4d0b726a0 100644 --- a/components/esm/creaturelevliststate.hpp +++ b/components/esm/creaturelevliststate.hpp @@ -14,6 +14,15 @@ namespace ESM virtual void load (ESMReader &esm); virtual void save (ESMWriter &esm, bool inInventory = false) const; + + virtual CreatureLevListState& asCreatureLevListState() + { + return *this; + } + virtual const CreatureLevListState& asCreatureLevListState() const + { + return *this; + } }; } diff --git a/components/esm/creaturestate.hpp b/components/esm/creaturestate.hpp index 9a3d41daa..ca0d2601a 100644 --- a/components/esm/creaturestate.hpp +++ b/components/esm/creaturestate.hpp @@ -19,6 +19,15 @@ namespace ESM virtual void load (ESMReader &esm); virtual void save (ESMWriter &esm, bool inInventory = false) const; + + virtual CreatureState& asCreatureState() + { + return *this; + } + virtual const CreatureState& asCreatureState() const + { + return *this; + } }; } diff --git a/components/esm/doorstate.hpp b/components/esm/doorstate.hpp index c2e765880..1251b9059 100644 --- a/components/esm/doorstate.hpp +++ b/components/esm/doorstate.hpp @@ -13,6 +13,15 @@ namespace ESM virtual void load (ESMReader &esm); virtual void save (ESMWriter &esm, bool inInventory = false) const; + + virtual DoorState& asDoorState() + { + return *this; + } + virtual const DoorState& asDoorState() const + { + return *this; + } }; } diff --git a/components/esm/npcstate.hpp b/components/esm/npcstate.hpp index b90cd85e6..4ae026da6 100644 --- a/components/esm/npcstate.hpp +++ b/components/esm/npcstate.hpp @@ -21,6 +21,15 @@ namespace ESM virtual void load (ESMReader &esm); virtual void save (ESMWriter &esm, bool inInventory = false) const; + + virtual NpcState& asNpcState() + { + return *this; + } + virtual const NpcState& asNpcState() const + { + return *this; + } }; } diff --git a/components/esm/objectstate.cpp b/components/esm/objectstate.cpp index 18c030256..a7a452c82 100644 --- a/components/esm/objectstate.cpp +++ b/components/esm/objectstate.cpp @@ -1,5 +1,9 @@ #include "objectstate.hpp" +#include +#include +#include + #include "esmreader.hpp" #include "esmwriter.hpp" @@ -84,4 +88,74 @@ void ESM::ObjectState::blank() mHasCustomState = true; } +const ESM::NpcState& ESM::ObjectState::asNpcState() const +{ + std::stringstream error; + error << "bad cast " << typeid(this).name() << " to NpcState"; + throw std::logic_error(error.str()); +} + +ESM::NpcState& ESM::ObjectState::asNpcState() +{ + std::stringstream error; + error << "bad cast " << typeid(this).name() << " to NpcState"; + throw std::logic_error(error.str()); +} + +const ESM::CreatureState& ESM::ObjectState::asCreatureState() const +{ + std::stringstream error; + error << "bad cast " << typeid(this).name() << " to CreatureState"; + throw std::logic_error(error.str()); +} + +ESM::CreatureState& ESM::ObjectState::asCreatureState() +{ + std::stringstream error; + error << "bad cast " << typeid(this).name() << " to CreatureState"; + throw std::logic_error(error.str()); +} + +const ESM::ContainerState& ESM::ObjectState::asContainerState() const +{ + std::stringstream error; + error << "bad cast " << typeid(this).name() << " to ContainerState"; + throw std::logic_error(error.str()); +} + +ESM::ContainerState& ESM::ObjectState::asContainerState() +{ + std::stringstream error; + error << "bad cast " << typeid(this).name() << " to ContainerState"; + throw std::logic_error(error.str()); +} + +const ESM::DoorState& ESM::ObjectState::asDoorState() const +{ + std::stringstream error; + error << "bad cast " << typeid(this).name() << " to DoorState"; + throw std::logic_error(error.str()); +} + +ESM::DoorState& ESM::ObjectState::asDoorState() +{ + std::stringstream error; + error << "bad cast " << typeid(this).name() << " to DoorState"; + throw std::logic_error(error.str()); +} + +const ESM::CreatureLevListState& ESM::ObjectState::asCreatureLevListState() const +{ + std::stringstream error; + error << "bad cast " << typeid(this).name() << " to CreatureLevListState"; + throw std::logic_error(error.str()); +} + +ESM::CreatureLevListState& ESM::ObjectState::asCreatureLevListState() +{ + std::stringstream error; + error << "bad cast " << typeid(this).name() << " to CreatureLevListState"; + throw std::logic_error(error.str()); +} + ESM::ObjectState::~ObjectState() {} diff --git a/components/esm/objectstate.hpp b/components/esm/objectstate.hpp index d14c04b64..1079d6c72 100644 --- a/components/esm/objectstate.hpp +++ b/components/esm/objectstate.hpp @@ -12,6 +12,11 @@ namespace ESM { class ESMReader; class ESMWriter; + struct ContainerState; + struct CreatureLevListState; + struct CreatureState; + struct DoorState; + struct NpcState; // format 0, saved games only @@ -48,6 +53,21 @@ namespace ESM void blank(); virtual ~ObjectState(); + + virtual const NpcState& asNpcState() const; + virtual NpcState& asNpcState(); + + virtual const CreatureState& asCreatureState() const; + virtual CreatureState& asCreatureState(); + + virtual const ContainerState& asContainerState() const; + virtual ContainerState& asContainerState(); + + virtual const DoorState& asDoorState() const; + virtual DoorState& asDoorState(); + + virtual const CreatureLevListState& asCreatureLevListState() const; + virtual CreatureLevListState& asCreatureLevListState(); }; } From ea30e273705685b7d6523b570dd1b7358677ec54 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 17 Mar 2020 17:12:04 +0400 Subject: [PATCH 67/99] Cleanup ownership for items in containers --- apps/openmw/mwworld/containerstore.cpp | 2 +- components/esm/cellref.cpp | 14 ++++++++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 5dd380be0..6337ca440 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -290,7 +290,7 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr item.getCellRef().setOwner(""); item.getCellRef().resetGlobalVariable(); item.getCellRef().setFaction(""); - item.getCellRef().setFactionRank(-1); + item.getCellRef().setFactionRank(-2); // must reset the RefNum on the copied item, so that the RefNum on the original item stays unique // maybe we should do this in the copy constructor instead? diff --git a/components/esm/cellref.cpp b/components/esm/cellref.cpp index ba2be7b45..69841528f 100644 --- a/components/esm/cellref.cpp +++ b/components/esm/cellref.cpp @@ -153,13 +153,19 @@ void ESM::CellRef::save (ESMWriter &esm, bool wideRefNum, bool inInventory, bool esm.writeHNT("XSCL", scale); } - esm.writeHNOCString("ANAM", mOwner); + if (!inInventory) + esm.writeHNOCString("ANAM", mOwner); + esm.writeHNOCString("BNAM", mGlobalVariable); esm.writeHNOCString("XSOL", mSoul); - esm.writeHNOCString("CNAM", mFaction); - if (mFactionRank != -2) { - esm.writeHNT("INDX", mFactionRank); + if (!inInventory) + { + esm.writeHNOCString("CNAM", mFaction); + if (mFactionRank != -2) + { + esm.writeHNT("INDX", mFactionRank); + } } if (mEnchantmentCharge != -1) From 02444add2a115abfc9c03d5cf29e192e42a504e5 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 14 Mar 2020 16:39:32 +0400 Subject: [PATCH 68/99] Support for radial fog (feature #4708) --- CHANGELOG.md | 1 + apps/opencs/model/world/data.cpp | 1 + apps/openmw/mwrender/renderingmanager.cpp | 5 ++++- .../reference/modding/settings/shaders.rst | 11 ++++++++++ files/settings-default.cfg | 4 ++++ files/shaders/objects_fragment.glsl | 12 ++++++---- files/shaders/objects_vertex.glsl | 7 ++++-- files/shaders/terrain_fragment.glsl | 11 +++++++--- files/shaders/terrain_vertex.glsl | 6 +++-- files/shaders/water_fragment.glsl | 22 ++++++++++++++----- files/shaders/water_vertex.glsl | 4 ++-- 11 files changed, 64 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d44ab19d..32255262e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -225,6 +225,7 @@ Feature #4544: Actors movement deceleration Feature #4673: Weapon sheathing Feature #4675: Support for NiRollController + Feature #4708: Radial fog support Feature #4730: Native animated containers support Feature #4784: Launcher: Duplicate Content Lists Feature #4812: Support NiSwitchNode diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 23ea1a5a9..b45df35b1 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -82,6 +82,7 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, bool fsStrict, const Files::Pat defines["forcePPL"] = "0"; // Don't force per-pixel lighting defines["clamp"] = "1"; // Clamp lighting defines["preLightEnv"] = "0"; // Apply environment maps after lighting like Morrowind + defines["radialFog"] = "0"; for (const auto& define : shadowDefines) defines[define.first] = define.second; mResourceSystem->getSceneManager()->getShaderManager().setGlobalDefines(defines); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index dd570f558..0ab756747 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -217,7 +217,9 @@ namespace MWRender { resourceSystem->getSceneManager()->setParticleSystemMask(SceneUtil::Mask_ParticleSystem); resourceSystem->getSceneManager()->setShaderPath(resourcePath + "/shaders"); - resourceSystem->getSceneManager()->setForceShaders(Settings::Manager::getBool("force shaders", "Shaders") || Settings::Manager::getBool("enable shadows", "Shadows")); // Shadows have problems with fixed-function mode + // Shadows and radial fog have problems with fixed-function mode + bool forceShaders = Settings::Manager::getBool("radial fog", "Shaders") || Settings::Manager::getBool("force shaders", "Shaders") || Settings::Manager::getBool("enable shadows", "Shadows"); + resourceSystem->getSceneManager()->setForceShaders(forceShaders); // FIXME: calling dummy method because terrain needs to know whether lighting is clamped resourceSystem->getSceneManager()->setClampLighting(Settings::Manager::getBool("clamp lighting", "Shaders")); resourceSystem->getSceneManager()->setAutoUseNormalMaps(Settings::Manager::getBool("auto use object normal maps", "Shaders")); @@ -254,6 +256,7 @@ namespace MWRender globalDefines["forcePPL"] = Settings::Manager::getBool("force per pixel lighting", "Shaders") ? "1" : "0"; globalDefines["clamp"] = Settings::Manager::getBool("clamp lighting", "Shaders") ? "1" : "0"; globalDefines["preLightEnv"] = Settings::Manager::getBool("apply lighting to environment maps", "Shaders") ? "1" : "0"; + globalDefines["radialFog"] = Settings::Manager::getBool("radial fog", "Shaders") ? "1" : "0"; // It is unnecessary to stop/start the viewer as no frames are being rendered yet. mResourceSystem->getSceneManager()->getShaderManager().setGlobalDefines(globalDefines); diff --git a/docs/source/reference/modding/settings/shaders.rst b/docs/source/reference/modding/settings/shaders.rst index bc676d22f..e23cc3d54 100644 --- a/docs/source/reference/modding/settings/shaders.rst +++ b/docs/source/reference/modding/settings/shaders.rst @@ -135,3 +135,14 @@ apply lighting to environment maps Normally environment map reflections aren't affected by lighting, which makes environment-mapped (and thus bump-mapped objects) glow in the dark. Morrowind Code Patch includes an option to remedy that by doing environment-mapping before applying lighting, this is the equivalent of that option. Affected objects will use shaders. + +radial fog +---------- + +:Type: boolean +:Range: True/False +:Default: False + +By default, the fog becomes thicker proportionally to your distance from the clipping plane set at the clipping distance, which causes distortion at the edges of the screen. +This setting makes the fog use the actual eye point distance (or so called Euclidean distance) to calculate the fog, which makes the fog look less artificial, especially if you have a wide FOV. +Note that the rendering will act as if you have 'force shaders' option enabled with this on, which means that shaders will be used to render all objects and the terrain. diff --git a/files/settings-default.cfg b/files/settings-default.cfg index c6f7abfba..61ac5366e 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -350,6 +350,10 @@ terrain specular map pattern = _diffusespec # Affected objects use shaders. apply lighting to environment maps = false +# Determine fog intensity based on the distance from the eye point. +# This makes fogging independent from the viewing angle. Shaders will be used to render all objects. +radial fog = false + [Input] # Capture control of the cursor prevent movement outside the window. diff --git a/files/shaders/objects_fragment.glsl b/files/shaders/objects_fragment.glsl index a222d9c7b..31e929a90 100644 --- a/files/shaders/objects_fragment.glsl +++ b/files/shaders/objects_fragment.glsl @@ -49,7 +49,8 @@ uniform vec2 envMapLumaBias; uniform mat2 bumpMapMatrix; #endif -varying float depth; +varying float euclideanDepth; +varying float linearDepth; #define PER_PIXEL_LIGHTING (@normalMap || @forcePPL) @@ -146,7 +147,7 @@ void main() #endif - float shadowing = unshadowedLightRatio(depth); + float shadowing = unshadowedLightRatio(linearDepth); #if !PER_PIXEL_LIGHTING @@ -178,8 +179,11 @@ void main() #endif gl_FragData[0].xyz += getSpecular(normalize(viewNormal), normalize(passViewPos.xyz), shininess, matSpec) * shadowing; - - float fogValue = clamp((depth - gl_Fog.start) * gl_Fog.scale, 0.0, 1.0); +#if @radialFog + float fogValue = clamp((euclideanDepth - gl_Fog.start) * gl_Fog.scale, 0.0, 1.0); +#else + float fogValue = clamp((linearDepth - gl_Fog.start) * gl_Fog.scale, 0.0, 1.0); +#endif gl_FragData[0].xyz = mix(gl_FragData[0].xyz, gl_Fog.color.xyz, fogValue); applyShadowDebugOverlay(); diff --git a/files/shaders/objects_vertex.glsl b/files/shaders/objects_vertex.glsl index ec5449b87..dc8c91b03 100644 --- a/files/shaders/objects_vertex.glsl +++ b/files/shaders/objects_vertex.glsl @@ -37,7 +37,8 @@ varying vec2 bumpMapUV; varying vec2 specularMapUV; #endif -varying float depth; +varying float euclideanDepth; +varying float linearDepth; #define PER_PIXEL_LIGHTING (@normalMap || @forcePPL) @@ -57,10 +58,12 @@ varying vec3 passNormal; void main(void) { gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; - depth = gl_Position.z; vec4 viewPos = (gl_ModelViewMatrix * gl_Vertex); gl_ClipVertex = viewPos; + euclideanDepth = length(viewPos.xyz); + linearDepth = gl_Position.z; + vec3 viewNormal = normalize((gl_NormalMatrix * gl_Normal).xyz); #if @envMap diff --git a/files/shaders/terrain_fragment.glsl b/files/shaders/terrain_fragment.glsl index 4910788bb..b1917d33b 100644 --- a/files/shaders/terrain_fragment.glsl +++ b/files/shaders/terrain_fragment.glsl @@ -12,7 +12,8 @@ uniform sampler2D normalMap; uniform sampler2D blendMap; #endif -varying float depth; +varying float euclideanDepth; +varying float linearDepth; #define PER_PIXEL_LIGHTING (@normalMap || @forcePPL) @@ -66,7 +67,7 @@ void main() gl_FragData[0].a *= texture2D(blendMap, blendMapUV).a; #endif - float shadowing = unshadowedLightRatio(depth); + float shadowing = unshadowedLightRatio(linearDepth); #if !PER_PIXEL_LIGHTING @@ -90,7 +91,11 @@ void main() gl_FragData[0].xyz += getSpecular(normalize(viewNormal), normalize(passViewPos), shininess, matSpec) * shadowing; - float fogValue = clamp((depth - gl_Fog.start) * gl_Fog.scale, 0.0, 1.0); +#if @radialFog + float fogValue = clamp((euclideanDepth - gl_Fog.start) * gl_Fog.scale, 0.0, 1.0); +#else + float fogValue = clamp((linearDepth - gl_Fog.start) * gl_Fog.scale, 0.0, 1.0); +#endif gl_FragData[0].xyz = mix(gl_FragData[0].xyz, gl_Fog.color.xyz, fogValue); applyShadowDebugOverlay(); diff --git a/files/shaders/terrain_vertex.glsl b/files/shaders/terrain_vertex.glsl index 8a9cf82cc..14e291f43 100644 --- a/files/shaders/terrain_vertex.glsl +++ b/files/shaders/terrain_vertex.glsl @@ -1,7 +1,8 @@ #version 120 varying vec2 uv; -varying float depth; +varying float euclideanDepth; +varying float linearDepth; #define PER_PIXEL_LIGHTING (@normalMap || @forcePPL) @@ -21,10 +22,11 @@ varying vec3 passNormal; void main(void) { gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; - depth = gl_Position.z; vec4 viewPos = (gl_ModelViewMatrix * gl_Vertex); gl_ClipVertex = viewPos; + euclideanDepth = length(viewPos.xyz); + linearDepth = gl_Position.z; vec3 viewNormal = normalize((gl_NormalMatrix * gl_Normal).xyz); diff --git a/files/shaders/water_fragment.glsl b/files/shaders/water_fragment.glsl index 808fab5ee..6e51a2082 100644 --- a/files/shaders/water_fragment.glsl +++ b/files/shaders/water_fragment.glsl @@ -124,7 +124,7 @@ vec2 normalCoords(vec2 uv, float scale, float speed, float time, float timer1, f varying vec3 screenCoordsPassthrough; varying vec4 position; -varying float depthPassthrough; +varying float linearDepth; uniform sampler2D normalMap; @@ -160,7 +160,7 @@ void main(void) vec2 UV = worldPos.xy / (8192.0*5.0) * 3.0; UV.y *= -1.0; - float shadow = unshadowedLightRatio(depthPassthrough); + float shadow = unshadowedLightRatio(linearDepth); vec2 screenCoords = screenCoordsPassthrough.xy / screenCoordsPassthrough.z; screenCoords.y = (1.0-screenCoords.y); @@ -203,11 +203,17 @@ void main(void) float ior = (cameraPos.z>0.0)?(1.333/1.0):(1.0/1.333); // air to water; water to air float fresnel = clamp(fresnel_dielectric(vVec, normal, ior), 0.0, 1.0); +#if @radialFog + float radialDepth = distance(position.xyz, cameraPos); + float radialize = radialDepth / linearDepth; +#else + float radialize = 1.0; +#endif vec2 screenCoordsOffset = normal.xy * REFL_BUMP; #if REFRACTION - float depthSample = linearizeDepth(texture2D(refractionDepthMap,screenCoords).x); - float depthSampleDistorted = linearizeDepth(texture2D(refractionDepthMap,screenCoords-screenCoordsOffset).x); - float surfaceDepth = linearizeDepth(gl_FragCoord.z); + float depthSample = linearizeDepth(texture2D(refractionDepthMap,screenCoords).x) * radialize; + float depthSampleDistorted = linearizeDepth(texture2D(refractionDepthMap,screenCoords-screenCoordsOffset).x) * radialize; + float surfaceDepth = linearizeDepth(gl_FragCoord.z) * radialize; float realWaterDepth = depthSample - surfaceDepth; // undistorted water depth in view direction, independent of frustum screenCoordsOffset *= clamp(realWaterDepth / BUMP_SUPPRESS_DEPTH,0,1); #endif @@ -246,7 +252,11 @@ void main(void) #endif // fog - float fogValue = clamp((depthPassthrough - gl_Fog.start) * gl_Fog.scale, 0.0, 1.0); +#if @radialFog + float fogValue = clamp((radialDepth - gl_Fog.start) * gl_Fog.scale, 0.0, 1.0); +#else + float fogValue = clamp((linearDepth - gl_Fog.start) * gl_Fog.scale, 0.0, 1.0); +#endif gl_FragData[0].xyz = mix(gl_FragData[0].xyz, gl_Fog.color.xyz, fogValue); applyShadowDebugOverlay(); diff --git a/files/shaders/water_vertex.glsl b/files/shaders/water_vertex.glsl index 2377f0af4..02a395f95 100644 --- a/files/shaders/water_vertex.glsl +++ b/files/shaders/water_vertex.glsl @@ -2,7 +2,7 @@ varying vec3 screenCoordsPassthrough; varying vec4 position; -varying float depthPassthrough; +varying float linearDepth; #include "shadows_vertex.glsl" @@ -20,7 +20,7 @@ void main(void) position = gl_Vertex; - depthPassthrough = gl_Position.z; + linearDepth = gl_Position.z; setupShadowCoords(gl_ModelViewMatrix * gl_Vertex, normalize((gl_NormalMatrix * gl_Normal).xyz)); } From f063eeb36ee34c7230de7e670aaab923f08dcdd6 Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Thu, 27 Feb 2020 11:54:11 +0200 Subject: [PATCH 69/99] Drop objects with a keyboard shortcut --- apps/opencs/model/prefs/state.cpp | 2 + apps/opencs/view/render/instancemode.cpp | 84 +++++++++++++++++++- apps/opencs/view/render/instancemode.hpp | 14 +++- apps/opencs/view/render/object.cpp | 5 ++ apps/opencs/view/render/object.hpp | 3 + apps/opencs/view/render/worldspacewidget.cpp | 2 +- 6 files changed, 106 insertions(+), 4 deletions(-) diff --git a/apps/opencs/model/prefs/state.cpp b/apps/opencs/model/prefs/state.cpp index 442e707bf..422e2c1f4 100644 --- a/apps/opencs/model/prefs/state.cpp +++ b/apps/opencs/model/prefs/state.cpp @@ -356,6 +356,8 @@ void CSMPrefs::State::declare() QKeySequence(Qt::ControlModifier | (int)Qt::MiddleButton)); declareModifier ("scene-speed-modifier", "Speed Modifier", Qt::Key_Shift); declareShortcut ("scene-delete", "Delete Instance", QKeySequence(Qt::Key_Delete)); + declareShortcut ("scene-instance-groundlevel", "Instance to Ground Level", QKeySequence(Qt::Key_G)); + declareShortcut ("scene-instance-drop", "Drop Instance", QKeySequence(Qt::Key_H)); declareShortcut ("scene-load-cam-cell", "Load Camera Cell", QKeySequence(Qt::KeypadModifier | Qt::Key_5)); declareShortcut ("scene-load-cam-eastcell", "Load East Cell", QKeySequence(Qt::KeypadModifier | Qt::Key_6)); declareShortcut ("scene-load-cam-northcell", "Load North Cell", QKeySequence(Qt::KeypadModifier | Qt::Key_8)); diff --git a/apps/opencs/view/render/instancemode.cpp b/apps/opencs/view/render/instancemode.cpp index 92f5cbb2d..c0d478113 100644 --- a/apps/opencs/view/render/instancemode.cpp +++ b/apps/opencs/view/render/instancemode.cpp @@ -6,6 +6,10 @@ #include "../../model/prefs/state.hpp" +#include +#include +#include + #include "../../model/world/idtable.hpp" #include "../../model/world/idtree.hpp" #include "../../model/world/commands.hpp" @@ -90,16 +94,20 @@ osg::Vec3f CSVRender::InstanceMode::getScreenCoords(const osg::Vec3f& pos) return pos * combined; } -CSVRender::InstanceMode::InstanceMode (WorldspaceWidget *worldspaceWidget, QWidget *parent) +CSVRender::InstanceMode::InstanceMode (WorldspaceWidget *worldspaceWidget, osg::ref_ptr parentNode, QWidget *parent) : EditMode (worldspaceWidget, QIcon (":scenetoolbar/editing-instance"), SceneUtil::Mask_EditorReference | SceneUtil::Mask_Terrain, "Instance editing", parent), mSubMode (0), mSubModeId ("move"), mSelectionMode (0), mDragMode (DragMode_None), - mDragAxis (-1), mLocked (false), mUnitScaleDist(1) + mDragAxis (-1), mLocked (false), mUnitScaleDist(1), mParentNode (parentNode) { connect(this, SIGNAL(requestFocus(const std::string&)), worldspaceWidget, SIGNAL(requestFocus(const std::string&))); CSMPrefs::Shortcut* deleteShortcut = new CSMPrefs::Shortcut("scene-delete", worldspaceWidget); connect(deleteShortcut, SIGNAL(activated(bool)), this, SLOT(deleteSelectedInstances(bool))); + CSMPrefs::Shortcut* dropShortcut = new CSMPrefs::Shortcut("scene-instance-drop", worldspaceWidget); + connect(dropShortcut, SIGNAL(activated(bool)), this, SLOT(dropSelectedInstances(bool))); + CSMPrefs::Shortcut* groundlevelShortcut = new CSMPrefs::Shortcut("scene-instance-groundlevel", worldspaceWidget); + connect(groundlevelShortcut, SIGNAL(activated(bool)), this, SLOT(groundlevelSelectedInstances(bool))); } void CSVRender::InstanceMode::activate (CSVWidget::SceneToolbar *toolbar) @@ -681,3 +689,75 @@ void CSVRender::InstanceMode::deleteSelectedInstances(bool active) getWorldspaceWidget().clearSelection (SceneUtil::Mask_EditorReference); } + +void CSVRender::InstanceMode::dropInstance(DropMode dropMode, CSVRender::Object* object) +{ + const osg::Vec3d& point = object->getPosition().asVec3(); + osg::ref_ptr objectNode = object->getRootNode(); + osg::Node::NodeMask oldMask = objectNode->getNodeMask(); + objectNode->setNodeMask(SceneUtil::Mask_Disabled); + + osg::Vec3d start = point; + osg::Vec3d end = point; + start.z() += 1.0f; + end.z() = std::numeric_limits::min(); + + osg::ref_ptr intersector (new osgUtil::LineSegmentIntersector( + osgUtil::Intersector::MODEL, start, end) ); + intersector->setIntersectionLimit(osgUtil::LineSegmentIntersector::NO_LIMIT); + osgUtil::IntersectionVisitor visitor(intersector); + + if (dropMode == Ground_level) visitor.setTraversalMask(SceneUtil::Mask_Terrain); + if (dropMode == Coillision_Below) visitor.setTraversalMask(SceneUtil::Mask_Terrain | SceneUtil::Mask_EditorReference); + + mParentNode->accept(visitor); + + for (osgUtil::LineSegmentIntersector::Intersections::iterator it = intersector->getIntersections().begin(); + it != intersector->getIntersections().end(); ++it) + { + osgUtil::LineSegmentIntersector::Intersection intersection = *it; + ESM::Position position = object->getPosition(); + object->setEdited (Object::Override_Position); + position.pos[2] = intersection.getWorldIntersectPoint().z(); + object->setPosition(position.pos); + objectNode->setNodeMask(oldMask); + + return; + } + + objectNode->setNodeMask(oldMask); +} + +void CSVRender::InstanceMode::dropSelectedInstances(bool active) +{ + std::vector > selection = getWorldspaceWidget().getSelection (SceneUtil::Mask_EditorReference); + if (selection.empty()) return; + + CSMDoc::Document& document = getWorldspaceWidget().getDocument(); + QUndoStack& undoStack = document.getUndoStack(); + + CSMWorld::CommandMacro macro (undoStack, "Drop Instances to next coillision"); + for(osg::ref_ptr tag: selection) + if (CSVRender::ObjectTag *objectTag = dynamic_cast (tag.get())) + { + dropInstance(Coillision_Below, objectTag->mObject); + objectTag->mObject->apply (macro); + } +} + +void CSVRender::InstanceMode::groundlevelSelectedInstances(bool active) +{ + std::vector > selection = getWorldspaceWidget().getSelection (SceneUtil::Mask_EditorReference); + if (selection.empty()) return; + + CSMDoc::Document& document = getWorldspaceWidget().getDocument(); + QUndoStack& undoStack = document.getUndoStack(); + + CSMWorld::CommandMacro macro (undoStack, "Drop Instances to Ground Level"); + for(osg::ref_ptr tag: selection) + if (CSVRender::ObjectTag *objectTag = dynamic_cast (tag.get())) + { + dropInstance(Ground_level, objectTag->mObject); + objectTag->mObject->apply (macro); + } +} diff --git a/apps/opencs/view/render/instancemode.hpp b/apps/opencs/view/render/instancemode.hpp index 23df3f37d..4d7ac9cac 100644 --- a/apps/opencs/view/render/instancemode.hpp +++ b/apps/opencs/view/render/instancemode.hpp @@ -2,6 +2,7 @@ #define CSV_RENDER_INSTANCEMODE_H #include +#include #include #include @@ -16,6 +17,7 @@ namespace CSVRender { class TagBase; class InstanceSelectionMode; + class Object; class InstanceMode : public EditMode { @@ -29,6 +31,12 @@ namespace CSVRender DragMode_Scale }; + enum DropMode + { + Coillision_Below, + Ground_level + }; + CSVWidget::SceneToolMode *mSubMode; std::string mSubModeId; InstanceSelectionMode *mSelectionMode; @@ -36,6 +44,7 @@ namespace CSVRender int mDragAxis; bool mLocked; float mUnitScaleDist; + osg::ref_ptr mParentNode; int getSubModeFromId (const std::string& id) const; @@ -44,10 +53,11 @@ namespace CSVRender osg::Vec3f getSelectionCenter(const std::vector >& selection) const; osg::Vec3f getScreenCoords(const osg::Vec3f& pos); + void dropInstance(DropMode dropMode, CSVRender::Object* object); public: - InstanceMode (WorldspaceWidget *worldspaceWidget, QWidget *parent = 0); + InstanceMode (WorldspaceWidget *worldspaceWidget, osg::ref_ptr parentNode, QWidget *parent = 0); virtual void activate (CSVWidget::SceneToolbar *toolbar); @@ -93,6 +103,8 @@ namespace CSVRender void subModeChanged (const std::string& id); void deleteSelectedInstances(bool active); + void dropSelectedInstances(bool active); + void groundlevelSelectedInstances(bool active); }; } diff --git a/apps/opencs/view/render/object.cpp b/apps/opencs/view/render/object.cpp index 02f89b0ab..47754b448 100644 --- a/apps/opencs/view/render/object.cpp +++ b/apps/opencs/view/render/object.cpp @@ -477,6 +477,11 @@ bool CSVRender::Object::getSelected() const return mSelected; } +osg::ref_ptr CSVRender::Object::getRootNode() +{ + return mRootNode; +} + bool CSVRender::Object::referenceableDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) { diff --git a/apps/opencs/view/render/object.hpp b/apps/opencs/view/render/object.hpp index 10a46fc10..b2561d585 100644 --- a/apps/opencs/view/render/object.hpp +++ b/apps/opencs/view/render/object.hpp @@ -146,6 +146,9 @@ namespace CSVRender bool getSelected() const; + /// For direct altering of object rendering + osg::ref_ptr getRootNode(); + /// \return Did this call result in a modification of the visual representation of /// this object? bool referenceableDataChanged (const QModelIndex& topLeft, diff --git a/apps/opencs/view/render/worldspacewidget.cpp b/apps/opencs/view/render/worldspacewidget.cpp index 77ecba918..6ab4b041b 100644 --- a/apps/opencs/view/render/worldspacewidget.cpp +++ b/apps/opencs/view/render/worldspacewidget.cpp @@ -370,7 +370,7 @@ void CSVRender::WorldspaceWidget::addVisibilitySelectorButtons ( void CSVRender::WorldspaceWidget::addEditModeSelectorButtons (CSVWidget::SceneToolMode *tool) { /// \todo replace EditMode with suitable subclasses - tool->addButton (new InstanceMode (this, tool), "object"); + tool->addButton (new InstanceMode (this, mRootNode, tool), "object"); tool->addButton (new PathgridMode (this, tool), "pathgrid"); } From 711dc59f09775120e1e5373145c1cb1435d56727 Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Fri, 28 Feb 2020 13:20:57 +0200 Subject: [PATCH 70/99] Implement four different functions for dropping --- apps/opencs/model/prefs/state.cpp | 6 +- apps/opencs/view/render/instancemode.cpp | 139 ++++++++++++++++++----- apps/opencs/view/render/instancemode.hpp | 16 ++- 3 files changed, 124 insertions(+), 37 deletions(-) diff --git a/apps/opencs/model/prefs/state.cpp b/apps/opencs/model/prefs/state.cpp index 422e2c1f4..89b5283a4 100644 --- a/apps/opencs/model/prefs/state.cpp +++ b/apps/opencs/model/prefs/state.cpp @@ -356,8 +356,10 @@ void CSMPrefs::State::declare() QKeySequence(Qt::ControlModifier | (int)Qt::MiddleButton)); declareModifier ("scene-speed-modifier", "Speed Modifier", Qt::Key_Shift); declareShortcut ("scene-delete", "Delete Instance", QKeySequence(Qt::Key_Delete)); - declareShortcut ("scene-instance-groundlevel", "Instance to Ground Level", QKeySequence(Qt::Key_G)); - declareShortcut ("scene-instance-drop", "Drop Instance", QKeySequence(Qt::Key_H)); + declareShortcut ("scene-instance-drop-terrain", "Drop to terrain level", QKeySequence(Qt::Key_G)); + declareShortcut ("scene-instance-drop-collision", "Drop to collision", QKeySequence(Qt::Key_H)); + declareShortcut ("scene-instance-drop-terrain-separately", "Drop to terrain level separately", QKeySequence()); + declareShortcut ("scene-instance-drop-collision-separately", "Drop to collision separately", QKeySequence()); declareShortcut ("scene-load-cam-cell", "Load Camera Cell", QKeySequence(Qt::KeypadModifier | Qt::Key_5)); declareShortcut ("scene-load-cam-eastcell", "Load East Cell", QKeySequence(Qt::KeypadModifier | Qt::Key_6)); declareShortcut ("scene-load-cam-northcell", "Load North Cell", QKeySequence(Qt::KeypadModifier | Qt::Key_8)); diff --git a/apps/opencs/view/render/instancemode.cpp b/apps/opencs/view/render/instancemode.cpp index c0d478113..09294c122 100644 --- a/apps/opencs/view/render/instancemode.cpp +++ b/apps/opencs/view/render/instancemode.cpp @@ -3,6 +3,7 @@ #include #include +#include #include "../../model/prefs/state.hpp" @@ -104,10 +105,16 @@ CSVRender::InstanceMode::InstanceMode (WorldspaceWidget *worldspaceWidget, osg: CSMPrefs::Shortcut* deleteShortcut = new CSMPrefs::Shortcut("scene-delete", worldspaceWidget); connect(deleteShortcut, SIGNAL(activated(bool)), this, SLOT(deleteSelectedInstances(bool))); - CSMPrefs::Shortcut* dropShortcut = new CSMPrefs::Shortcut("scene-instance-drop", worldspaceWidget); - connect(dropShortcut, SIGNAL(activated(bool)), this, SLOT(dropSelectedInstances(bool))); - CSMPrefs::Shortcut* groundlevelShortcut = new CSMPrefs::Shortcut("scene-instance-groundlevel", worldspaceWidget); - connect(groundlevelShortcut, SIGNAL(activated(bool)), this, SLOT(groundlevelSelectedInstances(bool))); + + // Following classes could be simplified by using QSignalMapper, which is obsolete in Qt5.10, but not in Qt4.8 and Qt5.14 + CSMPrefs::Shortcut* dropToCollisionShortcut = new CSMPrefs::Shortcut("scene-instance-drop-collision", worldspaceWidget); + connect(dropToCollisionShortcut, SIGNAL(activated(bool)), this, SLOT(dropSelectedInstancesToCollision(bool))); + CSMPrefs::Shortcut* dropToTerrainLevelShortcut = new CSMPrefs::Shortcut("scene-instance-drop-terrain", worldspaceWidget); + connect(dropToTerrainLevelShortcut, SIGNAL(activated(bool)), this, SLOT(dropSelectedInstancesToTerrain(bool))); + CSMPrefs::Shortcut* dropToCollisionShortcut2 = new CSMPrefs::Shortcut("scene-instance-drop-collision-separately", worldspaceWidget); + connect(dropToCollisionShortcut2, SIGNAL(activated(bool)), this, SLOT(dropSelectedInstancesToCollisionSeparately(bool))); + CSMPrefs::Shortcut* dropToTerrainLevelShortcut2 = new CSMPrefs::Shortcut("scene-instance-drop-terrain-separately", worldspaceWidget); + connect(dropToTerrainLevelShortcut2, SIGNAL(activated(bool)), this, SLOT(dropSelectedInstancesToTerrainSeparately(bool))); } void CSVRender::InstanceMode::activate (CSVWidget::SceneToolbar *toolbar) @@ -707,8 +714,8 @@ void CSVRender::InstanceMode::dropInstance(DropMode dropMode, CSVRender::Object* intersector->setIntersectionLimit(osgUtil::LineSegmentIntersector::NO_LIMIT); osgUtil::IntersectionVisitor visitor(intersector); - if (dropMode == Ground_level) visitor.setTraversalMask(SceneUtil::Mask_Terrain); - if (dropMode == Coillision_Below) visitor.setTraversalMask(SceneUtil::Mask_Terrain | SceneUtil::Mask_EditorReference); + if (dropMode == Terrain_sep) visitor.setTraversalMask(SceneUtil::Mask_Terrain); + if (dropMode == Collision_sep) visitor.setTraversalMask(SceneUtil::Mask_Terrain | SceneUtil::Mask_EditorReference); mParentNode->accept(visitor); @@ -728,7 +735,63 @@ void CSVRender::InstanceMode::dropInstance(DropMode dropMode, CSVRender::Object* objectNode->setNodeMask(oldMask); } -void CSVRender::InstanceMode::dropSelectedInstances(bool active) +float CSVRender::InstanceMode::getDropHeight(DropMode dropMode, CSVRender::Object* object) +{ + const osg::Vec3d& point = object->getPosition().asVec3(); + osg::ref_ptr objectNode = object->getRootNode(); + osg::Node::NodeMask oldMask = objectNode->getNodeMask(); + objectNode->setNodeMask(SceneUtil::Mask_Disabled); + + osg::Vec3d start = point; + osg::Vec3d end = point; + start.z() += 1.0f; + end.z() = std::numeric_limits::min(); + + osg::ref_ptr intersector (new osgUtil::LineSegmentIntersector( + osgUtil::Intersector::MODEL, start, end) ); + intersector->setIntersectionLimit(osgUtil::LineSegmentIntersector::NO_LIMIT); + osgUtil::IntersectionVisitor visitor(intersector); + + if (dropMode == Terrain) visitor.setTraversalMask(SceneUtil::Mask_Terrain); + if (dropMode == Collision) visitor.setTraversalMask(SceneUtil::Mask_Terrain | SceneUtil::Mask_EditorReference); + + mParentNode->accept(visitor); + + for (osgUtil::LineSegmentIntersector::Intersections::iterator it = intersector->getIntersections().begin(); + it != intersector->getIntersections().end(); ++it) + { + osgUtil::LineSegmentIntersector::Intersection intersection = *it; + ESM::Position position = object->getPosition(); + float dropHeight = intersection.getWorldIntersectPoint().z(); + objectNode->setNodeMask(oldMask); + return position.pos[2] - dropHeight; + } + + objectNode->setNodeMask(oldMask); + return 0.0f; +} + +void CSVRender::InstanceMode::dropSelectedInstancesToCollision(bool active) +{ + handleDropMethod(Collision, "Drop instances to next collision"); +} + +void CSVRender::InstanceMode::dropSelectedInstancesToTerrain(bool active) +{ + handleDropMethod(Terrain, "Drop instances to terrain level"); +} + +void CSVRender::InstanceMode::dropSelectedInstancesToCollisionSeparately(bool active) +{ + handleDropMethod(Terrain_sep, "Drop instances to next collision level separately"); +} + +void CSVRender::InstanceMode::dropSelectedInstancesToTerrainSeparately(bool active) +{ + handleDropMethod(Collision_sep, "Drop instances to terrain level separately"); +} + +void CSVRender::InstanceMode::handleDropMethod(DropMode dropMode, QString commandMsg) { std::vector > selection = getWorldspaceWidget().getSelection (SceneUtil::Mask_EditorReference); if (selection.empty()) return; @@ -736,28 +799,42 @@ void CSVRender::InstanceMode::dropSelectedInstances(bool active) CSMDoc::Document& document = getWorldspaceWidget().getDocument(); QUndoStack& undoStack = document.getUndoStack(); - CSMWorld::CommandMacro macro (undoStack, "Drop Instances to next coillision"); - for(osg::ref_ptr tag: selection) - if (CSVRender::ObjectTag *objectTag = dynamic_cast (tag.get())) - { - dropInstance(Coillision_Below, objectTag->mObject); - objectTag->mObject->apply (macro); - } -} - -void CSVRender::InstanceMode::groundlevelSelectedInstances(bool active) -{ - std::vector > selection = getWorldspaceWidget().getSelection (SceneUtil::Mask_EditorReference); - if (selection.empty()) return; - - CSMDoc::Document& document = getWorldspaceWidget().getDocument(); - QUndoStack& undoStack = document.getUndoStack(); - - CSMWorld::CommandMacro macro (undoStack, "Drop Instances to Ground Level"); - for(osg::ref_ptr tag: selection) - if (CSVRender::ObjectTag *objectTag = dynamic_cast (tag.get())) - { - dropInstance(Ground_level, objectTag->mObject); - objectTag->mObject->apply (macro); - } + CSMWorld::CommandMacro macro (undoStack, commandMsg); + + switch (dropMode) + { + case Terrain: + case Collision: + { + float smallestDropHeight = std::numeric_limits::max(); + for(osg::ref_ptr tag: selection) + if (CSVRender::ObjectTag *objectTag = dynamic_cast (tag.get())) + { + float thisDrop = getDropHeight(dropMode, objectTag->mObject); + if (thisDrop < smallestDropHeight) smallestDropHeight = thisDrop; + } + for(osg::ref_ptr tag: selection) + if (CSVRender::ObjectTag *objectTag = dynamic_cast (tag.get())) + { + objectTag->mObject->setEdited (Object::Override_Position); + ESM::Position position = objectTag->mObject->getPosition(); + position.pos[2] -= smallestDropHeight; + objectTag->mObject->setPosition(position.pos); + objectTag->mObject->apply (macro); + } + } + break; + + case Terrain_sep: + case Collision_sep: + for(osg::ref_ptr tag: selection) + if (CSVRender::ObjectTag *objectTag = dynamic_cast (tag.get())) + { + dropInstance(dropMode, objectTag->mObject); + objectTag->mObject->apply (macro); + } + break; + default: + break; + } } diff --git a/apps/opencs/view/render/instancemode.hpp b/apps/opencs/view/render/instancemode.hpp index 4d7ac9cac..883dab405 100644 --- a/apps/opencs/view/render/instancemode.hpp +++ b/apps/opencs/view/render/instancemode.hpp @@ -1,6 +1,8 @@ #ifndef CSV_RENDER_INSTANCEMODE_H #define CSV_RENDER_INSTANCEMODE_H +#include + #include #include #include @@ -33,8 +35,10 @@ namespace CSVRender enum DropMode { - Coillision_Below, - Ground_level + Collision, + Terrain, + Collision_sep, + Terrain_sep }; CSVWidget::SceneToolMode *mSubMode; @@ -54,6 +58,7 @@ namespace CSVRender osg::Vec3f getSelectionCenter(const std::vector >& selection) const; osg::Vec3f getScreenCoords(const osg::Vec3f& pos); void dropInstance(DropMode dropMode, CSVRender::Object* object); + float getDropHeight(DropMode dropMode, CSVRender::Object* object); public: @@ -103,8 +108,11 @@ namespace CSVRender void subModeChanged (const std::string& id); void deleteSelectedInstances(bool active); - void dropSelectedInstances(bool active); - void groundlevelSelectedInstances(bool active); + void dropSelectedInstancesToCollision(bool active); + void dropSelectedInstancesToTerrain(bool active); + void dropSelectedInstancesToCollisionSeparately(bool active); + void dropSelectedInstancesToTerrainSeparately(bool active); + void handleDropMethod(DropMode dropMode, QString commandMsg); }; } From 54738e1e326308b9cd69c8cd26d4467fac59ca24 Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Sat, 29 Feb 2020 14:56:11 +0200 Subject: [PATCH 71/99] Handle mask disabling better --- apps/opencs/view/render/instancemode.cpp | 106 ++++++++++++++--------- 1 file changed, 63 insertions(+), 43 deletions(-) diff --git a/apps/opencs/view/render/instancemode.cpp b/apps/opencs/view/render/instancemode.cpp index 09294c122..12f14b48d 100644 --- a/apps/opencs/view/render/instancemode.cpp +++ b/apps/opencs/view/render/instancemode.cpp @@ -20,6 +20,7 @@ #include "../widget/scenetoolbar.hpp" #include "../widget/scenetoolmode.hpp" +#include #include #include "object.hpp" @@ -700,9 +701,6 @@ void CSVRender::InstanceMode::deleteSelectedInstances(bool active) void CSVRender::InstanceMode::dropInstance(DropMode dropMode, CSVRender::Object* object) { const osg::Vec3d& point = object->getPosition().asVec3(); - osg::ref_ptr objectNode = object->getRootNode(); - osg::Node::NodeMask oldMask = objectNode->getNodeMask(); - objectNode->setNodeMask(SceneUtil::Mask_Disabled); osg::Vec3d start = point; osg::Vec3d end = point; @@ -727,20 +725,15 @@ void CSVRender::InstanceMode::dropInstance(DropMode dropMode, CSVRender::Object* object->setEdited (Object::Override_Position); position.pos[2] = intersection.getWorldIntersectPoint().z(); object->setPosition(position.pos); - objectNode->setNodeMask(oldMask); return; } - - objectNode->setNodeMask(oldMask); } float CSVRender::InstanceMode::getDropHeight(DropMode dropMode, CSVRender::Object* object) { const osg::Vec3d& point = object->getPosition().asVec3(); osg::ref_ptr objectNode = object->getRootNode(); - osg::Node::NodeMask oldMask = objectNode->getNodeMask(); - objectNode->setNodeMask(SceneUtil::Mask_Disabled); osg::Vec3d start = point; osg::Vec3d end = point; @@ -763,11 +756,9 @@ float CSVRender::InstanceMode::getDropHeight(DropMode dropMode, CSVRender::Objec osgUtil::LineSegmentIntersector::Intersection intersection = *it; ESM::Position position = object->getPosition(); float dropHeight = intersection.getWorldIntersectPoint().z(); - objectNode->setNodeMask(oldMask); return position.pos[2] - dropHeight; } - objectNode->setNodeMask(oldMask); return 0.0f; } @@ -801,40 +792,69 @@ void CSVRender::InstanceMode::handleDropMethod(DropMode dropMode, QString comman CSMWorld::CommandMacro macro (undoStack, commandMsg); - switch (dropMode) + std::vector oldMasks; + for(osg::ref_ptr tag: selection) { - case Terrain: - case Collision: - { - float smallestDropHeight = std::numeric_limits::max(); - for(osg::ref_ptr tag: selection) - if (CSVRender::ObjectTag *objectTag = dynamic_cast (tag.get())) - { - float thisDrop = getDropHeight(dropMode, objectTag->mObject); - if (thisDrop < smallestDropHeight) smallestDropHeight = thisDrop; - } - for(osg::ref_ptr tag: selection) - if (CSVRender::ObjectTag *objectTag = dynamic_cast (tag.get())) - { - objectTag->mObject->setEdited (Object::Override_Position); - ESM::Position position = objectTag->mObject->getPosition(); - position.pos[2] -= smallestDropHeight; - objectTag->mObject->setPosition(position.pos); - objectTag->mObject->apply (macro); - } - } - break; + if (CSVRender::ObjectTag *objectTag = dynamic_cast (tag.get())) + { + osg::ref_ptr objectNode = objectTag->mObject->getRootNode(); + oldMasks.emplace_back(objectNode->getNodeMask()); + objectNode->setNodeMask(SceneUtil::Mask_Disabled); + } + } - case Terrain_sep: - case Collision_sep: - for(osg::ref_ptr tag: selection) - if (CSVRender::ObjectTag *objectTag = dynamic_cast (tag.get())) - { - dropInstance(dropMode, objectTag->mObject); - objectTag->mObject->apply (macro); - } - break; - default: - break; + try + { + switch (dropMode) + { + case Terrain: + case Collision: + { + float smallestDropHeight = std::numeric_limits::max(); + for(osg::ref_ptr tag: selection) + if (CSVRender::ObjectTag *objectTag = dynamic_cast (tag.get())) + { + float thisDrop = getDropHeight(dropMode, objectTag->mObject); + if (thisDrop < smallestDropHeight) smallestDropHeight = thisDrop; + } + for(osg::ref_ptr tag: selection) + if (CSVRender::ObjectTag *objectTag = dynamic_cast (tag.get())) + { + objectTag->mObject->setEdited (Object::Override_Position); + ESM::Position position = objectTag->mObject->getPosition(); + position.pos[2] -= smallestDropHeight; + objectTag->mObject->setPosition(position.pos); + objectTag->mObject->apply (macro); + } + } + break; + + case Terrain_sep: + case Collision_sep: + for(osg::ref_ptr tag: selection) + if (CSVRender::ObjectTag *objectTag = dynamic_cast (tag.get())) + { + dropInstance(dropMode, objectTag->mObject); + objectTag->mObject->apply (macro); + } + break; + default: + break; + } + } + catch (const std::exception& e) + { + Log(Debug::Error) << "Error in dropping instance: " << e.what(); + } + + int counter = 0; + for(osg::ref_ptr tag: selection) + { + if (CSVRender::ObjectTag *objectTag = dynamic_cast (tag.get())) + { + osg::ref_ptr objectNode = objectTag->mObject->getRootNode(); + objectNode->setNodeMask(oldMasks[counter]); + counter++; + } } } From b8e6257bebed1baffb177ce6fca35f859e604798 Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Sat, 29 Feb 2020 14:59:03 +0200 Subject: [PATCH 72/99] Add changelog --- CHANGELOG.md | 1 + CHANGELOG_PR.md | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 32255262e..73d6320d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -263,6 +263,7 @@ Feature #5193: Weapon sheathing Feature #5219: Impelement TestCells console command Feature #5224: Handle NiKeyframeController for NiTriShape + Feature #5274: Editor: Keyboard shortcut to drop objects to ground/obstacle in scene view Feature #5304: Morrowind-style bump-mapping Task #4686: Upgrade media decoder to a more current FFmpeg API Task #4695: Optimize Distant Terrain memory consumption diff --git a/CHANGELOG_PR.md b/CHANGELOG_PR.md index f462da32c..94b0d45ec 100644 --- a/CHANGELOG_PR.md +++ b/CHANGELOG_PR.md @@ -43,6 +43,7 @@ New Editor Features: - Changes to height editing can be cancelled without changes to data (press esc to cancel) (#4840) - Land heightmap/shape editing and vertex selection (#5170) - Deleting instances with a keypress (#5172) +- Dropping objects with keyboard shortcuts (#5274) Bug Fixes: - The Mouse Wheel can now be used for key bindings (#2679) From 035d5205d97faa8cc75341f77b14b67d057a20d2 Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Tue, 3 Mar 2020 21:18:09 +0200 Subject: [PATCH 73/99] Use collision boxes to calculate dropping height, variable naming --- apps/opencs/view/render/instancemode.cpp | 37 +++++++++++++++--------- apps/opencs/view/render/instancemode.hpp | 2 +- apps/opencs/view/render/object.cpp | 5 ++++ apps/opencs/view/render/object.hpp | 5 +++- 4 files changed, 34 insertions(+), 15 deletions(-) diff --git a/apps/opencs/view/render/instancemode.cpp b/apps/opencs/view/render/instancemode.cpp index 12f14b48d..6f0ed01a0 100644 --- a/apps/opencs/view/render/instancemode.cpp +++ b/apps/opencs/view/render/instancemode.cpp @@ -7,6 +7,7 @@ #include "../../model/prefs/state.hpp" +#include #include #include #include @@ -704,7 +705,6 @@ void CSVRender::InstanceMode::dropInstance(DropMode dropMode, CSVRender::Object* osg::Vec3d start = point; osg::Vec3d end = point; - start.z() += 1.0f; end.z() = std::numeric_limits::min(); osg::ref_ptr intersector (new osgUtil::LineSegmentIntersector( @@ -730,14 +730,12 @@ void CSVRender::InstanceMode::dropInstance(DropMode dropMode, CSVRender::Object* } } -float CSVRender::InstanceMode::getDropHeight(DropMode dropMode, CSVRender::Object* object) +float CSVRender::InstanceMode::getDropHeight(DropMode dropMode, CSVRender::Object* object, float objectHeight) { const osg::Vec3d& point = object->getPosition().asVec3(); - osg::ref_ptr objectNode = object->getRootNode(); osg::Vec3d start = point; osg::Vec3d end = point; - start.z() += 1.0f; end.z() = std::numeric_limits::min(); osg::ref_ptr intersector (new osgUtil::LineSegmentIntersector( @@ -754,9 +752,8 @@ float CSVRender::InstanceMode::getDropHeight(DropMode dropMode, CSVRender::Objec it != intersector->getIntersections().end(); ++it) { osgUtil::LineSegmentIntersector::Intersection intersection = *it; - ESM::Position position = object->getPosition(); - float dropHeight = intersection.getWorldIntersectPoint().z(); - return position.pos[2] - dropHeight; + float collisionLevel = intersection.getWorldIntersectPoint().z(); + return point.z() - collisionLevel + objectHeight; } return 0.0f; @@ -793,13 +790,25 @@ void CSVRender::InstanceMode::handleDropMethod(DropMode dropMode, QString comman CSMWorld::CommandMacro macro (undoStack, commandMsg); std::vector oldMasks; + std::vector objectHeights; for(osg::ref_ptr tag: selection) { if (CSVRender::ObjectTag *objectTag = dynamic_cast (tag.get())) { - osg::ref_ptr objectNode = objectTag->mObject->getRootNode(); - oldMasks.emplace_back(objectNode->getNodeMask()); - objectNode->setNodeMask(SceneUtil::Mask_Disabled); + osg::ref_ptr objectNodeWithGUI = objectTag->mObject->getRootNode(); + osg::ref_ptr objectNodeWithoutGUI = objectTag->mObject->getBaseNode(); + + osg::ComputeBoundsVisitor computeBounds; + computeBounds.setTraversalMask(SceneUtil::Mask_EditorReference); + objectNodeWithoutGUI->accept(computeBounds); + osg::BoundingBox bounds = computeBounds.getBoundingBox(); + float boundingBoxOffset = 0.0f; + if (bounds.valid()) boundingBoxOffset = bounds.zMin(); + + objectHeights.emplace_back(boundingBoxOffset); + oldMasks.emplace_back(objectNodeWithGUI->getNodeMask()); + + objectNodeWithGUI->setNodeMask(SceneUtil::Mask_Disabled); } } @@ -811,11 +820,13 @@ void CSVRender::InstanceMode::handleDropMethod(DropMode dropMode, QString comman case Collision: { float smallestDropHeight = std::numeric_limits::max(); + int counter = 0; for(osg::ref_ptr tag: selection) if (CSVRender::ObjectTag *objectTag = dynamic_cast (tag.get())) { - float thisDrop = getDropHeight(dropMode, objectTag->mObject); + float thisDrop = getDropHeight(dropMode, objectTag->mObject, objectHeights[counter]); if (thisDrop < smallestDropHeight) smallestDropHeight = thisDrop; + counter++; } for(osg::ref_ptr tag: selection) if (CSVRender::ObjectTag *objectTag = dynamic_cast (tag.get())) @@ -852,8 +863,8 @@ void CSVRender::InstanceMode::handleDropMethod(DropMode dropMode, QString comman { if (CSVRender::ObjectTag *objectTag = dynamic_cast (tag.get())) { - osg::ref_ptr objectNode = objectTag->mObject->getRootNode(); - objectNode->setNodeMask(oldMasks[counter]); + osg::ref_ptr objectNodeWithGUI = objectTag->mObject->getRootNode(); + objectNodeWithGUI->setNodeMask(oldMasks[counter]); counter++; } } diff --git a/apps/opencs/view/render/instancemode.hpp b/apps/opencs/view/render/instancemode.hpp index 883dab405..38280dd6f 100644 --- a/apps/opencs/view/render/instancemode.hpp +++ b/apps/opencs/view/render/instancemode.hpp @@ -58,7 +58,7 @@ namespace CSVRender osg::Vec3f getSelectionCenter(const std::vector >& selection) const; osg::Vec3f getScreenCoords(const osg::Vec3f& pos); void dropInstance(DropMode dropMode, CSVRender::Object* object); - float getDropHeight(DropMode dropMode, CSVRender::Object* object); + float getDropHeight(DropMode dropMode, CSVRender::Object* object, float objectHeight); public: diff --git a/apps/opencs/view/render/object.cpp b/apps/opencs/view/render/object.cpp index 47754b448..6b33cdeb2 100644 --- a/apps/opencs/view/render/object.cpp +++ b/apps/opencs/view/render/object.cpp @@ -482,6 +482,11 @@ osg::ref_ptr CSVRender::Object::getRootNode() return mRootNode; } +osg::ref_ptr CSVRender::Object::getBaseNode() +{ + return mBaseNode; +} + bool CSVRender::Object::referenceableDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) { diff --git a/apps/opencs/view/render/object.hpp b/apps/opencs/view/render/object.hpp index b2561d585..4d1763f80 100644 --- a/apps/opencs/view/render/object.hpp +++ b/apps/opencs/view/render/object.hpp @@ -146,9 +146,12 @@ namespace CSVRender bool getSelected() const; - /// For direct altering of object rendering + /// Get object node with GUI graphics osg::ref_ptr getRootNode(); + /// Get object node without GUI graphics + osg::ref_ptr getBaseNode(); + /// \return Did this call result in a modification of the visual representation of /// this object? bool referenceableDataChanged (const QModelIndex& topLeft, From af434cffba97965cfb28d1f90a4dc739d10ff2d9 Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Wed, 4 Mar 2020 16:49:37 +0200 Subject: [PATCH 74/99] fix numeric limits min() to lowest(), fix correct drop height --- apps/opencs/view/render/instancemode.cpp | 32 ++++++++++++++---------- apps/opencs/view/render/instancemode.hpp | 10 ++++---- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/apps/opencs/view/render/instancemode.cpp b/apps/opencs/view/render/instancemode.cpp index 6f0ed01a0..d7ab59b53 100644 --- a/apps/opencs/view/render/instancemode.cpp +++ b/apps/opencs/view/render/instancemode.cpp @@ -110,13 +110,13 @@ CSVRender::InstanceMode::InstanceMode (WorldspaceWidget *worldspaceWidget, osg: // Following classes could be simplified by using QSignalMapper, which is obsolete in Qt5.10, but not in Qt4.8 and Qt5.14 CSMPrefs::Shortcut* dropToCollisionShortcut = new CSMPrefs::Shortcut("scene-instance-drop-collision", worldspaceWidget); - connect(dropToCollisionShortcut, SIGNAL(activated(bool)), this, SLOT(dropSelectedInstancesToCollision(bool))); + connect(dropToCollisionShortcut, SIGNAL(activated()), this, SLOT(dropSelectedInstancesToCollision())); CSMPrefs::Shortcut* dropToTerrainLevelShortcut = new CSMPrefs::Shortcut("scene-instance-drop-terrain", worldspaceWidget); - connect(dropToTerrainLevelShortcut, SIGNAL(activated(bool)), this, SLOT(dropSelectedInstancesToTerrain(bool))); + connect(dropToTerrainLevelShortcut, SIGNAL(activated()), this, SLOT(dropSelectedInstancesToTerrain())); CSMPrefs::Shortcut* dropToCollisionShortcut2 = new CSMPrefs::Shortcut("scene-instance-drop-collision-separately", worldspaceWidget); - connect(dropToCollisionShortcut2, SIGNAL(activated(bool)), this, SLOT(dropSelectedInstancesToCollisionSeparately(bool))); + connect(dropToCollisionShortcut2, SIGNAL(activated()), this, SLOT(dropSelectedInstancesToCollisionSeparately())); CSMPrefs::Shortcut* dropToTerrainLevelShortcut2 = new CSMPrefs::Shortcut("scene-instance-drop-terrain-separately", worldspaceWidget); - connect(dropToTerrainLevelShortcut2, SIGNAL(activated(bool)), this, SLOT(dropSelectedInstancesToTerrainSeparately(bool))); + connect(dropToTerrainLevelShortcut2, SIGNAL(activated()), this, SLOT(dropSelectedInstancesToTerrainSeparately())); } void CSVRender::InstanceMode::activate (CSVWidget::SceneToolbar *toolbar) @@ -699,13 +699,14 @@ void CSVRender::InstanceMode::deleteSelectedInstances(bool active) getWorldspaceWidget().clearSelection (SceneUtil::Mask_EditorReference); } -void CSVRender::InstanceMode::dropInstance(DropMode dropMode, CSVRender::Object* object) +void CSVRender::InstanceMode::dropInstance(DropMode dropMode, CSVRender::Object* object, float objectHeight) { const osg::Vec3d& point = object->getPosition().asVec3(); osg::Vec3d start = point; + start.z() += objectHeight; osg::Vec3d end = point; - end.z() = std::numeric_limits::min(); + end.z() = std::numeric_limits::lowest(); osg::ref_ptr intersector (new osgUtil::LineSegmentIntersector( osgUtil::Intersector::MODEL, start, end) ); @@ -723,7 +724,7 @@ void CSVRender::InstanceMode::dropInstance(DropMode dropMode, CSVRender::Object* osgUtil::LineSegmentIntersector::Intersection intersection = *it; ESM::Position position = object->getPosition(); object->setEdited (Object::Override_Position); - position.pos[2] = intersection.getWorldIntersectPoint().z(); + position.pos[2] = intersection.getWorldIntersectPoint().z() + objectHeight; object->setPosition(position.pos); return; @@ -735,8 +736,9 @@ float CSVRender::InstanceMode::getDropHeight(DropMode dropMode, CSVRender::Objec const osg::Vec3d& point = object->getPosition().asVec3(); osg::Vec3d start = point; + start.z() += objectHeight; osg::Vec3d end = point; - end.z() = std::numeric_limits::min(); + end.z() = std::numeric_limits::lowest(); osg::ref_ptr intersector (new osgUtil::LineSegmentIntersector( osgUtil::Intersector::MODEL, start, end) ); @@ -759,22 +761,22 @@ float CSVRender::InstanceMode::getDropHeight(DropMode dropMode, CSVRender::Objec return 0.0f; } -void CSVRender::InstanceMode::dropSelectedInstancesToCollision(bool active) +void CSVRender::InstanceMode::dropSelectedInstancesToCollision() { handleDropMethod(Collision, "Drop instances to next collision"); } -void CSVRender::InstanceMode::dropSelectedInstancesToTerrain(bool active) +void CSVRender::InstanceMode::dropSelectedInstancesToTerrain() { handleDropMethod(Terrain, "Drop instances to terrain level"); } -void CSVRender::InstanceMode::dropSelectedInstancesToCollisionSeparately(bool active) +void CSVRender::InstanceMode::dropSelectedInstancesToCollisionSeparately() { handleDropMethod(Terrain_sep, "Drop instances to next collision level separately"); } -void CSVRender::InstanceMode::dropSelectedInstancesToTerrainSeparately(bool active) +void CSVRender::InstanceMode::dropSelectedInstancesToTerrainSeparately() { handleDropMethod(Collision_sep, "Drop instances to terrain level separately"); } @@ -842,12 +844,16 @@ void CSVRender::InstanceMode::handleDropMethod(DropMode dropMode, QString comman case Terrain_sep: case Collision_sep: + { + int counter = 0; for(osg::ref_ptr tag: selection) if (CSVRender::ObjectTag *objectTag = dynamic_cast (tag.get())) { - dropInstance(dropMode, objectTag->mObject); + dropInstance(dropMode, objectTag->mObject, objectHeights[counter]); objectTag->mObject->apply (macro); + counter++; } + } break; default: break; diff --git a/apps/opencs/view/render/instancemode.hpp b/apps/opencs/view/render/instancemode.hpp index 38280dd6f..de7004f4f 100644 --- a/apps/opencs/view/render/instancemode.hpp +++ b/apps/opencs/view/render/instancemode.hpp @@ -57,7 +57,7 @@ namespace CSVRender osg::Vec3f getSelectionCenter(const std::vector >& selection) const; osg::Vec3f getScreenCoords(const osg::Vec3f& pos); - void dropInstance(DropMode dropMode, CSVRender::Object* object); + void dropInstance(DropMode dropMode, CSVRender::Object* object, float objectHeight); float getDropHeight(DropMode dropMode, CSVRender::Object* object, float objectHeight); public: @@ -108,10 +108,10 @@ namespace CSVRender void subModeChanged (const std::string& id); void deleteSelectedInstances(bool active); - void dropSelectedInstancesToCollision(bool active); - void dropSelectedInstancesToTerrain(bool active); - void dropSelectedInstancesToCollisionSeparately(bool active); - void dropSelectedInstancesToTerrainSeparately(bool active); + void dropSelectedInstancesToCollision(); + void dropSelectedInstancesToTerrain(); + void dropSelectedInstancesToCollisionSeparately(); + void dropSelectedInstancesToTerrainSeparately(); void handleDropMethod(DropMode dropMode, QString commandMsg); }; } From 41aa90bfa7c97a5a26ba05498bb3d51bfbd84b39 Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Wed, 18 Mar 2020 11:16:21 +0200 Subject: [PATCH 75/99] Add helper class for safer mask handling --- apps/opencs/view/render/instancemode.cpp | 113 ++++++++++++----------- apps/opencs/view/render/instancemode.hpp | 13 +++ 2 files changed, 71 insertions(+), 55 deletions(-) diff --git a/apps/opencs/view/render/instancemode.cpp b/apps/opencs/view/render/instancemode.cpp index d7ab59b53..42ab8e62e 100644 --- a/apps/opencs/view/render/instancemode.cpp +++ b/apps/opencs/view/render/instancemode.cpp @@ -791,8 +791,57 @@ void CSVRender::InstanceMode::handleDropMethod(DropMode dropMode, QString comman CSMWorld::CommandMacro macro (undoStack, commandMsg); - std::vector oldMasks; - std::vector objectHeights; + DropObjectDataHandler dropObjectDataHandler(&getWorldspaceWidget()); + + switch (dropMode) + { + case Terrain: + case Collision: + { + float smallestDropHeight = std::numeric_limits::max(); + int counter = 0; + for(osg::ref_ptr tag: selection) + if (CSVRender::ObjectTag *objectTag = dynamic_cast (tag.get())) + { + float thisDrop = getDropHeight(dropMode, objectTag->mObject, dropObjectDataHandler.mObjectHeights[counter]); + if (thisDrop < smallestDropHeight) smallestDropHeight = thisDrop; + counter++; + } + for(osg::ref_ptr tag: selection) + if (CSVRender::ObjectTag *objectTag = dynamic_cast (tag.get())) + { + objectTag->mObject->setEdited (Object::Override_Position); + ESM::Position position = objectTag->mObject->getPosition(); + position.pos[2] -= smallestDropHeight; + objectTag->mObject->setPosition(position.pos); + objectTag->mObject->apply (macro); + } + } + break; + + case Terrain_sep: + case Collision_sep: + { + int counter = 0; + for(osg::ref_ptr tag: selection) + if (CSVRender::ObjectTag *objectTag = dynamic_cast (tag.get())) + { + dropInstance(dropMode, objectTag->mObject, dropObjectDataHandler.mObjectHeights[counter]); + objectTag->mObject->apply (macro); + counter++; + } + } + break; + + default: + break; + } +} + +CSVRender::DropObjectDataHandler::DropObjectDataHandler(WorldspaceWidget* worldspacewidget) + : mWorldspaceWidget(worldspacewidget) +{ + std::vector > selection = mWorldspaceWidget->getSelection (SceneUtil::Mask_EditorReference); for(osg::ref_ptr tag: selection) { if (CSVRender::ObjectTag *objectTag = dynamic_cast (tag.get())) @@ -807,70 +856,24 @@ void CSVRender::InstanceMode::handleDropMethod(DropMode dropMode, QString comman float boundingBoxOffset = 0.0f; if (bounds.valid()) boundingBoxOffset = bounds.zMin(); - objectHeights.emplace_back(boundingBoxOffset); - oldMasks.emplace_back(objectNodeWithGUI->getNodeMask()); + mObjectHeights.emplace_back(boundingBoxOffset); + mOldMasks.emplace_back(objectNodeWithGUI->getNodeMask()); objectNodeWithGUI->setNodeMask(SceneUtil::Mask_Disabled); } } +} - try - { - switch (dropMode) - { - case Terrain: - case Collision: - { - float smallestDropHeight = std::numeric_limits::max(); - int counter = 0; - for(osg::ref_ptr tag: selection) - if (CSVRender::ObjectTag *objectTag = dynamic_cast (tag.get())) - { - float thisDrop = getDropHeight(dropMode, objectTag->mObject, objectHeights[counter]); - if (thisDrop < smallestDropHeight) smallestDropHeight = thisDrop; - counter++; - } - for(osg::ref_ptr tag: selection) - if (CSVRender::ObjectTag *objectTag = dynamic_cast (tag.get())) - { - objectTag->mObject->setEdited (Object::Override_Position); - ESM::Position position = objectTag->mObject->getPosition(); - position.pos[2] -= smallestDropHeight; - objectTag->mObject->setPosition(position.pos); - objectTag->mObject->apply (macro); - } - } - break; - - case Terrain_sep: - case Collision_sep: - { - int counter = 0; - for(osg::ref_ptr tag: selection) - if (CSVRender::ObjectTag *objectTag = dynamic_cast (tag.get())) - { - dropInstance(dropMode, objectTag->mObject, objectHeights[counter]); - objectTag->mObject->apply (macro); - counter++; - } - } - break; - default: - break; - } - } - catch (const std::exception& e) - { - Log(Debug::Error) << "Error in dropping instance: " << e.what(); - } - +CSVRender::DropObjectDataHandler::~DropObjectDataHandler() +{ + std::vector > selection = mWorldspaceWidget->getSelection (SceneUtil::Mask_EditorReference); int counter = 0; for(osg::ref_ptr tag: selection) { if (CSVRender::ObjectTag *objectTag = dynamic_cast (tag.get())) { osg::ref_ptr objectNodeWithGUI = objectTag->mObject->getRootNode(); - objectNodeWithGUI->setNodeMask(oldMasks[counter]); + objectNodeWithGUI->setNodeMask(mOldMasks[counter]); counter++; } } diff --git a/apps/opencs/view/render/instancemode.hpp b/apps/opencs/view/render/instancemode.hpp index de7004f4f..b1d5de7fe 100644 --- a/apps/opencs/view/render/instancemode.hpp +++ b/apps/opencs/view/render/instancemode.hpp @@ -114,6 +114,19 @@ namespace CSVRender void dropSelectedInstancesToTerrainSeparately(); void handleDropMethod(DropMode dropMode, QString commandMsg); }; + + /// \brief Helper class to handle object mask data in safe way + class DropObjectDataHandler + { + public: + DropObjectDataHandler(WorldspaceWidget* worldspacewidget); + ~DropObjectDataHandler(); + std::vector mObjectHeights; + + private: + WorldspaceWidget* mWorldspaceWidget; + std::vector mOldMasks; + }; } #endif From 75f6577f6113413ed5512d564064d047a41fa697 Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Wed, 18 Mar 2020 11:26:11 +0200 Subject: [PATCH 76/99] Remove unneeded include --- apps/opencs/view/render/instancemode.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/opencs/view/render/instancemode.cpp b/apps/opencs/view/render/instancemode.cpp index 42ab8e62e..ef7449f56 100644 --- a/apps/opencs/view/render/instancemode.cpp +++ b/apps/opencs/view/render/instancemode.cpp @@ -21,7 +21,6 @@ #include "../widget/scenetoolbar.hpp" #include "../widget/scenetoolmode.hpp" -#include #include #include "object.hpp" From e20d15690411af6d9215bce33ed338822560329a Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 17 Mar 2020 22:41:33 +0400 Subject: [PATCH 77/99] Do not write empty trade time --- components/esm/creaturestats.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/esm/creaturestats.cpp b/components/esm/creaturestats.cpp index beb2c4c64..d337b1434 100644 --- a/components/esm/creaturestats.cpp +++ b/components/esm/creaturestats.cpp @@ -144,7 +144,8 @@ void ESM::CreatureStats::save (ESMWriter &esm) const if (mGoldPool) esm.writeHNT ("GOLD", mGoldPool); - esm.writeHNT ("TIME", mTradeTime); + if (mTradeTime.mDay != 0 || mTradeTime.mHour != 0) + esm.writeHNT ("TIME", mTradeTime); if (mDead) esm.writeHNT ("DEAD", mDead); From 17cb3414d832c7128e44e5715e15933d196db952 Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Thu, 19 Mar 2020 11:18:54 +0200 Subject: [PATCH 78/99] clean-up, improve formatting --- apps/opencs/view/render/instancemode.cpp | 36 +++++++++++++----------- apps/opencs/view/render/instancemode.hpp | 4 +-- 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/apps/opencs/view/render/instancemode.cpp b/apps/opencs/view/render/instancemode.cpp index ef7449f56..7c0020f1e 100644 --- a/apps/opencs/view/render/instancemode.cpp +++ b/apps/opencs/view/render/instancemode.cpp @@ -700,7 +700,7 @@ void CSVRender::InstanceMode::deleteSelectedInstances(bool active) void CSVRender::InstanceMode::dropInstance(DropMode dropMode, CSVRender::Object* object, float objectHeight) { - const osg::Vec3d& point = object->getPosition().asVec3(); + osg::Vec3d point = object->getPosition().asVec3(); osg::Vec3d start = point; start.z() += objectHeight; @@ -712,8 +712,10 @@ void CSVRender::InstanceMode::dropInstance(DropMode dropMode, CSVRender::Object* intersector->setIntersectionLimit(osgUtil::LineSegmentIntersector::NO_LIMIT); osgUtil::IntersectionVisitor visitor(intersector); - if (dropMode == Terrain_sep) visitor.setTraversalMask(SceneUtil::Mask_Terrain); - if (dropMode == Collision_sep) visitor.setTraversalMask(SceneUtil::Mask_Terrain | SceneUtil::Mask_EditorReference); + if (dropMode == TerrainSep) + visitor.setTraversalMask(SceneUtil::Mask_Terrain); + if (dropMode == CollisionSep) + visitor.setTraversalMask(SceneUtil::Mask_Terrain | SceneUtil::Mask_EditorReference); mParentNode->accept(visitor); @@ -732,7 +734,7 @@ void CSVRender::InstanceMode::dropInstance(DropMode dropMode, CSVRender::Object* float CSVRender::InstanceMode::getDropHeight(DropMode dropMode, CSVRender::Object* object, float objectHeight) { - const osg::Vec3d& point = object->getPosition().asVec3(); + osg::Vec3d point = object->getPosition().asVec3(); osg::Vec3d start = point; start.z() += objectHeight; @@ -744,8 +746,10 @@ float CSVRender::InstanceMode::getDropHeight(DropMode dropMode, CSVRender::Objec intersector->setIntersectionLimit(osgUtil::LineSegmentIntersector::NO_LIMIT); osgUtil::IntersectionVisitor visitor(intersector); - if (dropMode == Terrain) visitor.setTraversalMask(SceneUtil::Mask_Terrain); - if (dropMode == Collision) visitor.setTraversalMask(SceneUtil::Mask_Terrain | SceneUtil::Mask_EditorReference); + if (dropMode == Terrain) + visitor.setTraversalMask(SceneUtil::Mask_Terrain); + if (dropMode == Collision) + visitor.setTraversalMask(SceneUtil::Mask_Terrain | SceneUtil::Mask_EditorReference); mParentNode->accept(visitor); @@ -772,18 +776,19 @@ void CSVRender::InstanceMode::dropSelectedInstancesToTerrain() void CSVRender::InstanceMode::dropSelectedInstancesToCollisionSeparately() { - handleDropMethod(Terrain_sep, "Drop instances to next collision level separately"); + handleDropMethod(TerrainSep, "Drop instances to next collision level separately"); } void CSVRender::InstanceMode::dropSelectedInstancesToTerrainSeparately() { - handleDropMethod(Collision_sep, "Drop instances to terrain level separately"); + handleDropMethod(CollisionSep, "Drop instances to terrain level separately"); } void CSVRender::InstanceMode::handleDropMethod(DropMode dropMode, QString commandMsg) { std::vector > selection = getWorldspaceWidget().getSelection (SceneUtil::Mask_EditorReference); - if (selection.empty()) return; + if (selection.empty()) + return; CSMDoc::Document& document = getWorldspaceWidget().getDocument(); QUndoStack& undoStack = document.getUndoStack(); @@ -803,7 +808,8 @@ void CSVRender::InstanceMode::handleDropMethod(DropMode dropMode, QString comman if (CSVRender::ObjectTag *objectTag = dynamic_cast (tag.get())) { float thisDrop = getDropHeight(dropMode, objectTag->mObject, dropObjectDataHandler.mObjectHeights[counter]); - if (thisDrop < smallestDropHeight) smallestDropHeight = thisDrop; + if (thisDrop < smallestDropHeight) + smallestDropHeight = thisDrop; counter++; } for(osg::ref_ptr tag: selection) @@ -818,8 +824,8 @@ void CSVRender::InstanceMode::handleDropMethod(DropMode dropMode, QString comman } break; - case Terrain_sep: - case Collision_sep: + case TerrainSep: + case CollisionSep: { int counter = 0; for(osg::ref_ptr tag: selection) @@ -831,9 +837,6 @@ void CSVRender::InstanceMode::handleDropMethod(DropMode dropMode, QString comman } } break; - - default: - break; } } @@ -853,7 +856,8 @@ CSVRender::DropObjectDataHandler::DropObjectDataHandler(WorldspaceWidget* worlds objectNodeWithoutGUI->accept(computeBounds); osg::BoundingBox bounds = computeBounds.getBoundingBox(); float boundingBoxOffset = 0.0f; - if (bounds.valid()) boundingBoxOffset = bounds.zMin(); + if (bounds.valid()) + boundingBoxOffset = bounds.zMin(); mObjectHeights.emplace_back(boundingBoxOffset); mOldMasks.emplace_back(objectNodeWithGUI->getNodeMask()); diff --git a/apps/opencs/view/render/instancemode.hpp b/apps/opencs/view/render/instancemode.hpp index b1d5de7fe..84b9e9ee2 100644 --- a/apps/opencs/view/render/instancemode.hpp +++ b/apps/opencs/view/render/instancemode.hpp @@ -37,8 +37,8 @@ namespace CSVRender { Collision, Terrain, - Collision_sep, - Terrain_sep + CollisionSep, + Terrainsep }; CSVWidget::SceneToolMode *mSubMode; From 793ea8566fc62bcd3a9f635a11b77972588c67f9 Mon Sep 17 00:00:00 2001 From: Nelsson Huotari Date: Thu, 19 Mar 2020 12:14:10 +0200 Subject: [PATCH 79/99] Fix typo --- apps/opencs/view/render/instancemode.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/view/render/instancemode.hpp b/apps/opencs/view/render/instancemode.hpp index 84b9e9ee2..beb60aff3 100644 --- a/apps/opencs/view/render/instancemode.hpp +++ b/apps/opencs/view/render/instancemode.hpp @@ -38,7 +38,7 @@ namespace CSVRender Collision, Terrain, CollisionSep, - Terrainsep + TerrainSep }; CSVWidget::SceneToolMode *mSubMode; From 4f4982545ac1fe87170e3e59bb67fd3b20b14321 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Thu, 19 Mar 2020 21:30:22 +0100 Subject: [PATCH 80/99] make robes cover the chest slot --- apps/openmw/mwrender/npcanimation.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 5def7bb96..18ecd30fc 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -587,7 +587,7 @@ void NpcAnimation::updateParts() int mBasePriority; } slotlist[] = { // FIXME: Priority is based on the number of reserved slots. There should be a better way. - { MWWorld::InventoryStore::Slot_Robe, 12 }, + { MWWorld::InventoryStore::Slot_Robe, 11 }, { MWWorld::InventoryStore::Slot_Skirt, 3 }, { MWWorld::InventoryStore::Slot_Helmet, 0 }, { MWWorld::InventoryStore::Slot_Cuirass, 0 }, @@ -641,7 +641,7 @@ void NpcAnimation::updateParts() ESM::PartReferenceType parts[] = { ESM::PRT_Groin, ESM::PRT_Skirt, ESM::PRT_RLeg, ESM::PRT_LLeg, ESM::PRT_RUpperarm, ESM::PRT_LUpperarm, ESM::PRT_RKnee, ESM::PRT_LKnee, - ESM::PRT_RForearm, ESM::PRT_LForearm + ESM::PRT_RForearm, ESM::PRT_LForearm, ESM::PRT_Cuirass }; size_t parts_size = sizeof(parts)/sizeof(parts[0]); for(size_t p = 0;p < parts_size;++p) From e1b5dd97b861b86142de63faa0dce672a6511755 Mon Sep 17 00:00:00 2001 From: fredzio Date: Mon, 6 May 2019 06:27:38 +0200 Subject: [PATCH 81/99] Add a filter in the alchemy window. A button allow to switch between ingredient name and magic effect. Switching reset the filter. The default filter can be set in the layout file. The player can show only ingredients whose either name or effect match the filter Only effect that are known to the player (via alchemy skill) are taken into account --- AUTHORS.md | 1 + CHANGELOG.md | 1 + apps/openmw/mwgui/alchemywindow.cpp | 107 +++++++++++++++++++++- apps/openmw/mwgui/alchemywindow.hpp | 15 +++ apps/openmw/mwgui/sortfilteritemmodel.cpp | 44 ++++++++- apps/openmw/mwgui/sortfilteritemmodel.hpp | 2 + apps/openmw/mwmechanics/alchemy.cpp | 34 +++++++ apps/openmw/mwmechanics/alchemy.hpp | 2 + files/mygui/openmw_alchemy_window.layout | 18 +++- 9 files changed, 218 insertions(+), 6 deletions(-) diff --git a/AUTHORS.md b/AUTHORS.md index 1ec909d10..3cbe9b44b 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -74,6 +74,7 @@ Programmers Fil Krynicki (filkry) Finbar Crago (finbar-crago) Florian Weber (Florianjw) + Frédéric Chardon (fr3dz10) Gaëtan Dezeiraud (Brouilles) Gašper Sedej Gijsbert ter Horst (Ghostbird) diff --git a/CHANGELOG.md b/CHANGELOG.md index 32255262e..f20ca90cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -264,6 +264,7 @@ Feature #5219: Impelement TestCells console command Feature #5224: Handle NiKeyframeController for NiTriShape Feature #5304: Morrowind-style bump-mapping + Feature #5314: Ingredient filter in the alchemy window Task #4686: Upgrade media decoder to a more current FFmpeg API Task #4695: Optimize Distant Terrain memory consumption Task #4789: Optimize cell transitions diff --git a/apps/openmw/mwgui/alchemywindow.cpp b/apps/openmw/mwgui/alchemywindow.cpp index ad66735bf..8dc44059f 100644 --- a/apps/openmw/mwgui/alchemywindow.cpp +++ b/apps/openmw/mwgui/alchemywindow.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -17,6 +18,7 @@ #include "../mwworld/class.hpp" #include "../mwworld/esmstore.hpp" +#include #include #include "inventoryitemmodel.hpp" @@ -29,6 +31,7 @@ namespace MWGui { AlchemyWindow::AlchemyWindow() : WindowBase("openmw_alchemy_window.layout") + , mModel(nullptr) , mSortModel(nullptr) , mAlchemy(new MWMechanics::Alchemy()) , mApparatus (4) @@ -50,6 +53,8 @@ namespace MWGui getWidget(mDecreaseButton, "DecreaseButton"); getWidget(mNameEdit, "NameEdit"); getWidget(mItemView, "ItemView"); + getWidget(mFilterValue, "FilterValue"); + getWidget(mFilterType, "FilterType"); mBrewCountEdit->eventValueChanged += MyGUI::newDelegate(this, &AlchemyWindow::onCountValueChanged); mBrewCountEdit->eventEditSelectAccept += MyGUI::newDelegate(this, &AlchemyWindow::onAccept); @@ -72,6 +77,9 @@ namespace MWGui mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onCancelButtonClicked); mNameEdit->eventEditSelectAccept += MyGUI::newDelegate(this, &AlchemyWindow::onAccept); + mFilterValue->eventComboChangePosition += MyGUI::newDelegate(this, &AlchemyWindow::onFilterChanged); + mFilterValue->eventEditTextChange += MyGUI::newDelegate(this, &AlchemyWindow::onFilterEdited); + mFilterType->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::switchFilterType); center(); } @@ -136,16 +144,110 @@ namespace MWGui removeIngredient(mIngredients[i]); } + updateFilters(); update(); } + void AlchemyWindow::initFilter() + { + auto const& wm = MWBase::Environment::get().getWindowManager(); + auto const ingredient = wm->getGameSettingString("sIngredients", "Ingredients"); + auto const effect = wm->getGameSettingString("sMagicEffects", "Magic Effects"); + + if (mFilterType->getCaption() == ingredient) + mCurrentFilter = FilterType::ByName; + else + mCurrentFilter = FilterType::ByEffect; + updateFilters(); + mFilterValue->clearIndexSelected(); + updateFilters(); + } + + void AlchemyWindow::switchFilterType(MyGUI::Widget* _sender) + { + auto const& wm = MWBase::Environment::get().getWindowManager(); + auto const ingredient = wm->getGameSettingString("sIngredients", "Ingredients"); + auto const effect = wm->getGameSettingString("sMagicEffects", "Magic Effects"); + auto *button = _sender->castType(); + + if (button->getCaption() == ingredient) + { + button->setCaption(effect); + mCurrentFilter = FilterType::ByEffect; + } + else + { + button->setCaption(ingredient); + mCurrentFilter = FilterType::ByName; + } + mSortModel->setNameFilter({}); + mSortModel->setEffectFilter({}); + mFilterValue->clearIndexSelected(); + updateFilters(); + mItemView->update(); + } + + void AlchemyWindow::updateFilters() + { + std::set itemNames, itemEffects; + for (size_t i = 0; i < mModel->getItemCount(); ++i) + { + auto const& base = mModel->getItem(i).mBase; + if (base.getTypeName() != typeid(ESM::Ingredient).name()) + continue; + + itemNames.insert(base.getClass().getName(base)); + + MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); + auto const alchemySkill = player.getClass().getSkill(player, ESM::Skill::Alchemy); + + auto const effects = MWMechanics::Alchemy::effectsDescription(base, alchemySkill); + itemEffects.insert(effects.begin(), effects.end()); + } + + mFilterValue->removeAllItems(); + auto const addItems = [&](auto const& container) + { + for (auto const& item : container) + mFilterValue->addItem(item); + }; + switch (mCurrentFilter) + { + case FilterType::ByName: addItems(itemNames); break; + case FilterType::ByEffect: addItems(itemEffects); break; + } + } + + void AlchemyWindow::applyFilter(const std::string& filter) + { + switch (mCurrentFilter) + { + case FilterType::ByName: mSortModel->setNameFilter(filter); break; + case FilterType::ByEffect: mSortModel->setEffectFilter(filter); break; + } + mItemView->update(); + } + + void AlchemyWindow::onFilterChanged(MyGUI::ComboBox* _sender, size_t _index) + { + // ignore spurious event fired when one edit the content after selection. + // onFilterEdited will handle it. + if (_index != MyGUI::ITEM_NONE) + applyFilter(_sender->getItemNameAt(_index)); + } + + void AlchemyWindow::onFilterEdited(MyGUI::EditBox* _sender) + { + applyFilter(_sender->getCaption()); + } + void AlchemyWindow::onOpen() { mAlchemy->clear(); mAlchemy->setAlchemist (MWMechanics::getPlayer()); - InventoryItemModel* model = new InventoryItemModel(MWMechanics::getPlayer()); - mSortModel = new SortFilterItemModel(model); + mModel = new InventoryItemModel(MWMechanics::getPlayer()); + mSortModel = new SortFilterItemModel(mModel); mSortModel->setFilter(SortFilterItemModel::Filter_OnlyIngredients); mItemView->setModel (mSortModel); mItemView->resetScrollBars(); @@ -167,6 +269,7 @@ namespace MWGui } update(); + initFilter(); MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mNameEdit); } diff --git a/apps/openmw/mwgui/alchemywindow.hpp b/apps/openmw/mwgui/alchemywindow.hpp index a3f1cb52e..82dd0a243 100644 --- a/apps/openmw/mwgui/alchemywindow.hpp +++ b/apps/openmw/mwgui/alchemywindow.hpp @@ -5,7 +5,9 @@ #include #include +#include +#include #include #include "windowbase.hpp" @@ -19,6 +21,7 @@ namespace MWGui { class ItemView; class ItemWidget; + class InventoryItemModel; class SortFilterItemModel; class AlchemyWindow : public WindowBase @@ -36,8 +39,11 @@ namespace MWGui static const float sCountChangeInterval; // in seconds std::string mSuggestedPotionName; + enum class FilterType { ByName, ByEffect }; + FilterType mCurrentFilter; ItemView* mItemView; + InventoryItemModel* mModel; SortFilterItemModel* mSortModel; MyGUI::Button* mCreateButton; @@ -47,6 +53,8 @@ namespace MWGui MyGUI::Button* mIncreaseButton; MyGUI::Button* mDecreaseButton; + Gui::AutoSizedButton* mFilterType; + MyGUI::ComboBox* mFilterValue; MyGUI::EditBox* mNameEdit; Gui::NumericEditBox* mBrewCountEdit; @@ -60,6 +68,13 @@ namespace MWGui void onCountValueChanged(int value); void onRepeatClick(MyGUI::Widget* widget, MyGUI::ControllerItem* controller); + void applyFilter(const std::string& filter); + void initFilter(); + void onFilterChanged(MyGUI::ComboBox* _sender, size_t _index); + void onFilterEdited(MyGUI::EditBox* _sender); + void switchFilterType(MyGUI::Widget* _sender); + void updateFilters(); + void addRepeatController(MyGUI::Widget* widget); void onIncreaseButtonTriggered(); diff --git a/apps/openmw/mwgui/sortfilteritemmodel.cpp b/apps/openmw/mwgui/sortfilteritemmodel.cpp index 88ae5fd1b..615e2dfc9 100644 --- a/apps/openmw/mwgui/sortfilteritemmodel.cpp +++ b/apps/openmw/mwgui/sortfilteritemmodel.cpp @@ -23,6 +23,8 @@ #include "../mwworld/nullaction.hpp" #include "../mwworld/esmstore.hpp" +#include "../mwmechanics/alchemy.hpp" + namespace { bool compareType(const std::string& type1, const std::string& type2) @@ -151,6 +153,8 @@ namespace MWGui : mCategory(Category_All) , mFilter(0) , mSortByType(true) + , mNameFilter("") + , mEffectFilter("") { mSourceModel = sourceModel; } @@ -199,8 +203,39 @@ namespace MWGui if (!(category & mCategory)) return false; - if ((mFilter & Filter_OnlyIngredients) && base.getTypeName() != typeid(ESM::Ingredient).name()) - return false; + if (mFilter & Filter_OnlyIngredients) + { + if (base.getTypeName() != typeid(ESM::Ingredient).name()) + return false; + + if (!mNameFilter.empty() && !mEffectFilter.empty()) + throw std::logic_error("name and magic effect filter are mutually exclusive"); + + if (!mNameFilter.empty()) + { + const auto itemName = Misc::StringUtils::lowerCase(base.getClass().getName(base)); + return itemName.find(mNameFilter) != std::string::npos; + } + + if (!mEffectFilter.empty()) + { + MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); + const auto alchemySkill = player.getClass().getSkill(player, ESM::Skill::Alchemy); + + const auto effects = MWMechanics::Alchemy::effectsDescription(base, alchemySkill); + + for (const auto& effect : effects) + { + const auto ciEffect = Misc::StringUtils::lowerCase(effect); + + if (ciEffect.find(mEffectFilter) != std::string::npos) + return true; + } + return false; + } + return true; + } + if ((mFilter & Filter_OnlyEnchanted) && !(item.mFlags & ItemStack::Flag_Enchanted)) return false; if ((mFilter & Filter_OnlyChargedSoulstones) && (base.getTypeName() != typeid(ESM::Miscellaneous).name() @@ -286,6 +321,11 @@ namespace MWGui mNameFilter = Misc::StringUtils::lowerCase(filter); } + void SortFilterItemModel::setEffectFilter (const std::string& filter) + { + mEffectFilter = Misc::StringUtils::lowerCase(filter); + } + void SortFilterItemModel::update() { mSourceModel->update(); diff --git a/apps/openmw/mwgui/sortfilteritemmodel.hpp b/apps/openmw/mwgui/sortfilteritemmodel.hpp index 6e400ddc9..3e616875e 100644 --- a/apps/openmw/mwgui/sortfilteritemmodel.hpp +++ b/apps/openmw/mwgui/sortfilteritemmodel.hpp @@ -26,6 +26,7 @@ namespace MWGui void setCategory (int category); void setFilter (int filter); void setNameFilter (const std::string& filter); + void setEffectFilter (const std::string& filter); /// Use ItemStack::Type for sorting? void setSortByType(bool sort) { mSortByType = sort; } @@ -60,6 +61,7 @@ namespace MWGui bool mSortByType; std::string mNameFilter; // filter by item name + std::string mEffectFilter; // filter by magic effect }; } diff --git a/apps/openmw/mwmechanics/alchemy.cpp b/apps/openmw/mwmechanics/alchemy.cpp index 87ee47b49..b490db436 100644 --- a/apps/openmw/mwmechanics/alchemy.cpp +++ b/apps/openmw/mwmechanics/alchemy.cpp @@ -554,3 +554,37 @@ std::string MWMechanics::Alchemy::suggestPotionName() return MWBase::Environment::get().getWorld()->getStore().get().find( ESM::MagicEffect::effectIdToString(effectId))->mValue.getString(); } + +std::vector MWMechanics::Alchemy::effectsDescription (const MWWorld::ConstPtr &ptr, const int alchemySkill) +{ + std::vector effects; + + const auto& item = ptr.get()->mBase; + const auto& gmst = MWBase::Environment::get().getWorld()->getStore().get(); + const static auto fWortChanceValue = gmst.find("fWortChanceValue")->mValue.getFloat(); + const auto& data = item->mData; + + for (auto i = 0; i < 4; ++i) + { + const auto effectID = data.mEffectID[i]; + const auto skillID = data.mSkills[i]; + const auto attributeID = data.mAttributes[i]; + + if (alchemySkill < fWortChanceValue * (i + 1)) + break; + + if (effectID != -1) + { + std::string effect = gmst.find(ESM::MagicEffect::effectIdToString(effectID))->mValue.getString(); + + if (skillID != -1) + effect += " " + gmst.find(ESM::Skill::sSkillNameIds[skillID])->mValue.getString(); + else if (attributeID != -1) + effect += " " + gmst.find(ESM::Attribute::sGmstAttributeIds[attributeID])->mValue.getString(); + + effects.push_back(effect); + + } + } + return effects; +} diff --git a/apps/openmw/mwmechanics/alchemy.hpp b/apps/openmw/mwmechanics/alchemy.hpp index 9f9f0b21c..d23f978ea 100644 --- a/apps/openmw/mwmechanics/alchemy.hpp +++ b/apps/openmw/mwmechanics/alchemy.hpp @@ -131,6 +131,8 @@ namespace MWMechanics ///< Try to create potions from the ingredients, place them in the inventory of the alchemist and /// adjust the skills of the alchemist accordingly. /// \param name must not be an empty string, or Result_NoName is returned + + static std::vector effectsDescription (const MWWorld::ConstPtr &ptr, const int alchemySKill); }; } diff --git a/files/mygui/openmw_alchemy_window.layout b/files/mygui/openmw_alchemy_window.layout index 714872fc3..8e1082952 100644 --- a/files/mygui/openmw_alchemy_window.layout +++ b/files/mygui/openmw_alchemy_window.layout @@ -57,8 +57,7 @@ - - + @@ -71,6 +70,21 @@ + + + + + + + + + + + + + + + From 90508237b1778a1117943d2252fd27628cc4bbe8 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 20 Mar 2020 11:58:22 +0400 Subject: [PATCH 82/99] Make slider control in the wait window to be a more intuitive --- apps/openmw/mwgui/waitdialog.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index d18eaebdd..64f912298 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -227,9 +227,9 @@ namespace MWGui void WaitDialog::onKeyButtonPressed(MyGUI::Widget *sender, MyGUI::KeyCode key, MyGUI::Char character) { - if (key == MyGUI::KeyCode::ArrowDown) + if (key == MyGUI::KeyCode::ArrowUp) mHourSlider->setScrollPosition(std::min(mHourSlider->getScrollPosition()+1, mHourSlider->getScrollRange()-1)); - else if (key == MyGUI::KeyCode::ArrowUp) + else if (key == MyGUI::KeyCode::ArrowDown) mHourSlider->setScrollPosition(std::max(static_cast(mHourSlider->getScrollPosition())-1, 0)); else return; From d2a2c74e08444ca33a0e3642cb68a333899915a6 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 21 Mar 2020 10:32:47 +0400 Subject: [PATCH 83/99] Trace down dead persistent actors underwater (regression #5317) --- apps/openmw/mwworld/worldimp.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 7cc7fef65..26eac954b 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1424,7 +1424,9 @@ namespace MWWorld pos.z() += 20; // place slightly above. will snap down to ground with code below - if (force || !ptr.getClass().isActor() || (!isFlying(ptr) && !isSwimming(ptr) && isActorCollisionEnabled(ptr))) + // We still should trace down dead persistent actors - they do not use the "swimdeath" animation. + bool swims = ptr.getClass().isActor() && isSwimming(ptr) && !(ptr.getClass().isPersistent(ptr) && ptr.getClass().getCreatureStats(ptr).isDeathAnimationFinished()); + if (force || !ptr.getClass().isActor() || (!isFlying(ptr) && !swims && isActorCollisionEnabled(ptr))) { osg::Vec3f traced = mPhysics->traceDown(ptr, pos, Constants::CellSizeInUnits); if (traced.z() < pos.z()) From a37bdfd4928e28f3be746b7ae0e17538bf075907 Mon Sep 17 00:00:00 2001 From: James Moore Date: Sat, 21 Mar 2020 19:35:20 +0000 Subject: [PATCH 84/99] Implement a refresh button on data files page --- AUTHORS.md | 1 + CHANGELOG.md | 1 + apps/launcher/datafilespage.cpp | 31 +++++++++++- apps/launcher/datafilespage.hpp | 2 + .../contentselector/view/contentselector.hpp | 3 ++ files/ui/contentselector.ui | 48 +++++++++++++++++-- files/ui/datafilespage.ui | 16 +++++++ 7 files changed, 97 insertions(+), 5 deletions(-) diff --git a/AUTHORS.md b/AUTHORS.md index 3cbe9b44b..2c77e522e 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -88,6 +88,7 @@ Programmers Jacob Essex (Yacoby) Jake Westrip (16bitint) James Carty (MrTopCat) + James Moore (moore.work) James Stephens (james-h-stephens) Jan-Peter Nilsson (peppe) Jan Borsodi (am0s) diff --git a/CHANGELOG.md b/CHANGELOG.md index f20ca90cd..2ad109160 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -252,6 +252,7 @@ Feature #5091: Human-readable light source duration Feature #5094: Unix like console hotkeys Feature #5098: Allow user controller bindings + Feature #5114: Refresh launcher mod list Feature #5121: Handle NiTriStrips and NiTriStripsData Feature #5122: Use magic glow for enchanted arrows Feature #5131: Custom skeleton bones diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index e679392c4..81544b094 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -62,10 +62,13 @@ void Launcher::DataFilesPage::buildView() { ui.verticalLayout->insertWidget (0, mSelector->uiWidget()); + QToolButton * refreshButton = mSelector->refreshButton(); + //tool buttons ui.newProfileButton->setToolTip ("Create a new Content List"); ui.cloneProfileButton->setToolTip ("Clone the current Content List"); ui.deleteProfileButton->setToolTip ("Delete an existing Content List"); + refreshButton->setToolTip("Refresh Data Files"); //combo box ui.profilesComboBox->addItem(mDefaultContentListName); @@ -76,6 +79,7 @@ void Launcher::DataFilesPage::buildView() ui.newProfileButton->setDefaultAction (ui.newProfileAction); ui.cloneProfileButton->setDefaultAction (ui.cloneProfileAction); ui.deleteProfileButton->setDefaultAction (ui.deleteProfileAction); + refreshButton->setDefaultAction(ui.refreshDataFilesAction); //establish connections connect (ui.profilesComboBox, SIGNAL (currentIndexChanged(int)), @@ -86,6 +90,8 @@ void Launcher::DataFilesPage::buildView() connect (ui.profilesComboBox, SIGNAL (signalProfileChanged(QString, QString)), this, SLOT (slotProfileChangedByUser(QString, QString))); + + connect(ui.refreshDataFilesAction, SIGNAL(triggered()),this, SLOT(slotRefreshButtonClicked())); } bool Launcher::DataFilesPage::loadSettings() @@ -114,6 +120,8 @@ void Launcher::DataFilesPage::populateFileViews(const QString& contentModelName) if (!mDataLocal.isEmpty()) paths.insert(0, mDataLocal); + mSelector->clearFiles(); + for (const QString &path : paths) mSelector->addFiles(path); @@ -167,7 +175,16 @@ QStringList Launcher::DataFilesPage::selectedFilePaths() QStringList filePaths; for (const ContentSelectorModel::EsmFile *item : items) { - filePaths.append(item->filePath()); + QFile file(item->filePath()); + + if(file.exists()) + { + filePaths.append(item->filePath()); + } + else + { + slotRefreshButtonClicked(); + } } return filePaths; } @@ -221,6 +238,18 @@ void Launcher::DataFilesPage::slotProfileDeleted (const QString &item) removeProfile (item); } +void Launcher::DataFilesPage:: refreshDataFilesView () +{ + QString currentProfile = ui.profilesComboBox->currentText(); + saveSettings(currentProfile); + populateFileViews(currentProfile); +} + +void Launcher::DataFilesPage::slotRefreshButtonClicked () +{ + refreshDataFilesView(); +} + void Launcher::DataFilesPage::slotProfileChangedByUser(const QString &previous, const QString ¤t) { setProfile(previous, current, true); diff --git a/apps/launcher/datafilespage.hpp b/apps/launcher/datafilespage.hpp index 36a0db616..af54fe75e 100644 --- a/apps/launcher/datafilespage.hpp +++ b/apps/launcher/datafilespage.hpp @@ -61,6 +61,7 @@ namespace Launcher void slotProfileRenamed(const QString &previous, const QString ¤t); void slotProfileDeleted(const QString &item); void slotAddonDataChanged (); + void slotRefreshButtonClicked (); void updateNewProfileOkButton(const QString &text); void updateCloneProfileOkButton(const QString &text); @@ -100,6 +101,7 @@ namespace Launcher void checkForDefaultProfile(); void populateFileViews(const QString& contentModelName); void reloadCells(QStringList selectedFiles); + void refreshDataFilesView (); class PathIterator { diff --git a/components/contentselector/view/contentselector.hpp b/components/contentselector/view/contentselector.hpp index bc47224e4..9c34e24fd 100644 --- a/components/contentselector/view/contentselector.hpp +++ b/components/contentselector/view/contentselector.hpp @@ -44,6 +44,9 @@ namespace ContentSelectorView QWidget *uiWidget() const { return ui.contentGroupBox; } + + QToolButton *refreshButton() const + { return ui.refreshButton; } private: diff --git a/files/ui/contentselector.ui b/files/ui/contentselector.ui index 7832239b5..d13cb314e 100644 --- a/files/ui/contentselector.ui +++ b/files/ui/contentselector.ui @@ -20,7 +20,16 @@ Qt::DefaultContextMenu
- + + 0 + + + 0 + + + 0 + + 0 @@ -33,10 +42,41 @@ 3 - - - false + + + Qt::NoFocus + + + 0 + + + 0 + + + 0 + + + 0 + + + + + false + + + + + + + true + + + true + + + + diff --git a/files/ui/datafilespage.ui b/files/ui/datafilespage.ui index 8e42ee7cb..ccac5050e 100644 --- a/files/ui/datafilespage.ui +++ b/files/ui/datafilespage.ui @@ -159,6 +159,22 @@ Uncheck Selection
+ + + + + + + + Refresh Data Files + + + Refresh Data Files + + + Ctrl+R + + From 18bfc6f2c59ccd06ffadd3c602bebb1982d2c5dc Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 22 Mar 2020 22:01:49 +0300 Subject: [PATCH 85/99] Fix Gender column filtering (bug #4601) and correct filter descs --- CHANGELOG.md | 1 + apps/opencs/model/world/columnimp.hpp | 2 +- apps/opencs/model/world/columns.cpp | 1 - apps/opencs/model/world/columns.hpp | 2 +- apps/opencs/model/world/refidcollection.cpp | 2 +- files/opencs/defaultfilters | Bin 10958 -> 10957 bytes 6 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 32255262e..75ca5c442 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,7 @@ Bug #4594: Actors without AI packages don't use Hello dialogue Bug #4598: Script parser does not support non-ASCII characters Bug #4600: Crash when no sound output is available or --no-sound is used. + Bug #4601: Filtering referenceables by gender is broken Bug #4639: Black screen after completing first mages guild mission + training Bug #4650: Focus is lost after pressing ESC in confirmation dialog inside savegame dialog Bug #4680: Heap corruption on faulty esp diff --git a/apps/opencs/model/world/columnimp.hpp b/apps/opencs/model/world/columnimp.hpp index 25f09cb0f..ccc18263b 100644 --- a/apps/opencs/model/world/columnimp.hpp +++ b/apps/opencs/model/world/columnimp.hpp @@ -1770,7 +1770,7 @@ namespace CSMWorld struct GenderNpcColumn : public Column { GenderNpcColumn() - : Column(Columns::ColumnId_GenderNpc, ColumnBase::Display_GenderNpc) + : Column(Columns::ColumnId_Gender, ColumnBase::Display_GenderNpc) {} virtual QVariant get(const Record& record) const diff --git a/apps/opencs/model/world/columns.cpp b/apps/opencs/model/world/columns.cpp index e2c3be789..eaea66c2f 100644 --- a/apps/opencs/model/world/columns.cpp +++ b/apps/opencs/model/world/columns.cpp @@ -288,7 +288,6 @@ namespace CSMWorld { ColumnId_UChar, "Value [0..255]" }, { ColumnId_NpcMisc, "NPC Misc" }, { ColumnId_Level, "Level" }, - { ColumnId_GenderNpc, "Gender"}, { ColumnId_Mana, "Mana" }, { ColumnId_Fatigue, "Fatigue" }, { ColumnId_NpcDisposition, "NPC Disposition" }, diff --git a/apps/opencs/model/world/columns.hpp b/apps/opencs/model/world/columns.hpp index 085e6e178..c85eaac5f 100644 --- a/apps/opencs/model/world/columns.hpp +++ b/apps/opencs/model/world/columns.hpp @@ -273,7 +273,7 @@ namespace CSMWorld ColumnId_UChar = 250, ColumnId_NpcMisc = 251, ColumnId_Level = 252, - ColumnId_GenderNpc = 254, + // unused ColumnId_Mana = 255, ColumnId_Fatigue = 256, ColumnId_NpcDisposition = 257, diff --git a/apps/opencs/model/world/refidcollection.cpp b/apps/opencs/model/world/refidcollection.cpp index ff146bb91..3502d6319 100644 --- a/apps/opencs/model/world/refidcollection.cpp +++ b/apps/opencs/model/world/refidcollection.cpp @@ -486,7 +486,7 @@ CSMWorld::RefIdCollection::RefIdCollection() mColumns.push_back (RefIdColumn (Columns::ColumnId_Head, ColumnBase::Display_BodyPart)); npcColumns.mHead = &mColumns.back(); - mColumns.push_back (RefIdColumn (Columns::ColumnId_GenderNpc, ColumnBase::Display_GenderNpc)); + mColumns.push_back (RefIdColumn (Columns::ColumnId_Gender, ColumnBase::Display_GenderNpc)); npcColumns.mGender = &mColumns.back(); npcColumns.mFlags.insert (std::make_pair (essential, ESM::NPC::Essential)); diff --git a/files/opencs/defaultfilters b/files/opencs/defaultfilters index 0ac3c8db4bd939b8b9d559ab4af4056fc0b8d80e..cd46590d98f049911689d2b2eb73280655d40d7b 100644 GIT binary patch delta 290 zcmX>XdNy zyE;U{<}8k*tc*)G8}M~9@+iSHWtOCFKE^N1#Mm?WmS6#+@?>uzmC1&RVw1Z?c_*(` zi&7B`yk+Pc1G^%`3@F%uxWUES|hi zSPZCCk(Yv*0)q$lNkUtom6N5 delta 310 zcmX>bdM7cq^LAiNmD_S!NoP$ wSqNrO8dx4=$mRl-4NQzRlNHtV7;Qm%H4%E#fEFP%3&1pkj0S66qOQpd0KHvXhyVZp From 5091e2c371178396eecc8391993339a8adb27bb0 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Sun, 22 Mar 2020 20:56:48 +0100 Subject: [PATCH 86/99] bump to bionic; gcc-9 and clang-10 --- .travis.yml | 32 ++++++++++++++++---------------- CI/before_install.linux.sh | 4 ++-- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/.travis.yml b/.travis.yml index 89b5317a2..c2f2d3f7f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,10 +15,10 @@ addons: sources: - sourceline: 'ppa:openmw/openmw' - ubuntu-toolchain-r-test - - llvm-toolchain-xenial-7 + - llvm-toolchain-bionic-10 packages: [ # Dev - cmake, clang-7, clang-tools-7, gcc-8, g++-8, ccache, + cmake, clang-10, clang-tools-10, gcc-9, g++-9, ccache, # Boost libboost-filesystem-dev, libboost-iostreams-dev, libboost-program-options-dev, libboost-system-dev, # FFmpeg @@ -44,43 +44,43 @@ matrix: if: branch != coverity_scan - name: OpenMW (all) on Ubuntu Xenial GCC-5 os: linux - dist: xenial + dist: bionic sudo: required if: branch != coverity_scan - - name: OpenMW (all) on Ubuntu Xenial GCC-8 + - name: OpenMW (all) on Ubuntu Xenial GCC-9 os: linux - dist: xenial + dist: bionic sudo: required env: - - MATRIX_EVAL="CC=gcc-8 && CXX=g++-8" + - MATRIX_EVAL="CC=gcc-9 && CXX=g++-9" if: branch != coverity_scan - - name: OpenMW (openmw) on Ubuntu Xenial Clang-7 with Static Analysis + - name: OpenMW (openmw) on Ubuntu Xenial Clang-10 with Static Analysis os: linux - dist: xenial + dist: bionic 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" + - MATRIX_EVAL="CC=clang-10 && CXX=clang++-10" + - ANALYZE="scan-build-10 --force-analyze-debug-code --use-cc clang-10 --use-c++ clang++-10" - BUILD_OPENMW_CS="OFF" if: branch != coverity_scan compiler: clang - - name: OpenMW (openmw-cs) on Ubuntu Xenial Clang-7 with Static Analysis + - name: OpenMW (openmw-cs) on Ubuntu Xenial Clang-10 with Static Analysis os: linux - dist: xenial + dist: bionic 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" + - MATRIX_EVAL="CC=clang-10 && CXX=clang++-10" + - ANALYZE="scan-build-10 --force-analyze-debug-code --use-cc clang-10 --use-c++ clang++-10" - BUILD_OPENMW="OFF" if: branch != coverity_scan compiler: clang - name: OpenMW Components Coverity Scan os: linux - dist: xenial + dist: bionic sudo: required if: branch = coverity_scan # allow_failures: -# - name: OpenMW (openmw) on Ubuntu Xenial Clang-7 with Static Analysis +# - name: OpenMW (openmw) on Ubuntu Xenial Clang-10 with Static Analysis before_install: - if [ "${TRAVIS_OS_NAME}" = "linux" ]; then eval "${MATRIX_EVAL}"; fi diff --git a/CI/before_install.linux.sh b/CI/before_install.linux.sh index fd4e4829c..30f38dd92 100755 --- a/CI/before_install.linux.sh +++ b/CI/before_install.linux.sh @@ -1,4 +1,4 @@ #!/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++ +sudo ln -sf /usr/bin/clang-10 /usr/local/bin/clang +sudo ln -sf /usr/bin/clang++-10 /usr/local/bin/clang++ From f2f5b1104fc031a28eb2fb38940fbccc645827a1 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Sun, 22 Mar 2020 21:19:44 +0100 Subject: [PATCH 87/99] try clang-6 since that is officially in repo --- .travis.yml | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index c2f2d3f7f..1ce58a3df 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,10 +15,9 @@ addons: sources: - sourceline: 'ppa:openmw/openmw' - ubuntu-toolchain-r-test - - llvm-toolchain-bionic-10 packages: [ # Dev - cmake, clang-10, clang-tools-10, gcc-9, g++-9, ccache, + cmake, clang-6, clang-tools-6, gcc-9, g++-9, ccache, # Boost libboost-filesystem-dev, libboost-iostreams-dev, libboost-program-options-dev, libboost-system-dev, # FFmpeg @@ -54,23 +53,23 @@ matrix: env: - MATRIX_EVAL="CC=gcc-9 && CXX=g++-9" if: branch != coverity_scan - - name: OpenMW (openmw) on Ubuntu Xenial Clang-10 with Static Analysis + - name: OpenMW (openmw) on Ubuntu Xenial Clang-6 with Static Analysis os: linux dist: bionic sudo: required env: - - MATRIX_EVAL="CC=clang-10 && CXX=clang++-10" - - ANALYZE="scan-build-10 --force-analyze-debug-code --use-cc clang-10 --use-c++ clang++-10" + - MATRIX_EVAL="CC=clang-6 && CXX=clang++-6" + - ANALYZE="scan-build-6 --force-analyze-debug-code --use-cc clang-6 --use-c++ clang++-6" - BUILD_OPENMW_CS="OFF" if: branch != coverity_scan compiler: clang - - name: OpenMW (openmw-cs) on Ubuntu Xenial Clang-10 with Static Analysis + - name: OpenMW (openmw-cs) on Ubuntu Xenial Clang-6 with Static Analysis os: linux dist: bionic sudo: required env: - - MATRIX_EVAL="CC=clang-10 && CXX=clang++-10" - - ANALYZE="scan-build-10 --force-analyze-debug-code --use-cc clang-10 --use-c++ clang++-10" + - MATRIX_EVAL="CC=clang-6 && CXX=clang++-6" + - ANALYZE="scan-build-6 --force-analyze-debug-code --use-cc clang-6 --use-c++ clang++-6" - BUILD_OPENMW="OFF" if: branch != coverity_scan compiler: clang @@ -80,7 +79,7 @@ matrix: sudo: required if: branch = coverity_scan # allow_failures: -# - name: OpenMW (openmw) on Ubuntu Xenial Clang-10 with Static Analysis +# - name: OpenMW (openmw) on Ubuntu Xenial Clang-6 with Static Analysis before_install: - if [ "${TRAVIS_OS_NAME}" = "linux" ]; then eval "${MATRIX_EVAL}"; fi From 0a96f4c847732ba1d7c5e0bb551765f34b7bc08c Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Sun, 22 Mar 2020 21:27:44 +0100 Subject: [PATCH 88/99] get the default... --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1ce58a3df..a6b09c681 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,7 +17,7 @@ addons: - ubuntu-toolchain-r-test packages: [ # Dev - cmake, clang-6, clang-tools-6, gcc-9, g++-9, ccache, + cmake, clang-tools, gcc-9, g++-9, ccache, # Boost libboost-filesystem-dev, libboost-iostreams-dev, libboost-program-options-dev, libboost-system-dev, # FFmpeg @@ -41,7 +41,7 @@ matrix: os: osx osx_image: xcode10.2 if: branch != coverity_scan - - name: OpenMW (all) on Ubuntu Xenial GCC-5 + - name: OpenMW (all) on Ubuntu Xenial GCC-7 os: linux dist: bionic sudo: required From 42640b781198e45efebdc0e27484a455af7bc13f Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Sun, 22 Mar 2020 21:34:46 +0100 Subject: [PATCH 89/99] no need to re-map when using system default --- CI/before_install.linux.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CI/before_install.linux.sh b/CI/before_install.linux.sh index 30f38dd92..eff3fd719 100755 --- a/CI/before_install.linux.sh +++ b/CI/before_install.linux.sh @@ -1,4 +1,4 @@ #!/bin/bash -ex -sudo ln -sf /usr/bin/clang-10 /usr/local/bin/clang -sudo ln -sf /usr/bin/clang++-10 /usr/local/bin/clang++ +#sudo ln -sf /usr/bin/clang-6 /usr/local/bin/clang +#sudo ln -sf /usr/bin/clang++-6 /usr/local/bin/clang++ From 3fc5c499fb851cab468e264c25fbd90291e4e764 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Sun, 22 Mar 2020 21:40:43 +0100 Subject: [PATCH 90/99] another try for system clang --- .travis.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index a6b09c681..780f4b103 100644 --- a/.travis.yml +++ b/.travis.yml @@ -58,8 +58,8 @@ matrix: dist: bionic sudo: required env: - - MATRIX_EVAL="CC=clang-6 && CXX=clang++-6" - - ANALYZE="scan-build-6 --force-analyze-debug-code --use-cc clang-6 --use-c++ clang++-6" + - MATRIX_EVAL="CC=clang && CXX=clang++" + - ANALYZE="scan-build --force-analyze-debug-code --use-cc clang --use-c++ clang++" - BUILD_OPENMW_CS="OFF" if: branch != coverity_scan compiler: clang @@ -68,8 +68,8 @@ matrix: dist: bionic sudo: required env: - - MATRIX_EVAL="CC=clang-6 && CXX=clang++-6" - - ANALYZE="scan-build-6 --force-analyze-debug-code --use-cc clang-6 --use-c++ clang++-6" + - MATRIX_EVAL="CC=clang && CXX=clang++" + - ANALYZE="scan-build --force-analyze-debug-code --use-cc clang --use-c++ clang++" - BUILD_OPENMW="OFF" if: branch != coverity_scan compiler: clang From 444d667d22b6a34ea84959e8d60aa559a76db1b0 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Sun, 22 Mar 2020 23:07:07 +0100 Subject: [PATCH 91/99] give sdl2 2.0.12 for windows a try --- 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 e9fbb117b..03595ce6d 100644 --- a/CI/before_script.msvc.sh +++ b/CI/before_script.msvc.sh @@ -428,9 +428,9 @@ if [ -z $SKIP_DOWNLOAD ]; then fi # SDL2 - download "SDL 2.0.7" \ - "https://www.libsdl.org/release/SDL2-devel-2.0.7-VC.zip" \ - "SDL2-2.0.7.zip" + download "SDL 2.0.12" \ + "https://www.libsdl.org/release/SDL2-devel-2.0.12-VC.zip" \ + "SDL2-2.0.12.zip" # Google test and mock if [ ! -z $TEST_FRAMEWORK ]; then @@ -697,16 +697,16 @@ fi cd $DEPS echo # SDL2 -printf "SDL 2.0.7... " +printf "SDL 2.0.12... " { - if [ -d SDL2-2.0.7 ]; then + if [ -d SDL2-2.0.12 ]; then printf "Exists. " elif [ -z $SKIP_EXTRACT ]; then - rm -rf SDL2-2.0.7 - eval 7z x -y SDL2-2.0.7.zip $STRIP + rm -rf SDL2-2.0.12 + eval 7z x -y SDL2-2.0.12.zip $STRIP fi - export SDL2DIR="$(real_pwd)/SDL2-2.0.7" - add_runtime_dlls "$(pwd)/SDL2-2.0.7/lib/x${ARCHSUFFIX}/SDL2.dll" + export SDL2DIR="$(real_pwd)/SDL2-2.0.12" + add_runtime_dlls "$(pwd)/SDL2-2.0.12/lib/x${ARCHSUFFIX}/SDL2.dll" echo Done. } cd $DEPS From 53da5b8c54691e83c33fd07c1d8bb375f8444da3 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Mon, 23 Mar 2020 14:38:22 +0100 Subject: [PATCH 92/99] Xenial -> Bionic name change --- .travis.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 780f4b103..383e30c61 100644 --- a/.travis.yml +++ b/.travis.yml @@ -41,19 +41,19 @@ matrix: os: osx osx_image: xcode10.2 if: branch != coverity_scan - - name: OpenMW (all) on Ubuntu Xenial GCC-7 + - name: OpenMW (all) on Ubuntu Bionic GCC-7 os: linux dist: bionic sudo: required if: branch != coverity_scan - - name: OpenMW (all) on Ubuntu Xenial GCC-9 + - name: OpenMW (all) on Ubuntu Bionic GCC-9 os: linux dist: bionic sudo: required env: - MATRIX_EVAL="CC=gcc-9 && CXX=g++-9" if: branch != coverity_scan - - name: OpenMW (openmw) on Ubuntu Xenial Clang-6 with Static Analysis + - name: OpenMW (openmw) on Ubuntu Bionic Clang-6 with Static Analysis os: linux dist: bionic sudo: required @@ -63,7 +63,7 @@ matrix: - BUILD_OPENMW_CS="OFF" if: branch != coverity_scan compiler: clang - - name: OpenMW (openmw-cs) on Ubuntu Xenial Clang-6 with Static Analysis + - name: OpenMW (openmw-cs) on Ubuntu Bionic Clang-6 with Static Analysis os: linux dist: bionic sudo: required @@ -79,7 +79,7 @@ matrix: sudo: required if: branch = coverity_scan # allow_failures: -# - name: OpenMW (openmw) on Ubuntu Xenial Clang-6 with Static Analysis +# - name: OpenMW (openmw) on Ubuntu Bionic Clang-6 with Static Analysis before_install: - if [ "${TRAVIS_OS_NAME}" = "linux" ]; then eval "${MATRIX_EVAL}"; fi From d2acac0ebe85065d0ef0eb446aca3bf999c01821 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 23 Mar 2020 17:40:30 +0400 Subject: [PATCH 93/99] Fix Qt download link for Windows --- CI/before_script.msvc.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CI/before_script.msvc.sh b/CI/before_script.msvc.sh index 03595ce6d..847435ac5 100644 --- a/CI/before_script.msvc.sh +++ b/CI/before_script.msvc.sh @@ -421,7 +421,7 @@ if [ -z $SKIP_DOWNLOAD ]; then fi download "Qt 5.7.0" \ - "https://download.qt.io/archive/qt/5.7/5.7.0/qt-opensource-windows-x86-msvc${MSVC_YEAR}${QT_SUFFIX}-5.7.0.exe" \ + "https://download.qt.io/new_archive/qt/5.7/5.7.0/qt-opensource-windows-x86-msvc${MSVC_YEAR}${QT_SUFFIX}-5.7.0.exe" \ "qt-5.7.0-msvc${MSVC_YEAR}-win${BITS}.exe" \ "https://www.lysator.liu.se/~ace/OpenMW/deps/qt-5-install.qs" \ "qt-5-install.qs" From 7319eda54e7fae1d2f36065c5f4d3288da99cdb3 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Mon, 23 Mar 2020 20:38:19 +0300 Subject: [PATCH 94/99] Correct drawable property apply order (bug #5313) --- CHANGELOG.md | 1 + components/nifosg/nifloader.cpp | 7 +++---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ad109160..9f4b16348 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -205,6 +205,7 @@ Bug #5278: Console command Show doesn't fall back to global variable after local var not found Bug #5300: NPCs don't switch from torch to shield when starting combat Bug #5308: World map copying makes save loading much slower + Bug #5313: Node properties of identical type are not applied in the correct order Feature #1774: Handle AvoidNode Feature #2229: Improve pathfinding AI Feature #3025: Analogue gamepad movement controls diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 644e82484..ea43fc9c6 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -63,6 +63,8 @@ namespace // Collect all properties affecting the given drawable that should be handled on drawable basis rather than on the node hierarchy above it. void collectDrawableProperties(const Nif::Node* nifNode, std::vector& out) { + if (nifNode->parent) + collectDrawableProperties(nifNode->parent, out); const Nif::PropertyList& props = nifNode->props; for (size_t i = 0; i parent) - collectDrawableProperties(nifNode->parent, out); } // NodeCallback used to have a node always oriented towards the camera. The node can have translation and scale @@ -1719,9 +1719,8 @@ namespace NifOsg int lightmode = 1; - for (std::vector::const_reverse_iterator it = properties.rbegin(); it != properties.rend(); ++it) + for (const Nif::Property* property : properties) { - const Nif::Property* property = *it; switch (property->recType) { case Nif::RC_NiSpecularProperty: From 3e3f5d66b28b60d66793f2112935500a58458f77 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Wed, 25 Mar 2020 21:06:21 +0300 Subject: [PATCH 95/99] Don't reset dead non-werewolf vampires' vampire NPC type --- apps/openmw/mwrender/npcanimation.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 18ecd30fc..261723db5 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -269,6 +269,9 @@ void HeadAnimationTime::setBlinkStop(float value) NpcAnimation::NpcType NpcAnimation::getNpcType() { const MWWorld::Class &cls = mPtr.getClass(); + // Dead vampires should typically stay vampires. + if (mNpcType == Type_Vampire && cls.getNpcStats(mPtr).isDead() && !cls.getNpcStats(mPtr).isWerewolf()) + return mNpcType; NpcAnimation::NpcType curType = Type_Normal; if (cls.getCreatureStats(mPtr).getMagicEffects().get(ESM::MagicEffect::Vampirism).getMagnitude() > 0) curType = Type_Vampire; From 04ebe5c4c9648c570574e193ed800d5460fdf9d3 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Thu, 26 Mar 2020 15:22:31 +0300 Subject: [PATCH 96/99] Reset skills of dead actors (bug #5328) --- CHANGELOG.md | 1 + apps/openmw/mwmechanics/actors.cpp | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 85fffdd1f..0101587fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -207,6 +207,7 @@ Bug #5300: NPCs don't switch from torch to shield when starting combat Bug #5308: World map copying makes save loading much slower Bug #5313: Node properties of identical type are not applied in the correct order + Bug #5328: Skills aren't properly reset for dead actors Feature #1774: Handle AvoidNode Feature #2229: Improve pathfinding AI Feature #3025: Analogue gamepad movement controls diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index bda78c0b5..29ed1a767 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1850,6 +1850,8 @@ namespace MWMechanics stats.getActiveSpells().visitEffectSources(soulTrap); } + // Magic effects will be reset later, and the magic effect that could kill the actor + // needs to be determined now calculateCreatureStatModifiers(iter->first, 0); if (cls.isEssential(iter->first)) @@ -1867,7 +1869,9 @@ namespace MWMechanics // Make sure spell effects are removed purgeSpellEffects(stats.getActorId()); + // Reset dynamic stats, attributes and skills calculateCreatureStatModifiers(iter->first, 0); + calculateNpcStatModifiers(iter->first, 0); if( iter->first == getPlayer()) { From bec5746fddcd5097f6b75d887c193a60a491af42 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Thu, 26 Mar 2020 23:30:08 +0300 Subject: [PATCH 97/99] Improve blank line handling in settings writer (bug #5326) --- CHANGELOG.md | 1 + components/settings/parser.cpp | 42 +++++++++++++++++++++++++--------- 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0101587fa..985c49c09 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -207,6 +207,7 @@ Bug #5300: NPCs don't switch from torch to shield when starting combat Bug #5308: World map copying makes save loading much slower Bug #5313: Node properties of identical type are not applied in the correct order + Bug #5326: Formatting issues in the settings.cfg Bug #5328: Skills aren't properly reset for dead actors Feature #1774: Handle AvoidNode Feature #2229: Improve pathfinding AI diff --git a/components/settings/parser.cpp b/components/settings/parser.cpp index e256da0d6..1dcb0fd8d 100644 --- a/components/settings/parser.cpp +++ b/components/settings/parser.cpp @@ -77,6 +77,9 @@ void Settings::SettingsFileParser::saveSettingsFile(const std::string& file, con // Were there any lines at all in the file? bool existing = false; + // Is an entirely blank line queued to be added? + bool emptyLineQueued = false; + // The category/section we're currently in. std::string currentCategory; @@ -103,9 +106,19 @@ void Settings::SettingsFileParser::saveSettingsFile(const std::string& file, con // Don't add additional newlines at the end of the file. if (istream.eof()) continue; - // Copy entirely blank lines. - if (!skipWhiteSpace(i, line)) { - ostream << line << std::endl; + // An empty line was queued. + if (emptyLineQueued) + { + emptyLineQueued = false; + // We're still going through the current category, so we should copy it. + if (currentCategory.empty() || line[i] != '[') + ostream << std::endl; + } + + // Queue entirely blank lines to add them if desired. + if (!skipWhiteSpace(i, line)) + { + emptyLineQueued = true; continue; } @@ -128,15 +141,22 @@ void Settings::SettingsFileParser::saveSettingsFile(const std::string& file, con continue; } - // Ensure that all options in the current category have been written. - for (CategorySettingStatusMap::iterator mit = written.begin(); mit != written.end(); ++mit) { - if (mit->second == false && mit->first.first == currentCategory) { - Log(Debug::Verbose) << "Added new setting: [" << currentCategory << "] " - << mit->first.second << " = " << settings.at(mit->first); - ostream << mit->first.second << " = " << settings.at(mit->first) << std::endl; - mit->second = true; - changed = true; + if (!currentCategory.empty()) + { + // Ensure that all options in the current category have been written. + for (CategorySettingStatusMap::iterator mit = written.begin(); mit != written.end(); ++mit) + { + if (mit->second == false && mit->first.first == currentCategory) + { + Log(Debug::Verbose) << "Added new setting: [" << currentCategory << "] " + << mit->first.second << " = " << settings.at(mit->first); + ostream << mit->first.second << " = " << settings.at(mit->first) << std::endl; + mit->second = true; + changed = true; + } } + // Add an empty line after the last option in a category. + ostream << std::endl; } // Update the current category. From 99e89f23a6943f2030a161e59a4b60d522dd6700 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 28 Mar 2020 19:13:02 +0300 Subject: [PATCH 98/99] Fix calculateNpcStatModifiers call for non-NPCs --- apps/openmw/mwmechanics/actors.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 29ed1a767..8ee248ee5 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1871,7 +1871,8 @@ namespace MWMechanics // Reset dynamic stats, attributes and skills calculateCreatureStatModifiers(iter->first, 0); - calculateNpcStatModifiers(iter->first, 0); + if (iter->first.getClass().isNpc()) + calculateNpcStatModifiers(iter->first, 0); if( iter->first == getPlayer()) { From 69219c18a7b42fd29cbe4d8d30bcbb35edbd3c58 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 28 Mar 2020 19:15:43 +0300 Subject: [PATCH 99/99] Make sure blank lines at the end of settings.cfg don't disappear --- components/settings/parser.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/components/settings/parser.cpp b/components/settings/parser.cpp index 1dcb0fd8d..9693bf511 100644 --- a/components/settings/parser.cpp +++ b/components/settings/parser.cpp @@ -103,18 +103,18 @@ void Settings::SettingsFileParser::saveSettingsFile(const std::string& file, con // The current character position in the line. size_t i = 0; - // Don't add additional newlines at the end of the file. - if (istream.eof()) continue; - // An empty line was queued. if (emptyLineQueued) { emptyLineQueued = false; // We're still going through the current category, so we should copy it. - if (currentCategory.empty() || line[i] != '[') + if (currentCategory.empty() || istream.eof() || line[i] != '[') ostream << std::endl; } + // Don't add additional newlines at the end of the file otherwise. + if (istream.eof()) continue; + // Queue entirely blank lines to add them if desired. if (!skipWhiteSpace(i, line)) {