Support water for NavMesh

pull/541/head
elsid 7 years ago
parent 72f211ef28
commit c95cea414c
No known key found for this signature in database
GPG Key ID: B845CB9FEE18AB40

@ -282,6 +282,9 @@ namespace MWWorld
mPhysics->remove(ptr);
}
const auto cellX = (*iter)->getCell()->getGridX();
const auto cellY = (*iter)->getCell()->getGridY();
if ((*iter)->getCell()->isExterior())
{
const ESM::Land* land =
@ -291,14 +294,15 @@ namespace MWWorld
);
if (land && land->mDataTypes&ESM::Land::DATA_VHGT)
{
const auto cellX = (*iter)->getCell()->getGridX();
const auto cellY = (*iter)->getCell()->getGridY();
if (const auto heightField = mPhysics->getHeightField(cellX, cellY))
navigator->removeObject(reinterpret_cast<std::size_t>(heightField));
mPhysics->removeHeightField(cellX, cellY);
}
}
if ((*iter)->getCell()->hasWater())
navigator->removeWater(osg::Vec2i(cellX, cellY));
navigator->update(player.getRefData().getPosition().asVec3());
MWBase::Environment::get().getMechanicsManager()->drop (*iter);
@ -326,11 +330,12 @@ namespace MWWorld
const auto navigator = MWBase::Environment::get().getWorld()->getNavigator();
const int cellX = cell->getCell()->getGridX();
const int cellY = cell->getCell()->getGridY();
// Load terrain physics first...
if (cell->getCell()->isExterior())
{
int cellX = cell->getCell()->getGridX();
int cellY = cell->getCell()->getGridY();
osg::ref_ptr<const ESMTerrain::LandObject> land = mRendering.getLandManager()->getLand(cellX, cellY);
const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VHGT) : 0;
if (data)
@ -368,6 +373,18 @@ namespace MWWorld
{
mPhysics->enableWater(waterLevel);
mRendering.setWaterHeight(waterLevel);
if (cell->getCell()->isExterior())
{
if (const auto heightField = mPhysics->getHeightField(cellX, cellY))
navigator->addWater(osg::Vec2i(cellX, cellY), ESM::Land::REAL_SIZE,
cell->getWaterLevel(), heightField->getCollisionObject()->getWorldTransform());
}
else
{
navigator->addWater(osg::Vec2i(cellX, cellY), std::numeric_limits<int>::max(),
cell->getWaterLevel(), btTransform::getIdentity());
}
}
else
mPhysics->disableWater();

@ -6,6 +6,7 @@
#include <set>
#include <memory>
#include <unordered_map>
namespace osg
{
@ -27,6 +28,11 @@ namespace Loading
class Listener;
}
namespace DetourNavigator
{
class Water;
}
namespace MWRender
{
class SkyManager;

@ -168,6 +168,34 @@ namespace MWWorld
mLevitationEnabled(true), mGoToJail(false), mDaysInPrison(0),
mPlayerTraveling(false), mPlayerInJail(false), mSpellPreloadTimer(0.f)
{
mEsm.resize(contentFiles.size());
Loading::Listener* listener = MWBase::Environment::get().getWindowManager()->getLoadingScreen();
listener->loadingOn();
GameContentLoader gameContentLoader(*listener);
EsmLoader esmLoader(mStore, mEsm, encoder, *listener);
gameContentLoader.addLoader(".esm", &esmLoader);
gameContentLoader.addLoader(".esp", &esmLoader);
gameContentLoader.addLoader(".omwgame", &esmLoader);
gameContentLoader.addLoader(".omwaddon", &esmLoader);
gameContentLoader.addLoader(".project", &esmLoader);
loadContentFiles(fileCollections, contentFiles, gameContentLoader);
listener->loadingOff();
// insert records that may not be present in all versions of MW
if (mEsm[0].getFormat() == 0)
ensureNeededRecords();
fillGlobalVariables();
mStore.setUp(true);
mStore.movePlayerRecord();
mSwimHeightScale = mStore.get<ESM::GameSetting>().find("fSwimHeightScale")->mValue.getFloat();
mPhysics.reset(new MWPhysics::PhysicsSystem(resourceSystem, rootNode));
DetourNavigator::Settings navigatorSettings;
@ -180,6 +208,7 @@ namespace MWWorld
navigatorSettings.mMaxSimplificationError = Settings::Manager::getFloat("max simplification error", "Navigator");
navigatorSettings.mMaxSlope = MWPhysics::sMaxSlope;
navigatorSettings.mRecastScaleFactor = Settings::Manager::getFloat("recast scale factor", "Navigator");
navigatorSettings.mSwimHeightScale = mSwimHeightScale;
navigatorSettings.mMaxEdgeLen = Settings::Manager::getInt("max edge len", "Navigator");
navigatorSettings.mMaxNavMeshQueryNodes = Settings::Manager::getInt("max nav mesh query nodes", "Navigator");
navigatorSettings.mMaxVertsPerPoly = Settings::Manager::getInt("max verts per poly", "Navigator");
@ -204,34 +233,6 @@ namespace MWWorld
mProjectileManager.reset(new ProjectileManager(mRendering->getLightRoot(), resourceSystem, mRendering.get(), mPhysics.get()));
mRendering->preloadCommonAssets();
mEsm.resize(contentFiles.size());
Loading::Listener* listener = MWBase::Environment::get().getWindowManager()->getLoadingScreen();
listener->loadingOn();
GameContentLoader gameContentLoader(*listener);
EsmLoader esmLoader(mStore, mEsm, encoder, *listener);
gameContentLoader.addLoader(".esm", &esmLoader);
gameContentLoader.addLoader(".esp", &esmLoader);
gameContentLoader.addLoader(".omwgame", &esmLoader);
gameContentLoader.addLoader(".omwaddon", &esmLoader);
gameContentLoader.addLoader(".project", &esmLoader);
loadContentFiles(fileCollections, contentFiles, gameContentLoader);
listener->loadingOff();
// insert records that may not be present in all versions of MW
if (mEsm[0].getFormat() == 0)
ensureNeededRecords();
fillGlobalVariables();
mStore.setUp(true);
mStore.movePlayerRecord();
mSwimHeightScale = mStore.get<ESM::GameSetting>().find("fSwimHeightScale")->mValue.getFloat();
mWeatherManager.reset(new MWWorld::WeatherManager(*mRendering, mFallback, mStore));
mWorldScene.reset(new Scene(*mRendering.get(), mPhysics.get()));

@ -48,6 +48,7 @@ namespace
mSettings.mMaxSimplificationError = 1.3f;
mSettings.mMaxSlope = 49;
mSettings.mRecastScaleFactor = 0.017647058823529415f;
mSettings.mSwimHeightScale = 0.89999997615814208984375f;
mSettings.mMaxEdgeLen = 12;
mSettings.mMaxNavMeshQueryNodes = 2048;
mSettings.mMaxVertsPerPoly = 6;
@ -415,4 +416,142 @@ namespace
osg::Vec3f(215, -215, 1.93937528133392333984375),
})) << mPath;
}
TEST_F(DetourNavigatorNavigatorTest, path_should_be_over_water_ground_lower_than_water)
{
std::array<btScalar, 5 * 5> heightfieldData {{
-50, -50, -50, -50, 0,
-50, -100, -150, -100, -50,
-50, -150, -200, -150, -100,
-50, -100, -150, -100, -100,
0, -50, -100, -100, -100,
}};
btHeightfieldTerrainShape shape(5, 5, heightfieldData.data(), 1, 0, 0, 2, PHY_FLOAT, false);
shape.setLocalScaling(btVector3(128, 128, 1));
mNavigator->addAgent(mAgentHalfExtents);
mNavigator->addWater(osg::Vec2i(0, 0), 128 * 4, 300, btTransform::getIdentity());
mNavigator->addObject(1, shape, btTransform::getIdentity());
mNavigator->update(mPlayerPosition);
mNavigator->wait();
mStart.x() = 0;
mStart.z() = 300;
mEnd.x() = 0;
mEnd.z() = 300;
mNavigator->findPath(mAgentHalfExtents, mStart, mEnd, mOut);
EXPECT_EQ(mPath, std::deque<osg::Vec3f>({
osg::Vec3f(0, 215, 185.33331298828125),
osg::Vec3f(0, 186.6666717529296875, 185.33331298828125),
osg::Vec3f(0, 158.333343505859375, 185.33331298828125),
osg::Vec3f(0, 130.0000152587890625, 185.33331298828125),
osg::Vec3f(0, 101.66667938232421875, 185.33331298828125),
osg::Vec3f(0, 73.333343505859375, 185.33331298828125),
osg::Vec3f(0, 45.0000152587890625, 185.33331298828125),
osg::Vec3f(0, 16.6666812896728515625, 185.33331298828125),
osg::Vec3f(0, -11.66664981842041015625, 185.33331298828125),
osg::Vec3f(0, -39.999980926513671875, 185.33331298828125),
osg::Vec3f(0, -68.33331298828125, 185.33331298828125),
osg::Vec3f(0, -96.66664886474609375, 185.33331298828125),
osg::Vec3f(0, -124.99997711181640625, 185.33331298828125),
osg::Vec3f(0, -153.33331298828125, 185.33331298828125),
osg::Vec3f(0, -181.6666412353515625, 185.33331298828125),
osg::Vec3f(0, -209.999969482421875, 185.33331298828125),
osg::Vec3f(0, -215, 185.33331298828125),
})) << mPath;
}
TEST_F(DetourNavigatorNavigatorTest, path_should_be_over_water_when_ground_cross_water)
{
std::array<btScalar, 7 * 7> heightfieldData {{
0, 0, 0, 0, 0, 0, 0,
0, -100, -100, -100, -100, -100, 0,
0, -100, -150, -150, -150, -100, 0,
0, -100, -150, -200, -150, -100, 0,
0, -100, -150, -150, -150, -100, 0,
0, -100, -100, -100, -100, -100, 0,
0, 0, 0, 0, 0, 0, 0,
}};
btHeightfieldTerrainShape shape(7, 7, heightfieldData.data(), 1, 0, 0, 2, PHY_FLOAT, false);
shape.setLocalScaling(btVector3(128, 128, 1));
mNavigator->addAgent(mAgentHalfExtents);
mNavigator->addWater(osg::Vec2i(0, 0), 128 * 4, -25, btTransform::getIdentity());
mNavigator->addObject(1, shape, btTransform::getIdentity());
mNavigator->update(mPlayerPosition);
mNavigator->wait();
mStart.x() = 0;
mEnd.x() = 0;
mNavigator->findPath(mAgentHalfExtents, mStart, mEnd, mOut);
EXPECT_EQ(mPath, std::deque<osg::Vec3f>({
osg::Vec3f(0, 215, -94.75363922119140625),
osg::Vec3f(0, 186.6666717529296875, -106.0000152587890625),
osg::Vec3f(0, 158.333343505859375, -115.85507965087890625),
osg::Vec3f(0, 130.0000152587890625, -125.71016693115234375),
osg::Vec3f(0, 101.66667938232421875, -135.5652313232421875),
osg::Vec3f(0, 73.333343505859375, -143.3333587646484375),
osg::Vec3f(0, 45.0000152587890625, -143.3333587646484375),
osg::Vec3f(0, 16.6666812896728515625, -143.3333587646484375),
osg::Vec3f(0, -11.66664981842041015625, -143.3333587646484375),
osg::Vec3f(0, -39.999980926513671875, -143.3333587646484375),
osg::Vec3f(0, -68.33331298828125, -143.3333587646484375),
osg::Vec3f(0, -96.66664886474609375, -137.3043670654296875),
osg::Vec3f(0, -124.99997711181640625, -127.44930267333984375),
osg::Vec3f(0, -153.33331298828125, -117.5942230224609375),
osg::Vec3f(0, -181.6666412353515625, -107.7391510009765625),
osg::Vec3f(0, -209.999969482421875, -97.79712677001953125),
osg::Vec3f(0, -215, -94.753631591796875),
})) << mPath;
}
TEST_F(DetourNavigatorNavigatorTest, path_should_be_over_water_when_ground_cross_water_with_max_int_cells_size)
{
std::array<btScalar, 7 * 7> heightfieldData {{
0, 0, 0, 0, 0, 0, 0,
0, -100, -100, -100, -100, -100, 0,
0, -100, -150, -150, -150, -100, 0,
0, -100, -150, -200, -150, -100, 0,
0, -100, -150, -150, -150, -100, 0,
0, -100, -100, -100, -100, -100, 0,
0, 0, 0, 0, 0, 0, 0,
}};
btHeightfieldTerrainShape shape(7, 7, heightfieldData.data(), 1, 0, 0, 2, PHY_FLOAT, false);
shape.setLocalScaling(btVector3(128, 128, 1));
mNavigator->addAgent(mAgentHalfExtents);
mNavigator->addObject(1, shape, btTransform::getIdentity());
mNavigator->addWater(osg::Vec2i(0, 0), std::numeric_limits<int>::max(), -25, btTransform::getIdentity());
mNavigator->update(mPlayerPosition);
mNavigator->wait();
mStart.x() = 0;
mEnd.x() = 0;
mNavigator->findPath(mAgentHalfExtents, mStart, mEnd, mOut);
EXPECT_EQ(mPath, std::deque<osg::Vec3f>({
osg::Vec3f(0, 215, -94.75363922119140625),
osg::Vec3f(0, 186.6666717529296875, -106.0000152587890625),
osg::Vec3f(0, 158.333343505859375, -115.85507965087890625),
osg::Vec3f(0, 130.0000152587890625, -125.71016693115234375),
osg::Vec3f(0, 101.66667938232421875, -135.5652313232421875),
osg::Vec3f(0, 73.333343505859375, -143.3333587646484375),
osg::Vec3f(0, 45.0000152587890625, -143.3333587646484375),
osg::Vec3f(0, 16.6666812896728515625, -143.3333587646484375),
osg::Vec3f(0, -11.66664981842041015625, -143.3333587646484375),
osg::Vec3f(0, -39.999980926513671875, -143.3333587646484375),
osg::Vec3f(0, -68.33331298828125, -143.3333587646484375),
osg::Vec3f(0, -96.66664886474609375, -137.3043670654296875),
osg::Vec3f(0, -124.99997711181640625, -127.44930267333984375),
osg::Vec3f(0, -153.33331298828125, -117.5942230224609375),
osg::Vec3f(0, -181.6666412353515625, -107.7391510009765625),
osg::Vec3f(0, -209.999969482421875, -97.79712677001953125),
osg::Vec3f(0, -215, -94.753631591796875),
})) << mPath;
}
}

@ -13,6 +13,14 @@
#include <gtest/gtest.h>
namespace DetourNavigator
{
static inline bool operator ==(const RecastMesh::Water& lhs, const RecastMesh::Water& rhs)
{
return lhs.mCellSize == rhs.mCellSize && lhs.mTransform == rhs.mTransform;
}
}
namespace
{
using namespace testing;
@ -391,4 +399,14 @@ namespace
EXPECT_EQ(recastMesh->getIndices(), std::vector<int>({0, 1, 2, 3, 4, 5}));
EXPECT_EQ(recastMesh->getAreaTypes(), std::vector<AreaType>({AreaType_ground, AreaType_null}));
}
TEST_F(DetourNavigatorRecastMeshBuilderTest, add_water_then_get_water_should_return_it)
{
RecastMeshBuilder builder(mSettings, mBounds);
builder.addWater(1000, btTransform(btMatrix3x3::getIdentity(), btVector3(100, 200, 300)));
const auto recastMesh = builder.create();
EXPECT_EQ(recastMesh->getWater(), std::vector<RecastMesh::Water>({
RecastMesh::Water {1000, btTransform(btMatrix3x3::getIdentity(), btVector3(100, 200, 300))}
}));
}
}

@ -8,6 +8,7 @@ namespace DetourNavigator
enum AreaType : unsigned char
{
AreaType_null = RC_NULL_AREA,
AreaType_water,
AreaType_ground = RC_WALKABLE_AREA,
};
}

@ -32,6 +32,23 @@ namespace DetourNavigator
return object;
}
bool CachedRecastMeshManager::addWater(const osg::Vec2i& cellPosition, const int cellSize,
const btTransform& transform)
{
if (!mImpl.addWater(cellPosition, cellSize, transform))
return false;
mCached.reset();
return true;
}
boost::optional<RecastMeshManager::Water> CachedRecastMeshManager::removeWater(const osg::Vec2i& cellPosition)
{
const auto water = mImpl.removeWater(cellPosition);
if (water)
mCached.reset();
return water;
}
std::shared_ptr<RecastMesh> CachedRecastMeshManager::getMesh()
{
if (!mCached)

@ -17,6 +17,10 @@ namespace DetourNavigator
bool updateObject(std::size_t id, const btTransform& transform, const AreaType areaType);
bool addWater(const osg::Vec2i& cellPosition, const int cellSize, const btTransform& transform);
boost::optional<RecastMeshManager::Water> removeWater(const osg::Vec2i& cellPosition);
boost::optional<RemovedRecastMeshObject> removeObject(std::size_t id);
std::shared_ptr<RecastMesh> getMesh();

@ -3,6 +3,7 @@
#include "dtstatus.hpp"
#include "exceptions.hpp"
#include "flags.hpp"
#include "settings.hpp"
#include "settingsutils.hpp"
@ -10,6 +11,8 @@
#include <DetourNavMesh.h>
#include <DetourNavMeshQuery.h>
#include <LinearMath/btVector3.h>
#include <boost/optional.hpp>
#include <osg/Vec3f>
@ -27,6 +30,11 @@ namespace DetourNavigator
return osg::Vec3f(values[0], values[1], values[2]);
}
inline osg::Vec3f makeOsgVec3f(const btVector3& value)
{
return osg::Vec3f(value.x(), value.y(), value.z());
}
inline bool inRange(const osg::Vec3f& v1, const osg::Vec3f& v2, const float r, const float h)
{
const auto d = v2 - v1;
@ -217,6 +225,7 @@ namespace DetourNavigator
OPENMW_CHECK_DT_STATUS(navMeshQuery.init(&navMesh, settings.mMaxNavMeshQueryNodes));
dtQueryFilter queryFilter;
queryFilter.setIncludeFlags(Flag_swim | Flag_walk);
dtPolyRef startRef = 0;
osg::Vec3f startPolygonPosition;

@ -9,6 +9,7 @@ namespace DetourNavigator
{
Flag_none = 0,
Flag_walk = 1 << 0,
Flag_swim = 1 << 1,
};
}

@ -51,6 +51,23 @@ namespace DetourNavigator
getTilesPositions(makeOsgVec3f(aabbMin), makeOsgVec3f(aabbMax), settings, std::forward<Callback>(callback));
}
template <class Callback>
void getTilesPositions(const int cellSize, const btTransform& transform,
const Settings& settings, Callback&& callback)
{
const auto halfCellSize = cellSize / 2;
auto aabbMin = transform(btVector3(-halfCellSize, -halfCellSize, 0));
auto aabbMax = transform(btVector3(halfCellSize, halfCellSize, 0));
aabbMin.setX(std::min(aabbMin.x(), aabbMax.x()));
aabbMin.setY(std::min(aabbMin.y(), aabbMax.y()));
aabbMax.setX(std::max(aabbMin.x(), aabbMax.x()));
aabbMax.setY(std::max(aabbMin.y(), aabbMax.y()));
getTilesPositions(makeOsgVec3f(aabbMin), makeOsgVec3f(aabbMax), settings, std::forward<Callback>(callback));
}
}
#endif

@ -15,6 +15,7 @@
#include <Recast.h>
#include <RecastAlloc.h>
#include <algorithm>
#include <iomanip>
#include <limits>
@ -64,6 +65,41 @@ namespace
{}
};
osg::Vec3f makeOsgVec3f(const btVector3& value)
{
return osg::Vec3f(value.x(), value.y(), value.z());
}
struct WaterBounds
{
osg::Vec3f mMin;
osg::Vec3f mMax;
};
WaterBounds getWaterBounds(const RecastMesh::Water& water, const Settings& settings,
const osg::Vec3f& agentHalfExtents)
{
if (water.mCellSize == std::numeric_limits<int>::max())
{
const auto transform = getSwimLevelTransform(settings, water.mTransform, agentHalfExtents.z());
const auto min = toNavMeshCoordinates(settings, makeOsgVec3f(transform(btVector3(-1, -1, 0))));
const auto max = toNavMeshCoordinates(settings, makeOsgVec3f(transform(btVector3(1, 1, 0))));
return WaterBounds {
osg::Vec3f(-std::numeric_limits<float>::max(), min.y(), -std::numeric_limits<float>::max()),
osg::Vec3f(std::numeric_limits<float>::max(), max.y(), std::numeric_limits<float>::max())
};
}
else
{
const auto transform = getSwimLevelTransform(settings, water.mTransform, agentHalfExtents.z());
const auto halfCellSize = water.mCellSize / 2.0f;
return WaterBounds {
toNavMeshCoordinates(settings, makeOsgVec3f(transform(btVector3(-halfCellSize, -halfCellSize, 0)))),
toNavMeshCoordinates(settings, makeOsgVec3f(transform(btVector3(halfCellSize, halfCellSize, 0))))
};
}
}
NavMeshData makeNavMeshTileData(const osg::Vec3f& agentHalfExtents, const RecastMesh& recastMesh,
const int tileX, const int tileY, const osg::Vec3f& boundsMin, const osg::Vec3f& boundsMax,
const Settings& settings)
@ -156,6 +192,56 @@ namespace
}
}
{
const std::array<unsigned char, 2> areas {{AreaType_water, AreaType_water}};
for (const auto& water : recastMesh.getWater())
{
const auto bounds = getWaterBounds(water, settings, agentHalfExtents);
const osg::Vec2f tileBoundsMin(
std::min(config.bmax[0], std::max(config.bmin[0], bounds.mMin.x())),
std::min(config.bmax[2], std::max(config.bmin[2], bounds.mMin.z()))
);
const osg::Vec2f tileBoundsMax(
std::min(config.bmax[0], std::max(config.bmin[0], bounds.mMax.x())),
std::min(config.bmax[2], std::max(config.bmin[2], bounds.mMax.z()))
);
if (tileBoundsMax == tileBoundsMin)
continue;
const std::array<osg::Vec3f, 4> vertices {{
osg::Vec3f(tileBoundsMin.x(), bounds.mMin.y(), tileBoundsMin.y()),
osg::Vec3f(tileBoundsMin.x(), bounds.mMin.y(), tileBoundsMax.y()),
osg::Vec3f(tileBoundsMax.x(), bounds.mMin.y(), tileBoundsMax.y()),
osg::Vec3f(tileBoundsMax.x(), bounds.mMin.y(), tileBoundsMin.y()),
}};
std::array<float, 4 * 3> convertedVertices;
auto convertedVerticesIt = convertedVertices.begin();
for (const auto& vertex : vertices)
convertedVerticesIt = std::copy(vertex.ptr(), vertex.ptr() + 3, convertedVerticesIt);
const std::array<int, 6> indices {{
0, 1, 2,
0, 2, 3,
}};
OPENMW_CHECK_DT_RESULT(rcRasterizeTriangles(
&context,
convertedVertices.data(),
static_cast<int>(convertedVertices.size() / 3),
indices.data(),
areas.data(),
static_cast<int>(areas.size()),
solid,
config.walkableClimb
));
}
}
rcFilterLowHangingWalkableObstacles(&context, config.walkableClimb, solid);
rcFilterLedgeSpans(&context, config.walkableHeight, config.walkableClimb, solid);
rcFilterWalkableLowHeightSpans(&context, config.walkableHeight, solid);
@ -187,8 +273,12 @@ namespace
}
for (int i = 0; i < polyMesh.npolys; ++i)
{
if (polyMesh.areas[i] == AreaType_ground)
polyMesh.flags[i] = Flag_walk;
else if (polyMesh.areas[i] == AreaType_water)
polyMesh.flags[i] = Flag_swim;
}
dtNavMeshCreateParams params;
params.verts = polyMesh.verts;
@ -302,8 +392,15 @@ namespace DetourNavigator
return removeTile();
}
const auto& boundsMin = recastMesh->getBoundsMin();
const auto& boundsMax = recastMesh->getBoundsMax();
auto boundsMin = recastMesh->getBoundsMin();
auto boundsMax = recastMesh->getBoundsMax();
for (const auto& water : recastMesh->getWater())
{
const auto bounds = getWaterBounds(water, settings, agentHalfExtents);
boundsMin.y() = std::min(boundsMin.y(), bounds.mMin.y());
boundsMax.y() = std::max(boundsMax.y(), bounds.mMax.y());
}
if (boundsMin == boundsMax)
{

@ -9,7 +9,6 @@
#include <osg/Vec3f>
#include <memory>
#include <set>
class dtNavMesh;

@ -71,9 +71,24 @@ namespace DetourNavigator
{
bool result = mNavMeshManager.removeObject(id);
const auto avoid = mAvoidIds.find(id);
if (avoid == mAvoidIds.end())
return result;
return mNavMeshManager.removeObject(avoid->second) || result;
if (avoid != mAvoidIds.end())
result = mNavMeshManager.removeObject(avoid->second) || result;
const auto water = mWaterIds.find(id);
if (water != mWaterIds.end())
result = mNavMeshManager.removeObject(water->second) || result;
return result;
}
bool Navigator::addWater(const osg::Vec2i& cellPosition, const int cellSize, const btScalar level,
const btTransform& transform)
{
return mNavMeshManager.addWater(cellPosition, cellSize,
btTransform(transform.getBasis(), btVector3(transform.getOrigin().x(), transform.getOrigin().y(), level)));
}
bool Navigator::removeWater(const osg::Vec2i& cellPosition)
{
return mNavMeshManager.removeWater(cellPosition);
}
void Navigator::update(const osg::Vec3f& playerPosition)
@ -97,13 +112,23 @@ namespace DetourNavigator
return mSettings;
}
void Navigator::updateAvoidShapeId(std::size_t id, std::size_t avoidId)
void Navigator::updateAvoidShapeId(const std::size_t id, const std::size_t avoidId)
{
updateId(id, avoidId, mWaterIds);
}
void Navigator::updateWaterShapeId(const std::size_t id, const std::size_t waterId)
{
updateId(id, waterId, mWaterIds);
}
void Navigator::updateId(const std::size_t id, const std::size_t updateId, std::unordered_map<std::size_t, std::size_t>& ids)
{
auto inserted = mAvoidIds.insert(std::make_pair(id, avoidId));
auto inserted = ids.insert(std::make_pair(id, updateId));
if (!inserted.second)
{
mNavMeshManager.removeObject(inserted.first->second);
inserted.second = avoidId;
inserted.second = updateId;
}
}
}

@ -37,6 +37,11 @@ namespace DetourNavigator
bool removeObject(std::size_t id);
bool addWater(const osg::Vec2i& cellPosition, const int cellSize, const btScalar level,
const btTransform& transform);
bool removeWater(const osg::Vec2i& cellPosition);
void update(const osg::Vec3f& playerPosition);
void wait();
@ -59,8 +64,11 @@ namespace DetourNavigator
NavMeshManager mNavMeshManager;
std::map<osg::Vec3f, std::size_t> mAgents;
std::unordered_map<std::size_t, std::size_t> mAvoidIds;
std::unordered_map<std::size_t, std::size_t> mWaterIds;
void updateAvoidShapeId(std::size_t id, std::size_t avoidId);
void updateAvoidShapeId(const std::size_t id, const std::size_t avoidId);
void updateWaterShapeId(const std::size_t id, const std::size_t waterId);
void updateId(const std::size_t id, const std::size_t waterId, std::unordered_map<std::size_t, std::size_t>& ids);
};
}

@ -58,6 +58,23 @@ namespace DetourNavigator
return true;
}
bool NavMeshManager::addWater(const osg::Vec2i& cellPosition, const int cellSize, const btTransform& transform)
{
if (!mRecastMeshManager.addWater(cellPosition, cellSize, transform))
return false;
addChangedTiles(cellSize, transform, ChangeType::add);
return true;
}
bool NavMeshManager::removeWater(const osg::Vec2i& cellPosition)
{
const auto water = mRecastMeshManager.removeWater(cellPosition);
if (!water)
return false;
addChangedTiles(water->mCellSize, water->mTransform, ChangeType::remove);
return true;
}
void NavMeshManager::addAgent(const osg::Vec3f& agentHalfExtents)
{
auto cached = mCache.find(agentHalfExtents);
@ -75,9 +92,7 @@ namespace DetourNavigator
void NavMeshManager::update(osg::Vec3f playerPosition, const osg::Vec3f& agentHalfExtents)
{
playerPosition *= mSettings.mRecastScaleFactor;
std::swap(playerPosition.y(), playerPosition.z());
const auto playerTile = getTilePosition(mSettings, playerPosition);
const auto playerTile = getTilePosition(mSettings, toNavMeshCoordinates(mSettings, playerPosition));
if (mLastRecastMeshManagerRevision >= mRecastMeshManager.getRevision() && mPlayerTile
&& *mPlayerTile == playerTile)
return;
@ -140,20 +155,36 @@ namespace DetourNavigator
}
void NavMeshManager::addChangedTiles(const btCollisionShape& shape, const btTransform& transform,
const ChangeType changeType)
{
getTilesPositions(shape, transform, mSettings, [&] (const TilePosition& v) {
for (const auto& cached : mCache)
if (cached.second)
{
auto& tiles = mChangedTiles[cached.first];
auto tile = tiles.find(v);
if (tile == tiles.end())
tiles.insert(std::make_pair(v, changeType));
else
tile->second = addChangeType(tile->second, changeType);
}
});
const ChangeType changeType)
{
getTilesPositions(shape, transform, mSettings,
[&] (const TilePosition& v) { addChangedTile(v, changeType); });
}
void NavMeshManager::addChangedTiles(const int cellSize, const btTransform& transform,
const ChangeType changeType)
{
if (cellSize == std::numeric_limits<int>::max())
return;
getTilesPositions(cellSize, transform, mSettings,
[&] (const TilePosition& v) { addChangedTile(v, changeType); });
}
void NavMeshManager::addChangedTile(const TilePosition& tilePosition, const ChangeType changeType)
{
for (const auto& cached : mCache)
{
if (cached.second)
{
auto& tiles = mChangedTiles[cached.first];
auto tile = tiles.find(tilePosition);
if (tile == tiles.end())
tiles.insert(std::make_pair(tilePosition, changeType));
else
tile->second = addChangeType(tile->second, changeType);
}
}
}
const std::shared_ptr<NavMeshCacheItem>& NavMeshManager::getCached(const osg::Vec3f& agentHalfExtents) const

@ -7,8 +7,6 @@
#include <BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h>
#include <BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h>
#include <osg/Vec3f>
#include <map>
@ -33,6 +31,10 @@ namespace DetourNavigator
void addAgent(const osg::Vec3f& agentHalfExtents);
bool addWater(const osg::Vec2i& cellPosition, const int cellSize, const btTransform& transform);
bool removeWater(const osg::Vec2i& cellPosition);
void reset(const osg::Vec3f& agentHalfExtents);
void update(osg::Vec3f playerPosition, const osg::Vec3f& agentHalfExtents);
@ -55,6 +57,10 @@ namespace DetourNavigator
void addChangedTiles(const btCollisionShape& shape, const btTransform& transform, const ChangeType changeType);
void addChangedTiles(const int cellSize, const btTransform& transform, const ChangeType changeType);
void addChangedTile(const TilePosition& tilePosition, const ChangeType changeType);
const std::shared_ptr<NavMeshCacheItem>& getCached(const osg::Vec3f& agentHalfExtents) const;
};
}

@ -7,10 +7,11 @@
namespace DetourNavigator
{
RecastMesh::RecastMesh(std::vector<int> indices, std::vector<float> vertices,
std::vector<AreaType> areaTypes, const Settings& settings)
std::vector<AreaType> areaTypes, std::vector<Water> water, const Settings& settings)
: mIndices(std::move(indices))
, mVertices(std::move(vertices))
, mAreaTypes(std::move(areaTypes))
, mWater(std::move(water))
, mChunkyTriMesh(mVertices, mIndices, mAreaTypes, settings.mTrianglesPerChunk)
{
if (getTrianglesCount() != mAreaTypes.size())

@ -9,6 +9,8 @@
#include <osg/Vec3f>
#include <LinearMath/btTransform.h>
namespace DetourNavigator
{
struct Settings;
@ -16,8 +18,14 @@ namespace DetourNavigator
class RecastMesh
{
public:
struct Water
{
int mCellSize;
btTransform mTransform;
};
RecastMesh(std::vector<int> indices, std::vector<float> vertices,
std::vector<AreaType> areaTypes, const Settings& settings);
std::vector<AreaType> areaTypes, std::vector<Water> water, const Settings& settings);
const std::vector<int>& getIndices() const
{
@ -34,6 +42,11 @@ namespace DetourNavigator
return mAreaTypes;
}
const std::vector<Water>& getWater() const
{
return mWater;
}
std::size_t getVerticesCount() const
{
return mVertices.size() / 3;
@ -63,6 +76,7 @@ namespace DetourNavigator
std::vector<int> mIndices;
std::vector<float> mVertices;
std::vector<AreaType> mAreaTypes;
std::vector<Water> mWater;
ChunkyTriMesh mChunkyTriMesh;
osg::Vec3f mBoundsMin;
osg::Vec3f mBoundsMax;

@ -82,19 +82,16 @@ namespace DetourNavigator
void RecastMeshBuilder::addObject(const btBoxShape& shape, const btTransform& transform, const AreaType areaType)
{
const auto indexOffset = int(mVertices.size() / 3);
const auto indexOffset = static_cast<int>(mVertices.size() / 3);
for (int vertex = 0; vertex < shape.getNumVertices(); ++vertex)
for (int vertex = 0, count = shape.getNumVertices(); vertex < count; ++vertex)
{
btVector3 position;
shape.getVertex(vertex, position);
addVertex(transform(position));
}
for (int vertex = 0; vertex < 12; ++vertex)
mAreaTypes.push_back(areaType);
static const std::array<int, 36> indices {{
const std::array<int, 36> indices {{
0, 2, 3,
3, 1, 0,
0, 4, 6,
@ -111,11 +108,18 @@ namespace DetourNavigator
std::transform(indices.begin(), indices.end(), std::back_inserter(mIndices),
[&] (int index) { return index + indexOffset; });
std::generate_n(std::back_inserter(mAreaTypes), 12, [=] { return areaType; });
}
void RecastMeshBuilder::addWater(const int cellSize, const btTransform& transform)
{
mWater.push_back(RecastMesh::Water {cellSize, transform});
}
std::shared_ptr<RecastMesh> RecastMeshBuilder::create() const
{
return std::make_shared<RecastMesh>(mIndices, mVertices, mAreaTypes, mSettings);
return std::make_shared<RecastMesh>(mIndices, mVertices, mAreaTypes, mWater, mSettings);
}
void RecastMeshBuilder::reset()
@ -123,6 +127,7 @@ namespace DetourNavigator
mIndices.clear();
mVertices.clear();
mAreaTypes.clear();
mWater.clear();
}
void RecastMeshBuilder::addObject(const btConcaveShape& shape, const btTransform& transform,

@ -4,14 +4,17 @@
#include "recastmesh.hpp"
#include "tilebounds.hpp"
#include <LinearMath/btTransform.h>
#include <osg/Vec2f>
#include <osg/Vec2i>
class btBoxShape;
class btCollisionShape;
class btCompoundShape;
class btConcaveShape;
class btHeightfieldTerrainShape;
class btTransform;
class btTriangleCallback;
class btVector3;
namespace DetourNavigator
{
@ -30,6 +33,8 @@ namespace DetourNavigator
void addObject(const btBoxShape& shape, const btTransform& transform, const AreaType areaType);
void addWater(const int mCellSize, const btTransform& transform);
std::shared_ptr<RecastMesh> create() const;
void reset();
@ -40,6 +45,7 @@ namespace DetourNavigator
std::vector<int> mIndices;
std::vector<float> mVertices;
std::vector<AreaType> mAreaTypes;
std::vector<RecastMesh::Water> mWater;
void addObject(const btConcaveShape& shape, const btTransform& transform, btTriangleCallback&& callback);

@ -42,6 +42,26 @@ namespace DetourNavigator
return result;
}
bool RecastMeshManager::addWater(const osg::Vec2i& cellPosition, const int cellSize,
const btTransform& transform)
{
if (!mWater.insert(std::make_pair(cellPosition, Water {cellSize, transform})).second)
return false;
mShouldRebuild = true;
return true;
}
boost::optional<RecastMeshManager::Water> RecastMeshManager::removeWater(const osg::Vec2i& cellPosition)
{
const auto water = mWater.find(cellPosition);
if (water == mWater.end())
return boost::none;
mShouldRebuild = true;
const auto result = water->second;
mWater.erase(water);
return result;
}
std::shared_ptr<RecastMesh> RecastMeshManager::getMesh()
{
rebuild();
@ -58,6 +78,8 @@ namespace DetourNavigator
if (!mShouldRebuild)
return;
mMeshBuilder.reset();
for (const auto& v : mWater)
mMeshBuilder.addWater(v.second.mCellSize, v.second.mTransform);
for (const auto& v : mObjects)
mMeshBuilder.addObject(v.second.getShape(), v.second.getTransform(), v.second.getAreaType());
mShouldRebuild = false;

@ -6,8 +6,11 @@
#include <LinearMath/btTransform.h>
#include <osg/Vec2i>
#include <boost/optional.hpp>
#include <map>
#include <unordered_map>
class btCollisionShape;
@ -23,6 +26,12 @@ namespace DetourNavigator
class RecastMeshManager
{
public:
struct Water
{
int mCellSize;
btTransform mTransform;
};
RecastMeshManager(const Settings& settings, const TileBounds& bounds);
bool addObject(std::size_t id, const btCollisionShape& shape, const btTransform& transform,
@ -30,6 +39,10 @@ namespace DetourNavigator
bool updateObject(std::size_t id, const btTransform& transform, const AreaType areaType);
bool addWater(const osg::Vec2i& cellPosition, const int cellSize, const btTransform& transform);
boost::optional<Water> removeWater(const osg::Vec2i& cellPosition);
boost::optional<RemovedRecastMeshObject> removeObject(std::size_t id);
std::shared_ptr<RecastMesh> getMesh();
@ -40,6 +53,7 @@ namespace DetourNavigator
bool mShouldRebuild;
RecastMeshBuilder mMeshBuilder;
std::unordered_map<std::size_t, RecastMeshObject> mObjects;
std::map<osg::Vec2i, Water> mWater;
void rebuild();
};

@ -19,6 +19,7 @@ namespace DetourNavigator
float mMaxSimplificationError;
float mMaxSlope;
float mRecastScaleFactor;
float mSwimHeightScale;
int mBorderSize;
int mMaxEdgeLen;
int mMaxNavMeshQueryNodes;

@ -6,6 +6,8 @@
#include "tileposition.hpp"
#include "tilebounds.hpp"
#include <LinearMath/btTransform.h>
#include <osg/Vec2f>
#include <osg/Vec2i>
#include <osg/Vec3f>
@ -68,6 +70,20 @@ namespace DetourNavigator
{
return settings.mBorderSize * settings.mCellSize;
}
inline float getSwimLevel(const Settings& settings, const float agentHalfExtentsZ)
{
return - settings.mSwimHeightScale * agentHalfExtentsZ;
}
inline btTransform getSwimLevelTransform(const Settings& settings, const btTransform& transform,
const float agentHalfExtentsZ)
{
return btTransform(
transform.getBasis(),
transform.getOrigin() + btVector3(0, 0, getSwimLevel(settings, agentHalfExtentsZ) - agentHalfExtentsZ)
);
}
}
#endif

@ -83,6 +83,80 @@ namespace DetourNavigator
return result;
}
bool TileCachedRecastMeshManager::addWater(const osg::Vec2i& cellPosition, const int cellSize,
const btTransform& transform)
{
const auto border = getBorderSize(mSettings);
auto& tilesPositions = mWaterTilesPositions[cellPosition];
bool result = false;
if (cellSize == std::numeric_limits<int>::max())
{
const std::lock_guard<std::mutex> lock(mTilesMutex);
for (auto& tile : mTiles)
{
if (tile.second.addWater(cellPosition, cellSize, transform))
{
tilesPositions.push_back(tile.first);
result = true;
}
}
}
else
{
getTilesPositions(cellSize, transform, mSettings, [&] (const TilePosition& tilePosition)
{
std::unique_lock<std::mutex> lock(mTilesMutex);
auto tile = mTiles.find(tilePosition);
if (tile == mTiles.end())
{
auto tileBounds = makeTileBounds(mSettings, tilePosition);
tileBounds.mMin -= osg::Vec2f(border, border);
tileBounds.mMax += osg::Vec2f(border, border);
tile = mTiles.insert(std::make_pair(tilePosition,
CachedRecastMeshManager(mSettings, tileBounds))).first;
}
if (tile->second.addWater(cellPosition, cellSize, transform))
{
lock.unlock();
tilesPositions.push_back(tilePosition);
result = true;
}
});
}
if (result)
++mRevision;
return result;
}
boost::optional<RecastMeshManager::Water> TileCachedRecastMeshManager::removeWater(const osg::Vec2i& cellPosition)
{
const auto object = mWaterTilesPositions.find(cellPosition);
if (object == mWaterTilesPositions.end())
return boost::none;
boost::optional<RecastMeshManager::Water> result;
for (const auto& tilePosition : object->second)
{
std::unique_lock<std::mutex> lock(mTilesMutex);
const auto tile = mTiles.find(tilePosition);
if (tile == mTiles.end())
continue;
const auto tileResult = tile->second.removeWater(cellPosition);
if (tile->second.isEmpty())
mTiles.erase(tile);
lock.unlock();
if (tileResult && !result)
result = tileResult;
}
if (result)
++mRevision;
return result;
}
std::shared_ptr<RecastMesh> TileCachedRecastMeshManager::getMesh(const TilePosition& tilePosition)
{
const std::lock_guard<std::mutex> lock(mTilesMutex);

@ -21,6 +21,10 @@ namespace DetourNavigator
boost::optional<RemovedRecastMeshObject> removeObject(std::size_t id);
bool addWater(const osg::Vec2i& cellPosition, const int cellSize, const btTransform& transform);
boost::optional<RecastMeshManager::Water> removeWater(const osg::Vec2i& cellPosition);
std::shared_ptr<RecastMesh> getMesh(const TilePosition& tilePosition);
bool hasTile(const TilePosition& tilePosition);
@ -40,6 +44,7 @@ namespace DetourNavigator
std::mutex mTilesMutex;
std::map<TilePosition, CachedRecastMeshManager> mTiles;
std::unordered_map<std::size_t, std::vector<TilePosition>> mObjectsTilesPositions;
std::map<osg::Vec2i, std::vector<TilePosition>> mWaterTilesPositions;
std::size_t mRevision = 0;
};
}

Loading…
Cancel
Save