diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index bfd21d5e27..ca19383d15 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -282,6 +282,9 @@ namespace MWWorld mPhysics->remove(ptr); } + const auto cellX = (*iter)->getCell()->getGridX(); + const auto cellY = (*iter)->getCell()->getGridY(); + if ((*iter)->getCell()->isExterior()) { const ESM::Land* land = @@ -291,14 +294,15 @@ namespace MWWorld ); if (land && land->mDataTypes&ESM::Land::DATA_VHGT) { - const auto cellX = (*iter)->getCell()->getGridX(); - const auto cellY = (*iter)->getCell()->getGridY(); if (const auto heightField = mPhysics->getHeightField(cellX, cellY)) navigator->removeObject(reinterpret_cast(heightField)); mPhysics->removeHeightField(cellX, cellY); } } + if ((*iter)->getCell()->hasWater()) + navigator->removeWater(osg::Vec2i(cellX, cellY)); + navigator->update(player.getRefData().getPosition().asVec3()); MWBase::Environment::get().getMechanicsManager()->drop (*iter); @@ -326,11 +330,12 @@ namespace MWWorld const auto navigator = MWBase::Environment::get().getWorld()->getNavigator(); + const int cellX = cell->getCell()->getGridX(); + const int cellY = cell->getCell()->getGridY(); + // Load terrain physics first... if (cell->getCell()->isExterior()) { - int cellX = cell->getCell()->getGridX(); - int cellY = cell->getCell()->getGridY(); osg::ref_ptr land = mRendering.getLandManager()->getLand(cellX, cellY); const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VHGT) : 0; if (data) @@ -368,6 +373,18 @@ namespace MWWorld { mPhysics->enableWater(waterLevel); mRendering.setWaterHeight(waterLevel); + + if (cell->getCell()->isExterior()) + { + if (const auto heightField = mPhysics->getHeightField(cellX, cellY)) + navigator->addWater(osg::Vec2i(cellX, cellY), ESM::Land::REAL_SIZE, + cell->getWaterLevel(), heightField->getCollisionObject()->getWorldTransform()); + } + else + { + navigator->addWater(osg::Vec2i(cellX, cellY), std::numeric_limits::max(), + cell->getWaterLevel(), btTransform::getIdentity()); + } } else mPhysics->disableWater(); diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index e1144d97a2..c8578fc354 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -6,6 +6,7 @@ #include #include +#include namespace osg { @@ -27,6 +28,11 @@ namespace Loading class Listener; } +namespace DetourNavigator +{ + class Water; +} + namespace MWRender { class SkyManager; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index a7bda8c0be..bb8bd9615a 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -168,42 +168,6 @@ namespace MWWorld mLevitationEnabled(true), mGoToJail(false), mDaysInPrison(0), mPlayerTraveling(false), mPlayerInJail(false), mSpellPreloadTimer(0.f) { - mPhysics.reset(new MWPhysics::PhysicsSystem(resourceSystem, rootNode)); - - DetourNavigator::Settings navigatorSettings; - navigatorSettings.mBorderSize = Settings::Manager::getInt("border size", "Navigator"); - navigatorSettings.mCellHeight = Settings::Manager::getFloat("cell height", "Navigator"); - navigatorSettings.mCellSize = Settings::Manager::getFloat("cell size", "Navigator"); - navigatorSettings.mDetailSampleDist = Settings::Manager::getFloat("detail sample dist", "Navigator"); - navigatorSettings.mDetailSampleMaxError = Settings::Manager::getFloat("detail sample max error", "Navigator"); - navigatorSettings.mMaxClimb = MWPhysics::sStepSizeUp; - navigatorSettings.mMaxSimplificationError = Settings::Manager::getFloat("max simplification error", "Navigator"); - navigatorSettings.mMaxSlope = MWPhysics::sMaxSlope; - navigatorSettings.mRecastScaleFactor = Settings::Manager::getFloat("recast scale factor", "Navigator"); - navigatorSettings.mMaxEdgeLen = Settings::Manager::getInt("max edge len", "Navigator"); - navigatorSettings.mMaxNavMeshQueryNodes = Settings::Manager::getInt("max nav mesh query nodes", "Navigator"); - navigatorSettings.mMaxVertsPerPoly = Settings::Manager::getInt("max verts per poly", "Navigator"); - navigatorSettings.mRegionMergeSize = Settings::Manager::getInt("region merge size", "Navigator"); - navigatorSettings.mRegionMinSize = Settings::Manager::getInt("region min size", "Navigator"); - navigatorSettings.mTileSize = Settings::Manager::getInt("tile size", "Navigator"); - navigatorSettings.mMaxPolygonPathSize = static_cast(Settings::Manager::getInt("max polygon path size", "Navigator")); - navigatorSettings.mMaxSmoothPathSize = static_cast(Settings::Manager::getInt("max smooth path size", "Navigator")); - navigatorSettings.mTrianglesPerChunk = static_cast(Settings::Manager::getInt("triangles per chunk", "Navigator")); - navigatorSettings.mEnableWriteRecastMeshToFile = Settings::Manager::getBool("enable write recast mesh to file", "Navigator"); - navigatorSettings.mEnableWriteNavMeshToFile = Settings::Manager::getBool("enable write nav mesh to file", "Navigator"); - navigatorSettings.mRecastMeshPathPrefix = Settings::Manager::getString("recast mesh path prefix", "Navigator"); - navigatorSettings.mNavMeshPathPrefix = Settings::Manager::getString("nav mesh path prefix", "Navigator"); - navigatorSettings.mEnableRecastMeshFileNameRevision = Settings::Manager::getBool("enable recast mesh file name revision", "Navigator"); - navigatorSettings.mEnableNavMeshFileNameRevision = Settings::Manager::getBool("enable nav mesh file name revision", "Navigator"); - if (Settings::Manager::getBool("enable log", "Navigator")) - DetourNavigator::Log::instance().setSink(std::unique_ptr( - new DetourNavigator::FileSink(Settings::Manager::getString("log path", "Navigator")))); - mNavigator.reset(new DetourNavigator::Navigator(navigatorSettings)); - - mRendering.reset(new MWRender::RenderingManager(viewer, rootNode, resourceSystem, workQueue, &mFallback, resourcePath, *mNavigator)); - mProjectileManager.reset(new ProjectileManager(mRendering->getLightRoot(), resourceSystem, mRendering.get(), mPhysics.get())); - mRendering->preloadCommonAssets(); - mEsm.resize(contentFiles.size()); Loading::Listener* listener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); listener->loadingOn(); @@ -232,6 +196,43 @@ namespace MWWorld mSwimHeightScale = mStore.get().find("fSwimHeightScale")->mValue.getFloat(); + mPhysics.reset(new MWPhysics::PhysicsSystem(resourceSystem, rootNode)); + + DetourNavigator::Settings navigatorSettings; + navigatorSettings.mBorderSize = Settings::Manager::getInt("border size", "Navigator"); + navigatorSettings.mCellHeight = Settings::Manager::getFloat("cell height", "Navigator"); + navigatorSettings.mCellSize = Settings::Manager::getFloat("cell size", "Navigator"); + navigatorSettings.mDetailSampleDist = Settings::Manager::getFloat("detail sample dist", "Navigator"); + navigatorSettings.mDetailSampleMaxError = Settings::Manager::getFloat("detail sample max error", "Navigator"); + navigatorSettings.mMaxClimb = MWPhysics::sStepSizeUp; + navigatorSettings.mMaxSimplificationError = Settings::Manager::getFloat("max simplification error", "Navigator"); + navigatorSettings.mMaxSlope = MWPhysics::sMaxSlope; + navigatorSettings.mRecastScaleFactor = Settings::Manager::getFloat("recast scale factor", "Navigator"); + navigatorSettings.mSwimHeightScale = mSwimHeightScale; + navigatorSettings.mMaxEdgeLen = Settings::Manager::getInt("max edge len", "Navigator"); + navigatorSettings.mMaxNavMeshQueryNodes = Settings::Manager::getInt("max nav mesh query nodes", "Navigator"); + navigatorSettings.mMaxVertsPerPoly = Settings::Manager::getInt("max verts per poly", "Navigator"); + navigatorSettings.mRegionMergeSize = Settings::Manager::getInt("region merge size", "Navigator"); + navigatorSettings.mRegionMinSize = Settings::Manager::getInt("region min size", "Navigator"); + navigatorSettings.mTileSize = Settings::Manager::getInt("tile size", "Navigator"); + navigatorSettings.mMaxPolygonPathSize = static_cast(Settings::Manager::getInt("max polygon path size", "Navigator")); + navigatorSettings.mMaxSmoothPathSize = static_cast(Settings::Manager::getInt("max smooth path size", "Navigator")); + navigatorSettings.mTrianglesPerChunk = static_cast(Settings::Manager::getInt("triangles per chunk", "Navigator")); + navigatorSettings.mEnableWriteRecastMeshToFile = Settings::Manager::getBool("enable write recast mesh to file", "Navigator"); + navigatorSettings.mEnableWriteNavMeshToFile = Settings::Manager::getBool("enable write nav mesh to file", "Navigator"); + navigatorSettings.mRecastMeshPathPrefix = Settings::Manager::getString("recast mesh path prefix", "Navigator"); + navigatorSettings.mNavMeshPathPrefix = Settings::Manager::getString("nav mesh path prefix", "Navigator"); + navigatorSettings.mEnableRecastMeshFileNameRevision = Settings::Manager::getBool("enable recast mesh file name revision", "Navigator"); + navigatorSettings.mEnableNavMeshFileNameRevision = Settings::Manager::getBool("enable nav mesh file name revision", "Navigator"); + if (Settings::Manager::getBool("enable log", "Navigator")) + DetourNavigator::Log::instance().setSink(std::unique_ptr( + new DetourNavigator::FileSink(Settings::Manager::getString("log path", "Navigator")))); + mNavigator.reset(new DetourNavigator::Navigator(navigatorSettings)); + + mRendering.reset(new MWRender::RenderingManager(viewer, rootNode, resourceSystem, workQueue, &mFallback, resourcePath, *mNavigator)); + mProjectileManager.reset(new ProjectileManager(mRendering->getLightRoot(), resourceSystem, mRendering.get(), mPhysics.get())); + mRendering->preloadCommonAssets(); + mWeatherManager.reset(new MWWorld::WeatherManager(*mRendering, mFallback, mStore)); mWorldScene.reset(new Scene(*mRendering.get(), mPhysics.get())); diff --git a/apps/openmw_test_suite/detournavigator/navigator.cpp b/apps/openmw_test_suite/detournavigator/navigator.cpp index aa7906008a..84aa344744 100644 --- a/apps/openmw_test_suite/detournavigator/navigator.cpp +++ b/apps/openmw_test_suite/detournavigator/navigator.cpp @@ -48,6 +48,7 @@ namespace mSettings.mMaxSimplificationError = 1.3f; mSettings.mMaxSlope = 49; mSettings.mRecastScaleFactor = 0.017647058823529415f; + mSettings.mSwimHeightScale = 0.89999997615814208984375f; mSettings.mMaxEdgeLen = 12; mSettings.mMaxNavMeshQueryNodes = 2048; mSettings.mMaxVertsPerPoly = 6; @@ -415,4 +416,142 @@ namespace osg::Vec3f(215, -215, 1.93937528133392333984375), })) << mPath; } + + TEST_F(DetourNavigatorNavigatorTest, path_should_be_over_water_ground_lower_than_water) + { + std::array heightfieldData {{ + -50, -50, -50, -50, 0, + -50, -100, -150, -100, -50, + -50, -150, -200, -150, -100, + -50, -100, -150, -100, -100, + 0, -50, -100, -100, -100, + }}; + btHeightfieldTerrainShape shape(5, 5, heightfieldData.data(), 1, 0, 0, 2, PHY_FLOAT, false); + shape.setLocalScaling(btVector3(128, 128, 1)); + + mNavigator->addAgent(mAgentHalfExtents); + mNavigator->addWater(osg::Vec2i(0, 0), 128 * 4, 300, btTransform::getIdentity()); + mNavigator->addObject(1, shape, btTransform::getIdentity()); + mNavigator->update(mPlayerPosition); + mNavigator->wait(); + + mStart.x() = 0; + mStart.z() = 300; + mEnd.x() = 0; + mEnd.z() = 300; + + mNavigator->findPath(mAgentHalfExtents, mStart, mEnd, mOut); + + EXPECT_EQ(mPath, std::deque({ + osg::Vec3f(0, 215, 185.33331298828125), + osg::Vec3f(0, 186.6666717529296875, 185.33331298828125), + osg::Vec3f(0, 158.333343505859375, 185.33331298828125), + osg::Vec3f(0, 130.0000152587890625, 185.33331298828125), + osg::Vec3f(0, 101.66667938232421875, 185.33331298828125), + osg::Vec3f(0, 73.333343505859375, 185.33331298828125), + osg::Vec3f(0, 45.0000152587890625, 185.33331298828125), + osg::Vec3f(0, 16.6666812896728515625, 185.33331298828125), + osg::Vec3f(0, -11.66664981842041015625, 185.33331298828125), + osg::Vec3f(0, -39.999980926513671875, 185.33331298828125), + osg::Vec3f(0, -68.33331298828125, 185.33331298828125), + osg::Vec3f(0, -96.66664886474609375, 185.33331298828125), + osg::Vec3f(0, -124.99997711181640625, 185.33331298828125), + osg::Vec3f(0, -153.33331298828125, 185.33331298828125), + osg::Vec3f(0, -181.6666412353515625, 185.33331298828125), + osg::Vec3f(0, -209.999969482421875, 185.33331298828125), + osg::Vec3f(0, -215, 185.33331298828125), + })) << mPath; + } + + TEST_F(DetourNavigatorNavigatorTest, path_should_be_over_water_when_ground_cross_water) + { + std::array heightfieldData {{ + 0, 0, 0, 0, 0, 0, 0, + 0, -100, -100, -100, -100, -100, 0, + 0, -100, -150, -150, -150, -100, 0, + 0, -100, -150, -200, -150, -100, 0, + 0, -100, -150, -150, -150, -100, 0, + 0, -100, -100, -100, -100, -100, 0, + 0, 0, 0, 0, 0, 0, 0, + }}; + btHeightfieldTerrainShape shape(7, 7, heightfieldData.data(), 1, 0, 0, 2, PHY_FLOAT, false); + shape.setLocalScaling(btVector3(128, 128, 1)); + + mNavigator->addAgent(mAgentHalfExtents); + mNavigator->addWater(osg::Vec2i(0, 0), 128 * 4, -25, btTransform::getIdentity()); + mNavigator->addObject(1, shape, btTransform::getIdentity()); + mNavigator->update(mPlayerPosition); + mNavigator->wait(); + + mStart.x() = 0; + mEnd.x() = 0; + + mNavigator->findPath(mAgentHalfExtents, mStart, mEnd, mOut); + + EXPECT_EQ(mPath, std::deque({ + osg::Vec3f(0, 215, -94.75363922119140625), + osg::Vec3f(0, 186.6666717529296875, -106.0000152587890625), + osg::Vec3f(0, 158.333343505859375, -115.85507965087890625), + osg::Vec3f(0, 130.0000152587890625, -125.71016693115234375), + osg::Vec3f(0, 101.66667938232421875, -135.5652313232421875), + osg::Vec3f(0, 73.333343505859375, -143.3333587646484375), + osg::Vec3f(0, 45.0000152587890625, -143.3333587646484375), + osg::Vec3f(0, 16.6666812896728515625, -143.3333587646484375), + osg::Vec3f(0, -11.66664981842041015625, -143.3333587646484375), + osg::Vec3f(0, -39.999980926513671875, -143.3333587646484375), + osg::Vec3f(0, -68.33331298828125, -143.3333587646484375), + osg::Vec3f(0, -96.66664886474609375, -137.3043670654296875), + osg::Vec3f(0, -124.99997711181640625, -127.44930267333984375), + osg::Vec3f(0, -153.33331298828125, -117.5942230224609375), + osg::Vec3f(0, -181.6666412353515625, -107.7391510009765625), + osg::Vec3f(0, -209.999969482421875, -97.79712677001953125), + osg::Vec3f(0, -215, -94.753631591796875), + })) << mPath; + } + + TEST_F(DetourNavigatorNavigatorTest, path_should_be_over_water_when_ground_cross_water_with_max_int_cells_size) + { + std::array heightfieldData {{ + 0, 0, 0, 0, 0, 0, 0, + 0, -100, -100, -100, -100, -100, 0, + 0, -100, -150, -150, -150, -100, 0, + 0, -100, -150, -200, -150, -100, 0, + 0, -100, -150, -150, -150, -100, 0, + 0, -100, -100, -100, -100, -100, 0, + 0, 0, 0, 0, 0, 0, 0, + }}; + btHeightfieldTerrainShape shape(7, 7, heightfieldData.data(), 1, 0, 0, 2, PHY_FLOAT, false); + shape.setLocalScaling(btVector3(128, 128, 1)); + + mNavigator->addAgent(mAgentHalfExtents); + mNavigator->addObject(1, shape, btTransform::getIdentity()); + mNavigator->addWater(osg::Vec2i(0, 0), std::numeric_limits::max(), -25, btTransform::getIdentity()); + mNavigator->update(mPlayerPosition); + mNavigator->wait(); + + mStart.x() = 0; + mEnd.x() = 0; + + mNavigator->findPath(mAgentHalfExtents, mStart, mEnd, mOut); + + EXPECT_EQ(mPath, std::deque({ + osg::Vec3f(0, 215, -94.75363922119140625), + osg::Vec3f(0, 186.6666717529296875, -106.0000152587890625), + osg::Vec3f(0, 158.333343505859375, -115.85507965087890625), + osg::Vec3f(0, 130.0000152587890625, -125.71016693115234375), + osg::Vec3f(0, 101.66667938232421875, -135.5652313232421875), + osg::Vec3f(0, 73.333343505859375, -143.3333587646484375), + osg::Vec3f(0, 45.0000152587890625, -143.3333587646484375), + osg::Vec3f(0, 16.6666812896728515625, -143.3333587646484375), + osg::Vec3f(0, -11.66664981842041015625, -143.3333587646484375), + osg::Vec3f(0, -39.999980926513671875, -143.3333587646484375), + osg::Vec3f(0, -68.33331298828125, -143.3333587646484375), + osg::Vec3f(0, -96.66664886474609375, -137.3043670654296875), + osg::Vec3f(0, -124.99997711181640625, -127.44930267333984375), + osg::Vec3f(0, -153.33331298828125, -117.5942230224609375), + osg::Vec3f(0, -181.6666412353515625, -107.7391510009765625), + osg::Vec3f(0, -209.999969482421875, -97.79712677001953125), + osg::Vec3f(0, -215, -94.753631591796875), + })) << mPath; + } } diff --git a/apps/openmw_test_suite/detournavigator/recastmeshbuilder.cpp b/apps/openmw_test_suite/detournavigator/recastmeshbuilder.cpp index c27f608a9e..ffef6114c7 100644 --- a/apps/openmw_test_suite/detournavigator/recastmeshbuilder.cpp +++ b/apps/openmw_test_suite/detournavigator/recastmeshbuilder.cpp @@ -13,6 +13,14 @@ #include +namespace DetourNavigator +{ + static inline bool operator ==(const RecastMesh::Water& lhs, const RecastMesh::Water& rhs) + { + return lhs.mCellSize == rhs.mCellSize && lhs.mTransform == rhs.mTransform; + } +} + namespace { using namespace testing; @@ -391,4 +399,14 @@ namespace EXPECT_EQ(recastMesh->getIndices(), std::vector({0, 1, 2, 3, 4, 5})); EXPECT_EQ(recastMesh->getAreaTypes(), std::vector({AreaType_ground, AreaType_null})); } + + TEST_F(DetourNavigatorRecastMeshBuilderTest, add_water_then_get_water_should_return_it) + { + RecastMeshBuilder builder(mSettings, mBounds); + builder.addWater(1000, btTransform(btMatrix3x3::getIdentity(), btVector3(100, 200, 300))); + const auto recastMesh = builder.create(); + EXPECT_EQ(recastMesh->getWater(), std::vector({ + RecastMesh::Water {1000, btTransform(btMatrix3x3::getIdentity(), btVector3(100, 200, 300))} + })); + } } diff --git a/components/detournavigator/areatype.hpp b/components/detournavigator/areatype.hpp index 3efaccc44c..0daa524ca1 100644 --- a/components/detournavigator/areatype.hpp +++ b/components/detournavigator/areatype.hpp @@ -8,6 +8,7 @@ namespace DetourNavigator enum AreaType : unsigned char { AreaType_null = RC_NULL_AREA, + AreaType_water, AreaType_ground = RC_WALKABLE_AREA, }; } diff --git a/components/detournavigator/cachedrecastmeshmanager.cpp b/components/detournavigator/cachedrecastmeshmanager.cpp index c5ff00a265..8a9fbd4ee8 100644 --- a/components/detournavigator/cachedrecastmeshmanager.cpp +++ b/components/detournavigator/cachedrecastmeshmanager.cpp @@ -32,6 +32,23 @@ namespace DetourNavigator return object; } + bool CachedRecastMeshManager::addWater(const osg::Vec2i& cellPosition, const int cellSize, + const btTransform& transform) + { + if (!mImpl.addWater(cellPosition, cellSize, transform)) + return false; + mCached.reset(); + return true; + } + + boost::optional CachedRecastMeshManager::removeWater(const osg::Vec2i& cellPosition) + { + const auto water = mImpl.removeWater(cellPosition); + if (water) + mCached.reset(); + return water; + } + std::shared_ptr CachedRecastMeshManager::getMesh() { if (!mCached) diff --git a/components/detournavigator/cachedrecastmeshmanager.hpp b/components/detournavigator/cachedrecastmeshmanager.hpp index 15168f9ad6..048d002701 100644 --- a/components/detournavigator/cachedrecastmeshmanager.hpp +++ b/components/detournavigator/cachedrecastmeshmanager.hpp @@ -17,6 +17,10 @@ namespace DetourNavigator bool updateObject(std::size_t id, const btTransform& transform, const AreaType areaType); + bool addWater(const osg::Vec2i& cellPosition, const int cellSize, const btTransform& transform); + + boost::optional removeWater(const osg::Vec2i& cellPosition); + boost::optional removeObject(std::size_t id); std::shared_ptr getMesh(); diff --git a/components/detournavigator/findsmoothpath.hpp b/components/detournavigator/findsmoothpath.hpp index 2fbc07c5fd..c145e828c6 100644 --- a/components/detournavigator/findsmoothpath.hpp +++ b/components/detournavigator/findsmoothpath.hpp @@ -3,6 +3,7 @@ #include "dtstatus.hpp" #include "exceptions.hpp" +#include "flags.hpp" #include "settings.hpp" #include "settingsutils.hpp" @@ -10,6 +11,8 @@ #include #include +#include + #include #include @@ -27,6 +30,11 @@ namespace DetourNavigator return osg::Vec3f(values[0], values[1], values[2]); } + inline osg::Vec3f makeOsgVec3f(const btVector3& value) + { + return osg::Vec3f(value.x(), value.y(), value.z()); + } + inline bool inRange(const osg::Vec3f& v1, const osg::Vec3f& v2, const float r, const float h) { const auto d = v2 - v1; @@ -217,6 +225,7 @@ namespace DetourNavigator OPENMW_CHECK_DT_STATUS(navMeshQuery.init(&navMesh, settings.mMaxNavMeshQueryNodes)); dtQueryFilter queryFilter; + queryFilter.setIncludeFlags(Flag_swim | Flag_walk); dtPolyRef startRef = 0; osg::Vec3f startPolygonPosition; diff --git a/components/detournavigator/flags.hpp b/components/detournavigator/flags.hpp index 21a1d7694c..38c0f13f6a 100644 --- a/components/detournavigator/flags.hpp +++ b/components/detournavigator/flags.hpp @@ -9,6 +9,7 @@ namespace DetourNavigator { Flag_none = 0, Flag_walk = 1 << 0, + Flag_swim = 1 << 1, }; } diff --git a/components/detournavigator/gettilespositions.hpp b/components/detournavigator/gettilespositions.hpp index b52984452a..86ce77402d 100644 --- a/components/detournavigator/gettilespositions.hpp +++ b/components/detournavigator/gettilespositions.hpp @@ -51,6 +51,23 @@ namespace DetourNavigator getTilesPositions(makeOsgVec3f(aabbMin), makeOsgVec3f(aabbMax), settings, std::forward(callback)); } + + template + void getTilesPositions(const int cellSize, const btTransform& transform, + const Settings& settings, Callback&& callback) + { + const auto halfCellSize = cellSize / 2; + auto aabbMin = transform(btVector3(-halfCellSize, -halfCellSize, 0)); + auto aabbMax = transform(btVector3(halfCellSize, halfCellSize, 0)); + + aabbMin.setX(std::min(aabbMin.x(), aabbMax.x())); + aabbMin.setY(std::min(aabbMin.y(), aabbMax.y())); + + aabbMax.setX(std::max(aabbMin.x(), aabbMax.x())); + aabbMax.setY(std::max(aabbMin.y(), aabbMax.y())); + + getTilesPositions(makeOsgVec3f(aabbMin), makeOsgVec3f(aabbMax), settings, std::forward(callback)); + } } #endif diff --git a/components/detournavigator/makenavmesh.cpp b/components/detournavigator/makenavmesh.cpp index 454b88f9ea..71e8881683 100644 --- a/components/detournavigator/makenavmesh.cpp +++ b/components/detournavigator/makenavmesh.cpp @@ -15,6 +15,7 @@ #include #include +#include #include #include @@ -64,6 +65,41 @@ namespace {} }; + osg::Vec3f makeOsgVec3f(const btVector3& value) + { + return osg::Vec3f(value.x(), value.y(), value.z()); + } + + struct WaterBounds + { + osg::Vec3f mMin; + osg::Vec3f mMax; + }; + + WaterBounds getWaterBounds(const RecastMesh::Water& water, const Settings& settings, + const osg::Vec3f& agentHalfExtents) + { + if (water.mCellSize == std::numeric_limits::max()) + { + const auto transform = getSwimLevelTransform(settings, water.mTransform, agentHalfExtents.z()); + const auto min = toNavMeshCoordinates(settings, makeOsgVec3f(transform(btVector3(-1, -1, 0)))); + const auto max = toNavMeshCoordinates(settings, makeOsgVec3f(transform(btVector3(1, 1, 0)))); + return WaterBounds { + osg::Vec3f(-std::numeric_limits::max(), min.y(), -std::numeric_limits::max()), + osg::Vec3f(std::numeric_limits::max(), max.y(), std::numeric_limits::max()) + }; + } + else + { + const auto transform = getSwimLevelTransform(settings, water.mTransform, agentHalfExtents.z()); + const auto halfCellSize = water.mCellSize / 2.0f; + return WaterBounds { + toNavMeshCoordinates(settings, makeOsgVec3f(transform(btVector3(-halfCellSize, -halfCellSize, 0)))), + toNavMeshCoordinates(settings, makeOsgVec3f(transform(btVector3(halfCellSize, halfCellSize, 0)))) + }; + } + } + NavMeshData makeNavMeshTileData(const osg::Vec3f& agentHalfExtents, const RecastMesh& recastMesh, const int tileX, const int tileY, const osg::Vec3f& boundsMin, const osg::Vec3f& boundsMax, const Settings& settings) @@ -156,6 +192,56 @@ namespace } } + { + const std::array areas {{AreaType_water, AreaType_water}}; + + for (const auto& water : recastMesh.getWater()) + { + const auto bounds = getWaterBounds(water, settings, agentHalfExtents); + + const osg::Vec2f tileBoundsMin( + std::min(config.bmax[0], std::max(config.bmin[0], bounds.mMin.x())), + std::min(config.bmax[2], std::max(config.bmin[2], bounds.mMin.z())) + ); + const osg::Vec2f tileBoundsMax( + std::min(config.bmax[0], std::max(config.bmin[0], bounds.mMax.x())), + std::min(config.bmax[2], std::max(config.bmin[2], bounds.mMax.z())) + ); + + if (tileBoundsMax == tileBoundsMin) + continue; + + const std::array vertices {{ + osg::Vec3f(tileBoundsMin.x(), bounds.mMin.y(), tileBoundsMin.y()), + osg::Vec3f(tileBoundsMin.x(), bounds.mMin.y(), tileBoundsMax.y()), + osg::Vec3f(tileBoundsMax.x(), bounds.mMin.y(), tileBoundsMax.y()), + osg::Vec3f(tileBoundsMax.x(), bounds.mMin.y(), tileBoundsMin.y()), + }}; + + std::array convertedVertices; + auto convertedVerticesIt = convertedVertices.begin(); + + for (const auto& vertex : vertices) + convertedVerticesIt = std::copy(vertex.ptr(), vertex.ptr() + 3, convertedVerticesIt); + + const std::array indices {{ + 0, 1, 2, + 0, 2, 3, + }}; + + OPENMW_CHECK_DT_RESULT(rcRasterizeTriangles( + &context, + convertedVertices.data(), + static_cast(convertedVertices.size() / 3), + indices.data(), + areas.data(), + static_cast(areas.size()), + solid, + config.walkableClimb + )); + } + } + rcFilterLowHangingWalkableObstacles(&context, config.walkableClimb, solid); rcFilterLedgeSpans(&context, config.walkableHeight, config.walkableClimb, solid); rcFilterWalkableLowHeightSpans(&context, config.walkableHeight, solid); @@ -187,8 +273,12 @@ namespace } for (int i = 0; i < polyMesh.npolys; ++i) + { if (polyMesh.areas[i] == AreaType_ground) polyMesh.flags[i] = Flag_walk; + else if (polyMesh.areas[i] == AreaType_water) + polyMesh.flags[i] = Flag_swim; + } dtNavMeshCreateParams params; params.verts = polyMesh.verts; @@ -302,8 +392,15 @@ namespace DetourNavigator return removeTile(); } - const auto& boundsMin = recastMesh->getBoundsMin(); - const auto& boundsMax = recastMesh->getBoundsMax(); + auto boundsMin = recastMesh->getBoundsMin(); + auto boundsMax = recastMesh->getBoundsMax(); + + for (const auto& water : recastMesh->getWater()) + { + const auto bounds = getWaterBounds(water, settings, agentHalfExtents); + boundsMin.y() = std::min(boundsMin.y(), bounds.mMin.y()); + boundsMax.y() = std::max(boundsMax.y(), bounds.mMax.y()); + } if (boundsMin == boundsMax) { diff --git a/components/detournavigator/makenavmesh.hpp b/components/detournavigator/makenavmesh.hpp index cc9e6d8559..7a60152d71 100644 --- a/components/detournavigator/makenavmesh.hpp +++ b/components/detournavigator/makenavmesh.hpp @@ -9,7 +9,6 @@ #include #include -#include class dtNavMesh; diff --git a/components/detournavigator/navigator.cpp b/components/detournavigator/navigator.cpp index f6ead7c7db..359a9a316e 100644 --- a/components/detournavigator/navigator.cpp +++ b/components/detournavigator/navigator.cpp @@ -71,9 +71,24 @@ namespace DetourNavigator { bool result = mNavMeshManager.removeObject(id); const auto avoid = mAvoidIds.find(id); - if (avoid == mAvoidIds.end()) - return result; - return mNavMeshManager.removeObject(avoid->second) || result; + if (avoid != mAvoidIds.end()) + result = mNavMeshManager.removeObject(avoid->second) || result; + const auto water = mWaterIds.find(id); + if (water != mWaterIds.end()) + result = mNavMeshManager.removeObject(water->second) || result; + return result; + } + + bool Navigator::addWater(const osg::Vec2i& cellPosition, const int cellSize, const btScalar level, + const btTransform& transform) + { + return mNavMeshManager.addWater(cellPosition, cellSize, + btTransform(transform.getBasis(), btVector3(transform.getOrigin().x(), transform.getOrigin().y(), level))); + } + + bool Navigator::removeWater(const osg::Vec2i& cellPosition) + { + return mNavMeshManager.removeWater(cellPosition); } void Navigator::update(const osg::Vec3f& playerPosition) @@ -97,13 +112,23 @@ namespace DetourNavigator return mSettings; } - void Navigator::updateAvoidShapeId(std::size_t id, std::size_t avoidId) + void Navigator::updateAvoidShapeId(const std::size_t id, const std::size_t avoidId) { - auto inserted = mAvoidIds.insert(std::make_pair(id, avoidId)); + updateId(id, avoidId, mWaterIds); + } + + void Navigator::updateWaterShapeId(const std::size_t id, const std::size_t waterId) + { + updateId(id, waterId, mWaterIds); + } + + void Navigator::updateId(const std::size_t id, const std::size_t updateId, std::unordered_map& ids) + { + auto inserted = ids.insert(std::make_pair(id, updateId)); if (!inserted.second) { mNavMeshManager.removeObject(inserted.first->second); - inserted.second = avoidId; + inserted.second = updateId; } } } diff --git a/components/detournavigator/navigator.hpp b/components/detournavigator/navigator.hpp index 0f82bd7abe..b063975254 100644 --- a/components/detournavigator/navigator.hpp +++ b/components/detournavigator/navigator.hpp @@ -37,6 +37,11 @@ namespace DetourNavigator bool removeObject(std::size_t id); + bool addWater(const osg::Vec2i& cellPosition, const int cellSize, const btScalar level, + const btTransform& transform); + + bool removeWater(const osg::Vec2i& cellPosition); + void update(const osg::Vec3f& playerPosition); void wait(); @@ -59,8 +64,11 @@ namespace DetourNavigator NavMeshManager mNavMeshManager; std::map mAgents; std::unordered_map mAvoidIds; + std::unordered_map mWaterIds; - void updateAvoidShapeId(std::size_t id, std::size_t avoidId); + void updateAvoidShapeId(const std::size_t id, const std::size_t avoidId); + void updateWaterShapeId(const std::size_t id, const std::size_t waterId); + void updateId(const std::size_t id, const std::size_t waterId, std::unordered_map& ids); }; } diff --git a/components/detournavigator/navmeshmanager.cpp b/components/detournavigator/navmeshmanager.cpp index 265ef749f5..bfca44f794 100644 --- a/components/detournavigator/navmeshmanager.cpp +++ b/components/detournavigator/navmeshmanager.cpp @@ -58,6 +58,23 @@ namespace DetourNavigator return true; } + bool NavMeshManager::addWater(const osg::Vec2i& cellPosition, const int cellSize, const btTransform& transform) + { + if (!mRecastMeshManager.addWater(cellPosition, cellSize, transform)) + return false; + addChangedTiles(cellSize, transform, ChangeType::add); + return true; + } + + bool NavMeshManager::removeWater(const osg::Vec2i& cellPosition) + { + const auto water = mRecastMeshManager.removeWater(cellPosition); + if (!water) + return false; + addChangedTiles(water->mCellSize, water->mTransform, ChangeType::remove); + return true; + } + void NavMeshManager::addAgent(const osg::Vec3f& agentHalfExtents) { auto cached = mCache.find(agentHalfExtents); @@ -75,9 +92,7 @@ namespace DetourNavigator void NavMeshManager::update(osg::Vec3f playerPosition, const osg::Vec3f& agentHalfExtents) { - playerPosition *= mSettings.mRecastScaleFactor; - std::swap(playerPosition.y(), playerPosition.z()); - const auto playerTile = getTilePosition(mSettings, playerPosition); + const auto playerTile = getTilePosition(mSettings, toNavMeshCoordinates(mSettings, playerPosition)); if (mLastRecastMeshManagerRevision >= mRecastMeshManager.getRevision() && mPlayerTile && *mPlayerTile == playerTile) return; @@ -140,20 +155,36 @@ namespace DetourNavigator } void NavMeshManager::addChangedTiles(const btCollisionShape& shape, const btTransform& transform, - const ChangeType changeType) + const ChangeType changeType) { - getTilesPositions(shape, transform, mSettings, [&] (const TilePosition& v) { - for (const auto& cached : mCache) - if (cached.second) - { - auto& tiles = mChangedTiles[cached.first]; - auto tile = tiles.find(v); - if (tile == tiles.end()) - tiles.insert(std::make_pair(v, changeType)); - else - tile->second = addChangeType(tile->second, changeType); - } - }); + getTilesPositions(shape, transform, mSettings, + [&] (const TilePosition& v) { addChangedTile(v, changeType); }); + } + + void NavMeshManager::addChangedTiles(const int cellSize, const btTransform& transform, + const ChangeType changeType) + { + if (cellSize == std::numeric_limits::max()) + return; + + getTilesPositions(cellSize, transform, mSettings, + [&] (const TilePosition& v) { addChangedTile(v, changeType); }); + } + + void NavMeshManager::addChangedTile(const TilePosition& tilePosition, const ChangeType changeType) + { + for (const auto& cached : mCache) + { + if (cached.second) + { + auto& tiles = mChangedTiles[cached.first]; + auto tile = tiles.find(tilePosition); + if (tile == tiles.end()) + tiles.insert(std::make_pair(tilePosition, changeType)); + else + tile->second = addChangeType(tile->second, changeType); + } + } } const std::shared_ptr& NavMeshManager::getCached(const osg::Vec3f& agentHalfExtents) const diff --git a/components/detournavigator/navmeshmanager.hpp b/components/detournavigator/navmeshmanager.hpp index 25ee305c4f..6636402bf0 100644 --- a/components/detournavigator/navmeshmanager.hpp +++ b/components/detournavigator/navmeshmanager.hpp @@ -7,8 +7,6 @@ #include -#include - #include #include @@ -33,6 +31,10 @@ namespace DetourNavigator void addAgent(const osg::Vec3f& agentHalfExtents); + bool addWater(const osg::Vec2i& cellPosition, const int cellSize, const btTransform& transform); + + bool removeWater(const osg::Vec2i& cellPosition); + void reset(const osg::Vec3f& agentHalfExtents); void update(osg::Vec3f playerPosition, const osg::Vec3f& agentHalfExtents); @@ -55,6 +57,10 @@ namespace DetourNavigator void addChangedTiles(const btCollisionShape& shape, const btTransform& transform, const ChangeType changeType); + void addChangedTiles(const int cellSize, const btTransform& transform, const ChangeType changeType); + + void addChangedTile(const TilePosition& tilePosition, const ChangeType changeType); + const std::shared_ptr& getCached(const osg::Vec3f& agentHalfExtents) const; }; } diff --git a/components/detournavigator/recastmesh.cpp b/components/detournavigator/recastmesh.cpp index 5a339d3427..b4897d14aa 100644 --- a/components/detournavigator/recastmesh.cpp +++ b/components/detournavigator/recastmesh.cpp @@ -7,10 +7,11 @@ namespace DetourNavigator { RecastMesh::RecastMesh(std::vector indices, std::vector vertices, - std::vector areaTypes, const Settings& settings) + std::vector areaTypes, std::vector water, const Settings& settings) : mIndices(std::move(indices)) , mVertices(std::move(vertices)) , mAreaTypes(std::move(areaTypes)) + , mWater(std::move(water)) , mChunkyTriMesh(mVertices, mIndices, mAreaTypes, settings.mTrianglesPerChunk) { if (getTrianglesCount() != mAreaTypes.size()) diff --git a/components/detournavigator/recastmesh.hpp b/components/detournavigator/recastmesh.hpp index 2e31cd27d2..12dc73e488 100644 --- a/components/detournavigator/recastmesh.hpp +++ b/components/detournavigator/recastmesh.hpp @@ -9,6 +9,8 @@ #include +#include + namespace DetourNavigator { struct Settings; @@ -16,8 +18,14 @@ namespace DetourNavigator class RecastMesh { public: + struct Water + { + int mCellSize; + btTransform mTransform; + }; + RecastMesh(std::vector indices, std::vector vertices, - std::vector areaTypes, const Settings& settings); + std::vector areaTypes, std::vector water, const Settings& settings); const std::vector& getIndices() const { @@ -34,6 +42,11 @@ namespace DetourNavigator return mAreaTypes; } + const std::vector& getWater() const + { + return mWater; + } + std::size_t getVerticesCount() const { return mVertices.size() / 3; @@ -63,6 +76,7 @@ namespace DetourNavigator std::vector mIndices; std::vector mVertices; std::vector mAreaTypes; + std::vector mWater; ChunkyTriMesh mChunkyTriMesh; osg::Vec3f mBoundsMin; osg::Vec3f mBoundsMax; diff --git a/components/detournavigator/recastmeshbuilder.cpp b/components/detournavigator/recastmeshbuilder.cpp index 23a8bb83e6..3e87aa34b7 100644 --- a/components/detournavigator/recastmeshbuilder.cpp +++ b/components/detournavigator/recastmeshbuilder.cpp @@ -82,19 +82,16 @@ namespace DetourNavigator void RecastMeshBuilder::addObject(const btBoxShape& shape, const btTransform& transform, const AreaType areaType) { - const auto indexOffset = int(mVertices.size() / 3); + const auto indexOffset = static_cast(mVertices.size() / 3); - for (int vertex = 0; vertex < shape.getNumVertices(); ++vertex) + for (int vertex = 0, count = shape.getNumVertices(); vertex < count; ++vertex) { btVector3 position; shape.getVertex(vertex, position); addVertex(transform(position)); } - for (int vertex = 0; vertex < 12; ++vertex) - mAreaTypes.push_back(areaType); - - static const std::array indices {{ + const std::array indices {{ 0, 2, 3, 3, 1, 0, 0, 4, 6, @@ -111,11 +108,18 @@ namespace DetourNavigator std::transform(indices.begin(), indices.end(), std::back_inserter(mIndices), [&] (int index) { return index + indexOffset; }); + + std::generate_n(std::back_inserter(mAreaTypes), 12, [=] { return areaType; }); + } + + void RecastMeshBuilder::addWater(const int cellSize, const btTransform& transform) + { + mWater.push_back(RecastMesh::Water {cellSize, transform}); } std::shared_ptr RecastMeshBuilder::create() const { - return std::make_shared(mIndices, mVertices, mAreaTypes, mSettings); + return std::make_shared(mIndices, mVertices, mAreaTypes, mWater, mSettings); } void RecastMeshBuilder::reset() @@ -123,6 +127,7 @@ namespace DetourNavigator mIndices.clear(); mVertices.clear(); mAreaTypes.clear(); + mWater.clear(); } void RecastMeshBuilder::addObject(const btConcaveShape& shape, const btTransform& transform, diff --git a/components/detournavigator/recastmeshbuilder.hpp b/components/detournavigator/recastmeshbuilder.hpp index 2210933627..af0565cabd 100644 --- a/components/detournavigator/recastmeshbuilder.hpp +++ b/components/detournavigator/recastmeshbuilder.hpp @@ -4,14 +4,17 @@ #include "recastmesh.hpp" #include "tilebounds.hpp" +#include + +#include +#include + class btBoxShape; class btCollisionShape; class btCompoundShape; class btConcaveShape; class btHeightfieldTerrainShape; -class btTransform; class btTriangleCallback; -class btVector3; namespace DetourNavigator { @@ -30,6 +33,8 @@ namespace DetourNavigator void addObject(const btBoxShape& shape, const btTransform& transform, const AreaType areaType); + void addWater(const int mCellSize, const btTransform& transform); + std::shared_ptr create() const; void reset(); @@ -40,6 +45,7 @@ namespace DetourNavigator std::vector mIndices; std::vector mVertices; std::vector mAreaTypes; + std::vector mWater; void addObject(const btConcaveShape& shape, const btTransform& transform, btTriangleCallback&& callback); diff --git a/components/detournavigator/recastmeshmanager.cpp b/components/detournavigator/recastmeshmanager.cpp index d511891942..cabde7fdf8 100644 --- a/components/detournavigator/recastmeshmanager.cpp +++ b/components/detournavigator/recastmeshmanager.cpp @@ -42,6 +42,26 @@ namespace DetourNavigator return result; } + bool RecastMeshManager::addWater(const osg::Vec2i& cellPosition, const int cellSize, + const btTransform& transform) + { + if (!mWater.insert(std::make_pair(cellPosition, Water {cellSize, transform})).second) + return false; + mShouldRebuild = true; + return true; + } + + boost::optional RecastMeshManager::removeWater(const osg::Vec2i& cellPosition) + { + const auto water = mWater.find(cellPosition); + if (water == mWater.end()) + return boost::none; + mShouldRebuild = true; + const auto result = water->second; + mWater.erase(water); + return result; + } + std::shared_ptr RecastMeshManager::getMesh() { rebuild(); @@ -58,6 +78,8 @@ namespace DetourNavigator if (!mShouldRebuild) return; mMeshBuilder.reset(); + for (const auto& v : mWater) + mMeshBuilder.addWater(v.second.mCellSize, v.second.mTransform); for (const auto& v : mObjects) mMeshBuilder.addObject(v.second.getShape(), v.second.getTransform(), v.second.getAreaType()); mShouldRebuild = false; diff --git a/components/detournavigator/recastmeshmanager.hpp b/components/detournavigator/recastmeshmanager.hpp index 7900592950..c4391b6821 100644 --- a/components/detournavigator/recastmeshmanager.hpp +++ b/components/detournavigator/recastmeshmanager.hpp @@ -6,8 +6,11 @@ #include +#include + #include +#include #include class btCollisionShape; @@ -23,6 +26,12 @@ namespace DetourNavigator class RecastMeshManager { public: + struct Water + { + int mCellSize; + btTransform mTransform; + }; + RecastMeshManager(const Settings& settings, const TileBounds& bounds); bool addObject(std::size_t id, const btCollisionShape& shape, const btTransform& transform, @@ -30,6 +39,10 @@ namespace DetourNavigator bool updateObject(std::size_t id, const btTransform& transform, const AreaType areaType); + bool addWater(const osg::Vec2i& cellPosition, const int cellSize, const btTransform& transform); + + boost::optional removeWater(const osg::Vec2i& cellPosition); + boost::optional removeObject(std::size_t id); std::shared_ptr getMesh(); @@ -40,6 +53,7 @@ namespace DetourNavigator bool mShouldRebuild; RecastMeshBuilder mMeshBuilder; std::unordered_map mObjects; + std::map mWater; void rebuild(); }; diff --git a/components/detournavigator/settings.hpp b/components/detournavigator/settings.hpp index 0d34ae7e95..ccfece3dae 100644 --- a/components/detournavigator/settings.hpp +++ b/components/detournavigator/settings.hpp @@ -19,6 +19,7 @@ namespace DetourNavigator float mMaxSimplificationError; float mMaxSlope; float mRecastScaleFactor; + float mSwimHeightScale; int mBorderSize; int mMaxEdgeLen; int mMaxNavMeshQueryNodes; diff --git a/components/detournavigator/settingsutils.hpp b/components/detournavigator/settingsutils.hpp index 8127536862..d96ea53cc2 100644 --- a/components/detournavigator/settingsutils.hpp +++ b/components/detournavigator/settingsutils.hpp @@ -6,6 +6,8 @@ #include "tileposition.hpp" #include "tilebounds.hpp" +#include + #include #include #include @@ -68,6 +70,20 @@ namespace DetourNavigator { return settings.mBorderSize * settings.mCellSize; } + + inline float getSwimLevel(const Settings& settings, const float agentHalfExtentsZ) + { + return - settings.mSwimHeightScale * agentHalfExtentsZ; + } + + inline btTransform getSwimLevelTransform(const Settings& settings, const btTransform& transform, + const float agentHalfExtentsZ) + { + return btTransform( + transform.getBasis(), + transform.getOrigin() + btVector3(0, 0, getSwimLevel(settings, agentHalfExtentsZ) - agentHalfExtentsZ) + ); + } } #endif diff --git a/components/detournavigator/tilecachedrecastmeshmanager.cpp b/components/detournavigator/tilecachedrecastmeshmanager.cpp index f044b3c43e..164253f856 100644 --- a/components/detournavigator/tilecachedrecastmeshmanager.cpp +++ b/components/detournavigator/tilecachedrecastmeshmanager.cpp @@ -83,6 +83,80 @@ namespace DetourNavigator return result; } + bool TileCachedRecastMeshManager::addWater(const osg::Vec2i& cellPosition, const int cellSize, + const btTransform& transform) + { + const auto border = getBorderSize(mSettings); + + auto& tilesPositions = mWaterTilesPositions[cellPosition]; + + bool result = false; + + if (cellSize == std::numeric_limits::max()) + { + const std::lock_guard lock(mTilesMutex); + for (auto& tile : mTiles) + { + if (tile.second.addWater(cellPosition, cellSize, transform)) + { + tilesPositions.push_back(tile.first); + result = true; + } + } + } + else + { + getTilesPositions(cellSize, transform, mSettings, [&] (const TilePosition& tilePosition) + { + std::unique_lock lock(mTilesMutex); + auto tile = mTiles.find(tilePosition); + if (tile == mTiles.end()) + { + auto tileBounds = makeTileBounds(mSettings, tilePosition); + tileBounds.mMin -= osg::Vec2f(border, border); + tileBounds.mMax += osg::Vec2f(border, border); + tile = mTiles.insert(std::make_pair(tilePosition, + CachedRecastMeshManager(mSettings, tileBounds))).first; + } + if (tile->second.addWater(cellPosition, cellSize, transform)) + { + lock.unlock(); + tilesPositions.push_back(tilePosition); + result = true; + } + }); + } + + if (result) + ++mRevision; + + return result; + } + + boost::optional TileCachedRecastMeshManager::removeWater(const osg::Vec2i& cellPosition) + { + const auto object = mWaterTilesPositions.find(cellPosition); + if (object == mWaterTilesPositions.end()) + return boost::none; + boost::optional result; + for (const auto& tilePosition : object->second) + { + std::unique_lock lock(mTilesMutex); + const auto tile = mTiles.find(tilePosition); + if (tile == mTiles.end()) + continue; + const auto tileResult = tile->second.removeWater(cellPosition); + if (tile->second.isEmpty()) + mTiles.erase(tile); + lock.unlock(); + if (tileResult && !result) + result = tileResult; + } + if (result) + ++mRevision; + return result; + } + std::shared_ptr TileCachedRecastMeshManager::getMesh(const TilePosition& tilePosition) { const std::lock_guard lock(mTilesMutex); diff --git a/components/detournavigator/tilecachedrecastmeshmanager.hpp b/components/detournavigator/tilecachedrecastmeshmanager.hpp index 32b06eadd6..f67d2c3922 100644 --- a/components/detournavigator/tilecachedrecastmeshmanager.hpp +++ b/components/detournavigator/tilecachedrecastmeshmanager.hpp @@ -21,6 +21,10 @@ namespace DetourNavigator boost::optional removeObject(std::size_t id); + bool addWater(const osg::Vec2i& cellPosition, const int cellSize, const btTransform& transform); + + boost::optional removeWater(const osg::Vec2i& cellPosition); + std::shared_ptr getMesh(const TilePosition& tilePosition); bool hasTile(const TilePosition& tilePosition); @@ -40,6 +44,7 @@ namespace DetourNavigator std::mutex mTilesMutex; std::map mTiles; std::unordered_map> mObjectsTilesPositions; + std::map> mWaterTilesPositions; std::size_t mRevision = 0; }; }