diff --git a/apps/openmw_test_suite/detournavigator/recastmeshbuilder.cpp b/apps/openmw_test_suite/detournavigator/recastmeshbuilder.cpp index 38b1ab3614..60b7549151 100644 --- a/apps/openmw_test_suite/detournavigator/recastmeshbuilder.cpp +++ b/apps/openmw_test_suite/detournavigator/recastmeshbuilder.cpp @@ -361,9 +361,9 @@ namespace ); const auto recastMesh = builder.create(); EXPECT_EQ(recastMesh->getVertices(), std::vector({ - 0.707107067108154296875, 0, -3.535533905029296875, - -0.70710659027099609375, 0, -3.535533905029296875, - 2.384185791015625e-07, 0, -4.24264049530029296875, + 1.41421353816986083984375, 0, 1.1920928955078125e-07, + -1.41421353816986083984375, 0, -1.1920928955078125e-07, + 1.1920928955078125e-07, 0, -1.41421353816986083984375, })); EXPECT_EQ(recastMesh->getIndices(), std::vector({0, 1, 2})); EXPECT_EQ(recastMesh->getAreaTypes(), std::vector({AreaType_ground})); diff --git a/components/bullethelpers/transformboundingbox.hpp b/components/bullethelpers/transformboundingbox.hpp new file mode 100644 index 0000000000..05632eec72 --- /dev/null +++ b/components/bullethelpers/transformboundingbox.hpp @@ -0,0 +1,38 @@ +#ifndef OPENMW_COMPONENTS_BULLETHELPERS_TRANSFORMBOUNDINGBOX_H +#define OPENMW_COMPONENTS_BULLETHELPERS_TRANSFORMBOUNDINGBOX_H + +#include +#include + +#include + +namespace BulletHelpers +{ + inline btVector3 min(const btVector3& a, const btVector3& b) + { + return btVector3(std::min(a.x(), b.x()), std::min(a.y(), b.y()), std::min(a.z(), b.z())); + } + + inline btVector3 max(const btVector3& a, const btVector3& b) + { + return btVector3(std::max(a.x(), b.x()), std::max(a.y(), b.y()), std::max(a.z(), b.z())); + } + + // http://dev.theomader.com/transform-bounding-boxes/ + inline void transformBoundingBox(const btTransform& transform, btVector3& aabbMin, btVector3& aabbMax) + { + const btVector3 xa(transform.getBasis().getColumn(0) * aabbMin.x()); + const btVector3 xb(transform.getBasis().getColumn(0) * aabbMax.x()); + + const btVector3 ya(transform.getBasis().getColumn(1) * aabbMin.y()); + const btVector3 yb(transform.getBasis().getColumn(1) * aabbMax.y()); + + const btVector3 za(transform.getBasis().getColumn(2) * aabbMin.z()); + const btVector3 zb(transform.getBasis().getColumn(2) * aabbMax.z()); + + aabbMin = min(xa, xb) + min(ya, yb) + min(za, zb) + transform.getOrigin(); + aabbMax = max(xa, xb) + max(ya, yb) + max(za, zb) + transform.getOrigin(); + } +} + +#endif diff --git a/components/detournavigator/asyncnavmeshupdater.cpp b/components/detournavigator/asyncnavmeshupdater.cpp index b42a6b6504..e085aba160 100644 --- a/components/detournavigator/asyncnavmeshupdater.cpp +++ b/components/detournavigator/asyncnavmeshupdater.cpp @@ -135,7 +135,7 @@ namespace DetourNavigator } catch (const std::exception& e) { - Log(Debug::Error) << "AsyncNavMeshUpdater::process exception: ", e.what(); + Log(Debug::Error) << "AsyncNavMeshUpdater::process exception: " << e.what(); } } Log(Debug::Debug) << "Stop navigator jobs processing"; diff --git a/components/detournavigator/recastmeshbuilder.cpp b/components/detournavigator/recastmeshbuilder.cpp index 71c4f04051..5d8a07055f 100644 --- a/components/detournavigator/recastmeshbuilder.cpp +++ b/components/detournavigator/recastmeshbuilder.cpp @@ -5,6 +5,7 @@ #include "settingsutils.hpp" #include "exceptions.hpp" +#include #include #include @@ -13,6 +14,7 @@ #include #include #include +#include #include @@ -57,7 +59,7 @@ namespace DetourNavigator return addObject(shape, transform, makeProcessTriangleCallback([&] (btVector3* triangle, int, int) { for (std::size_t i = 3; i > 0; --i) - addTriangleVertex(transform(triangle[i - 1])); + addTriangleVertex(triangle[i - 1]); mAreaTypes.push_back(areaType); })); } @@ -68,7 +70,7 @@ namespace DetourNavigator return addObject(shape, transform, makeProcessTriangleCallback([&] (btVector3* triangle, int, int) { for (std::size_t i = 0; i < 3; ++i) - addTriangleVertex(transform(triangle[i])); + addTriangleVertex(triangle[i]); mAreaTypes.push_back(areaType); })); } @@ -131,8 +133,34 @@ namespace DetourNavigator shape.getAabb(btTransform::getIdentity(), aabbMin, aabbMax); - aabbMin = transform(aabbMin); - aabbMax = transform(aabbMax); + const btVector3 boundsMin(mBounds.mMin.x(), mBounds.mMin.y(), + -std::numeric_limits::max() * std::numeric_limits::epsilon()); + const btVector3 boundsMax(mBounds.mMax.x(), mBounds.mMax.y(), + std::numeric_limits::max() * std::numeric_limits::epsilon()); + + auto wrapper = makeProcessTriangleCallback([&] (btVector3* triangle, int partId, int triangleIndex) + { + std::array transformed; + for (std::size_t i = 0; i < 3; ++i) + transformed[i] = transform(triangle[i]); + if (TestTriangleAgainstAabb2(transformed.data(), boundsMin, boundsMax)) + callback.processTriangle(transformed.data(), partId, triangleIndex); + }); + + shape.processAllTriangles(&wrapper, aabbMin, aabbMax); + } + + void RecastMeshBuilder::addObject(const btHeightfieldTerrainShape& shape, const btTransform& transform, + btTriangleCallback&& callback) + { + using BulletHelpers::transformBoundingBox; + + btVector3 aabbMin; + btVector3 aabbMax; + + shape.getAabb(btTransform::getIdentity(), aabbMin, aabbMax); + + transformBoundingBox(transform, aabbMin, aabbMax); aabbMin.setX(std::max(mBounds.mMin.x(), aabbMin.x())); aabbMin.setX(std::min(mBounds.mMax.x(), aabbMin.x())); @@ -144,20 +172,17 @@ namespace DetourNavigator aabbMax.setY(std::max(mBounds.mMin.y(), aabbMax.y())); aabbMax.setY(std::min(mBounds.mMax.y(), aabbMax.y())); - const auto inversedTransform = transform.inverse(); + transformBoundingBox(transform.inverse(), aabbMin, aabbMax); - aabbMin = inversedTransform(aabbMin); - aabbMax = inversedTransform(aabbMax); + auto wrapper = makeProcessTriangleCallback([&] (btVector3* triangle, int partId, int triangleIndex) + { + std::array transformed; + for (std::size_t i = 0; i < 3; ++i) + transformed[i] = transform(triangle[i]); + callback.processTriangle(transformed.data(), partId, triangleIndex); + }); - aabbMin.setX(std::min(aabbMin.x(), aabbMax.x())); - aabbMin.setY(std::min(aabbMin.y(), aabbMax.y())); - aabbMin.setZ(std::min(aabbMin.z(), aabbMax.z())); - - aabbMax.setX(std::max(aabbMin.x(), aabbMax.x())); - aabbMax.setY(std::max(aabbMin.y(), aabbMax.y())); - aabbMax.setZ(std::max(aabbMin.z(), aabbMax.z())); - - shape.processAllTriangles(&callback, aabbMin, aabbMax); + shape.processAllTriangles(&wrapper, aabbMin, aabbMax); } void RecastMeshBuilder::addTriangleVertex(const btVector3& worldPosition) diff --git a/components/detournavigator/recastmeshbuilder.hpp b/components/detournavigator/recastmeshbuilder.hpp index 2f9d0373db..070b9c67d1 100644 --- a/components/detournavigator/recastmeshbuilder.hpp +++ b/components/detournavigator/recastmeshbuilder.hpp @@ -48,6 +48,8 @@ namespace DetourNavigator void addObject(const btConcaveShape& shape, const btTransform& transform, btTriangleCallback&& callback); + void addObject(const btHeightfieldTerrainShape& shape, const btTransform& transform, btTriangleCallback&& callback); + void addTriangleVertex(const btVector3& worldPosition); void addVertex(const btVector3& worldPosition); diff --git a/docs/source/reference/modding/settings/navigator.rst b/docs/source/reference/modding/settings/navigator.rst index db2ad66cfa..083048332c 100644 --- a/docs/source/reference/modding/settings/navigator.rst +++ b/docs/source/reference/modding/settings/navigator.rst @@ -176,12 +176,12 @@ recast scale factor :Type: floating point :Range: > 0.0 -:Default: 0.023529411764705882 +:Default: 0.029411764705882353 Scale of nav mesh coordinates to world coordinates. Recastnavigation builds voxels for world geometry. Basically voxel size is 1 / "cell size". To reduce amount of voxels we apply scale factor, to make voxel size "recast scale factor" / "cell size". Default value calculates by this equation: -sStepSizeUp * "recast scale factor" / "cell size" = 4 (max climb height should be equal to 4 voxels). +sStepSizeUp * "recast scale factor" / "cell size" = 5 (max climb height should be equal to 4 voxels). Changing this value will change generated nav mesh. Some locations may become unavailable for NPC and creatures. Pay attention to slopes and roofs when change it. Increasing this value will reduce nav mesh update latency. diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 43efbc01a7..b85a86afe4 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -642,8 +642,8 @@ enable = true # Scale of NavMesh coordinates to world coordinates (value > 0.0). Recastnavigation builds voxels for world geometry. # Basically voxel size is 1 / "cell size". To reduce amount of voxels we apply scale factor, to make voxel size # "recast scale factor" / "cell size". Default value calculates by this equation: -# sStepSizeUp * "recast scale factor" / "cell size" = 4 (max climb height should be equal to 4 voxels) -recast scale factor = 0.023529411764705882 +# sStepSizeUp * "recast scale factor" / "cell size" = 5 (max climb height should be equal to 4 voxels) +recast scale factor = 0.029411764705882353 # The z-axis cell size to use for fields. (value > 0.0) # Defines voxel/grid/cell size. So their values have significant @@ -671,7 +671,7 @@ detail sample max error = 1.0 max simplification error = 1.3 # The width and height of each tile. (value > 0) -tile size = 64 +tile size = 128 # The size of the non-navigable border around the heightfield. (value >= 0) border size = 16