1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-03-30 09:36:43 +00:00

Split RecastMesh into tiles

This commit is contained in:
elsid 2018-04-16 01:07:18 +03:00
parent dbb1d99bff
commit d2fd9abd51
No known key found for this signature in database
GPG key ID: B845CB9FEE18AB40
19 changed files with 328 additions and 88 deletions

View file

@ -21,19 +21,23 @@ namespace
struct DetourNavigatorRecastMeshBuilderTest : Test struct DetourNavigatorRecastMeshBuilderTest : Test
{ {
Settings mSettings; Settings mSettings;
RecastMeshBuilder mBuilder; TileBounds mBounds;
DetourNavigatorRecastMeshBuilderTest() DetourNavigatorRecastMeshBuilderTest()
: mBuilder(mSettings)
{ {
mSettings.mRecastScaleFactor = 1.0f; mSettings.mRecastScaleFactor = 1.0f;
mSettings.mTrianglesPerChunk = 256; mSettings.mTrianglesPerChunk = 256;
mBounds.mMin = osg::Vec2f(-std::numeric_limits<float>::max() * std::numeric_limits<float>::epsilon(),
-std::numeric_limits<float>::max() * std::numeric_limits<float>::epsilon());
mBounds.mMax = osg::Vec2f(std::numeric_limits<float>::max() * std::numeric_limits<float>::epsilon(),
std::numeric_limits<float>::max() * std::numeric_limits<float>::epsilon());
} }
}; };
TEST_F(DetourNavigatorRecastMeshBuilderTest, create_for_empty_should_return_empty) TEST_F(DetourNavigatorRecastMeshBuilderTest, create_for_empty_should_return_empty)
{ {
const auto recastMesh = mBuilder.create(); RecastMeshBuilder builder(mSettings, mBounds);
const auto recastMesh = builder.create();
EXPECT_EQ(recastMesh->getVertices(), std::vector<float>()); EXPECT_EQ(recastMesh->getVertices(), std::vector<float>());
EXPECT_EQ(recastMesh->getIndices(), std::vector<int>()); EXPECT_EQ(recastMesh->getIndices(), std::vector<int>());
} }
@ -43,8 +47,9 @@ namespace
btTriangleMesh mesh; btTriangleMesh mesh;
mesh.addTriangle(btVector3(-1, -1, 0), btVector3(-1, 1, 0), btVector3(1, -1, 0)); mesh.addTriangle(btVector3(-1, -1, 0), btVector3(-1, 1, 0), btVector3(1, -1, 0));
btBvhTriangleMeshShape shape(&mesh, true); btBvhTriangleMeshShape shape(&mesh, true);
mBuilder.addObject(static_cast<const btCollisionShape&>(shape), btTransform::getIdentity()); RecastMeshBuilder builder(mSettings, mBounds);
const auto recastMesh = mBuilder.create(); builder.addObject(static_cast<const btCollisionShape&>(shape), btTransform::getIdentity());
const auto recastMesh = builder.create();
EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({ EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({
1, 0, -1, 1, 0, -1,
-1, 0, 1, -1, 0, 1,
@ -58,9 +63,10 @@ namespace
btTriangleMesh mesh; btTriangleMesh mesh;
mesh.addTriangle(btVector3(-1, -1, 0), btVector3(-1, 1, 0), btVector3(1, -1, 0)); mesh.addTriangle(btVector3(-1, -1, 0), btVector3(-1, 1, 0), btVector3(1, -1, 0));
btBvhTriangleMeshShape shape(&mesh, true); btBvhTriangleMeshShape shape(&mesh, true);
mBuilder.addObject(static_cast<const btCollisionShape&>(shape), RecastMeshBuilder builder(mSettings, mBounds);
builder.addObject(static_cast<const btCollisionShape&>(shape),
btTransform(btMatrix3x3::getIdentity().scaled(btVector3(1, 2, 3)), btVector3(1, 2, 3))); btTransform(btMatrix3x3::getIdentity().scaled(btVector3(1, 2, 3)), btVector3(1, 2, 3)));
const auto recastMesh = mBuilder.create(); const auto recastMesh = builder.create();
EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({ EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({
2, 3, 0, 2, 3, 0,
0, 3, 4, 0, 3, 4,
@ -73,8 +79,9 @@ namespace
{ {
const std::array<btScalar, 4> heightfieldData {{0, 0, 0, 0}}; const std::array<btScalar, 4> heightfieldData {{0, 0, 0, 0}};
btHeightfieldTerrainShape shape(2, 2, heightfieldData.data(), 1, 0, 0, 2, PHY_FLOAT, false); btHeightfieldTerrainShape shape(2, 2, heightfieldData.data(), 1, 0, 0, 2, PHY_FLOAT, false);
mBuilder.addObject(static_cast<const btCollisionShape&>(shape), btTransform::getIdentity()); RecastMeshBuilder builder(mSettings, mBounds);
const auto recastMesh = mBuilder.create(); builder.addObject(static_cast<const btCollisionShape&>(shape), btTransform::getIdentity());
const auto recastMesh = builder.create();
EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({ EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({
-0.5, 0, -0.5, -0.5, 0, -0.5,
-0.5, 0, 0.5, -0.5, 0, 0.5,
@ -89,8 +96,9 @@ namespace
TEST_F(DetourNavigatorRecastMeshBuilderTest, add_box_shape_should_produce_12_triangles) TEST_F(DetourNavigatorRecastMeshBuilderTest, add_box_shape_should_produce_12_triangles)
{ {
btBoxShape shape(btVector3(1, 1, 2)); btBoxShape shape(btVector3(1, 1, 2));
mBuilder.addObject(static_cast<const btCollisionShape&>(shape), btTransform::getIdentity()); RecastMeshBuilder builder(mSettings, mBounds);
const auto recastMesh = mBuilder.create(); builder.addObject(static_cast<const btCollisionShape&>(shape), btTransform::getIdentity());
const auto recastMesh = builder.create();
EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({ EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({
1, 2, 1, 1, 2, 1,
-1, 2, 1, -1, 2, 1,
@ -130,8 +138,9 @@ namespace
shape.addChildShape(btTransform::getIdentity(), &triangle1); shape.addChildShape(btTransform::getIdentity(), &triangle1);
shape.addChildShape(btTransform::getIdentity(), &box); shape.addChildShape(btTransform::getIdentity(), &box);
shape.addChildShape(btTransform::getIdentity(), &triangle2); shape.addChildShape(btTransform::getIdentity(), &triangle2);
mBuilder.addObject(static_cast<const btCollisionShape&>(shape), btTransform::getIdentity()); RecastMeshBuilder builder(mSettings, mBounds);
const auto recastMesh = mBuilder.create(); builder.addObject(static_cast<const btCollisionShape&>(shape), btTransform::getIdentity());
const auto recastMesh = builder.create();
EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({ EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({
1, 0, -1, 1, 0, -1,
-1, 0, 1, -1, 0, 1,
@ -173,9 +182,10 @@ namespace
btBvhTriangleMeshShape triangle(&mesh, true); btBvhTriangleMeshShape triangle(&mesh, true);
btCompoundShape shape; btCompoundShape shape;
shape.addChildShape(btTransform::getIdentity(), &triangle); shape.addChildShape(btTransform::getIdentity(), &triangle);
mBuilder.addObject(static_cast<const btCollisionShape&>(shape), RecastMeshBuilder builder(mSettings, mBounds);
btTransform(btMatrix3x3::getIdentity().scaled(btVector3(1, 2, 3)), btVector3(1, 2, 3))); builder.addObject(static_cast<const btCollisionShape&>(shape),
const auto recastMesh = mBuilder.create(); btTransform(btMatrix3x3::getIdentity().scaled(btVector3(1, 2, 3)), btVector3(1, 2, 3)));
const auto recastMesh = builder.create();
EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({ EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({
2, 3, 0, 2, 3, 0,
0, 3, 4, 0, 3, 4,
@ -192,9 +202,10 @@ namespace
btCompoundShape shape; btCompoundShape shape;
shape.addChildShape(btTransform(btMatrix3x3::getIdentity().scaled(btVector3(1, 2, 3)), btVector3(1, 2, 3)), shape.addChildShape(btTransform(btMatrix3x3::getIdentity().scaled(btVector3(1, 2, 3)), btVector3(1, 2, 3)),
&triangle); &triangle);
mBuilder.addObject(static_cast<const btCollisionShape&>(shape), RecastMeshBuilder builder(mSettings, mBounds);
btTransform(btMatrix3x3::getIdentity().scaled(btVector3(1, 2, 3)), btVector3(1, 2, 3))); builder.addObject(static_cast<const btCollisionShape&>(shape),
const auto recastMesh = mBuilder.create(); btTransform(btMatrix3x3::getIdentity().scaled(btVector3(1, 2, 3)), btVector3(1, 2, 3)));
const auto recastMesh = builder.create();
EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({ EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({
3, 12, 2, 3, 12, 2,
1, 12, 10, 1, 12, 10,
@ -202,4 +213,104 @@ namespace
})); }));
EXPECT_EQ(recastMesh->getIndices(), std::vector<int>({0, 1, 2})); EXPECT_EQ(recastMesh->getIndices(), std::vector<int>({0, 1, 2}));
} }
TEST_F(DetourNavigatorRecastMeshBuilderTest, without_bounds_add_transformed_compound_shape_with_transformed_bhv_triangle_shape_should_not_filter_by_bounds)
{
btTriangleMesh mesh;
mesh.addTriangle(btVector3(-1, -1, 0), btVector3(-1, 1, 0), btVector3(1, -1, 0));
mesh.addTriangle(btVector3(-3, -3, 0), btVector3(-3, -2, 0), btVector3(-2, -3, 0));
btBvhTriangleMeshShape shape(&mesh, true);
RecastMeshBuilder builder(mSettings, mBounds);
builder.addObject(static_cast<const btCollisionShape&>(shape), btTransform::getIdentity());
const auto recastMesh = builder.create();
EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({
1, 0, -1,
-1, 0, 1,
-1, 0, -1,
-2, 0, -3,
-3, 0, -2,
-3, 0, -3,
}));
EXPECT_EQ(recastMesh->getIndices(), std::vector<int>({0, 1, 2, 3, 4, 5}));
}
TEST_F(DetourNavigatorRecastMeshBuilderTest, with_bounds_add_transformed_compound_shape_with_transformed_bhv_triangle_shape_should_filter_by_bounds)
{
mSettings.mRecastScaleFactor = 0.1f;
mBounds.mMin = osg::Vec2f(-3, -3) * mSettings.mRecastScaleFactor;
mBounds.mMax = osg::Vec2f(-2, -2) * mSettings.mRecastScaleFactor;
btTriangleMesh mesh;
mesh.addTriangle(btVector3(-1, -1, 0), btVector3(-1, 1, 0), btVector3(1, -1, 0));
mesh.addTriangle(btVector3(-3, -3, 0), btVector3(-3, -2, 0), btVector3(-2, -3, 0));
btBvhTriangleMeshShape shape(&mesh, true);
RecastMeshBuilder builder(mSettings, mBounds);
builder.addObject(static_cast<const btCollisionShape&>(shape), btTransform::getIdentity());
const auto recastMesh = builder.create();
EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({
-0.2f, 0, -0.3f,
-0.3f, 0, -0.2f,
-0.3f, 0, -0.3f,
}));
EXPECT_EQ(recastMesh->getIndices(), std::vector<int>({0, 1, 2}));
}
TEST_F(DetourNavigatorRecastMeshBuilderTest, with_bounds_add_rotated_by_x_bhv_triangle_shape_should_filter_by_bounds)
{
mBounds.mMin = osg::Vec2f(-5, -5);
mBounds.mMax = osg::Vec2f(5, -3);
btTriangleMesh mesh;
mesh.addTriangle(btVector3(0, -1, -1), btVector3(0, -1, -1), btVector3(0, 1, -1));
mesh.addTriangle(btVector3(0, -3, -3), btVector3(0, -3, -2), btVector3(0, -2, -3));
btBvhTriangleMeshShape shape(&mesh, true);
RecastMeshBuilder builder(mSettings, mBounds);
builder.addObject(static_cast<const btCollisionShape&>(shape),
btTransform(btQuaternion(btVector3(1, 0, 0), static_cast<btScalar>(-osg::PI_4))));
const auto recastMesh = builder.create();
EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({
0, -0.70710659027099609375, -3.535533905029296875,
0, 0.707107067108154296875, -3.535533905029296875,
0, 2.384185791015625e-07, -4.24264049530029296875,
}));
EXPECT_EQ(recastMesh->getIndices(), std::vector<int>({0, 1, 2}));
}
TEST_F(DetourNavigatorRecastMeshBuilderTest, with_bounds_add_rotated_by_y_bhv_triangle_shape_should_filter_by_bounds)
{
mBounds.mMin = osg::Vec2f(-5, -5);
mBounds.mMax = osg::Vec2f(-3, 5);
btTriangleMesh mesh;
mesh.addTriangle(btVector3(-1, 0, -1), btVector3(-1, 0, 1), btVector3(1, 0, -1));
mesh.addTriangle(btVector3(-3, 0, -3), btVector3(-3, 0, -2), btVector3(-2, 0, -3));
btBvhTriangleMeshShape shape(&mesh, true);
RecastMeshBuilder builder(mSettings, mBounds);
builder.addObject(static_cast<const btCollisionShape&>(shape),
btTransform(btQuaternion(btVector3(0, 1, 0), static_cast<btScalar>(osg::PI_4))));
const auto recastMesh = builder.create();
EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({
-3.535533905029296875, -0.70710659027099609375, 0,
-3.535533905029296875, 0.707107067108154296875, 0,
-4.24264049530029296875, 2.384185791015625e-07, 0,
}));
EXPECT_EQ(recastMesh->getIndices(), std::vector<int>({0, 1, 2}));
}
TEST_F(DetourNavigatorRecastMeshBuilderTest, with_bounds_add_rotated_by_z_bhv_triangle_shape_should_filter_by_bounds)
{
mBounds.mMin = osg::Vec2f(-5, -5);
mBounds.mMax = osg::Vec2f(-1, -1);
btTriangleMesh mesh;
mesh.addTriangle(btVector3(-1, -1, 0), btVector3(-1, 1, 0), btVector3(1, -1, 0));
mesh.addTriangle(btVector3(-3, -3, 0), btVector3(-3, -2, 0), btVector3(-2, -3, 0));
btBvhTriangleMeshShape shape(&mesh, true);
RecastMeshBuilder builder(mSettings, mBounds);
builder.addObject(static_cast<const btCollisionShape&>(shape),
btTransform(btQuaternion(btVector3(0, 0, 1), static_cast<btScalar>(osg::PI_4))));
const auto recastMesh = builder.create();
EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({
0.707107067108154296875, 0, -3.535533905029296875,
-0.70710659027099609375, 0, -3.535533905029296875,
2.384185791015625e-07, 0, -4.24264049530029296875,
}));
EXPECT_EQ(recastMesh->getIndices(), std::vector<int>({0, 1, 2}));
}
} }

View file

@ -168,6 +168,7 @@ add_component_dir(detournavigator
asyncnavmeshupdater asyncnavmeshupdater
chunkytrimesh chunkytrimesh
recastmesh recastmesh
tilecachedrecastmeshmanager
) )
set (ESM_UI ${CMAKE_SOURCE_DIR}/files/ui/contentselector.ui set (ESM_UI ${CMAKE_SOURCE_DIR}/files/ui/contentselector.ui

View file

@ -40,8 +40,9 @@ namespace DetourNavigator
return stream << "unknown"; return stream << "unknown";
} }
AsyncNavMeshUpdater::AsyncNavMeshUpdater(const Settings& settings) AsyncNavMeshUpdater::AsyncNavMeshUpdater(const Settings& settings, TileCachedRecastMeshManager& recastMeshManager)
: mSettings(std::cref(settings)) : mSettings(settings)
, mRecastMeshManager(recastMeshManager)
, mShouldStop() , mShouldStop()
, mThread([&] { process(); }) , mThread([&] { process(); })
{ {
@ -57,11 +58,11 @@ namespace DetourNavigator
mThread.join(); mThread.join();
} }
void AsyncNavMeshUpdater::post(const osg::Vec3f& agentHalfExtents, const std::shared_ptr<RecastMesh>& recastMesh, void AsyncNavMeshUpdater::post(const osg::Vec3f& agentHalfExtents,
const std::shared_ptr<NavMeshCacheItem>& navMeshCacheItem, const TilePosition& playerTile, const std::shared_ptr<NavMeshCacheItem>& navMeshCacheItem, const TilePosition& playerTile,
const std::set<TilePosition>& changedTiles) const std::set<TilePosition>& changedTiles)
{ {
setRecastMesh(recastMesh); log("post jobs playerTile=", playerTile);
if (changedTiles.empty()) if (changedTiles.empty())
return; return;
@ -107,9 +108,9 @@ namespace DetourNavigator
setFirstStart(start); setFirstStart(start);
const auto recastMesh = getRecastMesh(); const auto recastMesh = mRecastMeshManager.get().getMesh(job.mChangedTile);
const auto status = updateNavMesh(job.mAgentHalfExtents, *recastMesh, job.mChangedTile, mSettings, const auto status = updateNavMesh(job.mAgentHalfExtents, recastMesh.get(), job.mChangedTile, mSettings,
*job.mNavMeshCacheItem); *job.mNavMeshCacheItem);
const auto finish = std::chrono::steady_clock::now(); const auto finish = std::chrono::steady_clock::now();
@ -162,18 +163,6 @@ namespace DetourNavigator
writeToFile(*job.mNavMeshCacheItem->mValue.lock(), mSettings.get().mNavMeshPathPrefix, navMeshRevision); writeToFile(*job.mNavMeshCacheItem->mValue.lock(), mSettings.get().mNavMeshPathPrefix, navMeshRevision);
} }
std::shared_ptr<RecastMesh> AsyncNavMeshUpdater::getRecastMesh()
{
const std::lock_guard<std::mutex> lock(mRecastMeshMutex);
return mRecastMesh;
}
void AsyncNavMeshUpdater::setRecastMesh(const std::shared_ptr<RecastMesh>& value)
{
const std::lock_guard<std::mutex> lock(mRecastMeshMutex);
mRecastMesh = value;
}
std::chrono::steady_clock::time_point AsyncNavMeshUpdater::getFirstStart() std::chrono::steady_clock::time_point AsyncNavMeshUpdater::getFirstStart()
{ {
const std::lock_guard<std::mutex> lock(mFirstStartMutex); const std::lock_guard<std::mutex> lock(mFirstStartMutex);

View file

@ -2,7 +2,7 @@
#define OPENMW_COMPONENTS_DETOURNAVIGATOR_ASYNCNAVMESHUPDATER_H #define OPENMW_COMPONENTS_DETOURNAVIGATOR_ASYNCNAVMESHUPDATER_H
#include "navmeshcacheitem.hpp" #include "navmeshcacheitem.hpp"
#include "recastmesh.hpp" #include "tilecachedrecastmeshmanager.hpp"
#include "tileposition.hpp" #include "tileposition.hpp"
#include <osg/Vec3f> #include <osg/Vec3f>
@ -25,12 +25,11 @@ namespace DetourNavigator
class AsyncNavMeshUpdater class AsyncNavMeshUpdater
{ {
public: public:
AsyncNavMeshUpdater(const Settings& settings); AsyncNavMeshUpdater(const Settings& settings, TileCachedRecastMeshManager& recastMeshManager);
~AsyncNavMeshUpdater(); ~AsyncNavMeshUpdater();
void post(const osg::Vec3f& agentHalfExtents, const std::shared_ptr<RecastMesh>& recastMesh, void post(const osg::Vec3f& agentHalfExtents, const std::shared_ptr<NavMeshCacheItem>& mNavMeshCacheItem,
const std::shared_ptr<NavMeshCacheItem>& navMeshCacheItem, const TilePosition& playerTile, const TilePosition& playerTile, const std::set<TilePosition>& changedTiles);
const std::set<TilePosition>& changedTiles);
void wait(); void wait();
@ -51,13 +50,12 @@ namespace DetourNavigator
using Jobs = std::priority_queue<Job, std::deque<Job>>; using Jobs = std::priority_queue<Job, std::deque<Job>>;
std::reference_wrapper<const Settings> mSettings; std::reference_wrapper<const Settings> mSettings;
std::reference_wrapper<TileCachedRecastMeshManager> mRecastMeshManager;
std::atomic_bool mShouldStop; std::atomic_bool mShouldStop;
std::mutex mMutex; std::mutex mMutex;
std::condition_variable mHasJob; std::condition_variable mHasJob;
std::condition_variable mDone; std::condition_variable mDone;
Jobs mJobs; Jobs mJobs;
std::mutex mRecastMeshMutex;
std::shared_ptr<RecastMesh> mRecastMesh;
std::mutex mFirstStartMutex; std::mutex mFirstStartMutex;
boost::optional<std::chrono::steady_clock::time_point> mFirstStart; boost::optional<std::chrono::steady_clock::time_point> mFirstStart;
std::thread mThread; std::thread mThread;
@ -72,10 +70,6 @@ namespace DetourNavigator
void writeDebugFiles(const Job& job, const RecastMesh& recastMesh) const; void writeDebugFiles(const Job& job, const RecastMesh& recastMesh) const;
std::shared_ptr<RecastMesh> getRecastMesh();
void setRecastMesh(const std::shared_ptr<RecastMesh>& value);
std::chrono::steady_clock::time_point getFirstStart(); std::chrono::steady_clock::time_point getFirstStart();
void setFirstStart(const std::chrono::steady_clock::time_point& value); void setFirstStart(const std::chrono::steady_clock::time_point& value);

View file

@ -3,9 +3,10 @@
namespace DetourNavigator namespace DetourNavigator
{ {
CachedRecastMeshManager::CachedRecastMeshManager(const Settings& settings) CachedRecastMeshManager::CachedRecastMeshManager(const Settings& settings, const TileBounds& bounds)
: mImpl(settings) : mImpl(settings, bounds)
{} {
}
bool CachedRecastMeshManager::addObject(std::size_t id, const btCollisionShape& shape, const btTransform& transform) bool CachedRecastMeshManager::addObject(std::size_t id, const btCollisionShape& shape, const btTransform& transform)
{ {
@ -29,4 +30,9 @@ namespace DetourNavigator
mCached = mImpl.getMesh(); mCached = mImpl.getMesh();
return mCached; return mCached;
} }
bool CachedRecastMeshManager::isEmpty() const
{
return mImpl.isEmpty();
}
} }

View file

@ -10,7 +10,7 @@ namespace DetourNavigator
class CachedRecastMeshManager class CachedRecastMeshManager
{ {
public: public:
CachedRecastMeshManager(const Settings& settings); CachedRecastMeshManager(const Settings& settings, const TileBounds& bounds);
bool addObject(std::size_t id, const btCollisionShape& shape, const btTransform& transform); bool addObject(std::size_t id, const btCollisionShape& shape, const btTransform& transform);
@ -18,6 +18,8 @@ namespace DetourNavigator
std::shared_ptr<RecastMesh> getMesh(); std::shared_ptr<RecastMesh> getMesh();
bool isEmpty() const;
private: private:
RecastMeshManager mImpl; RecastMeshManager mImpl;
std::shared_ptr<RecastMesh> mCached; std::shared_ptr<RecastMesh> mCached;

View file

@ -17,7 +17,7 @@ namespace DetourNavigator
template <class Callback> template <class Callback>
void getTilesPositions(const osg::Vec3f& aabbMin, const osg::Vec3f& aabbMax, void getTilesPositions(const osg::Vec3f& aabbMin, const osg::Vec3f& aabbMax,
const Settings& settings, Callback&& callback) const Settings& settings, Callback&& callback)
{ {
auto min = toNavMeshCoordinates(settings, aabbMin); auto min = toNavMeshCoordinates(settings, aabbMin);
auto max = toNavMeshCoordinates(settings, aabbMax); auto max = toNavMeshCoordinates(settings, aabbMax);
@ -42,7 +42,7 @@ namespace DetourNavigator
template <class Callback> template <class Callback>
void getTilesPositions(const btCollisionShape& shape, const btTransform& transform, void getTilesPositions(const btCollisionShape& shape, const btTransform& transform,
const Settings& settings, Callback&& callback) const Settings& settings, Callback&& callback)
{ {
btVector3 aabbMin; btVector3 aabbMin;
btVector3 aabbMax; btVector3 aabbMax;

View file

@ -7,6 +7,7 @@
#include "settings.hpp" #include "settings.hpp"
#include "settingsutils.hpp" #include "settingsutils.hpp"
#include "sharednavmesh.hpp" #include "sharednavmesh.hpp"
#include "settingsutils.hpp"
#include <DetourNavMesh.h> #include <DetourNavMesh.h>
#include <DetourNavMeshBuilder.h> #include <DetourNavMeshBuilder.h>
@ -269,8 +270,9 @@ namespace DetourNavigator
return navMesh; return navMesh;
} }
UpdateNavMeshStatus updateNavMesh(const osg::Vec3f& agentHalfExtents, const RecastMesh& recastMesh, UpdateNavMeshStatus updateNavMesh(const osg::Vec3f& agentHalfExtents,
const TilePosition& changedTile, const Settings& settings, NavMeshCacheItem& navMeshCacheItem) const RecastMesh* recastMesh, const TilePosition& changedTile, const Settings& settings,
NavMeshCacheItem& navMeshCacheItem)
{ {
log("update NavMesh with mutiple tiles:", log("update NavMesh with mutiple tiles:",
" agentHeight=", std::setprecision(std::numeric_limits<float>::max_exponent10), " agentHeight=", std::setprecision(std::numeric_limits<float>::max_exponent10),
@ -298,8 +300,14 @@ namespace DetourNavigator
incRev.mNavMeshChanged = removed; incRev.mNavMeshChanged = removed;
const auto& boundsMin = recastMesh.getBoundsMin(); if (!recastMesh)
const auto& boundsMax = recastMesh.getBoundsMax(); {
log("ignore add tile: recastMesh is null");
return makeUpdateNavMeshStatus(removed, false);
}
const auto& boundsMin = recastMesh->getBoundsMin();
const auto& boundsMax = recastMesh->getBoundsMax();
if (boundsMin == boundsMax) if (boundsMin == boundsMax)
{ {
@ -311,7 +319,7 @@ namespace DetourNavigator
const osg::Vec3f tileBorderMin(tileBounds.mMin.x(), boundsMin.y() - 1, tileBounds.mMin.y()); const osg::Vec3f tileBorderMin(tileBounds.mMin.x(), boundsMin.y() - 1, tileBounds.mMin.y());
const osg::Vec3f tileBorderMax(tileBounds.mMax.x(), boundsMax.y() + 1, tileBounds.mMax.y()); const osg::Vec3f tileBorderMax(tileBounds.mMax.x(), boundsMax.y() + 1, tileBounds.mMax.y());
auto navMeshData = makeNavMeshTileData(agentHalfExtents, recastMesh, x, y, auto navMeshData = makeNavMeshTileData(agentHalfExtents, *recastMesh, x, y,
tileBorderMin, tileBorderMax, settings); tileBorderMin, tileBorderMax, settings);
if (!navMeshData.mValue) if (!navMeshData.mValue)

View file

@ -4,6 +4,7 @@
#include "settings.hpp" #include "settings.hpp"
#include "navmeshcacheitem.hpp" #include "navmeshcacheitem.hpp"
#include "tileposition.hpp" #include "tileposition.hpp"
#include "tilebounds.hpp"
#include <osg/Vec3f> #include <osg/Vec3f>
@ -30,8 +31,8 @@ namespace DetourNavigator
NavMeshPtr makeEmptyNavMesh(const Settings& settings); NavMeshPtr makeEmptyNavMesh(const Settings& settings);
UpdateNavMeshStatus updateNavMesh(const osg::Vec3f& agentHalfExtents, const RecastMesh& recastMesh, UpdateNavMeshStatus updateNavMesh(const osg::Vec3f& agentHalfExtents, const RecastMesh* recastMesh,
const TilePosition& changedTile, const Settings& settings, NavMeshCacheItem& navMeshCacheItem); const TilePosition& changedTile, const Settings& settings, NavMeshCacheItem& navMeshCacheItem);
} }
#endif #endif

View file

@ -11,11 +11,10 @@ namespace DetourNavigator
{ {
SharedNavMesh mValue; SharedNavMesh mValue;
std::size_t mGeneration; std::size_t mGeneration;
std::size_t mRecastMeshRevision;
std::atomic_size_t mNavMeshRevision; std::atomic_size_t mNavMeshRevision;
NavMeshCacheItem(const NavMeshPtr& value, std::size_t generation, std::size_t revision) NavMeshCacheItem(const NavMeshPtr& value, std::size_t generation)
: mValue(value), mGeneration(generation), mRecastMeshRevision(revision), mNavMeshRevision(0) {} : mValue(value), mGeneration(generation), mNavMeshRevision(0) {}
}; };
} }

View file

@ -3,6 +3,7 @@
#include "exceptions.hpp" #include "exceptions.hpp"
#include "gettilespositions.hpp" #include "gettilespositions.hpp"
#include "makenavmesh.hpp" #include "makenavmesh.hpp"
#include "navmeshcacheitem.hpp"
#include "settings.hpp" #include "settings.hpp"
#include "sharednavmesh.hpp" #include "sharednavmesh.hpp"
@ -17,14 +18,14 @@ namespace DetourNavigator
NavMeshManager::NavMeshManager(const Settings& settings) NavMeshManager::NavMeshManager(const Settings& settings)
: mSettings(settings) : mSettings(settings)
, mRecastMeshManager(settings) , mRecastMeshManager(settings)
, mAsyncNavMeshUpdater(settings) , mAsyncNavMeshUpdater(settings, mRecastMeshManager)
{} {
}
bool NavMeshManager::addObject(std::size_t id, const btCollisionShape& shape, const btTransform& transform) bool NavMeshManager::addObject(std::size_t id, const btCollisionShape& shape, const btTransform& transform)
{ {
if (!mRecastMeshManager.addObject(id, shape, transform)) if (!mRecastMeshManager.addObject(id, shape, transform))
return false; return false;
++mRevision;
addChangedTiles(shape, transform); addChangedTiles(shape, transform);
return true; return true;
} }
@ -34,7 +35,6 @@ namespace DetourNavigator
const auto object = mRecastMeshManager.removeObject(id); const auto object = mRecastMeshManager.removeObject(id);
if (!object) if (!object)
return false; return false;
++mRevision;
addChangedTiles(*object->mShape, object->mTransform); addChangedTiles(*object->mShape, object->mTransform);
return true; return true;
} }
@ -45,8 +45,7 @@ namespace DetourNavigator
if (cached != mCache.end()) if (cached != mCache.end())
return; return;
mCache.insert(std::make_pair(agentHalfExtents, mCache.insert(std::make_pair(agentHalfExtents,
std::make_shared<NavMeshCacheItem>(makeEmptyNavMesh(mSettings), ++mGenerationCounter, mRevision)) std::make_shared<NavMeshCacheItem>(makeEmptyNavMesh(mSettings), ++mGenerationCounter)));
);
log("cache add for agent=", agentHalfExtents); log("cache add for agent=", agentHalfExtents);
} }
@ -58,18 +57,15 @@ namespace DetourNavigator
void NavMeshManager::update(osg::Vec3f playerPosition, const osg::Vec3f& agentHalfExtents) void NavMeshManager::update(osg::Vec3f playerPosition, const osg::Vec3f& agentHalfExtents)
{ {
const auto& cached = getCached(agentHalfExtents); const auto& cached = getCached(agentHalfExtents);
if (cached->mRecastMeshRevision >= mRevision)
return;
cached->mRecastMeshRevision = mRevision;
const auto changedTiles = mChangedTiles.find(agentHalfExtents); const auto changedTiles = mChangedTiles.find(agentHalfExtents);
if (changedTiles != mChangedTiles.end()) if (changedTiles != mChangedTiles.end())
{ {
playerPosition *= mSettings.mRecastScaleFactor; playerPosition *= mSettings.mRecastScaleFactor;
std::swap(playerPosition.y(), playerPosition.z()); std::swap(playerPosition.y(), playerPosition.z());
mAsyncNavMeshUpdater.post(agentHalfExtents, mRecastMeshManager.getMesh(), cached, mAsyncNavMeshUpdater.post(agentHalfExtents, cached, getTilePosition(mSettings, playerPosition),
getTilePosition(mSettings, playerPosition), changedTiles->second); changedTiles->second);
log("cache update posted for agent=", agentHalfExtents, " changedTiles=", changedTiles->second.size());
mChangedTiles.erase(changedTiles); mChangedTiles.erase(changedTiles);
log("cache update posted for agent=", agentHalfExtents);
} }
} }

View file

@ -40,9 +40,8 @@ namespace DetourNavigator
std::map<osg::Vec3f, std::shared_ptr<NavMeshCacheItem>> getNavMeshes() const; std::map<osg::Vec3f, std::shared_ptr<NavMeshCacheItem>> getNavMeshes() const;
private: private:
std::size_t mRevision = 0;
const Settings& mSettings; const Settings& mSettings;
CachedRecastMeshManager mRecastMeshManager; TileCachedRecastMeshManager mRecastMeshManager;
std::map<osg::Vec3f, std::shared_ptr<NavMeshCacheItem>> mCache; std::map<osg::Vec3f, std::shared_ptr<NavMeshCacheItem>> mCache;
std::map<osg::Vec3f, std::set<TilePosition>> mChangedTiles; std::map<osg::Vec3f, std::set<TilePosition>> mChangedTiles;
AsyncNavMeshUpdater mAsyncNavMeshUpdater; AsyncNavMeshUpdater mAsyncNavMeshUpdater;

View file

@ -27,9 +27,13 @@ namespace DetourNavigator
{ {
using BulletHelpers::makeProcessTriangleCallback; using BulletHelpers::makeProcessTriangleCallback;
RecastMeshBuilder::RecastMeshBuilder(const Settings& settings) RecastMeshBuilder::RecastMeshBuilder(const Settings& settings, const TileBounds& bounds)
: mSettings(settings) : mSettings(settings)
{} , mBounds(bounds)
{
mBounds.mMin /= mSettings.get().mRecastScaleFactor;
mBounds.mMax /= mSettings.get().mRecastScaleFactor;
}
void RecastMeshBuilder::addObject(const btCollisionShape& shape, const btTransform& transform) void RecastMeshBuilder::addObject(const btCollisionShape& shape, const btTransform& transform)
{ {
@ -54,7 +58,7 @@ namespace DetourNavigator
void RecastMeshBuilder::addObject(const btConcaveShape& shape, const btTransform& transform) void RecastMeshBuilder::addObject(const btConcaveShape& shape, const btTransform& transform)
{ {
return addObject(shape, makeProcessTriangleCallback([&] (btVector3* triangle, int, int) return addObject(shape, transform, makeProcessTriangleCallback([&] (btVector3* triangle, int, int)
{ {
for (std::size_t i = 3; i > 0; --i) for (std::size_t i = 3; i > 0; --i)
addTriangleVertex(transform(triangle[i - 1])); addTriangleVertex(transform(triangle[i - 1]));
@ -63,7 +67,7 @@ namespace DetourNavigator
void RecastMeshBuilder::addObject(const btHeightfieldTerrainShape& shape, const btTransform& transform) void RecastMeshBuilder::addObject(const btHeightfieldTerrainShape& shape, const btTransform& transform)
{ {
return addObject(shape, makeProcessTriangleCallback([&] (btVector3* triangle, int, int) return addObject(shape, transform, makeProcessTriangleCallback([&] (btVector3* triangle, int, int)
{ {
for (std::size_t i = 0; i < 3; ++i) for (std::size_t i = 0; i < 3; ++i)
addTriangleVertex(transform(triangle[i])); addTriangleVertex(transform(triangle[i]));
@ -111,11 +115,29 @@ namespace DetourNavigator
mVertices.clear(); mVertices.clear();
} }
void RecastMeshBuilder::addObject(const btConcaveShape& shape, btTriangleCallback&& callback) void RecastMeshBuilder::addObject(const btConcaveShape& shape, const btTransform& transform,
btTriangleCallback&& callback)
{ {
btVector3 aabbMin; btVector3 aabbMin;
btVector3 aabbMax; btVector3 aabbMax;
shape.getAabb(btTransform::getIdentity(), aabbMin, aabbMax); shape.getAabb(btTransform::getIdentity(), aabbMin, aabbMax);
const btVector3 boundsMinMin(mBounds.mMin.x(), mBounds.mMin.y(), 0);
const btVector3 boundsMinMax(mBounds.mMin.x(), mBounds.mMax.y(), 0);
const btVector3 boundsMaxMin(mBounds.mMax.x(), mBounds.mMin.y(), 0);
const btVector3 boundsMaxMax(mBounds.mMax.x(), mBounds.mMax.y(), 0);
const auto inversedTransform = transform.inverse();
const auto localBoundsMinMin = inversedTransform(boundsMinMin);
const auto localBoundsMinMax = inversedTransform(boundsMinMax);
const auto localBoundsMaxMin = inversedTransform(boundsMaxMin);
const auto localBoundsMaxMax = inversedTransform(boundsMaxMax);
aabbMin.setX(std::min({localBoundsMinMin.x(), localBoundsMinMax.x(),
localBoundsMaxMin.x(), localBoundsMaxMax.x()}));
aabbMin.setY(std::min({localBoundsMinMin.y(), localBoundsMinMax.y(),
localBoundsMaxMin.y(), localBoundsMaxMax.y()}));
aabbMax.setX(std::max({localBoundsMinMin.x(), localBoundsMinMax.x(),
localBoundsMaxMin.x(), localBoundsMaxMax.x()}));
aabbMax.setY(std::max({localBoundsMinMin.y(), localBoundsMinMax.y(),
localBoundsMaxMin.y(), localBoundsMaxMax.y()}));
shape.processAllTriangles(&callback, aabbMin, aabbMax); shape.processAllTriangles(&callback, aabbMin, aabbMax);
} }

View file

@ -2,6 +2,7 @@
#define OPENMW_COMPONENTS_DETOURNAVIGATOR_RECASTMESHBUILDER_H #define OPENMW_COMPONENTS_DETOURNAVIGATOR_RECASTMESHBUILDER_H
#include "recastmesh.hpp" #include "recastmesh.hpp"
#include "tilebounds.hpp"
class btBoxShape; class btBoxShape;
class btCollisionShape; class btCollisionShape;
@ -17,7 +18,7 @@ namespace DetourNavigator
class RecastMeshBuilder class RecastMeshBuilder
{ {
public: public:
RecastMeshBuilder(const Settings& settings); RecastMeshBuilder(const Settings& settings, const TileBounds& bounds);
void addObject(const btCollisionShape& shape, const btTransform& transform); void addObject(const btCollisionShape& shape, const btTransform& transform);
@ -35,10 +36,11 @@ namespace DetourNavigator
private: private:
std::reference_wrapper<const Settings> mSettings; std::reference_wrapper<const Settings> mSettings;
TileBounds mBounds;
std::vector<int> mIndices; std::vector<int> mIndices;
std::vector<float> mVertices; std::vector<float> mVertices;
void addObject(const btConcaveShape& shape, btTriangleCallback&& callback); void addObject(const btConcaveShape& shape, const btTransform& transform, btTriangleCallback&& callback);
void addTriangleVertex(const btVector3& worldPosition); void addTriangleVertex(const btVector3& worldPosition);

View file

@ -4,9 +4,9 @@
namespace DetourNavigator namespace DetourNavigator
{ {
RecastMeshManager::RecastMeshManager(const Settings& settings) RecastMeshManager::RecastMeshManager(const Settings& settings, const TileBounds& bounds)
: mShouldRebuild(false) : mShouldRebuild(false)
, mMeshBuilder(settings) , mMeshBuilder(settings, bounds)
{ {
} }
@ -35,6 +35,11 @@ namespace DetourNavigator
return mMeshBuilder.create(); return mMeshBuilder.create();
} }
bool RecastMeshManager::isEmpty() const
{
return mObjects.empty();
}
void RecastMeshManager::rebuild() void RecastMeshManager::rebuild()
{ {
if (!mShouldRebuild) if (!mShouldRebuild)

View file

@ -22,7 +22,7 @@ namespace DetourNavigator
btTransform mTransform; btTransform mTransform;
}; };
RecastMeshManager(const Settings& settings); RecastMeshManager(const Settings& settings, const TileBounds& bounds);
bool addObject(std::size_t id, const btCollisionShape& shape, const btTransform& transform); bool addObject(std::size_t id, const btCollisionShape& shape, const btTransform& transform);
@ -30,6 +30,8 @@ namespace DetourNavigator
std::shared_ptr<RecastMesh> getMesh(); std::shared_ptr<RecastMesh> getMesh();
bool isEmpty() const;
private: private:
bool mShouldRebuild; bool mShouldRebuild;
RecastMeshBuilder mMeshBuilder; RecastMeshBuilder mMeshBuilder;

View file

@ -2,6 +2,7 @@
#define OPENMW_COMPONENTS_DETOURNAVIGATOR_SETTINGSUTILS_H #define OPENMW_COMPONENTS_DETOURNAVIGATOR_SETTINGSUTILS_H
#include "settings.hpp" #include "settings.hpp"
#include "tilebounds.hpp"
#include "tileposition.hpp" #include "tileposition.hpp"
#include "tilebounds.hpp" #include "tilebounds.hpp"

View file

@ -0,0 +1,71 @@
#include "tilecachedrecastmeshmanager.hpp"
#include "makenavmesh.hpp"
#include "gettilespositions.hpp"
#include "settingsutils.hpp"
namespace DetourNavigator
{
TileCachedRecastMeshManager::TileCachedRecastMeshManager(const Settings& settings)
: mSettings(settings)
{
}
bool TileCachedRecastMeshManager::addObject(std::size_t id, const btCollisionShape& shape,
const btTransform& transform)
{
bool result = false;
auto& tilesPositions = mObjectsTilesPositions[id];
const auto border = getBorderSize(mSettings);
getTilesPositions(shape, transform, mSettings, [&] (const TilePosition& tilePosition)
{
std::unique_lock<std::mutex> 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.addObject(id, shape, transform))
{
lock.unlock();
tilesPositions.push_back(tilePosition);
result = true;
}
});
return result;
}
boost::optional<RecastMeshManager::Object> TileCachedRecastMeshManager::removeObject(std::size_t id)
{
const auto object = mObjectsTilesPositions.find(id);
if (object == mObjectsTilesPositions.end())
return boost::none;
boost::optional<RecastMeshManager::Object> result;
for (const auto& tilePosition : object->second)
{
std::unique_lock<std::mutex> lock(mTilesMutex);
const auto tile = mTiles.find(tilePosition);
if (tile == mTiles.end())
continue;
const auto tileResult = tile->second.removeObject(id);
if (tile->second.isEmpty())
mTiles.erase(tile);
lock.unlock();
if (tileResult && !result)
result = tileResult;
}
return result;
}
std::shared_ptr<RecastMesh> TileCachedRecastMeshManager::getMesh(const TilePosition& tilePosition)
{
const std::lock_guard<std::mutex> lock(mTilesMutex);
const auto it = mTiles.find(tilePosition);
if (it == mTiles.end())
return nullptr;
return it->second.getMesh();
}
}

View file

@ -0,0 +1,31 @@
#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_TILECACHEDRECASTMESHMANAGER_H
#define OPENMW_COMPONENTS_DETOURNAVIGATOR_TILECACHEDRECASTMESHMANAGER_H
#include "cachedrecastmeshmanager.hpp"
#include "tileposition.hpp"
#include <map>
#include <mutex>
namespace DetourNavigator
{
class TileCachedRecastMeshManager
{
public:
TileCachedRecastMeshManager(const Settings& settings);
bool addObject(std::size_t id, const btCollisionShape& shape, const btTransform& transform);
boost::optional<RecastMeshManager::Object> removeObject(std::size_t id);
std::shared_ptr<RecastMesh> getMesh(const TilePosition& tilePosition);
private:
const Settings& mSettings;
std::mutex mTilesMutex;
std::map<TilePosition, CachedRecastMeshManager> mTiles;
std::unordered_map<std::size_t, std::vector<TilePosition>> mObjectsTilesPositions;
};
}
#endif