diff --git a/apps/openmw/mwrender/navmesh.cpp b/apps/openmw/mwrender/navmesh.cpp index 548e85ad10..a3c26aeb59 100644 --- a/apps/openmw/mwrender/navmesh.cpp +++ b/apps/openmw/mwrender/navmesh.cpp @@ -4,8 +4,11 @@ #include #include #include +#include +#include #include +#include #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" @@ -16,10 +19,10 @@ namespace MWRender { NavMesh::NavMesh(const osg::ref_ptr& root, bool enabled) : mRootNode(root) + , mGroupStateSet(SceneUtil::makeNavMeshTileStateSet()) + , mDebugDrawStateSet(SceneUtil::DebugDraw::makeStateSet()) , mEnabled(enabled) , mId(std::numeric_limits::max()) - , mGeneration(0) - , mRevision(0) { } @@ -39,46 +42,71 @@ namespace MWRender return mEnabled; } - void NavMesh::update(const dtNavMesh& navMesh, const std::size_t id, - const std::size_t generation, const std::size_t revision, const DetourNavigator::Settings& settings) + void NavMesh::update(const DetourNavigator::NavMeshCacheItem& navMesh, std::size_t id, + const DetourNavigator::Settings& settings) { - if (!mEnabled || (mGroup && mId == id && mGeneration == generation && mRevision == revision)) + using DetourNavigator::TilePosition; + using DetourNavigator::Version; + + if (!mEnabled || (!mTiles.empty() && mId == id && mVersion == navMesh.getVersion())) return; - mId = id; - mGeneration = generation; - mRevision = revision; - if (mGroup) - mRootNode->removeChild(mGroup); - mGroup = SceneUtil::createNavMeshGroup(navMesh, settings); - if (mGroup) + if (mId != id) { - MWBase::Environment::get().getResourceSystem()->getSceneManager()->recreateShaders(mGroup, "debug"); - mGroup->setNodeMask(Mask_Debug); - mRootNode->addChild(mGroup); + reset(); + mId = id; + } + + mVersion = navMesh.getVersion(); + + std::vector updated; + navMesh.forEachUsedTile([&] (const TilePosition& position, const Version& version, const dtMeshTile& meshTile) + { + updated.push_back(position); + Tile& tile = mTiles[position]; + if (tile.mGroup != nullptr && tile.mVersion == version) + return; + if (tile.mGroup != nullptr) + mRootNode->removeChild(tile.mGroup); + tile.mGroup = SceneUtil::createNavMeshTileGroup(navMesh.getImpl(), meshTile, settings, + mGroupStateSet, mDebugDrawStateSet); + if (tile.mGroup == nullptr) + return; + MWBase::Environment::get().getResourceSystem()->getSceneManager()->recreateShaders(tile.mGroup, "debug"); + tile.mGroup->setNodeMask(Mask_Debug); + mRootNode->addChild(tile.mGroup); + }); + std::sort(updated.begin(), updated.end()); + for (auto it = mTiles.begin(); it != mTiles.end();) + { + if (!std::binary_search(updated.begin(), updated.end(), it->first)) + { + mRootNode->removeChild(it->second.mGroup); + it = mTiles.erase(it); + } + else + ++it; } } void NavMesh::reset() { - if (mGroup) - { - mRootNode->removeChild(mGroup); - mGroup = nullptr; - } + for (auto& [position, tile] : mTiles) + mRootNode->removeChild(tile.mGroup); + mTiles.clear(); } void NavMesh::enable() { - if (mGroup) - mRootNode->addChild(mGroup); + for (const auto& [position, tile] : mTiles) + mRootNode->addChild(tile.mGroup); mEnabled = true; } void NavMesh::disable() { - if (mGroup) - mRootNode->removeChild(mGroup); + for (const auto& [position, tile] : mTiles) + mRootNode->removeChild(tile.mGroup); mEnabled = false; } } diff --git a/apps/openmw/mwrender/navmesh.hpp b/apps/openmw/mwrender/navmesh.hpp index 4c71dc74ec..fd69a3e487 100644 --- a/apps/openmw/mwrender/navmesh.hpp +++ b/apps/openmw/mwrender/navmesh.hpp @@ -1,9 +1,13 @@ #ifndef OPENMW_MWRENDER_NAVMESH_H #define OPENMW_MWRENDER_NAVMESH_H +#include +#include + #include #include +#include class dtNavMesh; @@ -11,10 +15,12 @@ namespace osg { class Group; class Geometry; + class StateSet; } namespace DetourNavigator { + class NavMeshCacheItem; struct Settings; } @@ -28,8 +34,8 @@ namespace MWRender bool toggle(); - void update(const dtNavMesh& navMesh, const std::size_t number, const std::size_t generation, - const std::size_t revision, const DetourNavigator::Settings& settings); + void update(const DetourNavigator::NavMeshCacheItem& navMesh, std::size_t id, + const DetourNavigator::Settings& settings); void reset(); @@ -43,12 +49,19 @@ namespace MWRender } private: + struct Tile + { + DetourNavigator::Version mVersion; + osg::ref_ptr mGroup; + }; + osg::ref_ptr mRootNode; + osg::ref_ptr mGroupStateSet; + osg::ref_ptr mDebugDrawStateSet; bool mEnabled; std::size_t mId; - std::size_t mGeneration; - std::size_t mRevision; - osg::ref_ptr mGroup; + DetourNavigator::Version mVersion; + std::map mTiles; }; } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 52b8a58213..73ee40435e 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -1378,9 +1378,7 @@ namespace MWRender { try { - const auto locked = it->second->lockConst(); - mNavMesh->update(locked->getImpl(), mNavMeshNumber, locked->getGeneration(), - locked->getNavMeshRevision(), mNavigator.getSettings()); + mNavMesh->update(*it->second->lockConst(), mNavMeshNumber, mNavigator.getSettings()); } catch (const std::exception& e) { diff --git a/apps/openmw_test_suite/detournavigator/navigator.cpp b/apps/openmw_test_suite/detournavigator/navigator.cpp index d7422cda8b..0f4f1e3345 100644 --- a/apps/openmw_test_suite/detournavigator/navigator.cpp +++ b/apps/openmw_test_suite/detournavigator/navigator.cpp @@ -990,8 +990,7 @@ namespace ASSERT_EQ(navMeshes.size(), 1); { const auto navMesh = navMeshes.begin()->second->lockConst(); - ASSERT_EQ(navMesh->getGeneration(), 1); - ASSERT_EQ(navMesh->getNavMeshRevision(), 4); + ASSERT_EQ(navMesh->getVersion(), (Version {1, 4})); } for (int n = 0; n < 10; ++n) @@ -1006,8 +1005,7 @@ namespace ASSERT_EQ(navMeshes.size(), 1); { const auto navMesh = navMeshes.begin()->second->lockConst(); - ASSERT_EQ(navMesh->getGeneration(), 1); - ASSERT_EQ(navMesh->getNavMeshRevision(), 4); + ASSERT_EQ(navMesh->getVersion(), (Version {1, 4})); } } diff --git a/components/detournavigator/asyncnavmeshupdater.cpp b/components/detournavigator/asyncnavmeshupdater.cpp index 966e07bc5a..583fd1162a 100644 --- a/components/detournavigator/asyncnavmeshupdater.cpp +++ b/components/detournavigator/asyncnavmeshupdater.cpp @@ -311,12 +311,7 @@ namespace DetourNavigator if (recastMesh != nullptr) { - Version navMeshVersion; - { - const auto locked = navMeshCacheItem->lockConst(); - navMeshVersion.mGeneration = locked->getGeneration(); - navMeshVersion.mRevision = locked->getNavMeshRevision(); - } + const Version navMeshVersion = navMeshCacheItem->lockConst()->getVersion(); mRecastMeshManager.get().reportNavMeshChange(job.mChangedTile, Version {recastMesh->getGeneration(), recastMesh->getRevision()}, navMeshVersion); @@ -339,13 +334,13 @@ namespace DetourNavigator using FloatMs = std::chrono::duration; - const auto locked = navMeshCacheItem->lockConst(); + const Version version = navMeshCacheItem->lockConst()->getVersion(); Log(Debug::Debug) << std::fixed << std::setprecision(2) << "Cache updated for agent=(" << job.mAgentHalfExtents << ")" << " tile=" << job.mChangedTile << " status=" << status << - " generation=" << locked->getGeneration() << - " revision=" << locked->getNavMeshRevision() << + " generation=" << version.mGeneration << + " revision=" << version.mRevision << " time=" << std::chrono::duration_cast(finish - start).count() << "ms" << " thread=" << std::this_thread::get_id(); diff --git a/components/detournavigator/navmeshcacheitem.cpp b/components/detournavigator/navmeshcacheitem.cpp index ee6f3308d0..decf45de2c 100644 --- a/components/detournavigator/navmeshcacheitem.cpp +++ b/components/detournavigator/navmeshcacheitem.cpp @@ -13,12 +13,6 @@ namespace { using DetourNavigator::TilePosition; - const dtMeshTile* getTile(const dtNavMesh& navMesh, const TilePosition& position) - { - const int layer = 0; - return navMesh.getTileAt(position.x(), position.y(), layer); - } - bool removeTile(dtNavMesh& navMesh, const TilePosition& position) { const int layer = 0; @@ -41,10 +35,16 @@ namespace namespace DetourNavigator { + const dtMeshTile* getTile(const dtNavMesh& navMesh, const TilePosition& position) + { + const int layer = 0; + return navMesh.getTileAt(position.x(), position.y(), layer); + } + UpdateNavMeshStatus NavMeshCacheItem::updateTile(const TilePosition& position, NavMeshTilesCache::Value&& cached, NavMeshData&& navMeshData) { - const dtMeshTile* currentTile = ::getTile(*mImpl, position); + const dtMeshTile* currentTile = getTile(*mImpl, position); if (currentTile != nullptr && asNavMeshTileConstView(*currentTile) == asNavMeshTileConstView(navMeshData.mValue.get())) { @@ -54,8 +54,19 @@ namespace DetourNavigator const auto addStatus = addTile(*mImpl, navMeshData.mValue.get(), navMeshData.mSize); if (dtStatusSucceed(addStatus)) { - mUsedTiles[position] = std::make_pair(std::move(cached), std::move(navMeshData)); - ++mNavMeshRevision; + auto tile = mUsedTiles.find(position); + if (tile == mUsedTiles.end()) + { + mUsedTiles.emplace_hint(tile, position, + Tile {Version {mVersion.mRevision, 1}, std::move(cached), std::move(navMeshData)}); + } + else + { + ++tile->second.mVersion.mRevision; + tile->second.mCached = std::move(cached); + tile->second.mData = std::move(navMeshData); + } + ++mVersion.mRevision; return UpdateNavMeshStatusBuilder().added(true).removed(removed).getResult(); } else @@ -63,7 +74,7 @@ namespace DetourNavigator if (removed) { mUsedTiles.erase(position); - ++mNavMeshRevision; + ++mVersion.mRevision; } return UpdateNavMeshStatusBuilder().removed(removed).failed((addStatus & DT_OUT_OF_MEMORY) != 0).getResult(); } @@ -75,7 +86,7 @@ namespace DetourNavigator if (removed) { mUsedTiles.erase(position); - ++mNavMeshRevision; + ++mVersion.mRevision; } return UpdateNavMeshStatusBuilder().removed(removed).getResult(); } diff --git a/components/detournavigator/navmeshcacheitem.hpp b/components/detournavigator/navmeshcacheitem.hpp index ac68caedb3..5d3b404080 100644 --- a/components/detournavigator/navmeshcacheitem.hpp +++ b/components/detournavigator/navmeshcacheitem.hpp @@ -6,6 +6,7 @@ #include "navmeshtilescache.hpp" #include "dtstatus.hpp" #include "navmeshdata.hpp" +#include "version.hpp" #include @@ -123,11 +124,14 @@ namespace DetourNavigator } }; + const dtMeshTile* getTile(const dtNavMesh& navMesh, const TilePosition& position); + class NavMeshCacheItem { public: NavMeshCacheItem(const NavMeshPtr& impl, std::size_t generation) - : mImpl(impl), mGeneration(generation), mNavMeshRevision(0) + : mImpl(impl) + , mVersion {generation, 0} { } @@ -136,26 +140,32 @@ namespace DetourNavigator return *mImpl; } - std::size_t getGeneration() const - { - return mGeneration; - } - - std::size_t getNavMeshRevision() const - { - return mNavMeshRevision; - } + const Version& getVersion() const { return mVersion; } UpdateNavMeshStatus updateTile(const TilePosition& position, NavMeshTilesCache::Value&& cached, NavMeshData&& navMeshData); UpdateNavMeshStatus removeTile(const TilePosition& position); + template + void forEachUsedTile(Function&& function) const + { + for (const auto& [position, tile] : mUsedTiles) + if (const dtMeshTile* meshTile = getTile(*mImpl, position)) + function(position, tile.mVersion, *meshTile); + } + private: + struct Tile + { + Version mVersion; + NavMeshTilesCache::Value mCached; + NavMeshData mData; + }; + NavMeshPtr mImpl; - std::size_t mGeneration; - std::size_t mNavMeshRevision; - std::map> mUsedTiles; + Version mVersion; + std::map mUsedTiles; }; using GuardedNavMeshCacheItem = Misc::ScopeGuarded; diff --git a/components/detournavigator/version.hpp b/components/detournavigator/version.hpp index c9de98459d..792680a7d5 100644 --- a/components/detournavigator/version.hpp +++ b/components/detournavigator/version.hpp @@ -8,12 +8,32 @@ namespace DetourNavigator { struct Version { - std::size_t mGeneration; - std::size_t mRevision; + std::size_t mGeneration = 0; + std::size_t mRevision = 0; + + friend inline auto tie(const Version& value) + { + return std::tie(value.mGeneration, value.mRevision); + } friend inline bool operator<(const Version& lhs, const Version& rhs) { - return std::tie(lhs.mGeneration, lhs.mRevision) < std::tie(rhs.mGeneration, rhs.mRevision); + return tie(lhs) < tie(rhs); + } + + friend inline bool operator<=(const Version& lhs, const Version& rhs) + { + return tie(lhs) <= tie(rhs); + } + + friend inline bool operator==(const Version& lhs, const Version& rhs) + { + return tie(lhs) == tie(rhs); + } + + friend inline bool operator!=(const Version& lhs, const Version& rhs) + { + return !(lhs == rhs); } }; } diff --git a/components/sceneutil/agentpath.cpp b/components/sceneutil/agentpath.cpp index 5f9b574e7d..5721110f77 100644 --- a/components/sceneutil/agentpath.cpp +++ b/components/sceneutil/agentpath.cpp @@ -43,7 +43,7 @@ namespace SceneUtil const osg::ref_ptr group(new osg::Group); - DebugDraw debugDraw(*group, osg::Vec3f(0, 0, 0), 1); + DebugDraw debugDraw(*group, DebugDraw::makeStateSet(), osg::Vec3f(0, 0, 0), 1); const auto agentRadius = halfExtents.x(); const auto agentHeight = 2.0f * halfExtents.z(); diff --git a/components/sceneutil/detourdebugdraw.cpp b/components/sceneutil/detourdebugdraw.cpp index 7ef329fc16..f1325340ea 100644 --- a/components/sceneutil/detourdebugdraw.cpp +++ b/components/sceneutil/detourdebugdraw.cpp @@ -32,19 +32,19 @@ namespace namespace SceneUtil { - DebugDraw::DebugDraw(osg::Group& group, const osg::Vec3f& shift, float recastInvertedScaleFactor) + DebugDraw::DebugDraw(osg::Group& group, const osg::ref_ptr& stateSet, + const osg::Vec3f& shift, float recastInvertedScaleFactor) : mGroup(group) + , mStateSet(stateSet) , mShift(shift) , mRecastInvertedScaleFactor(recastInvertedScaleFactor) - , mDepthMask(false) , mMode(osg::PrimitiveSet::POINTS) , mSize(1.0f) { } - void DebugDraw::depthMask(bool state) + void DebugDraw::depthMask(bool) { - mDepthMask = state; } void DebugDraw::texture(bool) @@ -56,7 +56,7 @@ namespace SceneUtil mMode = mode; mVertices = new osg::Vec3Array; mColors = new osg::Vec4Array; - mSize = size * mRecastInvertedScaleFactor; + mSize = size; } void DebugDraw::begin(duDebugDrawPrimitives prim, float size) @@ -88,16 +88,8 @@ namespace SceneUtil void DebugDraw::end() { - osg::ref_ptr stateSet(new osg::StateSet); - stateSet->setMode(GL_BLEND, osg::StateAttribute::ON); - stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF); - stateSet->setMode(GL_DEPTH, (mDepthMask ? osg::StateAttribute::ON : osg::StateAttribute::OFF)); - stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); - stateSet->setAttributeAndModes(new osg::LineWidth(mSize)); - stateSet->setAttributeAndModes(new osg::BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - osg::ref_ptr geometry(new osg::Geometry); - geometry->setStateSet(stateSet); + geometry->setStateSet(mStateSet); geometry->setVertexArray(mVertices); geometry->setColorArray(mColors, osg::Array::BIND_PER_VERTEX); geometry->addPrimitiveSet(new osg::DrawArrays(mMode, 0, static_cast(mVertices->size()))); @@ -117,4 +109,16 @@ namespace SceneUtil { mColors->push_back(value); } + + osg::ref_ptr DebugDraw::makeStateSet() + { + osg::ref_ptr stateSet = new osg::StateSet; + stateSet->setMode(GL_BLEND, osg::StateAttribute::ON); + stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF); + stateSet->setMode(GL_DEPTH, osg::StateAttribute::OFF); + stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); + stateSet->setAttributeAndModes(new osg::LineWidth()); + stateSet->setAttributeAndModes(new osg::BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); + return stateSet; + } } diff --git a/components/sceneutil/detourdebugdraw.hpp b/components/sceneutil/detourdebugdraw.hpp index 9b6a28acea..1d00735ea0 100644 --- a/components/sceneutil/detourdebugdraw.hpp +++ b/components/sceneutil/detourdebugdraw.hpp @@ -15,7 +15,10 @@ namespace SceneUtil class DebugDraw : public duDebugDraw { public: - DebugDraw(osg::Group& group, const osg::Vec3f& shift, float recastInvertedScaleFactor); + explicit DebugDraw(osg::Group& group, const osg::ref_ptr& stateSet, + const osg::Vec3f& shift, float recastInvertedScaleFactor); + + static osg::ref_ptr makeStateSet(); void depthMask(bool state) override; @@ -38,9 +41,9 @@ namespace SceneUtil private: osg::Group& mGroup; + osg::ref_ptr mStateSet; osg::Vec3f mShift; float mRecastInvertedScaleFactor; - bool mDepthMask; osg::PrimitiveSet::Mode mMode; float mSize; osg::ref_ptr mVertices; diff --git a/components/sceneutil/navmesh.cpp b/components/sceneutil/navmesh.cpp index b0f356f089..d66f95381c 100644 --- a/components/sceneutil/navmesh.cpp +++ b/components/sceneutil/navmesh.cpp @@ -1,5 +1,6 @@ #include "navmesh.hpp" #include "detourdebugdraw.hpp" +#include "depth.hpp" #include @@ -7,21 +8,256 @@ #include #include +#include + +namespace +{ + // Copied from https://github.com/recastnavigation/recastnavigation/blob/c5cbd53024c8a9d8d097a4371215e3342d2fdc87/DebugUtils/Source/DetourDebugDraw.cpp#L26-L38 + float distancePtLine2d(const float* pt, const float* p, const float* q) + { + float pqx = q[0] - p[0]; + float pqz = q[2] - p[2]; + float dx = pt[0] - p[0]; + float dz = pt[2] - p[2]; + float d = pqx*pqx + pqz*pqz; + float t = pqx*dx + pqz*dz; + if (d != 0) t /= d; + dx = p[0] + t*pqx - pt[0]; + dz = p[2] + t*pqz - pt[2]; + return dx*dx + dz*dz; + } + + // Copied from https://github.com/recastnavigation/recastnavigation/blob/c5cbd53024c8a9d8d097a4371215e3342d2fdc87/DebugUtils/Source/DetourDebugDraw.cpp#L40-L118 + void drawPolyBoundaries(duDebugDraw* dd, const dtMeshTile* tile, const unsigned int col, + const float linew, bool inner) + { + static const float thr = 0.01f*0.01f; + + dd->begin(DU_DRAW_LINES, linew); + + for (int i = 0; i < tile->header->polyCount; ++i) + { + const dtPoly* p = &tile->polys[i]; + + if (p->getType() == DT_POLYTYPE_OFFMESH_CONNECTION) continue; + + const dtPolyDetail* pd = &tile->detailMeshes[i]; + + for (int j = 0, nj = (int)p->vertCount; j < nj; ++j) + { + unsigned int c = col; + if (inner) + { + if (p->neis[j] == 0) continue; + if (p->neis[j] & DT_EXT_LINK) + { + bool con = false; + for (unsigned int k = p->firstLink; k != DT_NULL_LINK; k = tile->links[k].next) + { + if (tile->links[k].edge == j) + { + con = true; + break; + } + } + if (con) + c = duRGBA(255,255,255,48); + else + c = duRGBA(0,0,0,48); + } + else + c = duRGBA(0,48,64,32); + } + else + { + if (p->neis[j] != 0) continue; + } + + const float* v0 = &tile->verts[p->verts[j]*3]; + const float* v1 = &tile->verts[p->verts[(j+1) % nj]*3]; + + // Draw detail mesh edges which align with the actual poly edge. + // This is really slow. + for (int k = 0; k < pd->triCount; ++k) + { + const unsigned char* t = &tile->detailTris[(pd->triBase+k)*4]; + const float* tv[3]; + for (int m = 0; m < 3; ++m) + { + if (t[m] < p->vertCount) + tv[m] = &tile->verts[p->verts[t[m]]*3]; + else + tv[m] = &tile->detailVerts[(pd->vertBase+(t[m]-p->vertCount))*3]; + } + for (int m = 0, n = 2; m < 3; n=m++) + { + if ((dtGetDetailTriEdgeFlags(t[3], n) & DT_DETAIL_EDGE_BOUNDARY) == 0) + continue; + + if (distancePtLine2d(tv[n],v0,v1) < thr && + distancePtLine2d(tv[m],v0,v1) < thr) + { + dd->vertex(tv[n], c); + dd->vertex(tv[m], c); + } + } + } + } + } + dd->end(); + } + + // Based on https://github.com/recastnavigation/recastnavigation/blob/c5cbd53024c8a9d8d097a4371215e3342d2fdc87/DebugUtils/Source/DetourDebugDraw.cpp#L120-L235 + void drawMeshTile(duDebugDraw* dd, const dtNavMesh& mesh, const dtNavMeshQuery* query, + const dtMeshTile* tile, unsigned char flags) + { + dtPolyRef base = mesh.getPolyRefBase(tile); + + int tileNum = mesh.decodePolyIdTile(base); + const unsigned int tileNumColor = duIntToCol(tileNum, 128); + const unsigned alpha = tile->header->userId == 0 ? 64 : 128; + + dd->depthMask(false); + + dd->begin(DU_DRAW_TRIS); + for (int i = 0; i < tile->header->polyCount; ++i) + { + const dtPoly* p = &tile->polys[i]; + if (p->getType() == DT_POLYTYPE_OFFMESH_CONNECTION) // Skip off-mesh links. + continue; + + const dtPolyDetail* pd = &tile->detailMeshes[i]; + + unsigned int col; + if (query && query->isInClosedList(base | (dtPolyRef)i)) + col = duRGBA(255, 196, 0, alpha); + else + { + if (flags & DU_DRAWNAVMESH_COLOR_TILES) + col = duTransCol(tileNumColor, alpha); + else + col = duTransCol(dd->areaToCol(p->getArea()), alpha); + } + + for (int j = 0; j < pd->triCount; ++j) + { + const unsigned char* t = &tile->detailTris[(pd->triBase+j)*4]; + for (int k = 0; k < 3; ++k) + { + if (t[k] < p->vertCount) + dd->vertex(&tile->verts[p->verts[t[k]]*3], col); + else + dd->vertex(&tile->detailVerts[(pd->vertBase+t[k]-p->vertCount)*3], col); + } + } + } + dd->end(); + + // Draw inter poly boundaries + drawPolyBoundaries(dd, tile, duRGBA(0,48,64,32), 1.5f, true); + + // Draw outer poly boundaries + drawPolyBoundaries(dd, tile, duRGBA(0,48,64,220), 2.5f, false); + + if (flags & DU_DRAWNAVMESH_OFFMESHCONS) + { + dd->begin(DU_DRAW_LINES, 2.0f); + for (int i = 0; i < tile->header->polyCount; ++i) + { + const dtPoly* p = &tile->polys[i]; + if (p->getType() != DT_POLYTYPE_OFFMESH_CONNECTION) // Skip regular polys. + continue; + + unsigned int col, col2; + if (query && query->isInClosedList(base | (dtPolyRef)i)) + col = duRGBA(255,196,0,220); + else + col = duDarkenCol(duTransCol(dd->areaToCol(p->getArea()), 220)); + + const dtOffMeshConnection* con = &tile->offMeshCons[i - tile->header->offMeshBase]; + const float* va = &tile->verts[p->verts[0]*3]; + const float* vb = &tile->verts[p->verts[1]*3]; + + // Check to see if start and end end-points have links. + bool startSet = false; + bool endSet = false; + for (unsigned int k = p->firstLink; k != DT_NULL_LINK; k = tile->links[k].next) + { + if (tile->links[k].edge == 0) + startSet = true; + if (tile->links[k].edge == 1) + endSet = true; + } + + // End points and their on-mesh locations. + dd->vertex(va[0],va[1],va[2], col); + dd->vertex(con->pos[0],con->pos[1],con->pos[2], col); + col2 = startSet ? col : duRGBA(220,32,16,196); + duAppendCircle(dd, con->pos[0],con->pos[1]+0.1f,con->pos[2], con->rad, col2); + + dd->vertex(vb[0],vb[1],vb[2], col); + dd->vertex(con->pos[3],con->pos[4],con->pos[5], col); + col2 = endSet ? col : duRGBA(220,32,16,196); + duAppendCircle(dd, con->pos[3],con->pos[4]+0.1f,con->pos[5], con->rad, col2); + + // End point vertices. + dd->vertex(con->pos[0],con->pos[1],con->pos[2], duRGBA(0,48,64,196)); + dd->vertex(con->pos[0],con->pos[1]+0.2f,con->pos[2], duRGBA(0,48,64,196)); + + dd->vertex(con->pos[3],con->pos[4],con->pos[5], duRGBA(0,48,64,196)); + dd->vertex(con->pos[3],con->pos[4]+0.2f,con->pos[5], duRGBA(0,48,64,196)); + + // Connection arc. + duAppendArc(dd, con->pos[0],con->pos[1],con->pos[2], con->pos[3],con->pos[4],con->pos[5], 0.25f, + (con->flags & 1) ? 0.6f : 0, 0.6f, col); + } + dd->end(); + } + + const unsigned int vcol = duRGBA(0,0,0,196); + dd->begin(DU_DRAW_POINTS, 3.0f); + for (int i = 0; i < tile->header->vertCount; ++i) + { + const float* v = &tile->verts[i*3]; + dd->vertex(v[0], v[1], v[2], vcol); + } + dd->end(); + + dd->depthMask(true); + } +} namespace SceneUtil { - osg::ref_ptr createNavMeshGroup(const dtNavMesh& navMesh, const DetourNavigator::Settings& settings) + osg::ref_ptr makeNavMeshTileStateSet() { - const osg::ref_ptr group(new osg::Group); - DebugDraw debugDraw(*group, osg::Vec3f(0, 0, 10), 1.0f / settings.mRecastScaleFactor); - dtNavMeshQuery navMeshQuery; - navMeshQuery.init(&navMesh, settings.mMaxNavMeshQueryNodes); - duDebugDrawNavMeshWithClosedList(&debugDraw, navMesh, navMeshQuery, - DU_DRAWNAVMESH_OFFMESHCONS | DU_DRAWNAVMESH_CLOSEDLIST); - osg::ref_ptr material = new osg::Material; material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE); - group->getOrCreateStateSet()->setAttribute(material); + + const float polygonOffsetFactor = SceneUtil::AutoDepth::isReversed() ? 1.0 : -1.0; + const float polygonOffsetUnits = SceneUtil::AutoDepth::isReversed() ? 1.0 : -1.0; + osg::ref_ptr polygonOffset = new osg::PolygonOffset(polygonOffsetFactor, polygonOffsetUnits); + + osg::ref_ptr stateSet = new osg::StateSet; + stateSet->setAttribute(material); + stateSet->setAttributeAndModes(polygonOffset); + return stateSet; + } + + osg::ref_ptr createNavMeshTileGroup(const dtNavMesh& navMesh, const dtMeshTile& meshTile, + const DetourNavigator::Settings& settings, const osg::ref_ptr& groupStateSet, + const osg::ref_ptr& debugDrawStateSet) + { + if (meshTile.header == nullptr) + return nullptr; + + osg::ref_ptr group(new osg::Group); + group->setStateSet(groupStateSet); + constexpr float shift = 10.0f; + DebugDraw debugDraw(*group, debugDrawStateSet, osg::Vec3f(0, 0, shift), 1.0f / settings.mRecastScaleFactor); + dtNavMeshQuery navMeshQuery; + navMeshQuery.init(&navMesh, settings.mMaxNavMeshQueryNodes); + drawMeshTile(&debugDraw, navMesh, &navMeshQuery, &meshTile, DU_DRAWNAVMESH_OFFMESHCONS | DU_DRAWNAVMESH_CLOSEDLIST); return group; } diff --git a/components/sceneutil/navmesh.hpp b/components/sceneutil/navmesh.hpp index b255b05756..ca9b8bd570 100644 --- a/components/sceneutil/navmesh.hpp +++ b/components/sceneutil/navmesh.hpp @@ -4,10 +4,12 @@ #include class dtNavMesh; +struct dtMeshTile; namespace osg { class Group; + class StateSet; } namespace DetourNavigator @@ -17,7 +19,11 @@ namespace DetourNavigator namespace SceneUtil { - osg::ref_ptr createNavMeshGroup(const dtNavMesh& navMesh, const DetourNavigator::Settings& settings); + osg::ref_ptr makeNavMeshTileStateSet(); + + osg::ref_ptr createNavMeshTileGroup(const dtNavMesh& navMesh, const dtMeshTile& meshTile, + const DetourNavigator::Settings& settings, const osg::ref_ptr& groupStateSet, + const osg::ref_ptr& debugDrawStateSet); } #endif diff --git a/components/sceneutil/recastmesh.cpp b/components/sceneutil/recastmesh.cpp index 5732f86959..9614673a64 100644 --- a/components/sceneutil/recastmesh.cpp +++ b/components/sceneutil/recastmesh.cpp @@ -1,5 +1,6 @@ #include "recastmesh.hpp" #include "detourdebugdraw.hpp" +#include "depth.hpp" #include #include @@ -9,6 +10,7 @@ #include #include +#include #include #include @@ -45,7 +47,7 @@ namespace SceneUtil using namespace DetourNavigator; const osg::ref_ptr group(new osg::Group); - DebugDraw debugDraw(*group, osg::Vec3f(0, 0, 0), 1.0f); + DebugDraw debugDraw(*group, DebugDraw::makeStateSet(), osg::Vec3f(0, 0, 0), 1.0f); const DetourNavigator::Mesh& mesh = recastMesh.getMesh(); std::vector indices = mesh.getIndices(); std::vector vertices = mesh.getVertices(); @@ -69,7 +71,14 @@ namespace SceneUtil osg::ref_ptr material = new osg::Material; material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE); - group->getOrCreateStateSet()->setAttribute(material); + + const float polygonOffsetFactor = SceneUtil::AutoDepth::isReversed() ? 1.0 : -1.0; + const float polygonOffsetUnits = SceneUtil::AutoDepth::isReversed() ? 1.0 : -1.0; + osg::ref_ptr polygonOffset = new osg::PolygonOffset(polygonOffsetFactor, polygonOffsetUnits); + + osg::ref_ptr stateSet = group->getOrCreateStateSet(); + stateSet->setAttribute(material); + stateSet->setAttributeAndModes(polygonOffset); return group; }