mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-16 19:19:56 +00:00
Detect and ignore updates for oscillating objects
To avoid triggering NavMesh update when RecastMesh change should not change NavMesh. Based on the following assumption: Given a set of transformations and a bounding shape for all these tranformations, a new object transformation that does not change this bounding shape also should not change navmesh if for all of this object transformations resulting navmesh tiles are equivalent The idea is to report back to RecastMeshManager all changes of NavMesh if there are any assiciated with RecastMesh version. So we know the last time when RecastMesh change resulted into the NavMesh change. When later report shows that there was no NavMesh change for a new RecastMesh version we can assume that any object transformation within the same bounding box should not change NavMesh.
This commit is contained in:
parent
64fb700ae9
commit
3e67f5ffa5
14 changed files with 236 additions and 7 deletions
|
@ -127,6 +127,7 @@
|
||||||
Feature #3983: Wizard: Add link to buy Morrowind
|
Feature #3983: Wizard: Add link to buy Morrowind
|
||||||
Feature #4894: Consider actors as obstacles for pathfinding
|
Feature #4894: Consider actors as obstacles for pathfinding
|
||||||
Feature #4899: Alpha-To-Coverage Anti-Aliasing for alpha testing
|
Feature #4899: Alpha-To-Coverage Anti-Aliasing for alpha testing
|
||||||
|
Feature #4917: Do not trigger NavMesh update when RecastMesh update should not change NavMesh
|
||||||
Feature #4977: Use the "default icon.tga" when an item's icon is not found
|
Feature #4977: Use the "default icon.tga" when an item's icon is not found
|
||||||
Feature #5043: Head Bobbing
|
Feature #5043: Head Bobbing
|
||||||
Feature #5199: OpenMW-CS: Improve scene view colors
|
Feature #5199: OpenMW-CS: Improve scene view colors
|
||||||
|
|
|
@ -835,4 +835,55 @@ namespace
|
||||||
|
|
||||||
ASSERT_THAT(result, Optional(Vec3fEq(mEnd.x(), mEnd.y(), 1.87719)));
|
ASSERT_THAT(result, Optional(Vec3fEq(mEnd.x(), mEnd.y(), 1.87719)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(DetourNavigatorNavigatorTest, update_for_oscillating_object_that_does_not_change_navmesh_should_not_trigger_navmesh_update)
|
||||||
|
{
|
||||||
|
const std::array<btScalar, 5 * 5> heightfieldData {{
|
||||||
|
0, 0, 0, 0, 0,
|
||||||
|
0, -25, -25, -25, -25,
|
||||||
|
0, -25, -100, -100, -100,
|
||||||
|
0, -25, -100, -100, -100,
|
||||||
|
0, -25, -100, -100, -100,
|
||||||
|
}};
|
||||||
|
btHeightfieldTerrainShape heightfieldShape = makeSquareHeightfieldTerrainShape(heightfieldData);
|
||||||
|
heightfieldShape.setLocalScaling(btVector3(128, 128, 1));
|
||||||
|
|
||||||
|
const btBoxShape oscillatingBoxShape(btVector3(20, 20, 20));
|
||||||
|
const btVector3 oscillatingBoxShapePosition(32, 32, 400);
|
||||||
|
const btBoxShape boderBoxShape(btVector3(50, 50, 50));
|
||||||
|
|
||||||
|
mNavigator->addAgent(mAgentHalfExtents);
|
||||||
|
mNavigator->addObject(ObjectId(&heightfieldShape), heightfieldShape, btTransform::getIdentity());
|
||||||
|
mNavigator->addObject(ObjectId(&oscillatingBoxShape), oscillatingBoxShape,
|
||||||
|
btTransform(btMatrix3x3::getIdentity(), oscillatingBoxShapePosition));
|
||||||
|
// add this box to make navmesh bound box independent from oscillatingBoxShape rotations
|
||||||
|
mNavigator->addObject(ObjectId(&boderBoxShape), boderBoxShape,
|
||||||
|
btTransform(btMatrix3x3::getIdentity(), oscillatingBoxShapePosition + btVector3(0, 0, 200)));
|
||||||
|
mNavigator->update(mPlayerPosition);
|
||||||
|
mNavigator->wait();
|
||||||
|
|
||||||
|
const auto navMeshes = mNavigator->getNavMeshes();
|
||||||
|
ASSERT_EQ(navMeshes.size(), 1);
|
||||||
|
{
|
||||||
|
const auto navMesh = navMeshes.begin()->second->lockConst();
|
||||||
|
ASSERT_EQ(navMesh->getGeneration(), 1);
|
||||||
|
ASSERT_EQ(navMesh->getNavMeshRevision(), 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int n = 0; n < 10; ++n)
|
||||||
|
{
|
||||||
|
const btTransform transform(btQuaternion(btVector3(0, 0, 1), n * 2 * osg::PI / 10),
|
||||||
|
oscillatingBoxShapePosition);
|
||||||
|
mNavigator->updateObject(ObjectId(&oscillatingBoxShape), oscillatingBoxShape, transform);
|
||||||
|
mNavigator->update(mPlayerPosition);
|
||||||
|
mNavigator->wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_EQ(navMeshes.size(), 1);
|
||||||
|
{
|
||||||
|
const auto navMesh = navMeshes.begin()->second->lockConst();
|
||||||
|
ASSERT_EQ(navMesh->getGeneration(), 1);
|
||||||
|
ASSERT_EQ(navMesh->getNavMeshRevision(), 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -183,6 +183,7 @@ add_component_dir(detournavigator
|
||||||
findrandompointaroundcircle
|
findrandompointaroundcircle
|
||||||
raycast
|
raycast
|
||||||
navmeshtileview
|
navmeshtileview
|
||||||
|
oscillatingrecastmeshobject
|
||||||
)
|
)
|
||||||
|
|
||||||
set (ESM_UI ${CMAKE_SOURCE_DIR}/files/ui/contentselector.ui
|
set (ESM_UI ${CMAKE_SOURCE_DIR}/files/ui/contentselector.ui
|
||||||
|
|
29
components/bullethelpers/aabb.hpp
Normal file
29
components/bullethelpers/aabb.hpp
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
#ifndef OPENMW_COMPONENTS_BULLETHELPERS_AABB_H
|
||||||
|
#define OPENMW_COMPONENTS_BULLETHELPERS_AABB_H
|
||||||
|
|
||||||
|
#include <LinearMath/btVector3.h>
|
||||||
|
#include <LinearMath/btTransform.h>
|
||||||
|
#include <BulletCollision/CollisionShapes/btCollisionShape.h>
|
||||||
|
#include <BulletCollision/Gimpact/btBoxCollision.h>
|
||||||
|
|
||||||
|
inline bool operator==(const btAABB& lhs, const btAABB& rhs)
|
||||||
|
{
|
||||||
|
return lhs.m_min == rhs.m_min && lhs.m_max == rhs.m_max;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool operator!=(const btAABB& lhs, const btAABB& rhs)
|
||||||
|
{
|
||||||
|
return !(lhs == rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace BulletHelpers
|
||||||
|
{
|
||||||
|
inline btAABB getAabb(const btCollisionShape& shape, const btTransform& transform)
|
||||||
|
{
|
||||||
|
btAABB result;
|
||||||
|
shape.getAabb(transform, result.m_min, result.m_max);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -2,6 +2,7 @@
|
||||||
#include "debug.hpp"
|
#include "debug.hpp"
|
||||||
#include "makenavmesh.hpp"
|
#include "makenavmesh.hpp"
|
||||||
#include "settings.hpp"
|
#include "settings.hpp"
|
||||||
|
#include "version.hpp"
|
||||||
|
|
||||||
#include <components/debug/debuglog.hpp>
|
#include <components/debug/debuglog.hpp>
|
||||||
#include <components/misc/thread.hpp>
|
#include <components/misc/thread.hpp>
|
||||||
|
@ -180,6 +181,19 @@ namespace DetourNavigator
|
||||||
const auto status = updateNavMesh(job.mAgentHalfExtents, recastMesh.get(), job.mChangedTile, playerTile,
|
const auto status = updateNavMesh(job.mAgentHalfExtents, recastMesh.get(), job.mChangedTile, playerTile,
|
||||||
offMeshConnections, mSettings, navMeshCacheItem, mNavMeshTilesCache);
|
offMeshConnections, mSettings, navMeshCacheItem, mNavMeshTilesCache);
|
||||||
|
|
||||||
|
if (recastMesh != nullptr)
|
||||||
|
{
|
||||||
|
Version navMeshVersion;
|
||||||
|
{
|
||||||
|
const auto locked = navMeshCacheItem->lockConst();
|
||||||
|
navMeshVersion.mGeneration = locked->getGeneration();
|
||||||
|
navMeshVersion.mRevision = locked->getNavMeshRevision();
|
||||||
|
}
|
||||||
|
mRecastMeshManager.get().reportNavMeshChange(job.mChangedTile,
|
||||||
|
Version {recastMesh->getGeneration(), recastMesh->getRevision()},
|
||||||
|
navMeshVersion);
|
||||||
|
}
|
||||||
|
|
||||||
const auto finish = std::chrono::steady_clock::now();
|
const auto finish = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
writeDebugFiles(job, recastMesh.get());
|
writeDebugFiles(job, recastMesh.get());
|
||||||
|
|
|
@ -61,4 +61,9 @@ namespace DetourNavigator
|
||||||
{
|
{
|
||||||
return mImpl.isEmpty();
|
return mImpl.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CachedRecastMeshManager::reportNavMeshChange(Version recastMeshVersion, Version navMeshVersion)
|
||||||
|
{
|
||||||
|
mImpl.reportNavMeshChange(recastMeshVersion, navMeshVersion);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#define OPENMW_COMPONENTS_DETOURNAVIGATOR_CACHEDRECASTMESHMANAGER_H
|
#define OPENMW_COMPONENTS_DETOURNAVIGATOR_CACHEDRECASTMESHMANAGER_H
|
||||||
|
|
||||||
#include "recastmeshmanager.hpp"
|
#include "recastmeshmanager.hpp"
|
||||||
|
#include "version.hpp"
|
||||||
|
|
||||||
namespace DetourNavigator
|
namespace DetourNavigator
|
||||||
{
|
{
|
||||||
|
@ -25,6 +26,8 @@ namespace DetourNavigator
|
||||||
|
|
||||||
bool isEmpty() const;
|
bool isEmpty() const;
|
||||||
|
|
||||||
|
void reportNavMeshChange(Version recastMeshVersion, Version navMeshVersion);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
RecastMeshManager mImpl;
|
RecastMeshManager mImpl;
|
||||||
std::shared_ptr<RecastMesh> mCached;
|
std::shared_ptr<RecastMesh> mCached;
|
||||||
|
|
35
components/detournavigator/oscillatingrecastmeshobject.cpp
Normal file
35
components/detournavigator/oscillatingrecastmeshobject.cpp
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
#include "oscillatingrecastmeshobject.hpp"
|
||||||
|
|
||||||
|
#include <components/bullethelpers/aabb.hpp>
|
||||||
|
|
||||||
|
namespace DetourNavigator
|
||||||
|
{
|
||||||
|
OscillatingRecastMeshObject::OscillatingRecastMeshObject(RecastMeshObject impl, std::size_t lastChangeRevision)
|
||||||
|
: mImpl(std::move(impl))
|
||||||
|
, mLastChangeRevision(lastChangeRevision)
|
||||||
|
, mAabb(BulletHelpers::getAabb(mImpl.getShape(), mImpl.getTransform()))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OscillatingRecastMeshObject::update(const btTransform& transform, const AreaType areaType,
|
||||||
|
std::size_t lastChangeRevision)
|
||||||
|
{
|
||||||
|
const btTransform oldTransform = mImpl.getTransform();
|
||||||
|
if (!mImpl.update(transform, areaType))
|
||||||
|
return false;
|
||||||
|
if (transform == oldTransform)
|
||||||
|
return true;
|
||||||
|
if (mLastChangeRevision != lastChangeRevision)
|
||||||
|
{
|
||||||
|
mLastChangeRevision = lastChangeRevision;
|
||||||
|
// btAABB doesn't have copy-assignment operator
|
||||||
|
const btAABB aabb = BulletHelpers::getAabb(mImpl.getShape(), transform);
|
||||||
|
mAabb.m_min = aabb.m_min;
|
||||||
|
mAabb.m_max = aabb.m_max;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const btAABB currentAabb = mAabb;
|
||||||
|
mAabb.merge(BulletHelpers::getAabb(mImpl.getShape(), transform));
|
||||||
|
return currentAabb != mAabb;
|
||||||
|
}
|
||||||
|
}
|
28
components/detournavigator/oscillatingrecastmeshobject.hpp
Normal file
28
components/detournavigator/oscillatingrecastmeshobject.hpp
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_OSCILLATINGRECASTMESHOBJECT_H
|
||||||
|
#define OPENMW_COMPONENTS_DETOURNAVIGATOR_OSCILLATINGRECASTMESHOBJECT_H
|
||||||
|
|
||||||
|
#include "areatype.hpp"
|
||||||
|
#include "recastmeshobject.hpp"
|
||||||
|
|
||||||
|
#include <LinearMath/btTransform.h>
|
||||||
|
#include <BulletCollision/Gimpact/btBoxCollision.h>
|
||||||
|
|
||||||
|
namespace DetourNavigator
|
||||||
|
{
|
||||||
|
class OscillatingRecastMeshObject
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit OscillatingRecastMeshObject(RecastMeshObject impl, std::size_t lastChangeRevision);
|
||||||
|
|
||||||
|
bool update(const btTransform& transform, const AreaType areaType, std::size_t lastChangeRevision);
|
||||||
|
|
||||||
|
const RecastMeshObject& getImpl() const { return mImpl; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
RecastMeshObject mImpl;
|
||||||
|
std::size_t mLastChangeRevision;
|
||||||
|
btAABB mAabb;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -13,7 +13,8 @@ namespace DetourNavigator
|
||||||
bool RecastMeshManager::addObject(const ObjectId id, const btCollisionShape& shape, const btTransform& transform,
|
bool RecastMeshManager::addObject(const ObjectId id, const btCollisionShape& shape, const btTransform& transform,
|
||||||
const AreaType areaType)
|
const AreaType areaType)
|
||||||
{
|
{
|
||||||
const auto iterator = mObjectsOrder.emplace(mObjectsOrder.end(), RecastMeshObject(shape, transform, areaType));
|
const auto iterator = mObjectsOrder.emplace(mObjectsOrder.end(),
|
||||||
|
OscillatingRecastMeshObject(RecastMeshObject(shape, transform, areaType), mRevision + 1));
|
||||||
if (!mObjects.emplace(id, iterator).second)
|
if (!mObjects.emplace(id, iterator).second)
|
||||||
{
|
{
|
||||||
mObjectsOrder.erase(iterator);
|
mObjectsOrder.erase(iterator);
|
||||||
|
@ -28,7 +29,9 @@ namespace DetourNavigator
|
||||||
const auto object = mObjects.find(id);
|
const auto object = mObjects.find(id);
|
||||||
if (object == mObjects.end())
|
if (object == mObjects.end())
|
||||||
return false;
|
return false;
|
||||||
if (!object->second->update(transform, areaType))
|
const std::size_t lastChangeRevision = mLastNavMeshReportedChange.has_value()
|
||||||
|
? mLastNavMeshReportedChange->mRevision : mRevision;
|
||||||
|
if (!object->second->update(transform, areaType, lastChangeRevision))
|
||||||
return false;
|
return false;
|
||||||
++mRevision;
|
++mRevision;
|
||||||
return true;
|
return true;
|
||||||
|
@ -39,7 +42,7 @@ namespace DetourNavigator
|
||||||
const auto object = mObjects.find(id);
|
const auto object = mObjects.find(id);
|
||||||
if (object == mObjects.end())
|
if (object == mObjects.end())
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
const RemovedRecastMeshObject result {object->second->getShape(), object->second->getTransform()};
|
const RemovedRecastMeshObject result {object->second->getImpl().getShape(), object->second->getImpl().getTransform()};
|
||||||
mObjectsOrder.erase(object->second);
|
mObjectsOrder.erase(object->second);
|
||||||
mObjects.erase(object);
|
mObjects.erase(object);
|
||||||
++mRevision;
|
++mRevision;
|
||||||
|
@ -82,12 +85,27 @@ namespace DetourNavigator
|
||||||
return mObjects.empty();
|
return mObjects.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RecastMeshManager::reportNavMeshChange(Version recastMeshVersion, Version navMeshVersion)
|
||||||
|
{
|
||||||
|
if (recastMeshVersion.mGeneration != mGeneration)
|
||||||
|
return;
|
||||||
|
if (mLastNavMeshReport.has_value() && navMeshVersion < mLastNavMeshReport->mNavMeshVersion)
|
||||||
|
return;
|
||||||
|
mLastNavMeshReport = {recastMeshVersion.mRevision, navMeshVersion};
|
||||||
|
if (!mLastNavMeshReportedChange.has_value()
|
||||||
|
|| mLastNavMeshReportedChange->mNavMeshVersion < mLastNavMeshReport->mNavMeshVersion)
|
||||||
|
mLastNavMeshReportedChange = mLastNavMeshReport;
|
||||||
|
}
|
||||||
|
|
||||||
void RecastMeshManager::rebuild()
|
void RecastMeshManager::rebuild()
|
||||||
{
|
{
|
||||||
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& object : mObjectsOrder)
|
||||||
|
{
|
||||||
|
const RecastMeshObject& v = object.getImpl();
|
||||||
mMeshBuilder.addObject(v.getShape(), v.getTransform(), v.getAreaType());
|
mMeshBuilder.addObject(v.getShape(), v.getTransform(), v.getAreaType());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,9 @@
|
||||||
#define OPENMW_COMPONENTS_DETOURNAVIGATOR_RECASTMESHMANAGER_H
|
#define OPENMW_COMPONENTS_DETOURNAVIGATOR_RECASTMESHMANAGER_H
|
||||||
|
|
||||||
#include "recastmeshbuilder.hpp"
|
#include "recastmeshbuilder.hpp"
|
||||||
#include "recastmeshobject.hpp"
|
#include "oscillatingrecastmeshobject.hpp"
|
||||||
#include "objectid.hpp"
|
#include "objectid.hpp"
|
||||||
|
#include "version.hpp"
|
||||||
|
|
||||||
#include <LinearMath/btTransform.h>
|
#include <LinearMath/btTransform.h>
|
||||||
|
|
||||||
|
@ -50,14 +51,24 @@ namespace DetourNavigator
|
||||||
|
|
||||||
bool isEmpty() const;
|
bool isEmpty() const;
|
||||||
|
|
||||||
|
void reportNavMeshChange(Version recastMeshVersion, Version navMeshVersion);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
struct Report
|
||||||
|
{
|
||||||
|
std::size_t mRevision;
|
||||||
|
Version mNavMeshVersion;
|
||||||
|
};
|
||||||
|
|
||||||
std::size_t mRevision = 0;
|
std::size_t mRevision = 0;
|
||||||
std::size_t mGeneration;
|
std::size_t mGeneration;
|
||||||
RecastMeshBuilder mMeshBuilder;
|
RecastMeshBuilder mMeshBuilder;
|
||||||
std::list<RecastMeshObject> mObjectsOrder;
|
std::list<OscillatingRecastMeshObject> mObjectsOrder;
|
||||||
std::unordered_map<ObjectId, std::list<RecastMeshObject>::iterator> mObjects;
|
std::unordered_map<ObjectId, std::list<OscillatingRecastMeshObject>::iterator> mObjects;
|
||||||
std::list<Water> mWaterOrder;
|
std::list<Water> mWaterOrder;
|
||||||
std::map<osg::Vec2i, std::list<Water>::iterator> mWater;
|
std::map<osg::Vec2i, std::list<Water>::iterator> mWater;
|
||||||
|
std::optional<Report> mLastNavMeshReportedChange;
|
||||||
|
std::optional<Report> mLastNavMeshReport;
|
||||||
|
|
||||||
void rebuild();
|
void rebuild();
|
||||||
};
|
};
|
||||||
|
|
|
@ -145,6 +145,15 @@ namespace DetourNavigator
|
||||||
return mRevision;
|
return mRevision;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TileCachedRecastMeshManager::reportNavMeshChange(const TilePosition& tilePosition, Version recastMeshVersion, Version navMeshVersion)
|
||||||
|
{
|
||||||
|
const auto tiles = mTiles.lock();
|
||||||
|
const auto it = tiles->find(tilePosition);
|
||||||
|
if (it == tiles->end())
|
||||||
|
return;
|
||||||
|
it->second.reportNavMeshChange(recastMeshVersion, navMeshVersion);
|
||||||
|
}
|
||||||
|
|
||||||
bool TileCachedRecastMeshManager::addTile(const ObjectId id, const btCollisionShape& shape,
|
bool TileCachedRecastMeshManager::addTile(const ObjectId id, const btCollisionShape& shape,
|
||||||
const btTransform& transform, const AreaType areaType, const TilePosition& tilePosition, float border,
|
const btTransform& transform, const AreaType areaType, const TilePosition& tilePosition, float border,
|
||||||
std::map<TilePosition, CachedRecastMeshManager>& tiles)
|
std::map<TilePosition, CachedRecastMeshManager>& tiles)
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include "tileposition.hpp"
|
#include "tileposition.hpp"
|
||||||
#include "settingsutils.hpp"
|
#include "settingsutils.hpp"
|
||||||
#include "gettilespositions.hpp"
|
#include "gettilespositions.hpp"
|
||||||
|
#include "version.hpp"
|
||||||
|
|
||||||
#include <components/misc/guarded.hpp>
|
#include <components/misc/guarded.hpp>
|
||||||
|
|
||||||
|
@ -88,6 +89,8 @@ namespace DetourNavigator
|
||||||
|
|
||||||
std::size_t getRevision() const;
|
std::size_t getRevision() const;
|
||||||
|
|
||||||
|
void reportNavMeshChange(const TilePosition& tilePosition, Version recastMeshVersion, Version navMeshVersion);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const Settings& mSettings;
|
const Settings& mSettings;
|
||||||
Misc::ScopeGuarded<std::map<TilePosition, CachedRecastMeshManager>> mTiles;
|
Misc::ScopeGuarded<std::map<TilePosition, CachedRecastMeshManager>> mTiles;
|
||||||
|
|
21
components/detournavigator/version.hpp
Normal file
21
components/detournavigator/version.hpp
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_VERSION_H
|
||||||
|
#define OPENMW_COMPONENTS_DETOURNAVIGATOR_VERSION_H
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
|
namespace DetourNavigator
|
||||||
|
{
|
||||||
|
struct Version
|
||||||
|
{
|
||||||
|
std::size_t mGeneration;
|
||||||
|
std::size_t mRevision;
|
||||||
|
|
||||||
|
friend inline bool operator<(const Version& lhs, const Version& rhs)
|
||||||
|
{
|
||||||
|
return std::tie(lhs.mGeneration, lhs.mRevision) < std::tie(rhs.mGeneration, rhs.mRevision);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in a new issue