Support recast mesh rendering

pull/578/head
elsid 5 years ago
parent f603daecdc
commit 7ae7cb181d
No known key found for this signature in database
GPG Key ID: B845CB9FEE18AB40

@ -21,7 +21,7 @@ add_openmw_dir (mwrender
actors objects renderingmanager animation rotatecontroller sky npcanimation actors objects renderingmanager animation rotatecontroller sky npcanimation
creatureanimation effectmanager util renderinginterface pathgrid rendermode weaponanimation creatureanimation effectmanager util renderinginterface pathgrid rendermode weaponanimation
bulletdebugdraw globalmap characterpreview camera localmap water terrainstorage ripplesimulation bulletdebugdraw globalmap characterpreview camera localmap water terrainstorage ripplesimulation
renderbin actoranimation landmanager navmesh actorspaths renderbin actoranimation landmanager navmesh actorspaths recastmesh
) )
add_openmw_dir (mwinput add_openmw_dir (mwinput

@ -0,0 +1,92 @@
#include "recastmesh.hpp"
#include <components/sceneutil/vismask.hpp>
#include <components/sceneutil/recastmesh.hpp>
#include <components/debug/debuglog.hpp>
#include <osg/PositionAttitudeTransform>
namespace MWRender
{
RecastMesh::RecastMesh(const osg::ref_ptr<osg::Group>& root, bool enabled)
: mRootNode(root)
, mEnabled(enabled)
{
}
RecastMesh::~RecastMesh()
{
if (mEnabled)
disable();
}
bool RecastMesh::toggle()
{
if (mEnabled)
disable();
else
enable();
return mEnabled;
}
void RecastMesh::update(const DetourNavigator::RecastMeshTiles& tiles, const DetourNavigator::Settings& settings)
{
if (!mEnabled)
return;
for (auto it = mGroups.begin(); it != mGroups.end();)
{
const auto tile = tiles.find(it->first);
if (tile == tiles.end())
{
mRootNode->removeChild(it->second.mValue);
it = mGroups.erase(it);
continue;
}
if (it->second.mGeneration != tile->second->getGeneration()
|| it->second.mRevision != tile->second->getRevision())
{
const auto group = SceneUtil::createRecastMeshGroup(*tile->second, settings);
group->setNodeMask(SceneUtil::Mask_Debug);
mRootNode->removeChild(it->second.mValue);
mRootNode->addChild(group);
it->second.mValue = group;
it->second.mGeneration = tile->second->getGeneration();
it->second.mRevision = tile->second->getRevision();
continue;
}
++it;
}
for (const auto& tile : tiles)
{
if (mGroups.count(tile.first))
continue;
const auto group = SceneUtil::createRecastMeshGroup(*tile.second, settings);
group->setNodeMask(SceneUtil::Mask_Debug);
mGroups.emplace(tile.first, Group {tile.second->getGeneration(), tile.second->getRevision(), group});
mRootNode->addChild(group);
}
}
void RecastMesh::reset()
{
std::for_each(mGroups.begin(), mGroups.end(), [&] (const auto& v) { mRootNode->removeChild(v.second.mValue); });
mGroups.clear();
}
void RecastMesh::enable()
{
std::for_each(mGroups.begin(), mGroups.end(), [&] (const auto& v) { mRootNode->addChild(v.second.mValue); });
mEnabled = true;
}
void RecastMesh::disable()
{
std::for_each(mGroups.begin(), mGroups.end(), [&] (const auto& v) { mRootNode->removeChild(v.second.mValue); });
mEnabled = false;
}
}

@ -0,0 +1,53 @@
#ifndef OPENMW_MWRENDER_RECASTMESH_H
#define OPENMW_MWRENDER_RECASTMESH_H
#include <components/detournavigator/navigator.hpp>
#include <osg/ref_ptr>
#include <vector>
namespace osg
{
class Group;
class Geometry;
}
namespace MWRender
{
class RecastMesh
{
public:
RecastMesh(const osg::ref_ptr<osg::Group>& root, bool enabled);
~RecastMesh();
bool toggle();
void update(const DetourNavigator::RecastMeshTiles& recastMeshTiles, const DetourNavigator::Settings& settings);
void reset();
void enable();
void disable();
bool isEnabled() const
{
return mEnabled;
}
private:
struct Group
{
std::size_t mGeneration;
std::size_t mRevision;
osg::ref_ptr<osg::Group> mValue;
};
osg::ref_ptr<osg::Group> mRootNode;
bool mEnabled;
std::map<DetourNavigator::TilePosition, Group> mGroups;
};
}
#endif

@ -66,6 +66,7 @@
#include "util.hpp" #include "util.hpp"
#include "navmesh.hpp" #include "navmesh.hpp"
#include "actorspaths.hpp" #include "actorspaths.hpp"
#include "recastmesh.hpp"
namespace namespace
{ {
@ -260,6 +261,7 @@ namespace MWRender
mNavMesh.reset(new NavMesh(mRootNode, Settings::Manager::getBool("enable nav mesh render", "Navigator"))); mNavMesh.reset(new NavMesh(mRootNode, Settings::Manager::getBool("enable nav mesh render", "Navigator")));
mActorsPaths.reset(new ActorsPaths(mRootNode, Settings::Manager::getBool("enable agents paths render", "Navigator"))); mActorsPaths.reset(new ActorsPaths(mRootNode, Settings::Manager::getBool("enable agents paths render", "Navigator")));
mRecastMesh.reset(new RecastMesh(mRootNode, Settings::Manager::getBool("enable recast mesh render", "Navigator")));
mPathgrid.reset(new Pathgrid(mRootNode)); mPathgrid.reset(new Pathgrid(mRootNode));
mObjects.reset(new Objects(mResourceSystem, sceneRoot, mUnrefQueue.get())); mObjects.reset(new Objects(mResourceSystem, sceneRoot, mUnrefQueue.get()));
@ -585,6 +587,10 @@ namespace MWRender
{ {
return mActorsPaths->toggle(); return mActorsPaths->toggle();
} }
else if (mode == Render_RecastMesh)
{
return mRecastMesh->toggle();
}
return false; return false;
} }
@ -651,6 +657,7 @@ namespace MWRender
} }
updateNavMesh(); updateNavMesh();
updateRecastMesh();
mCamera->update(dt, paused); mCamera->update(dt, paused);
@ -1463,4 +1470,12 @@ namespace MWRender
} }
} }
} }
void RenderingManager::updateRecastMesh()
{
if (!mRecastMesh->isEnabled())
return;
mRecastMesh->update(mNavigator.getRecastMeshTiles(), mNavigator.getSettings());
}
} }

@ -82,6 +82,7 @@ namespace MWRender
class LandManager; class LandManager;
class NavMesh; class NavMesh;
class ActorsPaths; class ActorsPaths;
class RecastMesh;
class RenderingManager : public MWRender::RenderingInterface class RenderingManager : public MWRender::RenderingInterface
{ {
@ -246,6 +247,8 @@ namespace MWRender
void updateNavMesh(); void updateNavMesh();
void updateRecastMesh();
osg::ref_ptr<osgUtil::IntersectionVisitor> getIntersectionVisitor(osgUtil::Intersector* intersector, bool ignorePlayer, bool ignoreActors); osg::ref_ptr<osgUtil::IntersectionVisitor> getIntersectionVisitor(osgUtil::Intersector* intersector, bool ignorePlayer, bool ignoreActors);
osg::ref_ptr<osgUtil::IntersectionVisitor> mIntersectionVisitor; osg::ref_ptr<osgUtil::IntersectionVisitor> mIntersectionVisitor;
@ -264,6 +267,7 @@ namespace MWRender
std::unique_ptr<NavMesh> mNavMesh; std::unique_ptr<NavMesh> mNavMesh;
std::size_t mNavMeshNumber = 0; std::size_t mNavMeshNumber = 0;
std::unique_ptr<ActorsPaths> mActorsPaths; std::unique_ptr<ActorsPaths> mActorsPaths;
std::unique_ptr<RecastMesh> mRecastMesh;
std::unique_ptr<Pathgrid> mPathgrid; std::unique_ptr<Pathgrid> mPathgrid;
std::unique_ptr<Objects> mObjects; std::unique_ptr<Objects> mObjects;
std::unique_ptr<Water> mWater; std::unique_ptr<Water> mWater;

@ -13,6 +13,7 @@ namespace MWRender
Render_Scene, Render_Scene,
Render_NavMesh, Render_NavMesh,
Render_ActorsPaths, Render_ActorsPaths,
Render_RecastMesh,
}; };
} }

@ -463,5 +463,6 @@ op 0x200030c: RepairedOnMe
op 0x200030d: RepairedOnMe, explicit op 0x200030d: RepairedOnMe, explicit
op 0x200030e: TestCells op 0x200030e: TestCells
op 0x200030f: TestInteriorCells op 0x200030f: TestInteriorCells
op 0x2000310: ToggleRecastMesh
opcodes 0x2000310-0x3ffffff unused opcodes 0x2000311-0x3ffffff unused

@ -1440,6 +1440,20 @@ namespace MWScript
} }
}; };
class OpToggleRecastMesh : public Interpreter::Opcode0
{
public:
virtual void execute (Interpreter::Runtime& runtime)
{
bool enabled =
MWBase::Environment::get().getWorld()->toggleRenderMode (MWRender::Render_RecastMesh);
runtime.getContext().report (enabled ?
"Recast Mesh Rendering -> On" : "Recast Mesh Rendering -> Off");
}
};
void installOpcodes (Interpreter::Interpreter& interpreter) void installOpcodes (Interpreter::Interpreter& interpreter)
{ {
interpreter.installSegment5 (Compiler::Misc::opcodeXBox, new OpXBox); interpreter.installSegment5 (Compiler::Misc::opcodeXBox, new OpXBox);
@ -1545,6 +1559,7 @@ namespace MWScript
interpreter.installSegment5 (Compiler::Misc::opcodeSetNavMeshNumberToRender, new OpSetNavMeshNumberToRender); interpreter.installSegment5 (Compiler::Misc::opcodeSetNavMeshNumberToRender, new OpSetNavMeshNumberToRender);
interpreter.installSegment5 (Compiler::Misc::opcodeRepairedOnMe, new OpRepairedOnMe<ImplicitRef>); interpreter.installSegment5 (Compiler::Misc::opcodeRepairedOnMe, new OpRepairedOnMe<ImplicitRef>);
interpreter.installSegment5 (Compiler::Misc::opcodeRepairedOnMeExplicit, new OpRepairedOnMe<ExplicitRef>); interpreter.installSegment5 (Compiler::Misc::opcodeRepairedOnMeExplicit, new OpRepairedOnMe<ExplicitRef>);
interpreter.installSegment5 (Compiler::Misc::opcodeToggleRecastMesh, new OpToggleRecastMesh);
} }
} }
} }

@ -25,12 +25,15 @@ namespace
{ {
const osg::Vec3f mAgentHalfExtents {1, 2, 3}; const osg::Vec3f mAgentHalfExtents {1, 2, 3};
const TilePosition mTilePosition {0, 0}; const TilePosition mTilePosition {0, 0};
const std::size_t mGeneration = 0;
const std::size_t mRevision = 0;
const std::vector<int> mIndices {{0, 1, 2}}; const std::vector<int> mIndices {{0, 1, 2}};
const std::vector<float> mVertices {{0, 0, 0, 1, 0, 0, 1, 1, 0}}; const std::vector<float> mVertices {{0, 0, 0, 1, 0, 0, 1, 1, 0}};
const std::vector<AreaType> mAreaTypes {1, AreaType_ground}; const std::vector<AreaType> mAreaTypes {1, AreaType_ground};
const std::vector<RecastMesh::Water> mWater {}; const std::vector<RecastMesh::Water> mWater {};
const std::size_t mTrianglesPerChunk {1}; const std::size_t mTrianglesPerChunk {1};
const RecastMesh mRecastMesh {mIndices, mVertices, mAreaTypes, mWater, mTrianglesPerChunk}; const RecastMesh mRecastMesh {mGeneration, mRevision, mIndices, mVertices,
mAreaTypes, mWater, mTrianglesPerChunk};
const std::vector<OffMeshConnection> mOffMeshConnections {}; const std::vector<OffMeshConnection> mOffMeshConnections {};
unsigned char* const mData = reinterpret_cast<unsigned char*>(dtAlloc(1, DT_ALLOC_PERM)); unsigned char* const mData = reinterpret_cast<unsigned char*>(dtAlloc(1, DT_ALLOC_PERM));
NavMeshData mNavMeshData {mData, 1}; NavMeshData mNavMeshData {mData, 1};
@ -128,7 +131,8 @@ namespace
const std::size_t maxSize = 1; const std::size_t maxSize = 1;
NavMeshTilesCache cache(maxSize); NavMeshTilesCache cache(maxSize);
const std::vector<RecastMesh::Water> water {1, RecastMesh::Water {1, btTransform::getIdentity()}}; const std::vector<RecastMesh::Water> water {1, RecastMesh::Water {1, btTransform::getIdentity()}};
const RecastMesh unexistentRecastMesh {mIndices, mVertices, mAreaTypes, water, mTrianglesPerChunk}; const RecastMesh unexistentRecastMesh {mGeneration, mRevision, mIndices, mVertices,
mAreaTypes, water, mTrianglesPerChunk};
cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, std::move(mNavMeshData)); cache.set(mAgentHalfExtents, mTilePosition, mRecastMesh, mOffMeshConnections, std::move(mNavMeshData));
EXPECT_FALSE(cache.get(mAgentHalfExtents, mTilePosition, unexistentRecastMesh, mOffMeshConnections)); EXPECT_FALSE(cache.get(mAgentHalfExtents, mTilePosition, unexistentRecastMesh, mOffMeshConnections));
@ -142,7 +146,8 @@ namespace
NavMeshTilesCache cache(maxSize); NavMeshTilesCache cache(maxSize);
const std::vector<RecastMesh::Water> water {1, RecastMesh::Water {1, btTransform::getIdentity()}}; const std::vector<RecastMesh::Water> water {1, RecastMesh::Water {1, btTransform::getIdentity()}};
const RecastMesh anotherRecastMesh {mIndices, mVertices, mAreaTypes, water, mTrianglesPerChunk}; const RecastMesh anotherRecastMesh {mGeneration, mRevision, mIndices, mVertices,
mAreaTypes, water, mTrianglesPerChunk};
const auto anotherData = reinterpret_cast<unsigned char*>(dtAlloc(1, DT_ALLOC_PERM)); const auto anotherData = reinterpret_cast<unsigned char*>(dtAlloc(1, DT_ALLOC_PERM));
NavMeshData anotherNavMeshData {anotherData, 1}; NavMeshData anotherNavMeshData {anotherData, 1};
@ -162,7 +167,8 @@ namespace
NavMeshTilesCache cache(maxSize); NavMeshTilesCache cache(maxSize);
const std::vector<RecastMesh::Water> water {1, RecastMesh::Water {1, btTransform::getIdentity()}}; const std::vector<RecastMesh::Water> water {1, RecastMesh::Water {1, btTransform::getIdentity()}};
const RecastMesh anotherRecastMesh {mIndices, mVertices, mAreaTypes, water, mTrianglesPerChunk}; const RecastMesh anotherRecastMesh {mGeneration, mRevision, mIndices, mVertices,
mAreaTypes, water, mTrianglesPerChunk};
const auto anotherData = reinterpret_cast<unsigned char*>(dtAlloc(1, DT_ALLOC_PERM)); const auto anotherData = reinterpret_cast<unsigned char*>(dtAlloc(1, DT_ALLOC_PERM));
NavMeshData anotherNavMeshData {anotherData, 1}; NavMeshData anotherNavMeshData {anotherData, 1};
@ -180,14 +186,14 @@ namespace
NavMeshTilesCache cache(maxSize); NavMeshTilesCache cache(maxSize);
const std::vector<RecastMesh::Water> leastRecentlySetWater {1, RecastMesh::Water {1, btTransform::getIdentity()}}; const std::vector<RecastMesh::Water> leastRecentlySetWater {1, RecastMesh::Water {1, btTransform::getIdentity()}};
const RecastMesh leastRecentlySetRecastMesh {mIndices, mVertices, mAreaTypes, leastRecentlySetWater, const RecastMesh leastRecentlySetRecastMesh {mGeneration, mRevision, mIndices, mVertices,
mTrianglesPerChunk}; mAreaTypes, leastRecentlySetWater, mTrianglesPerChunk};
const auto leastRecentlySetData = reinterpret_cast<unsigned char*>(dtAlloc(1, DT_ALLOC_PERM)); const auto leastRecentlySetData = reinterpret_cast<unsigned char*>(dtAlloc(1, DT_ALLOC_PERM));
NavMeshData leastRecentlySetNavMeshData {leastRecentlySetData, 1}; NavMeshData leastRecentlySetNavMeshData {leastRecentlySetData, 1};
const std::vector<RecastMesh::Water> mostRecentlySetWater {1, RecastMesh::Water {2, btTransform::getIdentity()}}; const std::vector<RecastMesh::Water> mostRecentlySetWater {1, RecastMesh::Water {2, btTransform::getIdentity()}};
const RecastMesh mostRecentlySetRecastMesh {mIndices, mVertices, mAreaTypes, mostRecentlySetWater, const RecastMesh mostRecentlySetRecastMesh {mGeneration, mRevision, mIndices, mVertices,
mTrianglesPerChunk}; mAreaTypes, mostRecentlySetWater, mTrianglesPerChunk};
const auto mostRecentlySetData = reinterpret_cast<unsigned char*>(dtAlloc(1, DT_ALLOC_PERM)); const auto mostRecentlySetData = reinterpret_cast<unsigned char*>(dtAlloc(1, DT_ALLOC_PERM));
NavMeshData mostRecentlySetNavMeshData {mostRecentlySetData, 1}; NavMeshData mostRecentlySetNavMeshData {mostRecentlySetData, 1};
@ -212,14 +218,14 @@ namespace
NavMeshTilesCache cache(maxSize); NavMeshTilesCache cache(maxSize);
const std::vector<RecastMesh::Water> leastRecentlyUsedWater {1, RecastMesh::Water {1, btTransform::getIdentity()}}; const std::vector<RecastMesh::Water> leastRecentlyUsedWater {1, RecastMesh::Water {1, btTransform::getIdentity()}};
const RecastMesh leastRecentlyUsedRecastMesh {mIndices, mVertices, mAreaTypes, leastRecentlyUsedWater, const RecastMesh leastRecentlyUsedRecastMesh {mGeneration, mRevision, mIndices, mVertices,
mTrianglesPerChunk}; mAreaTypes, leastRecentlyUsedWater, mTrianglesPerChunk};
const auto leastRecentlyUsedData = reinterpret_cast<unsigned char*>(dtAlloc(1, DT_ALLOC_PERM)); const auto leastRecentlyUsedData = reinterpret_cast<unsigned char*>(dtAlloc(1, DT_ALLOC_PERM));
NavMeshData leastRecentlyUsedNavMeshData {leastRecentlyUsedData, 1}; NavMeshData leastRecentlyUsedNavMeshData {leastRecentlyUsedData, 1};
const std::vector<RecastMesh::Water> mostRecentlyUsedWater {1, RecastMesh::Water {2, btTransform::getIdentity()}}; const std::vector<RecastMesh::Water> mostRecentlyUsedWater {1, RecastMesh::Water {2, btTransform::getIdentity()}};
const RecastMesh mostRecentlyUsedRecastMesh {mIndices, mVertices, mAreaTypes, mostRecentlyUsedWater, const RecastMesh mostRecentlyUsedRecastMesh {mGeneration, mRevision, mIndices, mVertices,
mTrianglesPerChunk}; mAreaTypes, mostRecentlyUsedWater, mTrianglesPerChunk};
const auto mostRecentlyUsedData = reinterpret_cast<unsigned char*>(dtAlloc(1, DT_ALLOC_PERM)); const auto mostRecentlyUsedData = reinterpret_cast<unsigned char*>(dtAlloc(1, DT_ALLOC_PERM));
NavMeshData mostRecentlyUsedNavMeshData {mostRecentlyUsedData, 1}; NavMeshData mostRecentlyUsedNavMeshData {mostRecentlyUsedData, 1};
@ -256,7 +262,7 @@ namespace
NavMeshTilesCache cache(maxSize); NavMeshTilesCache cache(maxSize);
const std::vector<RecastMesh::Water> water {1, RecastMesh::Water {1, btTransform::getIdentity()}}; const std::vector<RecastMesh::Water> water {1, RecastMesh::Water {1, btTransform::getIdentity()}};
const RecastMesh tooLargeRecastMesh {mIndices, mVertices, mAreaTypes, water, mTrianglesPerChunk}; const RecastMesh tooLargeRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, water, mTrianglesPerChunk};
const auto tooLargeData = reinterpret_cast<unsigned char*>(dtAlloc(2, DT_ALLOC_PERM)); const auto tooLargeData = reinterpret_cast<unsigned char*>(dtAlloc(2, DT_ALLOC_PERM));
NavMeshData tooLargeNavMeshData {tooLargeData, 2}; NavMeshData tooLargeNavMeshData {tooLargeData, 2};
@ -275,12 +281,13 @@ namespace
NavMeshTilesCache cache(maxSize); NavMeshTilesCache cache(maxSize);
const std::vector<RecastMesh::Water> anotherWater {1, RecastMesh::Water {1, btTransform::getIdentity()}}; const std::vector<RecastMesh::Water> anotherWater {1, RecastMesh::Water {1, btTransform::getIdentity()}};
const RecastMesh anotherRecastMesh {mIndices, mVertices, mAreaTypes, anotherWater, mTrianglesPerChunk}; const RecastMesh anotherRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, anotherWater, mTrianglesPerChunk};
const auto anotherData = reinterpret_cast<unsigned char*>(dtAlloc(1, DT_ALLOC_PERM)); const auto anotherData = reinterpret_cast<unsigned char*>(dtAlloc(1, DT_ALLOC_PERM));
NavMeshData anotherNavMeshData {anotherData, 1}; NavMeshData anotherNavMeshData {anotherData, 1};
const std::vector<RecastMesh::Water> tooLargeWater {1, RecastMesh::Water {2, btTransform::getIdentity()}}; const std::vector<RecastMesh::Water> tooLargeWater {1, RecastMesh::Water {2, btTransform::getIdentity()}};
const RecastMesh tooLargeRecastMesh {mIndices, mVertices, mAreaTypes, tooLargeWater, mTrianglesPerChunk}; const RecastMesh tooLargeRecastMesh {mGeneration, mRevision, mIndices, mVertices,
mAreaTypes, tooLargeWater, mTrianglesPerChunk};
const auto tooLargeData = reinterpret_cast<unsigned char*>(dtAlloc(2, DT_ALLOC_PERM)); const auto tooLargeData = reinterpret_cast<unsigned char*>(dtAlloc(2, DT_ALLOC_PERM));
NavMeshData tooLargeNavMeshData {tooLargeData, 2}; NavMeshData tooLargeNavMeshData {tooLargeData, 2};
@ -303,7 +310,8 @@ namespace
NavMeshTilesCache cache(maxSize); NavMeshTilesCache cache(maxSize);
const std::vector<RecastMesh::Water> water {1, RecastMesh::Water {1, btTransform::getIdentity()}}; const std::vector<RecastMesh::Water> water {1, RecastMesh::Water {1, btTransform::getIdentity()}};
const RecastMesh anotherRecastMesh {mIndices, mVertices, mAreaTypes, water, mTrianglesPerChunk}; const RecastMesh anotherRecastMesh {mGeneration, mRevision, mIndices, mVertices,
mAreaTypes, water, mTrianglesPerChunk};
const auto anotherData = reinterpret_cast<unsigned char*>(dtAlloc(1, DT_ALLOC_PERM)); const auto anotherData = reinterpret_cast<unsigned char*>(dtAlloc(1, DT_ALLOC_PERM));
NavMeshData anotherNavMeshData {anotherData, 1}; NavMeshData anotherNavMeshData {anotherData, 1};
@ -326,7 +334,7 @@ namespace
NavMeshTilesCache cache(maxSize); NavMeshTilesCache cache(maxSize);
const std::vector<RecastMesh::Water> water {1, RecastMesh::Water {1, btTransform::getIdentity()}}; const std::vector<RecastMesh::Water> water {1, RecastMesh::Water {1, btTransform::getIdentity()}};
const RecastMesh anotherRecastMesh {mIndices, mVertices, mAreaTypes, water, mTrianglesPerChunk}; const RecastMesh anotherRecastMesh {mGeneration, mRevision, mIndices, mVertices, mAreaTypes, water, mTrianglesPerChunk};
const auto anotherData = reinterpret_cast<unsigned char*>(dtAlloc(1, DT_ALLOC_PERM)); const auto anotherData = reinterpret_cast<unsigned char*>(dtAlloc(1, DT_ALLOC_PERM));
NavMeshData anotherNavMeshData {anotherData, 1}; NavMeshData anotherNavMeshData {anotherData, 1};

@ -30,6 +30,8 @@ namespace
{ {
Settings mSettings; Settings mSettings;
TileBounds mBounds; TileBounds mBounds;
const std::size_t mGeneration = 0;
const std::size_t mRevision = 0;
DetourNavigatorRecastMeshBuilderTest() DetourNavigatorRecastMeshBuilderTest()
{ {
@ -45,7 +47,7 @@ namespace
TEST_F(DetourNavigatorRecastMeshBuilderTest, create_for_empty_should_return_empty) TEST_F(DetourNavigatorRecastMeshBuilderTest, create_for_empty_should_return_empty)
{ {
RecastMeshBuilder builder(mSettings, mBounds); RecastMeshBuilder builder(mSettings, mBounds);
const auto recastMesh = builder.create(); const auto recastMesh = builder.create(mGeneration, mRevision);
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>());
EXPECT_EQ(recastMesh->getAreaTypes(), std::vector<AreaType>()); EXPECT_EQ(recastMesh->getAreaTypes(), std::vector<AreaType>());
@ -59,7 +61,7 @@ namespace
RecastMeshBuilder builder(mSettings, mBounds); RecastMeshBuilder builder(mSettings, mBounds);
builder.addObject(static_cast<const btCollisionShape&>(shape), btTransform::getIdentity(), AreaType_ground); builder.addObject(static_cast<const btCollisionShape&>(shape), btTransform::getIdentity(), AreaType_ground);
const auto recastMesh = builder.create(); const auto recastMesh = builder.create(mGeneration, mRevision);
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,
@ -80,7 +82,7 @@ namespace
btTransform(btMatrix3x3::getIdentity().scaled(btVector3(1, 2, 3)), btVector3(1, 2, 3)), btTransform(btMatrix3x3::getIdentity().scaled(btVector3(1, 2, 3)), btVector3(1, 2, 3)),
AreaType_ground AreaType_ground
); );
const auto recastMesh = builder.create(); const auto recastMesh = builder.create(mGeneration, mRevision);
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,
@ -96,7 +98,7 @@ namespace
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);
RecastMeshBuilder builder(mSettings, mBounds); RecastMeshBuilder builder(mSettings, mBounds);
builder.addObject(static_cast<const btCollisionShape&>(shape), btTransform::getIdentity(), AreaType_ground); builder.addObject(static_cast<const btCollisionShape&>(shape), btTransform::getIdentity(), AreaType_ground);
const auto recastMesh = builder.create(); const auto recastMesh = builder.create(mGeneration, mRevision);
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,
@ -114,7 +116,7 @@ namespace
btBoxShape shape(btVector3(1, 1, 2)); btBoxShape shape(btVector3(1, 1, 2));
RecastMeshBuilder builder(mSettings, mBounds); RecastMeshBuilder builder(mSettings, mBounds);
builder.addObject(static_cast<const btCollisionShape&>(shape), btTransform::getIdentity(), AreaType_ground); builder.addObject(static_cast<const btCollisionShape&>(shape), btTransform::getIdentity(), AreaType_ground);
const auto recastMesh = builder.create(); const auto recastMesh = builder.create(mGeneration, mRevision);
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,
@ -161,7 +163,7 @@ namespace
btTransform::getIdentity(), btTransform::getIdentity(),
AreaType_ground AreaType_ground
); );
const auto recastMesh = builder.create(); const auto recastMesh = builder.create(mGeneration, mRevision);
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,
@ -210,7 +212,7 @@ namespace
btTransform(btMatrix3x3::getIdentity().scaled(btVector3(1, 2, 3)), btVector3(1, 2, 3)), btTransform(btMatrix3x3::getIdentity().scaled(btVector3(1, 2, 3)), btVector3(1, 2, 3)),
AreaType_ground AreaType_ground
); );
const auto recastMesh = builder.create(); const auto recastMesh = builder.create(mGeneration, mRevision);
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,
@ -234,7 +236,7 @@ namespace
btTransform(btMatrix3x3::getIdentity().scaled(btVector3(1, 2, 3)), btVector3(1, 2, 3)), btTransform(btMatrix3x3::getIdentity().scaled(btVector3(1, 2, 3)), btVector3(1, 2, 3)),
AreaType_ground AreaType_ground
); );
const auto recastMesh = builder.create(); const auto recastMesh = builder.create(mGeneration, mRevision);
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,
@ -256,7 +258,7 @@ namespace
btTransform::getIdentity(), btTransform::getIdentity(),
AreaType_ground AreaType_ground
); );
const auto recastMesh = builder.create(); const auto recastMesh = builder.create(mGeneration, mRevision);
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,
@ -284,7 +286,7 @@ namespace
btTransform::getIdentity(), btTransform::getIdentity(),
AreaType_ground AreaType_ground
); );
const auto recastMesh = builder.create(); const auto recastMesh = builder.create(mGeneration, mRevision);
EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({ EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({
-0.2f, 0, -0.3f, -0.2f, 0, -0.3f,
-0.3f, 0, -0.2f, -0.3f, 0, -0.2f,
@ -309,7 +311,7 @@ namespace
static_cast<btScalar>(-osg::PI_4))), static_cast<btScalar>(-osg::PI_4))),
AreaType_ground AreaType_ground
); );
const auto recastMesh = builder.create(); const auto recastMesh = builder.create(mGeneration, mRevision);
EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({ EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({
0, -0.70710659027099609375, -3.535533905029296875, 0, -0.70710659027099609375, -3.535533905029296875,
0, 0.707107067108154296875, -3.535533905029296875, 0, 0.707107067108154296875, -3.535533905029296875,
@ -334,7 +336,7 @@ namespace
static_cast<btScalar>(osg::PI_4))), static_cast<btScalar>(osg::PI_4))),
AreaType_ground AreaType_ground
); );
const auto recastMesh = builder.create(); const auto recastMesh = builder.create(mGeneration, mRevision);
EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({ EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({
-3.535533905029296875, -0.70710659027099609375, 0, -3.535533905029296875, -0.70710659027099609375, 0,
-3.535533905029296875, 0.707107067108154296875, 0, -3.535533905029296875, 0.707107067108154296875, 0,
@ -359,7 +361,7 @@ namespace
static_cast<btScalar>(osg::PI_4))), static_cast<btScalar>(osg::PI_4))),
AreaType_ground AreaType_ground
); );
const auto recastMesh = builder.create(); const auto recastMesh = builder.create(mGeneration, mRevision);
EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({ EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({
1.41421353816986083984375, 0, 1.1920928955078125e-07, 1.41421353816986083984375, 0, 1.1920928955078125e-07,
-1.41421353816986083984375, 0, -1.1920928955078125e-07, -1.41421353816986083984375, 0, -1.1920928955078125e-07,
@ -388,7 +390,7 @@ namespace
btTransform::getIdentity(), btTransform::getIdentity(),
AreaType_null AreaType_null
); );
const auto recastMesh = builder.create(); const auto recastMesh = builder.create(mGeneration, mRevision);
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,
@ -405,7 +407,7 @@ namespace
{ {
RecastMeshBuilder builder(mSettings, mBounds); RecastMeshBuilder builder(mSettings, mBounds);
builder.addWater(1000, btTransform(btMatrix3x3::getIdentity(), btVector3(100, 200, 300))); builder.addWater(1000, btTransform(btMatrix3x3::getIdentity(), btVector3(100, 200, 300)));
const auto recastMesh = builder.create(); const auto recastMesh = builder.create(mGeneration, mRevision);
EXPECT_EQ(recastMesh->getWater(), std::vector<RecastMesh::Water>({ EXPECT_EQ(recastMesh->getWater(), std::vector<RecastMesh::Water>({
RecastMesh::Water {1000, btTransform(btMatrix3x3::getIdentity(), btVector3(100, 200, 300))} RecastMesh::Water {1000, btTransform(btMatrix3x3::getIdentity(), btVector3(100, 200, 300))}
})); }));

@ -51,7 +51,7 @@ add_component_dir (shader
add_component_dir (sceneutil add_component_dir (sceneutil
clone attach visitor util statesetupdater controller skeleton riggeometry morphgeometry lightcontroller clone attach visitor util statesetupdater controller skeleton riggeometry morphgeometry lightcontroller
lightmanager lightutil positionattitudetransform workqueue unrefqueue pathgridutil waterutil writescene serialize optimizer lightmanager lightutil positionattitudetransform workqueue unrefqueue pathgridutil waterutil writescene serialize optimizer
actorutil detourdebugdraw navmesh agentpath shadow mwshadowtechnique vismask actorutil detourdebugdraw navmesh agentpath shadow mwshadowtechnique vismask recastmesh
) )
add_component_dir (nif add_component_dir (nif

@ -327,6 +327,7 @@ namespace Compiler
extensions.registerInstruction ("toggleactorspaths", "", opcodeToggleActorsPaths); extensions.registerInstruction ("toggleactorspaths", "", opcodeToggleActorsPaths);
extensions.registerInstruction ("setnavmeshnumber", "l", opcodeSetNavMeshNumberToRender); extensions.registerInstruction ("setnavmeshnumber", "l", opcodeSetNavMeshNumberToRender);
extensions.registerFunction ("repairedonme", 'l', "S", opcodeRepairedOnMe, opcodeRepairedOnMeExplicit); extensions.registerFunction ("repairedonme", 'l', "S", opcodeRepairedOnMe, opcodeRepairedOnMeExplicit);
extensions.registerInstruction ("togglerecastmesh", "", opcodeToggleRecastMesh);
} }
} }

@ -304,6 +304,7 @@ namespace Compiler
const int opcodeSetNavMeshNumberToRender = 0x200030a; const int opcodeSetNavMeshNumberToRender = 0x200030a;
const int opcodeRepairedOnMe = 0x200030c; const int opcodeRepairedOnMe = 0x200030c;
const int opcodeRepairedOnMeExplicit = 0x200030d; const int opcodeRepairedOnMeExplicit = 0x200030d;
const int opcodeToggleRecastMesh = 0x2000310;
} }
namespace Sky namespace Sky

@ -3,8 +3,9 @@
namespace DetourNavigator namespace DetourNavigator
{ {
CachedRecastMeshManager::CachedRecastMeshManager(const Settings& settings, const TileBounds& bounds) CachedRecastMeshManager::CachedRecastMeshManager(const Settings& settings, const TileBounds& bounds,
: mImpl(settings, bounds) std::size_t generation)
: mImpl(settings, bounds, generation)
{} {}
bool CachedRecastMeshManager::addObject(const ObjectId id, const btCollisionShape& shape, bool CachedRecastMeshManager::addObject(const ObjectId id, const btCollisionShape& shape,

@ -10,7 +10,7 @@ namespace DetourNavigator
class CachedRecastMeshManager class CachedRecastMeshManager
{ {
public: public:
CachedRecastMeshManager(const Settings& settings, const TileBounds& bounds); CachedRecastMeshManager(const Settings& settings, const TileBounds& bounds, std::size_t generation);
bool addObject(const ObjectId id, const btCollisionShape& shape, const btTransform& transform, bool addObject(const ObjectId id, const btCollisionShape& shape, const btTransform& transform,
const AreaType areaType); const AreaType areaType);

@ -6,6 +6,8 @@
#include "settings.hpp" #include "settings.hpp"
#include "objectid.hpp" #include "objectid.hpp"
#include "navmeshcacheitem.hpp" #include "navmeshcacheitem.hpp"
#include "recastmesh.hpp"
#include "recastmeshtiles.hpp"
namespace DetourNavigator namespace DetourNavigator
{ {
@ -204,6 +206,8 @@ namespace DetourNavigator
*/ */
boost::optional<osg::Vec3f> findRandomPointAroundCircle(const osg::Vec3f& agentHalfExtents, boost::optional<osg::Vec3f> findRandomPointAroundCircle(const osg::Vec3f& agentHalfExtents,
const osg::Vec3f& start, const float maxRadius, const Flags includeFlags) const; const osg::Vec3f& start, const float maxRadius, const Flags includeFlags) const;
virtual RecastMeshTiles getRecastMeshTiles() = 0;
}; };
} }

@ -143,6 +143,11 @@ namespace DetourNavigator
mNavMeshManager.reportStats(frameNumber, stats); mNavMeshManager.reportStats(frameNumber, stats);
} }
RecastMeshTiles NavigatorImpl::getRecastMeshTiles()
{
return mNavMeshManager.getRecastMeshTiles();
}
void NavigatorImpl::updateAvoidShapeId(const ObjectId id, const ObjectId avoidId) void NavigatorImpl::updateAvoidShapeId(const ObjectId id, const ObjectId avoidId)
{ {
updateId(id, avoidId, mWaterIds); updateId(id, avoidId, mWaterIds);

@ -50,6 +50,8 @@ namespace DetourNavigator
void reportStats(unsigned int frameNumber, osg::Stats& stats) const override; void reportStats(unsigned int frameNumber, osg::Stats& stats) const override;
RecastMeshTiles getRecastMeshTiles() override;
private: private:
Settings mSettings; Settings mSettings;
NavMeshManager mNavMeshManager; NavMeshManager mNavMeshManager;

@ -81,6 +81,11 @@ namespace DetourNavigator
void reportStats(unsigned int /*frameNumber*/, osg::Stats& /*stats*/) const override {} void reportStats(unsigned int /*frameNumber*/, osg::Stats& /*stats*/) const override {}
RecastMeshTiles getRecastMeshTiles() override
{
return {};
}
private: private:
Settings mDefaultSettings {}; Settings mDefaultSettings {};
SharedNavMeshCacheItem mEmptyNavMeshCacheItem; SharedNavMeshCacheItem mEmptyNavMeshCacheItem;

@ -220,6 +220,17 @@ namespace DetourNavigator
mAsyncNavMeshUpdater.reportStats(frameNumber, stats); mAsyncNavMeshUpdater.reportStats(frameNumber, stats);
} }
RecastMeshTiles NavMeshManager::getRecastMeshTiles()
{
std::vector<TilePosition> tiles;
mRecastMeshManager.forEachTilePosition(
[&tiles] (const TilePosition& tile) { tiles.push_back(tile); });
RecastMeshTiles result;
std::transform(tiles.begin(), tiles.end(), std::inserter(result, result.end()),
[this] (const TilePosition& tile) { return std::make_pair(tile, mRecastMeshManager.getMesh(tile)); });
return result;
}
void NavMeshManager::addChangedTiles(const btCollisionShape& shape, const btTransform& transform, void NavMeshManager::addChangedTiles(const btCollisionShape& shape, const btTransform& transform,
const ChangeType changeType) const ChangeType changeType)
{ {

@ -5,6 +5,7 @@
#include "cachedrecastmeshmanager.hpp" #include "cachedrecastmeshmanager.hpp"
#include "offmeshconnectionsmanager.hpp" #include "offmeshconnectionsmanager.hpp"
#include "sharednavmesh.hpp" #include "sharednavmesh.hpp"
#include "recastmeshtiles.hpp"
#include <BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h> #include <BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h>
@ -52,6 +53,8 @@ namespace DetourNavigator
void reportStats(unsigned int frameNumber, osg::Stats& stats) const; void reportStats(unsigned int frameNumber, osg::Stats& stats) const;
RecastMeshTiles getRecastMeshTiles();
private: private:
const Settings& mSettings; const Settings& mSettings;
TileCachedRecastMeshManager mRecastMeshManager; TileCachedRecastMeshManager mRecastMeshManager;

@ -5,9 +5,11 @@
namespace DetourNavigator namespace DetourNavigator
{ {
RecastMesh::RecastMesh(std::vector<int> indices, std::vector<float> vertices, std::vector<AreaType> areaTypes, RecastMesh::RecastMesh(std::size_t generation, std::size_t revision, std::vector<int> indices, std::vector<float> vertices,
std::vector<Water> water, const std::size_t trianglesPerChunk) std::vector<AreaType> areaTypes, std::vector<Water> water, const std::size_t trianglesPerChunk)
: mIndices(std::move(indices)) : mGeneration(generation)
, mRevision(revision)
, mIndices(std::move(indices))
, mVertices(std::move(vertices)) , mVertices(std::move(vertices))
, mAreaTypes(std::move(areaTypes)) , mAreaTypes(std::move(areaTypes))
, mWater(std::move(water)) , mWater(std::move(water))

@ -24,8 +24,18 @@ namespace DetourNavigator
btTransform mTransform; btTransform mTransform;
}; };
RecastMesh(std::vector<int> indices, std::vector<float> vertices, std::vector<AreaType> areaTypes, RecastMesh(std::size_t generation, std::size_t revision, std::vector<int> indices, std::vector<float> vertices,
std::vector<Water> water, const std::size_t trianglesPerChunk); std::vector<AreaType> areaTypes, std::vector<Water> water, const std::size_t trianglesPerChunk);
std::size_t getGeneration() const
{
return mGeneration;
}
std::size_t getRevision() const
{
return mRevision;
}
const std::vector<int>& getIndices() const const std::vector<int>& getIndices() const
{ {
@ -68,6 +78,8 @@ namespace DetourNavigator
} }
private: private:
std::size_t mGeneration;
std::size_t mRevision;
std::vector<int> mIndices; std::vector<int> mIndices;
std::vector<float> mVertices; std::vector<float> mVertices;
std::vector<AreaType> mAreaTypes; std::vector<AreaType> mAreaTypes;

@ -112,9 +112,10 @@ namespace DetourNavigator
mWater.push_back(RecastMesh::Water {cellSize, transform}); mWater.push_back(RecastMesh::Water {cellSize, transform});
} }
std::shared_ptr<RecastMesh> RecastMeshBuilder::create() const std::shared_ptr<RecastMesh> RecastMeshBuilder::create(std::size_t generation, std::size_t revision) const
{ {
return std::make_shared<RecastMesh>(mIndices, mVertices, mAreaTypes, mWater, mSettings.get().mTrianglesPerChunk); return std::make_shared<RecastMesh>(generation, revision, mIndices, mVertices, mAreaTypes,
mWater, mSettings.get().mTrianglesPerChunk);
} }
void RecastMeshBuilder::reset() void RecastMeshBuilder::reset()

@ -34,7 +34,7 @@ namespace DetourNavigator
void addWater(const int mCellSize, const btTransform& transform); void addWater(const int mCellSize, const btTransform& transform);
std::shared_ptr<RecastMesh> create() const; std::shared_ptr<RecastMesh> create(std::size_t generation, std::size_t revision) const;
void reset(); void reset();

@ -4,8 +4,8 @@
namespace DetourNavigator namespace DetourNavigator
{ {
RecastMeshManager::RecastMeshManager(const Settings& settings, const TileBounds& bounds) RecastMeshManager::RecastMeshManager(const Settings& settings, const TileBounds& bounds, std::size_t generation)
: mShouldRebuild(false) : mGeneration(generation)
, mMeshBuilder(settings, bounds) , mMeshBuilder(settings, bounds)
{ {
} }
@ -19,8 +19,8 @@ namespace DetourNavigator
mObjectsOrder.erase(iterator); mObjectsOrder.erase(iterator);
return false; return false;
} }
mShouldRebuild = true; ++mRevision;
return mShouldRebuild; return true;
} }
bool RecastMeshManager::updateObject(const ObjectId id, const btTransform& transform, const AreaType areaType) bool RecastMeshManager::updateObject(const ObjectId id, const btTransform& transform, const AreaType areaType)
@ -30,8 +30,8 @@ namespace DetourNavigator
return false; return false;
if (!object->second->update(transform, areaType)) if (!object->second->update(transform, areaType))
return false; return false;
mShouldRebuild = true; ++mRevision;
return mShouldRebuild; return true;
} }
boost::optional<RemovedRecastMeshObject> RecastMeshManager::removeObject(const ObjectId id) boost::optional<RemovedRecastMeshObject> RecastMeshManager::removeObject(const ObjectId id)
@ -42,7 +42,7 @@ namespace DetourNavigator
const RemovedRecastMeshObject result {object->second->getShape(), object->second->getTransform()}; const RemovedRecastMeshObject result {object->second->getShape(), object->second->getTransform()};
mObjectsOrder.erase(object->second); mObjectsOrder.erase(object->second);
mObjects.erase(object); mObjects.erase(object);
mShouldRebuild = true; ++mRevision;
return result; return result;
} }
@ -55,7 +55,7 @@ namespace DetourNavigator
mWaterOrder.erase(iterator); mWaterOrder.erase(iterator);
return false; return false;
} }
mShouldRebuild = true; ++mRevision;
return true; return true;
} }
@ -64,7 +64,7 @@ namespace DetourNavigator
const auto water = mWater.find(cellPosition); const auto water = mWater.find(cellPosition);
if (water == mWater.end()) if (water == mWater.end())
return boost::none; return boost::none;
mShouldRebuild = true; ++mRevision;
const auto result = *water->second; const auto result = *water->second;
mWaterOrder.erase(water->second); mWaterOrder.erase(water->second);
mWater.erase(water); mWater.erase(water);
@ -74,7 +74,7 @@ namespace DetourNavigator
std::shared_ptr<RecastMesh> RecastMeshManager::getMesh() std::shared_ptr<RecastMesh> RecastMeshManager::getMesh()
{ {
rebuild(); rebuild();
return mMeshBuilder.create(); return mMeshBuilder.create(mGeneration, mLastBuildRevision);
} }
bool RecastMeshManager::isEmpty() const bool RecastMeshManager::isEmpty() const
@ -84,13 +84,13 @@ namespace DetourNavigator
void RecastMeshManager::rebuild() void RecastMeshManager::rebuild()
{ {
if (!mShouldRebuild) if (mLastBuildRevision == mRevision)
return; return;
mMeshBuilder.reset(); mMeshBuilder.reset();
for (const auto& v : mWaterOrder) for (const auto& v : mWaterOrder)
mMeshBuilder.addWater(v.mCellSize, v.mTransform); mMeshBuilder.addWater(v.mCellSize, v.mTransform);
for (const auto& v : mObjectsOrder) for (const auto& v : mObjectsOrder)
mMeshBuilder.addObject(v.getShape(), v.getTransform(), v.getAreaType()); mMeshBuilder.addObject(v.getShape(), v.getTransform(), v.getAreaType());
mShouldRebuild = false; mLastBuildRevision = mRevision;
} }
} }

@ -34,7 +34,7 @@ namespace DetourNavigator
btTransform mTransform; btTransform mTransform;
}; };
RecastMeshManager(const Settings& settings, const TileBounds& bounds); RecastMeshManager(const Settings& settings, const TileBounds& bounds, std::size_t generation);
bool addObject(const ObjectId id, const btCollisionShape& shape, const btTransform& transform, bool addObject(const ObjectId id, const btCollisionShape& shape, const btTransform& transform,
const AreaType areaType); const AreaType areaType);
@ -52,7 +52,9 @@ namespace DetourNavigator
bool isEmpty() const; bool isEmpty() const;
private: private:
bool mShouldRebuild; std::size_t mRevision = 0;
std::size_t mLastBuildRevision = 0;
std::size_t mGeneration;
RecastMeshBuilder mMeshBuilder; RecastMeshBuilder mMeshBuilder;
std::list<RecastMeshObject> mObjectsOrder; std::list<RecastMeshObject> mObjectsOrder;
std::unordered_map<ObjectId, std::list<RecastMeshObject>::iterator> mObjects; std::unordered_map<ObjectId, std::list<RecastMeshObject>::iterator> mObjects;

@ -0,0 +1,15 @@
#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_RECASTMESHTILE_H
#define OPENMW_COMPONENTS_DETOURNAVIGATOR_RECASTMESHTILE_H
#include "tileposition.hpp"
#include "recastmesh.hpp"
#include <map>
#include <memory>
namespace DetourNavigator
{
using RecastMeshTiles = std::map<TilePosition, std::shared_ptr<RecastMesh>>;
}
#endif

@ -121,7 +121,7 @@ namespace DetourNavigator
tileBounds.mMin -= osg::Vec2f(border, border); tileBounds.mMin -= osg::Vec2f(border, border);
tileBounds.mMax += osg::Vec2f(border, border); tileBounds.mMax += osg::Vec2f(border, border);
tile = tiles->insert(std::make_pair(tilePosition, tile = tiles->insert(std::make_pair(tilePosition,
CachedRecastMeshManager(mSettings, tileBounds))).first; CachedRecastMeshManager(mSettings, tileBounds, mTilesGeneration))).first;
} }
if (tile->second.addWater(cellPosition, cellSize, transform)) if (tile->second.addWater(cellPosition, cellSize, transform))
{ {
@ -151,7 +151,10 @@ namespace DetourNavigator
continue; continue;
const auto tileResult = tile->second.removeWater(cellPosition); const auto tileResult = tile->second.removeWater(cellPosition);
if (tile->second.isEmpty()) if (tile->second.isEmpty())
{
tiles->erase(tile); tiles->erase(tile);
++mTilesGeneration;
}
if (tileResult && !result) if (tileResult && !result)
result = tileResult; result = tileResult;
} }
@ -189,7 +192,8 @@ namespace DetourNavigator
auto tileBounds = makeTileBounds(mSettings, tilePosition); auto tileBounds = makeTileBounds(mSettings, tilePosition);
tileBounds.mMin -= osg::Vec2f(border, border); tileBounds.mMin -= osg::Vec2f(border, border);
tileBounds.mMax += osg::Vec2f(border, border); tileBounds.mMax += osg::Vec2f(border, border);
tile = tiles.insert(std::make_pair(tilePosition, CachedRecastMeshManager(mSettings, tileBounds))).first; tile = tiles.insert(std::make_pair(
tilePosition, CachedRecastMeshManager(mSettings, tileBounds, mTilesGeneration))).first;
} }
return tile->second.addObject(id, shape, transform, areaType); return tile->second.addObject(id, shape, transform, areaType);
} }
@ -209,7 +213,10 @@ namespace DetourNavigator
return boost::optional<RemovedRecastMeshObject>(); return boost::optional<RemovedRecastMeshObject>();
const auto tileResult = tile->second.removeObject(id); const auto tileResult = tile->second.removeObject(id);
if (tile->second.isEmpty()) if (tile->second.isEmpty())
{
tiles.erase(tile); tiles.erase(tile);
++mTilesGeneration;
}
return tileResult; return tileResult;
} }
} }

@ -48,6 +48,7 @@ namespace DetourNavigator
std::unordered_map<ObjectId, std::set<TilePosition>> mObjectsTilesPositions; std::unordered_map<ObjectId, std::set<TilePosition>> mObjectsTilesPositions;
std::map<osg::Vec2i, std::vector<TilePosition>> mWaterTilesPositions; std::map<osg::Vec2i, std::vector<TilePosition>> mWaterTilesPositions;
std::size_t mRevision = 0; std::size_t mRevision = 0;
std::size_t mTilesGeneration = 0;
bool addTile(const ObjectId id, const btCollisionShape& shape, const btTransform& transform, bool addTile(const ObjectId id, const btCollisionShape& shape, const btTransform& transform,
const AreaType areaType, const TilePosition& tilePosition, float border, const AreaType areaType, const TilePosition& tilePosition, float border,

@ -50,10 +50,8 @@ namespace SceneUtil
mDepthMask = state; mDepthMask = state;
} }
void DebugDraw::texture(bool state) void DebugDraw::texture(bool)
{ {
if (state)
throw std::logic_error("DebugDraw does not support textures (at " __FILE__ ":" OPENMW_LINE_STRING ")");
} }
void DebugDraw::begin(osg::PrimitiveSet::Mode mode, float size) void DebugDraw::begin(osg::PrimitiveSet::Mode mode, float size)
@ -85,9 +83,10 @@ namespace SceneUtil
vertex(pos[0], pos[1], pos[2], color, uv[0], uv[1]); vertex(pos[0], pos[1], pos[2], color, uv[0], uv[1]);
} }
void DebugDraw::vertex(const float, const float, const float, unsigned, const float, const float) void DebugDraw::vertex(const float x, const float y, const float z, unsigned color, const float, const float)
{ {
throw std::logic_error("Not implemented (at " __FILE__ ":" OPENMW_LINE_STRING ")"); addVertex(osg::Vec3f(x, y, z));
addColor(SceneUtil::colourFromRGBA(color));
} }
void DebugDraw::end() void DebugDraw::end()

@ -0,0 +1,48 @@
#include "navmesh.hpp"
#include "detourdebugdraw.hpp"
#include <components/detournavigator/settings.hpp>
#include <components/detournavigator/recastmesh.hpp>
#include <RecastDebugDraw.h>
#include <osg/Group>
namespace
{
std::vector<float> calculateNormals(const std::vector<float>& vertices, const std::vector<int>& indices)
{
std::vector<float> result(indices.size());
for (std::size_t i = 0, n = indices.size(); i < n; i += 3)
{
const float* v0_ptr = &vertices[indices[i] * 3];
const float* v1_ptr = &vertices[indices[i + 1] * 3];
const float* v2_ptr = &vertices[indices[i + 2] * 3];
const osg::Vec3f v0(v0_ptr[0], v0_ptr[1], v0_ptr[2]);
const osg::Vec3f v1(v1_ptr[0], v1_ptr[1], v1_ptr[2]);
const osg::Vec3f v2(v2_ptr[0], v2_ptr[1], v2_ptr[2]);
const osg::Vec3f e0 = v1 - v0;
const osg::Vec3f e1 = v2 - v0;
osg::Vec3f normal = e0 ^ e1;
normal.normalize();
for (std::size_t j = 0; j < 3; ++j)
result[i + j] = normal[j];
}
return result;
}
}
namespace SceneUtil
{
osg::ref_ptr<osg::Group> createRecastMeshGroup(const DetourNavigator::RecastMesh& recastMesh,
const DetourNavigator::Settings& settings)
{
const osg::ref_ptr<osg::Group> group(new osg::Group);
DebugDraw debugDraw(*group, osg::Vec3f(0, 0, 0), 1.0f / settings.mRecastScaleFactor);
const auto normals = calculateNormals(recastMesh.getVertices(), recastMesh.getIndices());
const auto texScale = 1.0f / (settings.mCellSize * 10.0f);
duDebugDrawTriMesh(&debugDraw, recastMesh.getVertices().data(), recastMesh.getVerticesCount(),
recastMesh.getIndices().data(), normals.data(), recastMesh.getTrianglesCount(), nullptr, texScale);
return group;
}
}

@ -0,0 +1,23 @@
#ifndef OPENMW_COMPONENTS_SCENEUTIL_RECASTMESH_H
#define OPENMW_COMPONENTS_SCENEUTIL_RECASTMESH_H
#include <osg/ref_ptr>
namespace osg
{
class Group;
}
namespace DetourNavigator
{
class RecastMesh;
struct Settings;
}
namespace SceneUtil
{
osg::ref_ptr<osg::Group> createRecastMeshGroup(const DetourNavigator::RecastMesh& recastMesh,
const DetourNavigator::Settings& settings);
}
#endif

@ -166,6 +166,20 @@ Make visible all NPC's and creaure's plans where they are going.
Works even if Navigator is disabled. Works even if Navigator is disabled.
Potentially decreases performance. Potentially decreases performance.
enable recast mesh render
----------------------
:Type: boolean
:Range: True/False
:Default: False
Render recast mesh that is built as set of culled tiles from physical mesh.
Should show similar mesh to physical one.
Little difference can be a result of floating point error.
Absent pieces usually mean a bug in recast mesh tiles building.
Allows to do in-game debug.
Potentially decreases performance.
Expert settings Expert settings
*************** ***************

@ -748,6 +748,9 @@ enable nav mesh render = false
# Render agents paths (true, false) # Render agents paths (true, false)
enable agents paths render = false enable agents paths render = false
# Render recast mesh (true, false)
enable recast mesh render = false
# Max number of navmesh tiles (value >= 0) # Max number of navmesh tiles (value >= 0)
max tiles number = 512 max tiles number = 512

Loading…
Cancel
Save