Support recast mesh rendering

pull/2619/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
creatureanimation effectmanager util renderinginterface pathgrid rendermode weaponanimation
bulletdebugdraw globalmap characterpreview camera localmap water terrainstorage ripplesimulation
renderbin actoranimation landmanager navmesh actorspaths
renderbin actoranimation landmanager navmesh actorspaths recastmesh
)
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 "navmesh.hpp"
#include "actorspaths.hpp"
#include "recastmesh.hpp"
namespace
{
@ -260,6 +261,7 @@ namespace MWRender
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")));
mRecastMesh.reset(new RecastMesh(mRootNode, Settings::Manager::getBool("enable recast mesh render", "Navigator")));
mPathgrid.reset(new Pathgrid(mRootNode));
mObjects.reset(new Objects(mResourceSystem, sceneRoot, mUnrefQueue.get()));
@ -585,6 +587,10 @@ namespace MWRender
{
return mActorsPaths->toggle();
}
else if (mode == Render_RecastMesh)
{
return mRecastMesh->toggle();
}
return false;
}
@ -651,6 +657,7 @@ namespace MWRender
}
updateNavMesh();
updateRecastMesh();
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 NavMesh;
class ActorsPaths;
class RecastMesh;
class RenderingManager : public MWRender::RenderingInterface
{
@ -246,6 +247,8 @@ namespace MWRender
void updateNavMesh();
void updateRecastMesh();
osg::ref_ptr<osgUtil::IntersectionVisitor> getIntersectionVisitor(osgUtil::Intersector* intersector, bool ignorePlayer, bool ignoreActors);
osg::ref_ptr<osgUtil::IntersectionVisitor> mIntersectionVisitor;
@ -264,6 +267,7 @@ namespace MWRender
std::unique_ptr<NavMesh> mNavMesh;
std::size_t mNavMeshNumber = 0;
std::unique_ptr<ActorsPaths> mActorsPaths;
std::unique_ptr<RecastMesh> mRecastMesh;
std::unique_ptr<Pathgrid> mPathgrid;
std::unique_ptr<Objects> mObjects;
std::unique_ptr<Water> mWater;

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

@ -463,5 +463,6 @@ op 0x200030c: RepairedOnMe
op 0x200030d: RepairedOnMe, explicit
op 0x200030e: TestCells
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)
{
interpreter.installSegment5 (Compiler::Misc::opcodeXBox, new OpXBox);
@ -1545,6 +1559,7 @@ namespace MWScript
interpreter.installSegment5 (Compiler::Misc::opcodeSetNavMeshNumberToRender, new OpSetNavMeshNumberToRender);
interpreter.installSegment5 (Compiler::Misc::opcodeRepairedOnMe, new OpRepairedOnMe<ImplicitRef>);
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 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<float> mVertices {{0, 0, 0, 1, 0, 0, 1, 1, 0}};
const std::vector<AreaType> mAreaTypes {1, AreaType_ground};
const std::vector<RecastMesh::Water> mWater {};
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 {};
unsigned char* const mData = reinterpret_cast<unsigned char*>(dtAlloc(1, DT_ALLOC_PERM));
NavMeshData mNavMeshData {mData, 1};
@ -128,7 +131,8 @@ namespace
const std::size_t maxSize = 1;
NavMeshTilesCache cache(maxSize);
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));
EXPECT_FALSE(cache.get(mAgentHalfExtents, mTilePosition, unexistentRecastMesh, mOffMeshConnections));
@ -142,7 +146,8 @@ namespace
NavMeshTilesCache cache(maxSize);
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));
NavMeshData anotherNavMeshData {anotherData, 1};
@ -162,7 +167,8 @@ namespace
NavMeshTilesCache cache(maxSize);
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));
NavMeshData anotherNavMeshData {anotherData, 1};
@ -180,14 +186,14 @@ namespace
NavMeshTilesCache cache(maxSize);
const std::vector<RecastMesh::Water> leastRecentlySetWater {1, RecastMesh::Water {1, btTransform::getIdentity()}};
const RecastMesh leastRecentlySetRecastMesh {mIndices, mVertices, mAreaTypes, leastRecentlySetWater,
mTrianglesPerChunk};
const RecastMesh leastRecentlySetRecastMesh {mGeneration, mRevision, mIndices, mVertices,
mAreaTypes, leastRecentlySetWater, mTrianglesPerChunk};
const auto leastRecentlySetData = reinterpret_cast<unsigned char*>(dtAlloc(1, DT_ALLOC_PERM));
NavMeshData leastRecentlySetNavMeshData {leastRecentlySetData, 1};
const std::vector<RecastMesh::Water> mostRecentlySetWater {1, RecastMesh::Water {2, btTransform::getIdentity()}};
const RecastMesh mostRecentlySetRecastMesh {mIndices, mVertices, mAreaTypes, mostRecentlySetWater,
mTrianglesPerChunk};
const RecastMesh mostRecentlySetRecastMesh {mGeneration, mRevision, mIndices, mVertices,
mAreaTypes, mostRecentlySetWater, mTrianglesPerChunk};
const auto mostRecentlySetData = reinterpret_cast<unsigned char*>(dtAlloc(1, DT_ALLOC_PERM));
NavMeshData mostRecentlySetNavMeshData {mostRecentlySetData, 1};
@ -212,14 +218,14 @@ namespace
NavMeshTilesCache cache(maxSize);
const std::vector<RecastMesh::Water> leastRecentlyUsedWater {1, RecastMesh::Water {1, btTransform::getIdentity()}};
const RecastMesh leastRecentlyUsedRecastMesh {mIndices, mVertices, mAreaTypes, leastRecentlyUsedWater,
mTrianglesPerChunk};
const RecastMesh leastRecentlyUsedRecastMesh {mGeneration, mRevision, mIndices, mVertices,
mAreaTypes, leastRecentlyUsedWater, mTrianglesPerChunk};
const auto leastRecentlyUsedData = reinterpret_cast<unsigned char*>(dtAlloc(1, DT_ALLOC_PERM));
NavMeshData leastRecentlyUsedNavMeshData {leastRecentlyUsedData, 1};
const std::vector<RecastMesh::Water> mostRecentlyUsedWater {1, RecastMesh::Water {2, btTransform::getIdentity()}};
const RecastMesh mostRecentlyUsedRecastMesh {mIndices, mVertices, mAreaTypes, mostRecentlyUsedWater,
mTrianglesPerChunk};
const RecastMesh mostRecentlyUsedRecastMesh {mGeneration, mRevision, mIndices, mVertices,
mAreaTypes, mostRecentlyUsedWater, mTrianglesPerChunk};
const auto mostRecentlyUsedData = reinterpret_cast<unsigned char*>(dtAlloc(1, DT_ALLOC_PERM));
NavMeshData mostRecentlyUsedNavMeshData {mostRecentlyUsedData, 1};
@ -256,7 +262,7 @@ namespace
NavMeshTilesCache cache(maxSize);
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));
NavMeshData tooLargeNavMeshData {tooLargeData, 2};
@ -275,12 +281,13 @@ namespace
NavMeshTilesCache cache(maxSize);
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));
NavMeshData anotherNavMeshData {anotherData, 1};
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));
NavMeshData tooLargeNavMeshData {tooLargeData, 2};
@ -303,7 +310,8 @@ namespace
NavMeshTilesCache cache(maxSize);
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));
NavMeshData anotherNavMeshData {anotherData, 1};
@ -326,7 +334,7 @@ namespace
NavMeshTilesCache cache(maxSize);
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));
NavMeshData anotherNavMeshData {anotherData, 1};

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

@ -51,7 +51,7 @@ add_component_dir (shader
add_component_dir (sceneutil
clone attach visitor util statesetupdater controller skeleton riggeometry morphgeometry lightcontroller
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

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

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

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

@ -10,7 +10,7 @@ namespace DetourNavigator
class CachedRecastMeshManager
{
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,
const AreaType areaType);

@ -6,6 +6,8 @@
#include "settings.hpp"
#include "objectid.hpp"
#include "navmeshcacheitem.hpp"
#include "recastmesh.hpp"
#include "recastmeshtiles.hpp"
namespace DetourNavigator
{
@ -204,6 +206,8 @@ namespace DetourNavigator
*/
boost::optional<osg::Vec3f> findRandomPointAroundCircle(const osg::Vec3f& agentHalfExtents,
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);
}
RecastMeshTiles NavigatorImpl::getRecastMeshTiles()
{
return mNavMeshManager.getRecastMeshTiles();
}
void NavigatorImpl::updateAvoidShapeId(const ObjectId id, const ObjectId avoidId)
{
updateId(id, avoidId, mWaterIds);

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

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

@ -220,6 +220,17 @@ namespace DetourNavigator
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,
const ChangeType changeType)
{

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

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

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

@ -112,9 +112,10 @@ namespace DetourNavigator
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()

@ -34,7 +34,7 @@ namespace DetourNavigator
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();

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

@ -34,7 +34,7 @@ namespace DetourNavigator
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,
const AreaType areaType);
@ -52,7 +52,9 @@ namespace DetourNavigator
bool isEmpty() const;
private:
bool mShouldRebuild;
std::size_t mRevision = 0;
std::size_t mLastBuildRevision = 0;
std::size_t mGeneration;
RecastMeshBuilder mMeshBuilder;
std::list<RecastMeshObject> mObjectsOrder;
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.mMax += osg::Vec2f(border, border);
tile = tiles->insert(std::make_pair(tilePosition,
CachedRecastMeshManager(mSettings, tileBounds))).first;
CachedRecastMeshManager(mSettings, tileBounds, mTilesGeneration))).first;
}
if (tile->second.addWater(cellPosition, cellSize, transform))
{
@ -151,7 +151,10 @@ namespace DetourNavigator
continue;
const auto tileResult = tile->second.removeWater(cellPosition);
if (tile->second.isEmpty())
{
tiles->erase(tile);
++mTilesGeneration;
}
if (tileResult && !result)
result = tileResult;
}
@ -189,7 +192,8 @@ namespace DetourNavigator
auto tileBounds = makeTileBounds(mSettings, tilePosition);
tileBounds.mMin -= 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);
}
@ -209,7 +213,10 @@ namespace DetourNavigator
return boost::optional<RemovedRecastMeshObject>();
const auto tileResult = tile->second.removeObject(id);
if (tile->second.isEmpty())
{
tiles.erase(tile);
++mTilesGeneration;
}
return tileResult;
}
}

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

@ -50,10 +50,8 @@ namespace SceneUtil
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)
@ -85,9 +83,10 @@ namespace SceneUtil
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()

@ -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.
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
***************

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

Loading…
Cancel
Save