diff --git a/apps/openmw_test_suite/detournavigator/navigator.cpp b/apps/openmw_test_suite/detournavigator/navigator.cpp index 367572d449..f6b09c1d55 100644 --- a/apps/openmw_test_suite/detournavigator/navigator.cpp +++ b/apps/openmw_test_suite/detournavigator/navigator.cpp @@ -1109,16 +1109,16 @@ namespace Status::PartialPath); EXPECT_THAT(mPath, ElementsAre( - Vec3fEq(56.66666412353515625, 460, -2.5371043682098388671875), - Vec3fEq(76.4206390380859375, 439.688446044921875, -2.913421630859375), - Vec3fEq(96.17461395263671875, 419.37689208984375, -4.508244037628173828125), - Vec3fEq(115.9285888671875, 399.06536865234375, -6.103069305419921875), - Vec3fEq(135.68255615234375, 378.753814697265625, -7.69789028167724609375), - Vec3fEq(155.4365386962890625, 358.44232177734375, -20.9574832916259765625), - Vec3fEq(175.190521240234375, 338.130767822265625, -35.907501220703125), - Vec3fEq(194.944488525390625, 317.8192138671875, -50.8574981689453125), - Vec3fEq(214.6984710693359375, 297.507720947265625, -65.8075103759765625), - Vec3fEq(222.0000457763671875, 290.00006103515625, -71.3334197998046875) + Vec3fEq(56.66664886474609375, 460, -2.5371043682098388671875), + Vec3fEq(76.42063140869140625, 439.6884765625, -2.9134314060211181640625), + Vec3fEq(96.17461395263671875, 419.376953125, -4.50826549530029296875), + Vec3fEq(115.9285888671875, 399.0654296875, -6.1030979156494140625), + Vec3fEq(135.6825714111328125, 378.753936767578125, -7.697928905487060546875), + Vec3fEq(155.436553955078125, 358.442413330078125, -20.9574985504150390625), + Vec3fEq(175.190521240234375, 338.130889892578125, -35.907512664794921875), + Vec3fEq(194.9445037841796875, 317.8193359375, -50.85752105712890625), + Vec3fEq(214.698486328125, 297.5078125, -65.807525634765625), + Vec3fEq(222.0001068115234375, 290.000091552734375, -71.333465576171875) )) << mPath; } diff --git a/components/detournavigator/makenavmesh.cpp b/components/detournavigator/makenavmesh.cpp index 890e2c994b..cac4be0fdd 100644 --- a/components/detournavigator/makenavmesh.cpp +++ b/components/detournavigator/makenavmesh.cpp @@ -121,7 +121,7 @@ namespace return result; } - rcConfig makeConfig(const osg::Vec3f& agentHalfExtents, const osg::Vec3f& boundsMin, const osg::Vec3f& boundsMax, + rcConfig makeConfig(const osg::Vec3f& agentHalfExtents, const TilePosition& tile, float minZ, float maxZ, const Settings& settings) { rcConfig config; @@ -140,15 +140,18 @@ namespace config.detailSampleDist = settings.mDetailSampleDist < 0.9f ? 0 : config.cs * settings.mDetailSampleDist; config.detailSampleMaxError = config.ch * settings.mDetailSampleMaxError; config.borderSize = settings.mBorderSize; - config.width = settings.mTileSize + config.borderSize * 2; - config.height = settings.mTileSize + config.borderSize * 2; - rcVcopy(config.bmin, boundsMin.ptr()); - rcVcopy(config.bmax, boundsMax.ptr()); - config.bmin[0] -= getBorderSize(settings); - config.bmin[2] -= getBorderSize(settings); - config.bmax[0] += getBorderSize(settings); - config.bmax[2] += getBorderSize(settings); config.tileSize = settings.mTileSize; + const int size = config.tileSize + config.borderSize * 2; + config.width = size; + config.height = size; + const float halfBoundsSize = size * config.cs * 0.5f; + const osg::Vec2f shift = osg::Vec2f(tile.x() + 0.5f, tile.y() + 0.5f) * getTileSize(settings); + config.bmin[0] = shift.x() - halfBoundsSize; + config.bmin[1] = minZ; + config.bmin[2] = shift.y() - halfBoundsSize; + config.bmax[0] = shift.x() + halfBoundsSize; + config.bmax[1] = maxZ; + config.bmax[2] = shift.y() + halfBoundsSize; return config; } @@ -388,20 +391,56 @@ namespace ++power; return power; } + + std::pair getBoundsByZ(const RecastMesh& recastMesh, const osg::Vec3f& agentHalfExtents, const Settings& settings) + { + float minZ = 0; + float maxZ = 0; + + const std::vector& vertices = recastMesh.getMesh().getVertices(); + for (std::size_t i = 0, n = vertices.size(); i < n; i += 3) + { + minZ = std::min(minZ, vertices[i + 2]); + maxZ = std::max(maxZ, vertices[i + 2]); + } + + for (const Cell& water : recastMesh.getWater()) + { + const float swimLevel = getSwimLevel(settings, water.mShift.z(), agentHalfExtents.z()); + minZ = std::min(minZ, swimLevel); + maxZ = std::max(maxZ, swimLevel); + } + + for (const Heightfield& heightfield : recastMesh.getHeightfields()) + { + if (heightfield.mHeights.empty()) + continue; + const auto [minHeight, maxHeight] = std::minmax_element(heightfield.mHeights.begin(), heightfield.mHeights.end()); + minZ = std::min(minZ, *minHeight); + maxZ = std::max(maxZ, *maxHeight); + } + + for (const FlatHeightfield& heightfield : recastMesh.getFlatHeightfields()) + { + minZ = std::min(minZ, heightfield.mHeight); + maxZ = std::max(maxZ, heightfield.mHeight); + } + + return {minZ, maxZ}; + } } } // namespace DetourNavigator namespace DetourNavigator { std::unique_ptr prepareNavMeshTileData(const RecastMesh& recastMesh, - const TilePosition& tile, const Bounds& bounds, const osg::Vec3f& agentHalfExtents, const Settings& settings) + const TilePosition& tilePosition, const osg::Vec3f& agentHalfExtents, const Settings& settings) { - const TileBounds tileBounds = makeTileBounds(settings, tile); - const osg::Vec3f boundsMin(tileBounds.mMin.x(), bounds.mMin.y() - 1, tileBounds.mMin.y()); - const osg::Vec3f boundsMax(tileBounds.mMax.x(), bounds.mMax.y() + 1, tileBounds.mMax.y()); + const auto [minZ, maxZ] = getBoundsByZ(recastMesh, agentHalfExtents, settings); rcContext context; - const auto config = makeConfig(agentHalfExtents, boundsMin, boundsMax, settings); + const auto config = makeConfig(agentHalfExtents, tilePosition, toNavMeshCoordinates(settings, minZ), + toNavMeshCoordinates(settings, maxZ), settings); rcHeightfield solid; createHeightfield(context, solid, config.width, config.height, config.bmin, config.bmax, config.cs, config.ch); @@ -529,18 +568,8 @@ namespace DetourNavigator return navMeshCacheItem->lock()->removeTile(changedTile); } - auto recastMeshBounds = recastMesh->getBounds(); - recastMeshBounds.mMin = toNavMeshCoordinates(settings, recastMeshBounds.mMin); - recastMeshBounds.mMax = toNavMeshCoordinates(settings, recastMeshBounds.mMax); - - for (const auto& water : recastMesh->getWater()) - { - const float height = toNavMeshCoordinates(settings, getSwimLevel(settings, water.mShift.z(), agentHalfExtents.z())); - recastMeshBounds.mMin.y() = std::min(recastMeshBounds.mMin.y(), height); - recastMeshBounds.mMax.y() = std::max(recastMeshBounds.mMax.y(), height); - } - - if (isEmpty(recastMeshBounds)) + if (recastMesh->getMesh().getIndices().empty() && recastMesh->getWater().empty() + && recastMesh->getHeightfields().empty() && recastMesh->getFlatHeightfields().empty()) { Log(Debug::Debug) << "Ignore add tile: recastMesh is empty"; return navMeshCacheItem->lock()->removeTile(changedTile); @@ -559,8 +588,7 @@ namespace DetourNavigator if (!cachedNavMeshData) { - auto prepared = prepareNavMeshTileData(*recastMesh, changedTile, recastMeshBounds, - agentHalfExtents, settings); + auto prepared = prepareNavMeshTileData(*recastMesh, changedTile, agentHalfExtents, settings); if (prepared == nullptr) { diff --git a/components/detournavigator/recastmesh.cpp b/components/detournavigator/recastmesh.cpp index e2dea0ad6e..258a700b0e 100644 --- a/components/detournavigator/recastmesh.cpp +++ b/components/detournavigator/recastmesh.cpp @@ -27,31 +27,9 @@ namespace DetourNavigator , mHeightfields(std::move(heightfields)) , mFlatHeightfields(std::move(flatHeightfields)) { - if (mMesh.getVerticesCount() > 0) - rcCalcBounds(mMesh.getVertices().data(), static_cast(mMesh.getVerticesCount()), - mBounds.mMin.ptr(), mBounds.mMax.ptr()); mWater.shrink_to_fit(); mHeightfields.shrink_to_fit(); for (Heightfield& v : mHeightfields) v.mHeights.shrink_to_fit(); - for (const Heightfield& v : mHeightfields) - { - const auto [min, max] = std::minmax_element(v.mHeights.begin(), v.mHeights.end()); - mBounds.mMin.x() = std::min(mBounds.mMin.x(), v.mBounds.mMin.x()); - mBounds.mMin.y() = std::min(mBounds.mMin.y(), v.mBounds.mMin.y()); - mBounds.mMin.z() = std::min(mBounds.mMin.z(), *min); - mBounds.mMax.x() = std::max(mBounds.mMax.x(), v.mBounds.mMax.x()); - mBounds.mMax.y() = std::max(mBounds.mMax.y(), v.mBounds.mMax.y()); - mBounds.mMax.z() = std::max(mBounds.mMax.z(), *max); - } - for (const FlatHeightfield& v : mFlatHeightfields) - { - mBounds.mMin.x() = std::min(mBounds.mMin.x(), v.mBounds.mMin.x()); - mBounds.mMin.y() = std::min(mBounds.mMin.y(), v.mBounds.mMin.y()); - mBounds.mMin.z() = std::min(mBounds.mMin.z(), v.mHeight); - mBounds.mMax.x() = std::max(mBounds.mMax.x(), v.mBounds.mMax.x()); - mBounds.mMax.y() = std::max(mBounds.mMax.y(), v.mBounds.mMax.y()); - mBounds.mMax.z() = std::max(mBounds.mMax.z(), v.mHeight); - } } } diff --git a/components/detournavigator/recastmesh.hpp b/components/detournavigator/recastmesh.hpp index e2e7b9e188..928b282d19 100644 --- a/components/detournavigator/recastmesh.hpp +++ b/components/detournavigator/recastmesh.hpp @@ -118,11 +118,6 @@ namespace DetourNavigator return mFlatHeightfields; } - const Bounds& getBounds() const - { - return mBounds; - } - private: std::size_t mGeneration; std::size_t mRevision; @@ -130,7 +125,6 @@ namespace DetourNavigator std::vector mWater; std::vector mHeightfields; std::vector mFlatHeightfields; - Bounds mBounds; friend inline std::size_t getSize(const RecastMesh& value) noexcept {