Add OpenMW 0.47 commits up to 4 Aug 2021

pull/593/head
David Cernat 3 years ago
commit 26033ff7e7

@ -83,6 +83,8 @@ void CSMDoc::Runner::start (bool delayed)
arguments << arguments <<
QString::fromUtf8 (("--data=\""+mProjectPath.parent_path().string()+"\"").c_str()); QString::fromUtf8 (("--data=\""+mProjectPath.parent_path().string()+"\"").c_str());
arguments << "--replace=content";
for (std::vector<std::string>::const_iterator iter (mContentFiles.begin()); for (std::vector<std::string>::const_iterator iter (mContentFiles.begin());
iter!=mContentFiles.end(); ++iter) iter!=mContentFiles.end(); ++iter)
{ {

@ -67,6 +67,10 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
desc.add_options() desc.add_options()
("help", "print help message") ("help", "print help message")
("version", "print version information and quit") ("version", "print version information and quit")
("replace", bpo::value<Files::EscapeStringVector>()->default_value(Files::EscapeStringVector(), "")
->multitoken()->composing(), "settings where the values from the current source should replace those from lower-priority sources instead of being appended")
("data", bpo::value<Files::EscapePathContainer>()->default_value(Files::EscapePathContainer(), "data") ("data", bpo::value<Files::EscapePathContainer>()->default_value(Files::EscapePathContainer(), "data")
->multitoken()->composing(), "set data directories (later directories have higher priority)") ->multitoken()->composing(), "set data directories (later directories have higher priority)")
@ -273,6 +277,15 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
Log(Debug::Error) << "No content file given (esm/esp, nor omwgame/omwaddon). Aborting..."; Log(Debug::Error) << "No content file given (esm/esp, nor omwgame/omwaddon). Aborting...";
return false; return false;
} }
std::set<std::string> contentDedupe;
for (const auto& contentFile : content)
{
if (!contentDedupe.insert(contentFile).second)
{
Log(Debug::Error) << "Content file specified more than once: " << contentFile << ". Aborting...";
return false;
}
}
for (auto& file : content) for (auto& file : content)
{ {

@ -50,6 +50,10 @@ namespace
namespace MWPhysics namespace MWPhysics
{ {
HeightField::HeightField() {}
HeightField::HeightField(const HeightField&, const osg::CopyOp&) {}
HeightField::HeightField(const float* heights, int x, int y, float triSize, float sqrtVerts, float minH, float maxH, const osg::Object* holdObject, PhysicsTaskScheduler* scheduler) HeightField::HeightField(const float* heights, int x, int y, float triSize, float sqrtVerts, float minH, float maxH, const osg::Object* holdObject, PhysicsTaskScheduler* scheduler)
: mHoldObject(holdObject) : mHoldObject(holdObject)
#if BT_BULLET_VERSION < 310 #if BT_BULLET_VERSION < 310

@ -2,6 +2,7 @@
#define OPENMW_MWPHYSICS_HEIGHTFIELD_H #define OPENMW_MWPHYSICS_HEIGHTFIELD_H
#include <osg/ref_ptr> #include <osg/ref_ptr>
#include <osg/Object>
#include <LinearMath/btScalar.h> #include <LinearMath/btScalar.h>
@ -20,12 +21,14 @@ namespace MWPhysics
{ {
class PhysicsTaskScheduler; class PhysicsTaskScheduler;
class HeightField class HeightField : public osg::Object
{ {
public: public:
HeightField(const float* heights, int x, int y, float triSize, float sqrtVerts, float minH, float maxH, const osg::Object* holdObject, PhysicsTaskScheduler* scheduler); HeightField(const float* heights, int x, int y, float triSize, float sqrtVerts, float minH, float maxH, const osg::Object* holdObject, PhysicsTaskScheduler* scheduler);
~HeightField(); ~HeightField();
META_Object(MWPhysics, HeightField)
btCollisionObject* getCollisionObject(); btCollisionObject* getCollisionObject();
const btCollisionObject* getCollisionObject() const; const btCollisionObject* getCollisionObject() const;
const btHeightfieldTerrainShape* getShape() const; const btHeightfieldTerrainShape* getShape() const;
@ -40,6 +43,8 @@ namespace MWPhysics
PhysicsTaskScheduler* mTaskScheduler; PhysicsTaskScheduler* mTaskScheduler;
HeightField();
HeightField(const HeightField&, const osg::CopyOp&);
void operator=(const HeightField&); void operator=(const HeightField&);
HeightField(const HeightField&); HeightField(const HeightField&);
}; };

@ -467,7 +467,7 @@ namespace MWPhysics
void PhysicsSystem::addHeightField (const float* heights, int x, int y, float triSize, float sqrtVerts, float minH, float maxH, const osg::Object* holdObject) void PhysicsSystem::addHeightField (const float* heights, int x, int y, float triSize, float sqrtVerts, float minH, float maxH, const osg::Object* holdObject)
{ {
mHeightFields[std::make_pair(x,y)] = std::make_unique<HeightField>(heights, x, y, triSize, sqrtVerts, minH, maxH, holdObject, mTaskScheduler.get()); mHeightFields[std::make_pair(x,y)] = osg::ref_ptr<HeightField>(new HeightField(heights, x, y, triSize, sqrtVerts, minH, maxH, holdObject, mTaskScheduler.get()));
} }
void PhysicsSystem::removeHeightField (int x, int y) void PhysicsSystem::removeHeightField (int x, int y)

@ -286,7 +286,7 @@ namespace MWPhysics
using ProjectileMap = std::map<int, std::shared_ptr<Projectile>>; using ProjectileMap = std::map<int, std::shared_ptr<Projectile>>;
ProjectileMap mProjectiles; ProjectileMap mProjectiles;
using HeightFieldMap = std::map<std::pair<int, int>, std::unique_ptr<HeightField>>; using HeightFieldMap = std::map<std::pair<int, int>, osg::ref_ptr<HeightField>>;
HeightFieldMap mHeightFields; HeightFieldMap mHeightFields;
bool mDebugDrawEnabled; bool mDebugDrawEnabled;

@ -148,11 +148,9 @@ namespace
{ {
if (ptr.getClass().isDoor() && !ptr.getCellRef().getTeleport()) if (ptr.getClass().isDoor() && !ptr.getCellRef().getTeleport())
{ {
const auto shape = object->getShapeInstance()->getCollisionShape();
btVector3 aabbMin; btVector3 aabbMin;
btVector3 aabbMax; btVector3 aabbMax;
shape->getAabb(btTransform::getIdentity(), aabbMin, aabbMax); object->getShapeInstance()->getCollisionShape()->getAabb(btTransform::getIdentity(), aabbMin, aabbMax);
const auto center = (aabbMax + aabbMin) * 0.5f; const auto center = (aabbMax + aabbMin) * 0.5f;
@ -179,12 +177,7 @@ namespace
navigator.addObject( navigator.addObject(
DetourNavigator::ObjectId(object), DetourNavigator::ObjectId(object),
DetourNavigator::DoorShapes( DetourNavigator::DoorShapes(object->getShapeInstance(), connectionStart, connectionEnd),
*shape,
object->getShapeInstance()->getAvoidCollisionShape(),
connectionStart,
connectionEnd
),
transform transform
); );
} }
@ -192,10 +185,7 @@ namespace
{ {
navigator.addObject( navigator.addObject(
DetourNavigator::ObjectId(object), DetourNavigator::ObjectId(object),
DetourNavigator::ObjectShapes { DetourNavigator::ObjectShapes(object->getShapeInstance()),
*object->getShapeInstance()->getCollisionShape(),
object->getShapeInstance()->getAvoidCollisionShape()
},
object->getTransform() object->getTransform()
); );
} }
@ -432,7 +422,7 @@ namespace MWWorld
} }
if (const auto heightField = mPhysics->getHeightField(cellX, cellY)) if (const auto heightField = mPhysics->getHeightField(cellX, cellY))
navigator->addObject(DetourNavigator::ObjectId(heightField), *heightField->getShape(), navigator->addObject(DetourNavigator::ObjectId(heightField), heightField, *heightField->getShape(),
heightField->getCollisionObject()->getWorldTransform()); heightField->getCollisionObject()->getWorldTransform());
} }

@ -1790,10 +1790,7 @@ namespace MWWorld
void World::updateNavigatorObject(const MWPhysics::Object& object) void World::updateNavigatorObject(const MWPhysics::Object& object)
{ {
const DetourNavigator::ObjectShapes shapes { const DetourNavigator::ObjectShapes shapes(object.getShapeInstance());
*object.getShapeInstance()->getCollisionShape(),
object.getShapeInstance()->getAvoidCollisionShape()
};
mShouldUpdateNavigator = mNavigator->updateObject(DetourNavigator::ObjectId(&object), shapes, object.getTransform()) mShouldUpdateNavigator = mNavigator->updateObject(DetourNavigator::ObjectId(&object), shapes, object.getTransform())
|| mShouldUpdateNavigator; || mShouldUpdateNavigator;
} }

@ -4,6 +4,9 @@
#include <components/detournavigator/exceptions.hpp> #include <components/detournavigator/exceptions.hpp>
#include <components/misc/rng.hpp> #include <components/misc/rng.hpp>
#include <components/loadinglistener/loadinglistener.hpp> #include <components/loadinglistener/loadinglistener.hpp>
#include <components/resource/bulletshape.hpp>
#include <osg/ref_ptr>
#include <BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h> #include <BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h>
#include <BulletCollision/CollisionShapes/btBoxShape.h> #include <BulletCollision/CollisionShapes/btBoxShape.h>
@ -14,6 +17,7 @@
#include <array> #include <array>
#include <deque> #include <deque>
#include <memory>
MATCHER_P3(Vec3fEq, x, y, z, "") MATCHER_P3(Vec3fEq, x, y, z, "")
{ {
@ -80,16 +84,38 @@ namespace
}; };
template <std::size_t size> template <std::size_t size>
btHeightfieldTerrainShape makeSquareHeightfieldTerrainShape(const std::array<btScalar, size>& values, std::unique_ptr<btHeightfieldTerrainShape> makeSquareHeightfieldTerrainShape(const std::array<btScalar, size>& values,
btScalar heightScale = 1, int upAxis = 2, PHY_ScalarType heightDataType = PHY_FLOAT, bool flipQuadEdges = false) btScalar heightScale = 1, int upAxis = 2, PHY_ScalarType heightDataType = PHY_FLOAT, bool flipQuadEdges = false)
{ {
const int width = static_cast<int>(std::sqrt(size)); const int width = static_cast<int>(std::sqrt(size));
const btScalar min = *std::min_element(values.begin(), values.end()); const btScalar min = *std::min_element(values.begin(), values.end());
const btScalar max = *std::max_element(values.begin(), values.end()); const btScalar max = *std::max_element(values.begin(), values.end());
const btScalar greater = std::max(std::abs(min), std::abs(max)); const btScalar greater = std::max(std::abs(min), std::abs(max));
return btHeightfieldTerrainShape(width, width, values.data(), heightScale, -greater, greater, upAxis, heightDataType, flipQuadEdges); return std::make_unique<btHeightfieldTerrainShape>(width, width, values.data(), heightScale, -greater, greater,
upAxis, heightDataType, flipQuadEdges);
}
template <class T>
osg::ref_ptr<const Resource::BulletShapeInstance> makeBulletShapeInstance(std::unique_ptr<T>&& shape)
{
osg::ref_ptr<Resource::BulletShape> bulletShape(new Resource::BulletShape);
bulletShape->mCollisionShape = std::move(shape).release();
return new Resource::BulletShapeInstance(bulletShape);
} }
template <class T>
class CollisionShapeInstance
{
public:
CollisionShapeInstance(std::unique_ptr<T>&& shape) : mInstance(makeBulletShapeInstance(std::move(shape))) {}
T& shape() { return static_cast<T&>(*mInstance->mCollisionShape); }
const osg::ref_ptr<const Resource::BulletShapeInstance>& instance() const { return mInstance; }
private:
osg::ref_ptr<const Resource::BulletShapeInstance> mInstance;
};
TEST_F(DetourNavigatorNavigatorTest, find_path_for_empty_should_return_empty) TEST_F(DetourNavigatorNavigatorTest, find_path_for_empty_should_return_empty)
{ {
EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut), EXPECT_EQ(mNavigator->findPath(mAgentHalfExtents, mStepSize, mStart, mEnd, Flag_walk, mAreaCosts, mOut),
@ -122,11 +148,12 @@ namespace
0, -25, -100, -100, -100, 0, -25, -100, -100, -100,
0, -25, -100, -100, -100, 0, -25, -100, -100, -100,
}}; }};
btHeightfieldTerrainShape shape = makeSquareHeightfieldTerrainShape(heightfieldData); const auto shapePtr = makeSquareHeightfieldTerrainShape(heightfieldData);
btHeightfieldTerrainShape& shape = *shapePtr;
shape.setLocalScaling(btVector3(128, 128, 1)); shape.setLocalScaling(btVector3(128, 128, 1));
mNavigator->addAgent(mAgentHalfExtents); mNavigator->addAgent(mAgentHalfExtents);
mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity()); mNavigator->addObject(ObjectId(&shape), nullptr, shape, btTransform::getIdentity());
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition);
mNavigator->wait(mListener, WaitConditionType::requiredTilesPresent); mNavigator->wait(mListener, WaitConditionType::requiredTilesPresent);
@ -167,15 +194,15 @@ namespace
0, -25, -100, -100, -100, 0, -25, -100, -100, -100,
0, -25, -100, -100, -100, 0, -25, -100, -100, -100,
}}; }};
btHeightfieldTerrainShape heightfieldShape = makeSquareHeightfieldTerrainShape(heightfieldData); const auto heightfieldShapePtr = makeSquareHeightfieldTerrainShape(heightfieldData);
btHeightfieldTerrainShape& heightfieldShape = *heightfieldShapePtr;
heightfieldShape.setLocalScaling(btVector3(128, 128, 1)); heightfieldShape.setLocalScaling(btVector3(128, 128, 1));
btBoxShape boxShape(btVector3(20, 20, 100)); CollisionShapeInstance compound(std::make_unique<btCompoundShape>());
btCompoundShape compoundShape; compound.shape().addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(0, 0, 0)), new btBoxShape(btVector3(20, 20, 100)));
compoundShape.addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(0, 0, 0)), &boxShape);
mNavigator->addAgent(mAgentHalfExtents); mNavigator->addAgent(mAgentHalfExtents);
mNavigator->addObject(ObjectId(&heightfieldShape), heightfieldShape, btTransform::getIdentity()); mNavigator->addObject(ObjectId(&heightfieldShape), nullptr, heightfieldShape, btTransform::getIdentity());
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(mListener, WaitConditionType::allJobsDone);
@ -206,7 +233,7 @@ namespace
Vec3fEq(204, -204, 1.99998295307159423828125) Vec3fEq(204, -204, 1.99998295307159423828125)
)) << mPath; )) << mPath;
mNavigator->addObject(ObjectId(&compoundShape), compoundShape, btTransform::getIdentity()); mNavigator->addObject(ObjectId(&compound.shape()), ObjectShapes(compound.instance()), btTransform::getIdentity());
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(mListener, WaitConditionType::allJobsDone);
@ -250,16 +277,16 @@ namespace
0, -25, -100, -100, -100, 0, -25, -100, -100, -100,
0, -25, -100, -100, -100, 0, -25, -100, -100, -100,
}}; }};
btHeightfieldTerrainShape heightfieldShape = makeSquareHeightfieldTerrainShape(heightfieldData); const auto heightfieldShapePtr = makeSquareHeightfieldTerrainShape(heightfieldData);
btHeightfieldTerrainShape& heightfieldShape = *heightfieldShapePtr;
heightfieldShape.setLocalScaling(btVector3(128, 128, 1)); heightfieldShape.setLocalScaling(btVector3(128, 128, 1));
btBoxShape boxShape(btVector3(20, 20, 100)); CollisionShapeInstance compound(std::make_unique<btCompoundShape>());
btCompoundShape compoundShape; compound.shape().addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(0, 0, 0)), new btBoxShape(btVector3(20, 20, 100)));
compoundShape.addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(0, 0, 0)), &boxShape);
mNavigator->addAgent(mAgentHalfExtents); mNavigator->addAgent(mAgentHalfExtents);
mNavigator->addObject(ObjectId(&heightfieldShape), heightfieldShape, btTransform::getIdentity()); mNavigator->addObject(ObjectId(&heightfieldShape), nullptr, heightfieldShape, btTransform::getIdentity());
mNavigator->addObject(ObjectId(&compoundShape), compoundShape, btTransform::getIdentity()); mNavigator->addObject(ObjectId(&compound.shape()), ObjectShapes(compound.instance()), btTransform::getIdentity());
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(mListener, WaitConditionType::allJobsDone);
@ -291,9 +318,9 @@ namespace
Vec3fEq(204, -204, 1.99998295307159423828125) Vec3fEq(204, -204, 1.99998295307159423828125)
)) << mPath; )) << mPath;
compoundShape.updateChildTransform(0, btTransform(btMatrix3x3::getIdentity(), btVector3(1000, 0, 0))); compound.shape().updateChildTransform(0, btTransform(btMatrix3x3::getIdentity(), btVector3(1000, 0, 0)));
mNavigator->updateObject(ObjectId(&compoundShape), compoundShape, btTransform::getIdentity()); mNavigator->updateObject(ObjectId(&compound.shape()), ObjectShapes(compound.instance()), btTransform::getIdentity());
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(mListener, WaitConditionType::allJobsDone);
@ -336,7 +363,8 @@ namespace
0, -25, -100, -100, -100, 0, -25, -100, -100, -100,
0, -25, -100, -100, -100, 0, -25, -100, -100, -100,
}}; }};
btHeightfieldTerrainShape shape = makeSquareHeightfieldTerrainShape(heightfieldData); const auto shapePtr = makeSquareHeightfieldTerrainShape(heightfieldData);
btHeightfieldTerrainShape& shape = *shapePtr;
shape.setLocalScaling(btVector3(128, 128, 1)); shape.setLocalScaling(btVector3(128, 128, 1));
const std::array<btScalar, 5 * 5> heightfieldData2 {{ const std::array<btScalar, 5 * 5> heightfieldData2 {{
@ -346,12 +374,13 @@ namespace
-25, -25, -25, -25, -25, -25, -25, -25, -25, -25,
-25, -25, -25, -25, -25, -25, -25, -25, -25, -25,
}}; }};
btHeightfieldTerrainShape shape2 = makeSquareHeightfieldTerrainShape(heightfieldData2); const auto shapePtr2 = makeSquareHeightfieldTerrainShape(heightfieldData2);
btHeightfieldTerrainShape& shape2 = *shapePtr2;
shape2.setLocalScaling(btVector3(128, 128, 1)); shape2.setLocalScaling(btVector3(128, 128, 1));
mNavigator->addAgent(mAgentHalfExtents); mNavigator->addAgent(mAgentHalfExtents);
mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity()); mNavigator->addObject(ObjectId(&shape), nullptr, shape, btTransform::getIdentity());
mNavigator->addObject(ObjectId(&shape2), shape2, btTransform::getIdentity()); mNavigator->addObject(ObjectId(&shape2), nullptr, shape2, btTransform::getIdentity());
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(mListener, WaitConditionType::allJobsDone);
@ -385,6 +414,8 @@ namespace
TEST_F(DetourNavigatorNavigatorTest, path_should_be_around_avoid_shape) TEST_F(DetourNavigatorNavigatorTest, path_should_be_around_avoid_shape)
{ {
osg::ref_ptr<Resource::BulletShape> bulletShape(new Resource::BulletShape);
std::array<btScalar, 5 * 5> heightfieldData {{ std::array<btScalar, 5 * 5> heightfieldData {{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, -25, -25, -25, -25, 0, -25, -25, -25, -25,
@ -392,8 +423,9 @@ namespace
0, -25, -100, -100, -100, 0, -25, -100, -100, -100,
0, -25, -100, -100, -100, 0, -25, -100, -100, -100,
}}; }};
btHeightfieldTerrainShape shape = makeSquareHeightfieldTerrainShape(heightfieldData); auto shapePtr = makeSquareHeightfieldTerrainShape(heightfieldData);
shape.setLocalScaling(btVector3(128, 128, 1)); shapePtr->setLocalScaling(btVector3(128, 128, 1));
bulletShape->mCollisionShape = shapePtr.release();
std::array<btScalar, 5 * 5> heightfieldDataAvoid {{ std::array<btScalar, 5 * 5> heightfieldDataAvoid {{
-25, -25, -25, -25, -25, -25, -25, -25, -25, -25,
@ -402,11 +434,14 @@ namespace
-25, -25, -25, -25, -25, -25, -25, -25, -25, -25,
-25, -25, -25, -25, -25, -25, -25, -25, -25, -25,
}}; }};
btHeightfieldTerrainShape shapeAvoid = makeSquareHeightfieldTerrainShape(heightfieldDataAvoid); auto shapeAvoidPtr = makeSquareHeightfieldTerrainShape(heightfieldDataAvoid);
shapeAvoid.setLocalScaling(btVector3(128, 128, 1)); shapeAvoidPtr->setLocalScaling(btVector3(128, 128, 1));
bulletShape->mAvoidCollisionShape = shapeAvoidPtr.release();
osg::ref_ptr<const Resource::BulletShapeInstance> instance(new Resource::BulletShapeInstance(bulletShape));
mNavigator->addAgent(mAgentHalfExtents); mNavigator->addAgent(mAgentHalfExtents);
mNavigator->addObject(ObjectId(&shape), ObjectShapes {shape, &shapeAvoid}, btTransform::getIdentity()); mNavigator->addObject(ObjectId(instance->getCollisionShape()), ObjectShapes(instance), btTransform::getIdentity());
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(mListener, WaitConditionType::allJobsDone);
@ -448,12 +483,13 @@ namespace
-50, -100, -150, -100, -100, -50, -100, -150, -100, -100,
0, -50, -100, -100, -100, 0, -50, -100, -100, -100,
}}; }};
btHeightfieldTerrainShape shape = makeSquareHeightfieldTerrainShape(heightfieldData); const auto shapePtr = makeSquareHeightfieldTerrainShape(heightfieldData);
btHeightfieldTerrainShape& shape = *shapePtr;
shape.setLocalScaling(btVector3(128, 128, 1)); shape.setLocalScaling(btVector3(128, 128, 1));
mNavigator->addAgent(mAgentHalfExtents); mNavigator->addAgent(mAgentHalfExtents);
mNavigator->addWater(osg::Vec2i(0, 0), 128 * 4, 300, btTransform::getIdentity()); mNavigator->addWater(osg::Vec2i(0, 0), 128 * 4, 300, btTransform::getIdentity());
mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity()); mNavigator->addObject(ObjectId(&shape), nullptr, shape, btTransform::getIdentity());
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(mListener, WaitConditionType::allJobsDone);
@ -495,12 +531,13 @@ namespace
0, -100, -100, -100, -100, -100, 0, 0, -100, -100, -100, -100, -100, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
}}; }};
btHeightfieldTerrainShape shape = makeSquareHeightfieldTerrainShape(heightfieldData); const auto shapePtr = makeSquareHeightfieldTerrainShape(heightfieldData);
btHeightfieldTerrainShape& shape = *shapePtr;
shape.setLocalScaling(btVector3(128, 128, 1)); shape.setLocalScaling(btVector3(128, 128, 1));
mNavigator->addAgent(mAgentHalfExtents); mNavigator->addAgent(mAgentHalfExtents);
mNavigator->addWater(osg::Vec2i(0, 0), 128 * 4, -25, btTransform::getIdentity()); mNavigator->addWater(osg::Vec2i(0, 0), 128 * 4, -25, btTransform::getIdentity());
mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity()); mNavigator->addObject(ObjectId(&shape), nullptr, shape, btTransform::getIdentity());
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(mListener, WaitConditionType::allJobsDone);
@ -541,11 +578,12 @@ namespace
0, -100, -100, -100, -100, -100, 0, 0, -100, -100, -100, -100, -100, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
}}; }};
btHeightfieldTerrainShape shape = makeSquareHeightfieldTerrainShape(heightfieldData); const auto shapePtr = makeSquareHeightfieldTerrainShape(heightfieldData);
btHeightfieldTerrainShape& shape = *shapePtr;
shape.setLocalScaling(btVector3(128, 128, 1)); shape.setLocalScaling(btVector3(128, 128, 1));
mNavigator->addAgent(mAgentHalfExtents); mNavigator->addAgent(mAgentHalfExtents);
mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity()); mNavigator->addObject(ObjectId(&shape), nullptr, shape, btTransform::getIdentity());
mNavigator->addWater(osg::Vec2i(0, 0), std::numeric_limits<int>::max(), -25, btTransform::getIdentity()); mNavigator->addWater(osg::Vec2i(0, 0), std::numeric_limits<int>::max(), -25, btTransform::getIdentity());
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(mListener, WaitConditionType::allJobsDone);
@ -587,12 +625,13 @@ namespace
0, -100, -100, -100, -100, -100, 0, 0, -100, -100, -100, -100, -100, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
}}; }};
btHeightfieldTerrainShape shape = makeSquareHeightfieldTerrainShape(heightfieldData); const auto shapePtr = makeSquareHeightfieldTerrainShape(heightfieldData);
btHeightfieldTerrainShape& shape = *shapePtr;
shape.setLocalScaling(btVector3(128, 128, 1)); shape.setLocalScaling(btVector3(128, 128, 1));
mNavigator->addAgent(mAgentHalfExtents); mNavigator->addAgent(mAgentHalfExtents);
mNavigator->addWater(osg::Vec2i(0, 0), 128 * 4, -25, btTransform::getIdentity()); mNavigator->addWater(osg::Vec2i(0, 0), 128 * 4, -25, btTransform::getIdentity());
mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity()); mNavigator->addObject(ObjectId(&shape), nullptr, shape, btTransform::getIdentity());
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(mListener, WaitConditionType::allJobsDone);
@ -631,11 +670,12 @@ namespace
0, -25, -100, -100, -100, 0, -25, -100, -100, -100,
0, -25, -100, -100, -100, 0, -25, -100, -100, -100,
}}; }};
btHeightfieldTerrainShape shape = makeSquareHeightfieldTerrainShape(heightfieldData); const auto shapePtr = makeSquareHeightfieldTerrainShape(heightfieldData);
btHeightfieldTerrainShape& shape = *shapePtr;
shape.setLocalScaling(btVector3(128, 128, 1)); shape.setLocalScaling(btVector3(128, 128, 1));
mNavigator->addAgent(mAgentHalfExtents); mNavigator->addAgent(mAgentHalfExtents);
mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity()); mNavigator->addObject(ObjectId(&shape), nullptr, shape, btTransform::getIdentity());
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(mListener, WaitConditionType::allJobsDone);
@ -643,7 +683,7 @@ namespace
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(mListener, WaitConditionType::allJobsDone);
mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity()); mNavigator->addObject(ObjectId(&shape), nullptr, shape, btTransform::getIdentity());
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(mListener, WaitConditionType::allJobsDone);
@ -684,11 +724,12 @@ namespace
0, -25, -100, -100, -100, 0, -25, -100, -100, -100,
0, -25, -100, -100, -100, 0, -25, -100, -100, -100,
}}; }};
btHeightfieldTerrainShape shape = makeSquareHeightfieldTerrainShape(heightfieldData); const auto shapePtr = makeSquareHeightfieldTerrainShape(heightfieldData);
btHeightfieldTerrainShape& shape = *shapePtr;
shape.setLocalScaling(btVector3(128, 128, 1)); shape.setLocalScaling(btVector3(128, 128, 1));
mNavigator->addAgent(mAgentHalfExtents); mNavigator->addAgent(mAgentHalfExtents);
mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity()); mNavigator->addObject(ObjectId(&shape), nullptr, shape, btTransform::getIdentity());
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(mListener, WaitConditionType::allJobsDone);
@ -716,27 +757,29 @@ namespace
0, -25, -100, -100, -100, 0, -25, -100, -100, -100,
0, -25, -100, -100, -100, 0, -25, -100, -100, -100,
}}; }};
btHeightfieldTerrainShape heightfieldShape = makeSquareHeightfieldTerrainShape(heightfieldData); const auto heightfieldShapePtr = makeSquareHeightfieldTerrainShape(heightfieldData);
btHeightfieldTerrainShape& heightfieldShape = *heightfieldShapePtr;
heightfieldShape.setLocalScaling(btVector3(128, 128, 1)); heightfieldShape.setLocalScaling(btVector3(128, 128, 1));
const std::vector<btBoxShape> boxShapes(100, btVector3(20, 20, 100)); std::vector<CollisionShapeInstance<btBoxShape>> boxes;
std::generate_n(std::back_inserter(boxes), 100, [] { return std::make_unique<btBoxShape>(btVector3(20, 20, 100)); });
mNavigator->addAgent(mAgentHalfExtents); mNavigator->addAgent(mAgentHalfExtents);
mNavigator->addObject(ObjectId(&heightfieldShape), heightfieldShape, btTransform::getIdentity()); mNavigator->addObject(ObjectId(&heightfieldShape), nullptr, heightfieldShape, btTransform::getIdentity());
for (std::size_t i = 0; i < boxShapes.size(); ++i) for (std::size_t i = 0; i < boxes.size(); ++i)
{ {
const btTransform transform(btMatrix3x3::getIdentity(), btVector3(i * 10, i * 10, i * 10)); const btTransform transform(btMatrix3x3::getIdentity(), btVector3(i * 10, i * 10, i * 10));
mNavigator->addObject(ObjectId(&boxShapes[i]), boxShapes[i], transform); mNavigator->addObject(ObjectId(&boxes[i].shape()), ObjectShapes(boxes[i].instance()), transform);
} }
std::this_thread::sleep_for(std::chrono::microseconds(1)); std::this_thread::sleep_for(std::chrono::microseconds(1));
for (std::size_t i = 0; i < boxShapes.size(); ++i) for (std::size_t i = 0; i < boxes.size(); ++i)
{ {
const btTransform transform(btMatrix3x3::getIdentity(), btVector3(i * 10 + 1, i * 10 + 1, i * 10 + 1)); const btTransform transform(btMatrix3x3::getIdentity(), btVector3(i * 10 + 1, i * 10 + 1, i * 10 + 1));
mNavigator->updateObject(ObjectId(&boxShapes[i]), boxShapes[i], transform); mNavigator->updateObject(ObjectId(&boxes[i].shape()), ObjectShapes(boxes[i].instance()), transform);
} }
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition);
@ -773,14 +816,15 @@ namespace
TEST_F(DetourNavigatorNavigatorTest, update_changed_multiple_times_object_should_delay_navmesh_change) TEST_F(DetourNavigatorNavigatorTest, update_changed_multiple_times_object_should_delay_navmesh_change)
{ {
const std::vector<btBoxShape> shapes(100, btVector3(64, 64, 64)); std::vector<CollisionShapeInstance<btBoxShape>> shapes;
std::generate_n(std::back_inserter(shapes), 100, [] { return std::make_unique<btBoxShape>(btVector3(64, 64, 64)); });
mNavigator->addAgent(mAgentHalfExtents); mNavigator->addAgent(mAgentHalfExtents);
for (std::size_t i = 0; i < shapes.size(); ++i) for (std::size_t i = 0; i < shapes.size(); ++i)
{ {
const btTransform transform(btMatrix3x3::getIdentity(), btVector3(i * 32, i * 32, i * 32)); const btTransform transform(btMatrix3x3::getIdentity(), btVector3(i * 32, i * 32, i * 32));
mNavigator->addObject(ObjectId(&shapes[i]), shapes[i], transform); mNavigator->addObject(ObjectId(&shapes[i].shape()), ObjectShapes(shapes[i].instance()), transform);
} }
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(mListener, WaitConditionType::allJobsDone);
@ -789,7 +833,7 @@ namespace
for (std::size_t i = 0; i < shapes.size(); ++i) for (std::size_t i = 0; i < shapes.size(); ++i)
{ {
const btTransform transform(btMatrix3x3::getIdentity(), btVector3(i * 32 + 1, i * 32 + 1, i * 32 + 1)); const btTransform transform(btMatrix3x3::getIdentity(), btVector3(i * 32 + 1, i * 32 + 1, i * 32 + 1));
mNavigator->updateObject(ObjectId(&shapes[i]), shapes[i], transform); mNavigator->updateObject(ObjectId(&shapes[i].shape()), ObjectShapes(shapes[i].instance()), transform);
} }
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(mListener, WaitConditionType::allJobsDone);
@ -797,7 +841,7 @@ namespace
for (std::size_t i = 0; i < shapes.size(); ++i) for (std::size_t i = 0; i < shapes.size(); ++i)
{ {
const btTransform transform(btMatrix3x3::getIdentity(), btVector3(i * 32 + 2, i * 32 + 2, i * 32 + 2)); const btTransform transform(btMatrix3x3::getIdentity(), btVector3(i * 32 + 2, i * 32 + 2, i * 32 + 2));
mNavigator->updateObject(ObjectId(&shapes[i]), shapes[i], transform); mNavigator->updateObject(ObjectId(&shapes[i].shape()), ObjectShapes(shapes[i].instance()), transform);
} }
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(mListener, WaitConditionType::allJobsDone);
@ -817,11 +861,12 @@ namespace
0, -25, -100, -100, -100, 0, -25, -100, -100, -100,
0, -25, -100, -100, -100, 0, -25, -100, -100, -100,
}}; }};
btHeightfieldTerrainShape shape = makeSquareHeightfieldTerrainShape(heightfieldData); const auto shapePtr = makeSquareHeightfieldTerrainShape(heightfieldData);
btHeightfieldTerrainShape& shape = *shapePtr;
shape.setLocalScaling(btVector3(128, 128, 1)); shape.setLocalScaling(btVector3(128, 128, 1));
mNavigator->addAgent(mAgentHalfExtents); mNavigator->addAgent(mAgentHalfExtents);
mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity()); mNavigator->addObject(ObjectId(&shape), nullptr, shape, btTransform::getIdentity());
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(mListener, WaitConditionType::allJobsDone);
@ -840,19 +885,20 @@ namespace
0, -25, -100, -100, -100, 0, -25, -100, -100, -100,
0, -25, -100, -100, -100, 0, -25, -100, -100, -100,
}}; }};
btHeightfieldTerrainShape heightfieldShape = makeSquareHeightfieldTerrainShape(heightfieldData); const auto heightfieldShapePtr = makeSquareHeightfieldTerrainShape(heightfieldData);
btHeightfieldTerrainShape& heightfieldShape = *heightfieldShapePtr;
heightfieldShape.setLocalScaling(btVector3(128, 128, 1)); heightfieldShape.setLocalScaling(btVector3(128, 128, 1));
const btBoxShape oscillatingBoxShape(btVector3(20, 20, 20)); CollisionShapeInstance oscillatingBox(std::make_unique<btBoxShape>(btVector3(20, 20, 20)));
const btVector3 oscillatingBoxShapePosition(32, 32, 400); const btVector3 oscillatingBoxShapePosition(32, 32, 400);
const btBoxShape boderBoxShape(btVector3(50, 50, 50)); CollisionShapeInstance boderBox(std::make_unique<btBoxShape>(btVector3(50, 50, 50)));
mNavigator->addAgent(mAgentHalfExtents); mNavigator->addAgent(mAgentHalfExtents);
mNavigator->addObject(ObjectId(&heightfieldShape), heightfieldShape, btTransform::getIdentity()); mNavigator->addObject(ObjectId(&heightfieldShape), nullptr, heightfieldShape, btTransform::getIdentity());
mNavigator->addObject(ObjectId(&oscillatingBoxShape), oscillatingBoxShape, mNavigator->addObject(ObjectId(&oscillatingBox.shape()), ObjectShapes(oscillatingBox.instance()),
btTransform(btMatrix3x3::getIdentity(), oscillatingBoxShapePosition)); btTransform(btMatrix3x3::getIdentity(), oscillatingBoxShapePosition));
// add this box to make navmesh bound box independent from oscillatingBoxShape rotations // add this box to make navmesh bound box independent from oscillatingBoxShape rotations
mNavigator->addObject(ObjectId(&boderBoxShape), boderBoxShape, mNavigator->addObject(ObjectId(&boderBox.shape()), ObjectShapes(boderBox.instance()),
btTransform(btMatrix3x3::getIdentity(), oscillatingBoxShapePosition + btVector3(0, 0, 200))); btTransform(btMatrix3x3::getIdentity(), oscillatingBoxShapePosition + btVector3(0, 0, 200)));
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(mListener, WaitConditionType::allJobsDone);
@ -869,7 +915,7 @@ namespace
{ {
const btTransform transform(btQuaternion(btVector3(0, 0, 1), n * 2 * osg::PI / 10), const btTransform transform(btQuaternion(btVector3(0, 0, 1), n * 2 * osg::PI / 10),
oscillatingBoxShapePosition); oscillatingBoxShapePosition);
mNavigator->updateObject(ObjectId(&oscillatingBoxShape), oscillatingBoxShape, transform); mNavigator->updateObject(ObjectId(&oscillatingBox.shape()), ObjectShapes(oscillatingBox.instance()), transform);
mNavigator->update(mPlayerPosition); mNavigator->update(mPlayerPosition);
mNavigator->wait(mListener, WaitConditionType::allJobsDone); mNavigator->wait(mListener, WaitConditionType::allJobsDone);
} }

@ -49,7 +49,7 @@ namespace
TEST_F(DetourNavigatorRecastMeshBuilderTest, create_for_empty_should_return_empty) TEST_F(DetourNavigatorRecastMeshBuilderTest, create_for_empty_should_return_empty)
{ {
RecastMeshBuilder builder(mSettings, mBounds); RecastMeshBuilder builder(mSettings, mBounds);
const auto recastMesh = builder.create(mGeneration, mRevision); const auto recastMesh = std::move(builder).create(mGeneration, mRevision);
EXPECT_EQ(recastMesh->getVertices(), std::vector<float>()); EXPECT_EQ(recastMesh->getVertices(), std::vector<float>());
EXPECT_EQ(recastMesh->getIndices(), std::vector<int>()); EXPECT_EQ(recastMesh->getIndices(), std::vector<int>());
EXPECT_EQ(recastMesh->getAreaTypes(), std::vector<AreaType>()); EXPECT_EQ(recastMesh->getAreaTypes(), std::vector<AreaType>());
@ -63,7 +63,7 @@ namespace
RecastMeshBuilder builder(mSettings, mBounds); RecastMeshBuilder builder(mSettings, mBounds);
builder.addObject(static_cast<const btCollisionShape&>(shape), btTransform::getIdentity(), AreaType_ground); builder.addObject(static_cast<const btCollisionShape&>(shape), btTransform::getIdentity(), AreaType_ground);
const auto recastMesh = builder.create(mGeneration, mRevision); const auto recastMesh = std::move(builder).create(mGeneration, mRevision);
EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({ EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({
1, 0, -1, 1, 0, -1,
-1, 0, 1, -1, 0, 1,
@ -84,7 +84,7 @@ namespace
btTransform(btMatrix3x3::getIdentity().scaled(btVector3(1, 2, 3)), btVector3(1, 2, 3)), btTransform(btMatrix3x3::getIdentity().scaled(btVector3(1, 2, 3)), btVector3(1, 2, 3)),
AreaType_ground AreaType_ground
); );
const auto recastMesh = builder.create(mGeneration, mRevision); const auto recastMesh = std::move(builder).create(mGeneration, mRevision);
EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({ EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({
2, 3, 0, 2, 3, 0,
0, 3, 4, 0, 3, 4,
@ -100,7 +100,7 @@ namespace
btHeightfieldTerrainShape shape(2, 2, heightfieldData.data(), 1, 0, 0, 2, PHY_FLOAT, false); btHeightfieldTerrainShape shape(2, 2, heightfieldData.data(), 1, 0, 0, 2, PHY_FLOAT, false);
RecastMeshBuilder builder(mSettings, mBounds); RecastMeshBuilder builder(mSettings, mBounds);
builder.addObject(static_cast<const btCollisionShape&>(shape), btTransform::getIdentity(), AreaType_ground); builder.addObject(static_cast<const btCollisionShape&>(shape), btTransform::getIdentity(), AreaType_ground);
const auto recastMesh = builder.create(mGeneration, mRevision); const auto recastMesh = std::move(builder).create(mGeneration, mRevision);
EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({ EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({
-0.5, 0, -0.5, -0.5, 0, -0.5,
-0.5, 0, 0.5, -0.5, 0, 0.5,
@ -116,7 +116,7 @@ namespace
btBoxShape shape(btVector3(1, 1, 2)); btBoxShape shape(btVector3(1, 1, 2));
RecastMeshBuilder builder(mSettings, mBounds); RecastMeshBuilder builder(mSettings, mBounds);
builder.addObject(static_cast<const btCollisionShape&>(shape), btTransform::getIdentity(), AreaType_ground); builder.addObject(static_cast<const btCollisionShape&>(shape), btTransform::getIdentity(), AreaType_ground);
const auto recastMesh = builder.create(mGeneration, mRevision); const auto recastMesh = std::move(builder).create(mGeneration, mRevision);
EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({ EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({
1, 2, 1, 1, 2, 1,
-1, 2, 1, -1, 2, 1,
@ -163,7 +163,7 @@ namespace
btTransform::getIdentity(), btTransform::getIdentity(),
AreaType_ground AreaType_ground
); );
const auto recastMesh = builder.create(mGeneration, mRevision); const auto recastMesh = std::move(builder).create(mGeneration, mRevision);
EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({ EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({
-1, -2, -1, -1, -2, -1,
-1, -2, 1, -1, -2, 1,
@ -210,7 +210,7 @@ namespace
btTransform(btMatrix3x3::getIdentity().scaled(btVector3(1, 2, 3)), btVector3(1, 2, 3)), btTransform(btMatrix3x3::getIdentity().scaled(btVector3(1, 2, 3)), btVector3(1, 2, 3)),
AreaType_ground AreaType_ground
); );
const auto recastMesh = builder.create(mGeneration, mRevision); const auto recastMesh = std::move(builder).create(mGeneration, mRevision);
EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({ EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({
2, 3, 0, 2, 3, 0,
0, 3, 4, 0, 3, 4,
@ -234,7 +234,7 @@ namespace
btTransform(btMatrix3x3::getIdentity().scaled(btVector3(1, 2, 3)), btVector3(1, 2, 3)), btTransform(btMatrix3x3::getIdentity().scaled(btVector3(1, 2, 3)), btVector3(1, 2, 3)),
AreaType_ground AreaType_ground
); );
const auto recastMesh = builder.create(mGeneration, mRevision); const auto recastMesh = std::move(builder).create(mGeneration, mRevision);
EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({ EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({
3, 12, 2, 3, 12, 2,
1, 12, 10, 1, 12, 10,
@ -256,7 +256,7 @@ namespace
btTransform::getIdentity(), btTransform::getIdentity(),
AreaType_ground AreaType_ground
); );
const auto recastMesh = builder.create(mGeneration, mRevision); const auto recastMesh = std::move(builder).create(mGeneration, mRevision);
EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({ EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({
1, 0, -1, 1, 0, -1,
-1, 0, 1, -1, 0, 1,
@ -284,7 +284,7 @@ namespace
btTransform::getIdentity(), btTransform::getIdentity(),
AreaType_ground AreaType_ground
); );
const auto recastMesh = builder.create(mGeneration, mRevision); const auto recastMesh = std::move(builder).create(mGeneration, mRevision);
EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({ EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({
-0.2f, 0, -0.3f, -0.2f, 0, -0.3f,
-0.3f, 0, -0.2f, -0.3f, 0, -0.2f,
@ -309,7 +309,7 @@ namespace
static_cast<btScalar>(-osg::PI_4))), static_cast<btScalar>(-osg::PI_4))),
AreaType_ground AreaType_ground
); );
const auto recastMesh = builder.create(mGeneration, mRevision); const auto recastMesh = std::move(builder).create(mGeneration, mRevision);
EXPECT_THAT(recastMesh->getVertices(), Pointwise(FloatNear(1e-5), std::vector<float>({ EXPECT_THAT(recastMesh->getVertices(), Pointwise(FloatNear(1e-5), std::vector<float>({
0, -0.70710659027099609375, -3.535533905029296875, 0, -0.70710659027099609375, -3.535533905029296875,
0, 0.707107067108154296875, -3.535533905029296875, 0, 0.707107067108154296875, -3.535533905029296875,
@ -334,7 +334,7 @@ namespace
static_cast<btScalar>(osg::PI_4))), static_cast<btScalar>(osg::PI_4))),
AreaType_ground AreaType_ground
); );
const auto recastMesh = builder.create(mGeneration, mRevision); const auto recastMesh = std::move(builder).create(mGeneration, mRevision);
EXPECT_THAT(recastMesh->getVertices(), Pointwise(FloatNear(1e-5), std::vector<float>({ EXPECT_THAT(recastMesh->getVertices(), Pointwise(FloatNear(1e-5), std::vector<float>({
-3.535533905029296875, -0.70710659027099609375, 0, -3.535533905029296875, -0.70710659027099609375, 0,
-3.535533905029296875, 0.707107067108154296875, 0, -3.535533905029296875, 0.707107067108154296875, 0,
@ -359,7 +359,7 @@ namespace
static_cast<btScalar>(osg::PI_4))), static_cast<btScalar>(osg::PI_4))),
AreaType_ground AreaType_ground
); );
const auto recastMesh = builder.create(mGeneration, mRevision); const auto recastMesh = std::move(builder).create(mGeneration, mRevision);
EXPECT_THAT(recastMesh->getVertices(), Pointwise(FloatNear(1e-5), std::vector<float>({ EXPECT_THAT(recastMesh->getVertices(), Pointwise(FloatNear(1e-5), std::vector<float>({
1.41421353816986083984375, 0, 1.1920928955078125e-07, 1.41421353816986083984375, 0, 1.1920928955078125e-07,
-1.41421353816986083984375, 0, -1.1920928955078125e-07, -1.41421353816986083984375, 0, -1.1920928955078125e-07,
@ -388,7 +388,7 @@ namespace
btTransform::getIdentity(), btTransform::getIdentity(),
AreaType_null AreaType_null
); );
const auto recastMesh = builder.create(mGeneration, mRevision); const auto recastMesh = std::move(builder).create(mGeneration, mRevision);
EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({ EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({
1, 0, -1, 1, 0, -1,
-1, 0, 1, -1, 0, 1,
@ -405,7 +405,7 @@ namespace
{ {
RecastMeshBuilder builder(mSettings, mBounds); RecastMeshBuilder builder(mSettings, mBounds);
builder.addWater(1000, btTransform(btMatrix3x3::getIdentity(), btVector3(100, 200, 300))); builder.addWater(1000, btTransform(btMatrix3x3::getIdentity(), btVector3(100, 200, 300)));
const auto recastMesh = builder.create(mGeneration, mRevision); const auto recastMesh = std::move(builder).create(mGeneration, mRevision);
EXPECT_EQ(recastMesh->getWater(), std::vector<RecastMesh::Water>({ EXPECT_EQ(recastMesh->getWater(), std::vector<RecastMesh::Water>({
RecastMesh::Water {1000, btTransform(btMatrix3x3::getIdentity(), btVector3(100, 200, 300))} RecastMesh::Water {1000, btTransform(btMatrix3x3::getIdentity(), btVector3(100, 200, 300))}
})); }));
@ -420,7 +420,7 @@ namespace
RecastMeshBuilder builder(mSettings, mBounds); RecastMeshBuilder builder(mSettings, mBounds);
builder.addObject(static_cast<const btCollisionShape&>(shape), btTransform::getIdentity(), AreaType_ground); builder.addObject(static_cast<const btCollisionShape&>(shape), btTransform::getIdentity(), AreaType_ground);
const auto recastMesh = builder.create(mGeneration, mRevision); const auto recastMesh = std::move(builder).create(mGeneration, mRevision);
EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({ EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({
-1, 0, -1, -1, 0, -1,
-1, 0, 1, -1, 0, 1,

@ -14,20 +14,22 @@ namespace
struct DetourNavigatorRecastMeshObjectTest : Test struct DetourNavigatorRecastMeshObjectTest : Test
{ {
btBoxShape mBoxShape {btVector3(1, 2, 3)}; btBoxShape mBoxShapeImpl {btVector3(1, 2, 3)};
btCompoundShape mCompoundShape {true}; CollisionShape mBoxShape {nullptr, mBoxShapeImpl};
btCompoundShape mCompoundShapeImpl {true};
CollisionShape mCompoundShape {nullptr, mCompoundShapeImpl};
btTransform mTransform {btQuaternion(btVector3(1, 2, 3), 1), btVector3(1, 2, 3)}; btTransform mTransform {btQuaternion(btVector3(1, 2, 3), 1), btVector3(1, 2, 3)};
DetourNavigatorRecastMeshObjectTest() DetourNavigatorRecastMeshObjectTest()
{ {
mCompoundShape.addChildShape(mTransform, std::addressof(mBoxShape)); mCompoundShapeImpl.addChildShape(mTransform, std::addressof(mBoxShapeImpl));
} }
}; };
TEST_F(DetourNavigatorRecastMeshObjectTest, constructed_object_should_have_shape_and_transform) TEST_F(DetourNavigatorRecastMeshObjectTest, constructed_object_should_have_shape_and_transform)
{ {
const RecastMeshObject object(mBoxShape, mTransform, AreaType_ground); const RecastMeshObject object(mBoxShape, mTransform, AreaType_ground);
EXPECT_EQ(std::addressof(object.getShape()), std::addressof(mBoxShape)); EXPECT_EQ(std::addressof(object.getShape()), std::addressof(mBoxShapeImpl));
EXPECT_EQ(object.getTransform(), mTransform); EXPECT_EQ(object.getTransform(), mTransform);
} }
@ -58,14 +60,14 @@ namespace
TEST_F(DetourNavigatorRecastMeshObjectTest, update_for_compound_shape_with_same_transform_and_changed_child_transform_should_return_true) TEST_F(DetourNavigatorRecastMeshObjectTest, update_for_compound_shape_with_same_transform_and_changed_child_transform_should_return_true)
{ {
RecastMeshObject object(mCompoundShape, mTransform, AreaType_ground); RecastMeshObject object(mCompoundShape, mTransform, AreaType_ground);
mCompoundShape.updateChildTransform(0, btTransform::getIdentity()); mCompoundShapeImpl.updateChildTransform(0, btTransform::getIdentity());
EXPECT_TRUE(object.update(mTransform, AreaType_ground)); EXPECT_TRUE(object.update(mTransform, AreaType_ground));
} }
TEST_F(DetourNavigatorRecastMeshObjectTest, repeated_update_for_compound_shape_without_changes_should_return_false) TEST_F(DetourNavigatorRecastMeshObjectTest, repeated_update_for_compound_shape_without_changes_should_return_false)
{ {
RecastMeshObject object(mCompoundShape, mTransform, AreaType_ground); RecastMeshObject object(mCompoundShape, mTransform, AreaType_ground);
mCompoundShape.updateChildTransform(0, btTransform::getIdentity()); mCompoundShapeImpl.updateChildTransform(0, btTransform::getIdentity());
object.update(mTransform, AreaType_ground); object.update(mTransform, AreaType_ground);
EXPECT_FALSE(object.update(mTransform, AreaType_ground)); EXPECT_FALSE(object.update(mTransform, AreaType_ground));
} }
@ -73,7 +75,7 @@ namespace
TEST_F(DetourNavigatorRecastMeshObjectTest, update_for_changed_local_scaling_should_return_true) TEST_F(DetourNavigatorRecastMeshObjectTest, update_for_changed_local_scaling_should_return_true)
{ {
RecastMeshObject object(mBoxShape, mTransform, AreaType_ground); RecastMeshObject object(mBoxShape, mTransform, AreaType_ground);
mBoxShape.setLocalScaling(btVector3(2, 2, 2)); mBoxShapeImpl.setLocalScaling(btVector3(2, 2, 2));
EXPECT_TRUE(object.update(mTransform, AreaType_ground)); EXPECT_TRUE(object.update(mTransform, AreaType_ground));
} }
} }

@ -62,22 +62,25 @@ namespace
{ {
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
EXPECT_TRUE(manager.addObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground)); const CollisionShape shape(nullptr, boxShape);
EXPECT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground));
} }
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_object_for_existing_object_should_return_false) TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_object_for_existing_object_should_return_false)
{ {
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
manager.addObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground); const CollisionShape shape(nullptr, boxShape);
EXPECT_FALSE(manager.addObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground)); manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground);
EXPECT_FALSE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground));
} }
TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_object_should_add_tiles) TEST_F(DetourNavigatorTileCachedRecastMeshManagerTest, add_object_should_add_tiles)
{ {
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground)); const CollisionShape shape(nullptr, boxShape);
ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground));
for (int x = -1; x < 1; ++x) for (int x = -1; x < 1; ++x)
for (int y = -1; y < 1; ++y) for (int y = -1; y < 1; ++y)
ASSERT_TRUE(manager.hasTile(TilePosition(x, y))); ASSERT_TRUE(manager.hasTile(TilePosition(x, y)));
@ -88,8 +91,9 @@ namespace
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
const btTransform transform(btMatrix3x3::getIdentity(), btVector3(getTileSize(mSettings) / mSettings.mRecastScaleFactor, 0, 0)); const btTransform transform(btMatrix3x3::getIdentity(), btVector3(getTileSize(mSettings) / mSettings.mRecastScaleFactor, 0, 0));
manager.addObject(ObjectId(&boxShape), boxShape, transform, AreaType::AreaType_ground); const CollisionShape shape(nullptr, boxShape);
EXPECT_TRUE(manager.updateObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground, manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground);
EXPECT_TRUE(manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground,
[&] (const auto& v) { onChangedTile(v); })); [&] (const auto& v) { onChangedTile(v); }));
EXPECT_THAT( EXPECT_THAT(
mChangedTiles, mChangedTiles,
@ -102,8 +106,9 @@ namespace
{ {
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
manager.addObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground); const CollisionShape shape(nullptr, boxShape);
EXPECT_FALSE(manager.updateObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground, manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground);
EXPECT_FALSE(manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground,
[&] (const auto& v) { onChangedTile(v); })); [&] (const auto& v) { onChangedTile(v); }));
EXPECT_EQ(mChangedTiles, std::vector<TilePosition>()); EXPECT_EQ(mChangedTiles, std::vector<TilePosition>());
} }
@ -112,7 +117,8 @@ namespace
{ {
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
manager.addObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground); const CollisionShape shape(nullptr, boxShape);
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground);
EXPECT_NE(manager.getMesh(TilePosition(-1, -1)), nullptr); EXPECT_NE(manager.getMesh(TilePosition(-1, -1)), nullptr);
EXPECT_NE(manager.getMesh(TilePosition(-1, 0)), nullptr); EXPECT_NE(manager.getMesh(TilePosition(-1, 0)), nullptr);
EXPECT_NE(manager.getMesh(TilePosition(0, -1)), nullptr); EXPECT_NE(manager.getMesh(TilePosition(0, -1)), nullptr);
@ -123,7 +129,8 @@ namespace
{ {
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
manager.addObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground); const CollisionShape shape(nullptr, boxShape);
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground);
EXPECT_EQ(manager.getMesh(TilePosition(1, 0)), nullptr); EXPECT_EQ(manager.getMesh(TilePosition(1, 0)), nullptr);
} }
@ -132,14 +139,15 @@ namespace
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
const btTransform transform(btMatrix3x3::getIdentity(), btVector3(getTileSize(mSettings) / mSettings.mRecastScaleFactor, 0, 0)); const btTransform transform(btMatrix3x3::getIdentity(), btVector3(getTileSize(mSettings) / mSettings.mRecastScaleFactor, 0, 0));
const CollisionShape shape(nullptr, boxShape);
manager.addObject(ObjectId(&boxShape), boxShape, transform, AreaType::AreaType_ground); manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground);
EXPECT_NE(manager.getMesh(TilePosition(0, -1)), nullptr); EXPECT_NE(manager.getMesh(TilePosition(0, -1)), nullptr);
EXPECT_NE(manager.getMesh(TilePosition(0, 0)), nullptr); EXPECT_NE(manager.getMesh(TilePosition(0, 0)), nullptr);
EXPECT_NE(manager.getMesh(TilePosition(1, 0)), nullptr); EXPECT_NE(manager.getMesh(TilePosition(1, 0)), nullptr);
EXPECT_NE(manager.getMesh(TilePosition(1, -1)), nullptr); EXPECT_NE(manager.getMesh(TilePosition(1, -1)), nullptr);
manager.updateObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {}); manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {});
EXPECT_NE(manager.getMesh(TilePosition(-1, -1)), nullptr); EXPECT_NE(manager.getMesh(TilePosition(-1, -1)), nullptr);
EXPECT_NE(manager.getMesh(TilePosition(-1, 0)), nullptr); EXPECT_NE(manager.getMesh(TilePosition(-1, 0)), nullptr);
EXPECT_NE(manager.getMesh(TilePosition(0, -1)), nullptr); EXPECT_NE(manager.getMesh(TilePosition(0, -1)), nullptr);
@ -151,12 +159,13 @@ namespace
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
const btTransform transform(btMatrix3x3::getIdentity(), btVector3(getTileSize(mSettings) / mSettings.mRecastScaleFactor, 0, 0)); const btTransform transform(btMatrix3x3::getIdentity(), btVector3(getTileSize(mSettings) / mSettings.mRecastScaleFactor, 0, 0));
const CollisionShape shape(nullptr, boxShape);
manager.addObject(ObjectId(&boxShape), boxShape, transform, AreaType::AreaType_ground); manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground);
EXPECT_EQ(manager.getMesh(TilePosition(-1, -1)), nullptr); EXPECT_EQ(manager.getMesh(TilePosition(-1, -1)), nullptr);
EXPECT_EQ(manager.getMesh(TilePosition(-1, 0)), nullptr); EXPECT_EQ(manager.getMesh(TilePosition(-1, 0)), nullptr);
manager.updateObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {}); manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {});
EXPECT_EQ(manager.getMesh(TilePosition(1, 0)), nullptr); EXPECT_EQ(manager.getMesh(TilePosition(1, 0)), nullptr);
EXPECT_EQ(manager.getMesh(TilePosition(1, -1)), nullptr); EXPECT_EQ(manager.getMesh(TilePosition(1, -1)), nullptr);
} }
@ -165,7 +174,8 @@ namespace
{ {
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
manager.addObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground); const CollisionShape shape(nullptr, boxShape);
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground);
manager.removeObject(ObjectId(&boxShape)); manager.removeObject(ObjectId(&boxShape));
EXPECT_EQ(manager.getMesh(TilePosition(-1, -1)), nullptr); EXPECT_EQ(manager.getMesh(TilePosition(-1, -1)), nullptr);
EXPECT_EQ(manager.getMesh(TilePosition(-1, 0)), nullptr); EXPECT_EQ(manager.getMesh(TilePosition(-1, 0)), nullptr);
@ -177,14 +187,15 @@ namespace
{ {
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
const CollisionShape shape(nullptr, boxShape);
manager.addObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground); manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground);
EXPECT_NE(manager.getMesh(TilePosition(-1, -1)), nullptr); EXPECT_NE(manager.getMesh(TilePosition(-1, -1)), nullptr);
EXPECT_NE(manager.getMesh(TilePosition(-1, 0)), nullptr); EXPECT_NE(manager.getMesh(TilePosition(-1, 0)), nullptr);
EXPECT_NE(manager.getMesh(TilePosition(0, -1)), nullptr); EXPECT_NE(manager.getMesh(TilePosition(0, -1)), nullptr);
EXPECT_NE(manager.getMesh(TilePosition(0, 0)), nullptr); EXPECT_NE(manager.getMesh(TilePosition(0, 0)), nullptr);
manager.updateObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {}); manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {});
EXPECT_NE(manager.getMesh(TilePosition(-1, -1)), nullptr); EXPECT_NE(manager.getMesh(TilePosition(-1, -1)), nullptr);
EXPECT_NE(manager.getMesh(TilePosition(-1, 0)), nullptr); EXPECT_NE(manager.getMesh(TilePosition(-1, 0)), nullptr);
EXPECT_NE(manager.getMesh(TilePosition(0, -1)), nullptr); EXPECT_NE(manager.getMesh(TilePosition(0, -1)), nullptr);
@ -196,7 +207,8 @@ namespace
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
const auto initialRevision = manager.getRevision(); const auto initialRevision = manager.getRevision();
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
manager.addObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground); const CollisionShape shape(nullptr, boxShape);
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground);
EXPECT_EQ(manager.getRevision(), initialRevision + 1); EXPECT_EQ(manager.getRevision(), initialRevision + 1);
} }
@ -204,9 +216,10 @@ namespace
{ {
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
manager.addObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground); const CollisionShape shape(nullptr, boxShape);
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground);
const auto beforeAddRevision = manager.getRevision(); const auto beforeAddRevision = manager.getRevision();
manager.addObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground); manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground);
EXPECT_EQ(manager.getRevision(), beforeAddRevision); EXPECT_EQ(manager.getRevision(), beforeAddRevision);
} }
@ -215,9 +228,10 @@ namespace
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
const btTransform transform(btMatrix3x3::getIdentity(), btVector3(getTileSize(mSettings) / mSettings.mRecastScaleFactor, 0, 0)); const btTransform transform(btMatrix3x3::getIdentity(), btVector3(getTileSize(mSettings) / mSettings.mRecastScaleFactor, 0, 0));
manager.addObject(ObjectId(&boxShape), boxShape, transform, AreaType::AreaType_ground); const CollisionShape shape(nullptr, boxShape);
manager.addObject(ObjectId(&boxShape), shape, transform, AreaType::AreaType_ground);
const auto beforeUpdateRevision = manager.getRevision(); const auto beforeUpdateRevision = manager.getRevision();
manager.updateObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {}); manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {});
EXPECT_EQ(manager.getRevision(), beforeUpdateRevision + 1); EXPECT_EQ(manager.getRevision(), beforeUpdateRevision + 1);
} }
@ -225,9 +239,10 @@ namespace
{ {
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
manager.addObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground); const CollisionShape shape(nullptr, boxShape);
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground);
const auto beforeUpdateRevision = manager.getRevision(); const auto beforeUpdateRevision = manager.getRevision();
manager.updateObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {}); manager.updateObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground, [] (auto) {});
EXPECT_EQ(manager.getRevision(), beforeUpdateRevision); EXPECT_EQ(manager.getRevision(), beforeUpdateRevision);
} }
@ -235,7 +250,8 @@ namespace
{ {
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
manager.addObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground); const CollisionShape shape(nullptr, boxShape);
manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground);
const auto beforeRemoveRevision = manager.getRevision(); const auto beforeRemoveRevision = manager.getRevision();
manager.removeObject(ObjectId(&boxShape)); manager.removeObject(ObjectId(&boxShape));
EXPECT_EQ(manager.getRevision(), beforeRemoveRevision + 1); EXPECT_EQ(manager.getRevision(), beforeRemoveRevision + 1);
@ -272,7 +288,8 @@ namespace
{ {
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground)); const CollisionShape shape(nullptr, boxShape);
ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground));
const osg::Vec2i cellPosition(0, 0); const osg::Vec2i cellPosition(0, 0);
const int cellSize = std::numeric_limits<int>::max(); const int cellSize = std::numeric_limits<int>::max();
ASSERT_TRUE(manager.addWater(cellPosition, cellSize, btTransform::getIdentity())); ASSERT_TRUE(manager.addWater(cellPosition, cellSize, btTransform::getIdentity()));
@ -314,7 +331,8 @@ namespace
{ {
TileCachedRecastMeshManager manager(mSettings); TileCachedRecastMeshManager manager(mSettings);
const btBoxShape boxShape(btVector3(20, 20, 100)); const btBoxShape boxShape(btVector3(20, 20, 100));
ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), boxShape, btTransform::getIdentity(), AreaType::AreaType_ground)); const CollisionShape shape(nullptr, boxShape);
ASSERT_TRUE(manager.addObject(ObjectId(&boxShape), shape, btTransform::getIdentity(), AreaType::AreaType_ground));
const osg::Vec2i cellPosition(0, 0); const osg::Vec2i cellPosition(0, 0);
const int cellSize = 8192; const int cellSize = 8192;
ASSERT_TRUE(manager.addWater(cellPosition, cellSize, btTransform::getIdentity())); ASSERT_TRUE(manager.addWater(cellPosition, cellSize, btTransform::getIdentity()));

@ -8,12 +8,12 @@ namespace DetourNavigator
: mImpl(settings, bounds, generation) : mImpl(settings, bounds, generation)
{} {}
bool CachedRecastMeshManager::addObject(const ObjectId id, const btCollisionShape& shape, bool CachedRecastMeshManager::addObject(const ObjectId id, const CollisionShape& shape,
const btTransform& transform, const AreaType areaType) const btTransform& transform, const AreaType areaType)
{ {
if (!mImpl.addObject(id, shape, transform, areaType)) if (!mImpl.addObject(id, shape, transform, areaType))
return false; return false;
mCached.reset(); mCached.lock()->reset();
return true; return true;
} }
@ -21,7 +21,7 @@ namespace DetourNavigator
{ {
if (!mImpl.updateObject(id, transform, areaType)) if (!mImpl.updateObject(id, transform, areaType))
return false; return false;
mCached.reset(); mCached.lock()->reset();
return true; return true;
} }
@ -29,7 +29,7 @@ namespace DetourNavigator
{ {
const auto object = mImpl.removeObject(id); const auto object = mImpl.removeObject(id);
if (object) if (object)
mCached.reset(); mCached.lock()->reset();
return object; return object;
} }
@ -38,7 +38,7 @@ namespace DetourNavigator
{ {
if (!mImpl.addWater(cellPosition, cellSize, transform)) if (!mImpl.addWater(cellPosition, cellSize, transform))
return false; return false;
mCached.reset(); mCached.lock()->reset();
return true; return true;
} }
@ -46,15 +46,18 @@ namespace DetourNavigator
{ {
const auto water = mImpl.removeWater(cellPosition); const auto water = mImpl.removeWater(cellPosition);
if (water) if (water)
mCached.reset(); mCached.lock()->reset();
return water; return water;
} }
std::shared_ptr<RecastMesh> CachedRecastMeshManager::getMesh() std::shared_ptr<RecastMesh> CachedRecastMeshManager::getMesh()
{ {
if (!mCached) std::shared_ptr<RecastMesh> cached = *mCached.lock();
mCached = mImpl.getMesh(); if (cached != nullptr)
return mCached; return cached;
cached = mImpl.getMesh();
*mCached.lock() = cached;
return cached;
} }
bool CachedRecastMeshManager::isEmpty() const bool CachedRecastMeshManager::isEmpty() const

@ -4,6 +4,8 @@
#include "recastmeshmanager.hpp" #include "recastmeshmanager.hpp"
#include "version.hpp" #include "version.hpp"
#include <components/misc/guarded.hpp>
namespace DetourNavigator namespace DetourNavigator
{ {
class CachedRecastMeshManager class CachedRecastMeshManager
@ -11,7 +13,7 @@ namespace DetourNavigator
public: public:
CachedRecastMeshManager(const Settings& settings, const TileBounds& bounds, std::size_t generation); CachedRecastMeshManager(const Settings& settings, const TileBounds& bounds, std::size_t generation);
bool addObject(const ObjectId id, const btCollisionShape& shape, const btTransform& transform, bool addObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform,
const AreaType areaType); const AreaType areaType);
bool updateObject(const ObjectId id, const btTransform& transform, const AreaType areaType); bool updateObject(const ObjectId id, const btTransform& transform, const AreaType areaType);
@ -32,7 +34,7 @@ namespace DetourNavigator
private: private:
RecastMeshManager mImpl; RecastMeshManager mImpl;
std::shared_ptr<RecastMesh> mCached; Misc::ScopeGuarded<std::shared_ptr<RecastMesh>> mCached;
}; };
} }

@ -9,6 +9,8 @@
#include "recastmeshtiles.hpp" #include "recastmeshtiles.hpp"
#include "waitconditiontype.hpp" #include "waitconditiontype.hpp"
#include <components/resource/bulletshape.hpp>
namespace ESM namespace ESM
{ {
struct Cell; struct Cell;
@ -24,11 +26,10 @@ namespace DetourNavigator
{ {
struct ObjectShapes struct ObjectShapes
{ {
const btCollisionShape& mShape; osg::ref_ptr<const Resource::BulletShapeInstance> mShapeInstance;
const btCollisionShape* mAvoid;
ObjectShapes(const btCollisionShape& shape, const btCollisionShape* avoid) ObjectShapes(const osg::ref_ptr<const Resource::BulletShapeInstance>& shapeInstance)
: mShape(shape), mAvoid(avoid) : mShapeInstance(shapeInstance)
{} {}
}; };
@ -37,9 +38,9 @@ namespace DetourNavigator
osg::Vec3f mConnectionStart; osg::Vec3f mConnectionStart;
osg::Vec3f mConnectionEnd; osg::Vec3f mConnectionEnd;
DoorShapes(const btCollisionShape& shape, const btCollisionShape* avoid, DoorShapes(const osg::ref_ptr<const Resource::BulletShapeInstance>& shapeInstance,
const osg::Vec3f& connectionStart,const osg::Vec3f& connectionEnd) const osg::Vec3f& connectionStart,const osg::Vec3f& connectionEnd)
: ObjectShapes(shape, avoid) : ObjectShapes(shapeInstance)
, mConnectionStart(connectionStart) , mConnectionStart(connectionStart)
, mConnectionEnd(connectionEnd) , mConnectionEnd(connectionEnd)
{} {}
@ -70,13 +71,15 @@ namespace DetourNavigator
virtual void removeAgent(const osg::Vec3f& agentHalfExtents) = 0; virtual void removeAgent(const osg::Vec3f& agentHalfExtents) = 0;
/** /**
* @brief addObject is used to add object represented by single btCollisionShape and btTransform. * @brief addObject is used to add object represented by single btHeightfieldTerrainShape and btTransform.
* @param id is used to distinguish different objects. * @param id is used to distinguish different objects.
* @param shape must live until object is updated by another shape removed from Navigator. * @param holder shape wrapper to keep shape lifetime after object is removed.
* @param shape must be wrapped by holder.
* @param transform allows to setup object geometry according to its world state. * @param transform allows to setup object geometry according to its world state.
* @return true if object is added, false if there is already object with given id. * @return true if object is added, false if there is already object with given id.
*/ */
virtual bool addObject(const ObjectId id, const btCollisionShape& shape, const btTransform& transform) = 0; virtual bool addObject(const ObjectId id, const osg::ref_ptr<const osg::Object>& holder,
const btHeightfieldTerrainShape& shape, const btTransform& transform) = 0;
/** /**
* @brief addObject is used to add complex object with allowed to walk and avoided to walk shapes * @brief addObject is used to add complex object with allowed to walk and avoided to walk shapes
@ -96,15 +99,6 @@ namespace DetourNavigator
*/ */
virtual bool addObject(const ObjectId id, const DoorShapes& shapes, const btTransform& transform) = 0; virtual bool addObject(const ObjectId id, const DoorShapes& shapes, const btTransform& transform) = 0;
/**
* @brief updateObject replace object geometry by given data.
* @param id is used to find object.
* @param shape must live until object is updated by another shape removed from Navigator.
* @param transform allows to setup objects geometry according to its world state.
* @return true if object is updated, false if there is no object with given id.
*/
virtual bool updateObject(const ObjectId id, const btCollisionShape& shape, const btTransform& transform) = 0;
/** /**
* @brief updateObject replace object geometry by given data. * @brief updateObject replace object geometry by given data.
* @param id is used to find object. * @param id is used to find object.

@ -31,18 +31,22 @@ namespace DetourNavigator
--it->second; --it->second;
} }
bool NavigatorImpl::addObject(const ObjectId id, const btCollisionShape& shape, const btTransform& transform) bool NavigatorImpl::addObject(const ObjectId id, const osg::ref_ptr<const osg::Object>& holder,
const btHeightfieldTerrainShape& shape, const btTransform& transform)
{ {
return mNavMeshManager.addObject(id, shape, transform, AreaType_ground); const CollisionShape collisionShape {holder, shape};
return mNavMeshManager.addObject(id, collisionShape, transform, AreaType_ground);
} }
bool NavigatorImpl::addObject(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform) bool NavigatorImpl::addObject(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform)
{ {
bool result = addObject(id, shapes.mShape, transform); const CollisionShape collisionShape {shapes.mShapeInstance, *shapes.mShapeInstance->getCollisionShape()};
if (shapes.mAvoid) bool result = mNavMeshManager.addObject(id, collisionShape, transform, AreaType_ground);
if (const btCollisionShape* const avoidShape = shapes.mShapeInstance->getAvoidCollisionShape())
{ {
const ObjectId avoidId(shapes.mAvoid); const ObjectId avoidId(avoidShape);
if (mNavMeshManager.addObject(avoidId, *shapes.mAvoid, transform, AreaType_null)) const CollisionShape collisionShape {shapes.mShapeInstance, *avoidShape};
if (mNavMeshManager.addObject(avoidId, collisionShape, transform, AreaType_null))
{ {
updateAvoidShapeId(id, avoidId); updateAvoidShapeId(id, avoidId);
result = true; result = true;
@ -64,18 +68,15 @@ namespace DetourNavigator
return false; return false;
} }
bool NavigatorImpl::updateObject(const ObjectId id, const btCollisionShape& shape, const btTransform& transform)
{
return mNavMeshManager.updateObject(id, shape, transform, AreaType_ground);
}
bool NavigatorImpl::updateObject(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform) bool NavigatorImpl::updateObject(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform)
{ {
bool result = updateObject(id, shapes.mShape, transform); const CollisionShape collisionShape {shapes.mShapeInstance, *shapes.mShapeInstance->getCollisionShape()};
if (shapes.mAvoid) bool result = mNavMeshManager.updateObject(id, collisionShape, transform, AreaType_ground);
if (const btCollisionShape* const avoidShape = shapes.mShapeInstance->getAvoidCollisionShape())
{ {
const ObjectId avoidId(shapes.mAvoid); const ObjectId avoidId(avoidShape);
if (mNavMeshManager.updateObject(avoidId, *shapes.mAvoid, transform, AreaType_null)) const CollisionShape collisionShape {shapes.mShapeInstance, *avoidShape};
if (mNavMeshManager.updateObject(avoidId, collisionShape, transform, AreaType_null))
{ {
updateAvoidShapeId(id, avoidId); updateAvoidShapeId(id, avoidId);
result = true; result = true;

@ -21,14 +21,13 @@ namespace DetourNavigator
void removeAgent(const osg::Vec3f& agentHalfExtents) override; void removeAgent(const osg::Vec3f& agentHalfExtents) override;
bool addObject(const ObjectId id, const btCollisionShape& shape, const btTransform& transform) override; bool addObject(const ObjectId id, const osg::ref_ptr<const osg::Object>& holder,
const btHeightfieldTerrainShape& shape, const btTransform& transform) override;
bool addObject(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform) override; bool addObject(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform) override;
bool addObject(const ObjectId id, const DoorShapes& shapes, const btTransform& transform) override; bool addObject(const ObjectId id, const DoorShapes& shapes, const btTransform& transform) override;
bool updateObject(const ObjectId id, const btCollisionShape& shape, const btTransform& transform) override;
bool updateObject(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform) override; bool updateObject(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform) override;
bool updateObject(const ObjectId id, const DoorShapes& shapes, const btTransform& transform) override; bool updateObject(const ObjectId id, const DoorShapes& shapes, const btTransform& transform) override;

@ -19,7 +19,8 @@ namespace DetourNavigator
void removeAgent(const osg::Vec3f& /*agentHalfExtents*/) override {} void removeAgent(const osg::Vec3f& /*agentHalfExtents*/) override {}
bool addObject(const ObjectId /*id*/, const btCollisionShape& /*shape*/, const btTransform& /*transform*/) override bool addObject(const ObjectId /*id*/, const osg::ref_ptr<const osg::Object>& /*holder*/,
const btHeightfieldTerrainShape& /*shape*/, const btTransform& /*transform*/) override
{ {
return false; return false;
} }
@ -34,11 +35,6 @@ namespace DetourNavigator
return false; return false;
} }
bool updateObject(const ObjectId /*id*/, const btCollisionShape& /*shape*/, const btTransform& /*transform*/) override
{
return false;
}
bool updateObject(const ObjectId /*id*/, const ObjectShapes& /*shapes*/, const btTransform& /*transform*/) override bool updateObject(const ObjectId /*id*/, const ObjectShapes& /*shapes*/, const btTransform& /*transform*/) override
{ {
return false; return false;

@ -47,16 +47,17 @@ namespace DetourNavigator
, mAsyncNavMeshUpdater(settings, mRecastMeshManager, mOffMeshConnectionsManager) , mAsyncNavMeshUpdater(settings, mRecastMeshManager, mOffMeshConnectionsManager)
{} {}
bool NavMeshManager::addObject(const ObjectId id, const btCollisionShape& shape, const btTransform& transform, bool NavMeshManager::addObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform,
const AreaType areaType) const AreaType areaType)
{ {
const btCollisionShape& collisionShape = shape.getShape();
if (!mRecastMeshManager.addObject(id, shape, transform, areaType)) if (!mRecastMeshManager.addObject(id, shape, transform, areaType))
return false; return false;
addChangedTiles(shape, transform, ChangeType::add); addChangedTiles(collisionShape, transform, ChangeType::add);
return true; return true;
} }
bool NavMeshManager::updateObject(const ObjectId id, const btCollisionShape& shape, const btTransform& transform, bool NavMeshManager::updateObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform,
const AreaType areaType) const AreaType areaType)
{ {
return mRecastMeshManager.updateObject(id, shape, transform, areaType, return mRecastMeshManager.updateObject(id, shape, transform, areaType,

@ -23,10 +23,10 @@ namespace DetourNavigator
public: public:
NavMeshManager(const Settings& settings); NavMeshManager(const Settings& settings);
bool addObject(const ObjectId id, const btCollisionShape& shape, const btTransform& transform, bool addObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform,
const AreaType areaType); const AreaType areaType);
bool updateObject(const ObjectId id, const btCollisionShape& shape, const btTransform& transform, bool updateObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform,
const AreaType areaType); const AreaType areaType);
bool removeObject(const ObjectId id); bool removeObject(const ObjectId id);

@ -19,5 +19,9 @@ namespace DetourNavigator
+ std::to_string(getTrianglesCount()) + ", areaTypes=" + std::to_string(mAreaTypes.size())); + std::to_string(getTrianglesCount()) + ", areaTypes=" + std::to_string(mAreaTypes.size()));
if (getVerticesCount()) if (getVerticesCount())
rcCalcBounds(mVertices.data(), static_cast<int>(getVerticesCount()), mBounds.mMin.ptr(), mBounds.mMax.ptr()); rcCalcBounds(mVertices.data(), static_cast<int>(getVerticesCount()), mBounds.mMin.ptr(), mBounds.mMax.ptr());
mIndices.shrink_to_fit();
mVertices.shrink_to_fit();
mAreaTypes.shrink_to_fit();
mWater.shrink_to_fit();
} }
} }

@ -152,19 +152,11 @@ namespace DetourNavigator
mWater.push_back(RecastMesh::Water {cellSize, transform}); mWater.push_back(RecastMesh::Water {cellSize, transform});
} }
std::shared_ptr<RecastMesh> RecastMeshBuilder::create(std::size_t generation, std::size_t revision) std::shared_ptr<RecastMesh> RecastMeshBuilder::create(std::size_t generation, std::size_t revision) &&
{ {
optimizeRecastMesh(mIndices, mVertices); optimizeRecastMesh(mIndices, mVertices);
std::sort(mWater.begin(), mWater.end()); std::sort(mWater.begin(), mWater.end());
return std::make_shared<RecastMesh>(generation, revision, mIndices, mVertices, mAreaTypes, mWater); return std::make_shared<RecastMesh>(generation, revision, std::move(mIndices), std::move(mVertices), std::move(mAreaTypes), std::move(mWater));
}
void RecastMeshBuilder::reset()
{
mIndices.clear();
mVertices.clear();
mAreaTypes.clear();
mWater.clear();
} }
void RecastMeshBuilder::addObject(const btConcaveShape& shape, const btTransform& transform, void RecastMeshBuilder::addObject(const btConcaveShape& shape, const btTransform& transform,

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

@ -1,17 +1,19 @@
#include "recastmeshmanager.hpp" #include "recastmeshmanager.hpp"
#include "recastmeshbuilder.hpp"
namespace DetourNavigator namespace DetourNavigator
{ {
RecastMeshManager::RecastMeshManager(const Settings& settings, const TileBounds& bounds, std::size_t generation) RecastMeshManager::RecastMeshManager(const Settings& settings, const TileBounds& bounds, std::size_t generation)
: mGeneration(generation) : mSettings(settings)
, mMeshBuilder(settings, bounds) , mGeneration(generation)
, mTileBounds(bounds) , mTileBounds(bounds)
{ {
} }
bool RecastMeshManager::addObject(const ObjectId id, const btCollisionShape& shape, const btTransform& transform, bool RecastMeshManager::addObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform,
const AreaType areaType) const AreaType areaType)
{ {
const std::lock_guard lock(mMutex);
const auto object = mObjects.lower_bound(id); const auto object = mObjects.lower_bound(id);
if (object != mObjects.end() && object->first == id) if (object != mObjects.end() && object->first == id)
return false; return false;
@ -24,6 +26,7 @@ namespace DetourNavigator
bool RecastMeshManager::updateObject(const ObjectId id, const btTransform& transform, const AreaType areaType) bool RecastMeshManager::updateObject(const ObjectId id, const btTransform& transform, const AreaType areaType)
{ {
const std::lock_guard lock(mMutex);
const auto object = mObjects.find(id); const auto object = mObjects.find(id);
if (object == mObjects.end()) if (object == mObjects.end())
return false; return false;
@ -37,6 +40,7 @@ namespace DetourNavigator
std::optional<RemovedRecastMeshObject> RecastMeshManager::removeObject(const ObjectId id) std::optional<RemovedRecastMeshObject> RecastMeshManager::removeObject(const ObjectId id)
{ {
const std::lock_guard lock(mMutex);
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;
@ -50,6 +54,7 @@ namespace DetourNavigator
bool RecastMeshManager::addWater(const osg::Vec2i& cellPosition, const int cellSize, bool RecastMeshManager::addWater(const osg::Vec2i& cellPosition, const int cellSize,
const btTransform& transform) const btTransform& transform)
{ {
const std::lock_guard lock(mMutex);
const auto iterator = mWaterOrder.emplace(mWaterOrder.end(), Water {cellSize, transform}); const auto iterator = mWaterOrder.emplace(mWaterOrder.end(), Water {cellSize, transform});
if (!mWater.emplace(cellPosition, iterator).second) if (!mWater.emplace(cellPosition, iterator).second)
{ {
@ -62,6 +67,7 @@ namespace DetourNavigator
std::optional<RecastMeshManager::Water> RecastMeshManager::removeWater(const osg::Vec2i& cellPosition) std::optional<RecastMeshManager::Water> RecastMeshManager::removeWater(const osg::Vec2i& cellPosition)
{ {
const std::lock_guard lock(mMutex);
const auto water = mWater.find(cellPosition); const auto water = mWater.find(cellPosition);
if (water == mWater.end()) if (water == mWater.end())
return std::nullopt; return std::nullopt;
@ -74,12 +80,35 @@ namespace DetourNavigator
std::shared_ptr<RecastMesh> RecastMeshManager::getMesh() std::shared_ptr<RecastMesh> RecastMeshManager::getMesh()
{ {
rebuild(); RecastMeshBuilder builder(mSettings, mTileBounds);
return mMeshBuilder.create(mGeneration, mRevision); using Object = std::tuple<
osg::ref_ptr<const osg::Object>,
std::reference_wrapper<const btCollisionShape>,
btTransform,
AreaType
>;
std::vector<Object> objects;
std::size_t revision;
{
const std::lock_guard lock(mMutex);
for (const auto& v : mWaterOrder)
builder.addWater(v.mCellSize, v.mTransform);
objects.reserve(mObjectsOrder.size());
for (const auto& object : mObjectsOrder)
{
const RecastMeshObject& impl = object.getImpl();
objects.emplace_back(impl.getHolder(), impl.getShape(), impl.getTransform(), impl.getAreaType());
}
revision = mRevision;
}
for (const auto& [holder, shape, transform, areaType] : objects)
builder.addObject(shape, transform, areaType);
return std::move(builder).create(mGeneration, revision);
} }
bool RecastMeshManager::isEmpty() const bool RecastMeshManager::isEmpty() const
{ {
const std::lock_guard lock(mMutex);
return mObjects.empty(); return mObjects.empty();
} }
@ -87,6 +116,7 @@ namespace DetourNavigator
{ {
if (recastMeshVersion.mGeneration != mGeneration) if (recastMeshVersion.mGeneration != mGeneration)
return; return;
const std::lock_guard lock(mMutex);
if (mLastNavMeshReport.has_value() && navMeshVersion < mLastNavMeshReport->mNavMeshVersion) if (mLastNavMeshReport.has_value() && navMeshVersion < mLastNavMeshReport->mNavMeshVersion)
return; return;
mLastNavMeshReport = {recastMeshVersion.mRevision, navMeshVersion}; mLastNavMeshReport = {recastMeshVersion.mRevision, navMeshVersion};
@ -97,18 +127,7 @@ namespace DetourNavigator
Version RecastMeshManager::getVersion() const Version RecastMeshManager::getVersion() const
{ {
const std::lock_guard lock(mMutex);
return Version {mGeneration, mRevision}; return Version {mGeneration, mRevision};
} }
void RecastMeshManager::rebuild()
{
mMeshBuilder.reset();
for (const auto& v : mWaterOrder)
mMeshBuilder.addWater(v.mCellSize, v.mTransform);
for (const auto& object : mObjectsOrder)
{
const RecastMeshObject& v = object.getImpl();
mMeshBuilder.addObject(v.getShape(), v.getTransform(), v.getAreaType());
}
}
} }

@ -1,7 +1,6 @@
#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_RECASTMESHMANAGER_H #ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_RECASTMESHMANAGER_H
#define OPENMW_COMPONENTS_DETOURNAVIGATOR_RECASTMESHMANAGER_H #define OPENMW_COMPONENTS_DETOURNAVIGATOR_RECASTMESHMANAGER_H
#include "recastmeshbuilder.hpp"
#include "oscillatingrecastmeshobject.hpp" #include "oscillatingrecastmeshobject.hpp"
#include "objectid.hpp" #include "objectid.hpp"
#include "version.hpp" #include "version.hpp"
@ -13,11 +12,16 @@
#include <list> #include <list>
#include <map> #include <map>
#include <optional> #include <optional>
#include <memory>
#include <mutex>
class btCollisionShape; class btCollisionShape;
namespace DetourNavigator namespace DetourNavigator
{ {
struct Settings;
class RecastMesh;
struct RemovedRecastMeshObject struct RemovedRecastMeshObject
{ {
std::reference_wrapper<const btCollisionShape> mShape; std::reference_wrapper<const btCollisionShape> mShape;
@ -35,7 +39,7 @@ namespace DetourNavigator
RecastMeshManager(const Settings& settings, const TileBounds& bounds, std::size_t generation); RecastMeshManager(const Settings& settings, const TileBounds& bounds, std::size_t generation);
bool addObject(const ObjectId id, const btCollisionShape& shape, const btTransform& transform, bool addObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform,
const AreaType areaType); const AreaType areaType);
bool updateObject(const ObjectId id, const btTransform& transform, const AreaType areaType); bool updateObject(const ObjectId id, const btTransform& transform, const AreaType areaType);
@ -61,18 +65,17 @@ namespace DetourNavigator
Version mNavMeshVersion; Version mNavMeshVersion;
}; };
const Settings& mSettings;
const std::size_t mGeneration;
const TileBounds mTileBounds;
mutable std::mutex mMutex;
std::size_t mRevision = 0; std::size_t mRevision = 0;
std::size_t mGeneration;
RecastMeshBuilder mMeshBuilder;
TileBounds mTileBounds;
std::list<OscillatingRecastMeshObject> mObjectsOrder; std::list<OscillatingRecastMeshObject> mObjectsOrder;
std::map<ObjectId, std::list<OscillatingRecastMeshObject>::iterator> mObjects; std::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> mLastNavMeshReportedChange;
std::optional<Report> mLastNavMeshReport; std::optional<Report> mLastNavMeshReport;
void rebuild();
}; };
} }

@ -22,15 +22,36 @@ namespace DetourNavigator
} }
return result; return result;
} }
std::vector<RecastMeshObject> makeChildrenObjects(const osg::ref_ptr<const osg::Object>& holder,
const btCompoundShape& shape, const AreaType areaType)
{
std::vector<RecastMeshObject> result;
for (int i = 0, num = shape.getNumChildShapes(); i < num; ++i)
{
const CollisionShape collisionShape {holder, *shape.getChildShape(i)};
result.emplace_back(collisionShape, shape.getChildTransform(i), areaType);
}
return result;
}
std::vector<RecastMeshObject> makeChildrenObjects(const osg::ref_ptr<const osg::Object>& holder,
const btCollisionShape& shape, const AreaType areaType)
{
if (shape.isCompound())
return makeChildrenObjects(holder, static_cast<const btCompoundShape&>(shape), areaType);
return std::vector<RecastMeshObject>();
}
} }
RecastMeshObject::RecastMeshObject(const btCollisionShape& shape, const btTransform& transform, RecastMeshObject::RecastMeshObject(const CollisionShape& shape, const btTransform& transform,
const AreaType areaType) const AreaType areaType)
: mShape(shape) : mHolder(shape.getHolder())
, mShape(shape.getShape())
, mTransform(transform) , mTransform(transform)
, mAreaType(areaType) , mAreaType(areaType)
, mLocalScaling(shape.getLocalScaling()) , mLocalScaling(mShape.get().getLocalScaling())
, mChildren(makeChildrenObjects(shape, mAreaType)) , mChildren(makeChildrenObjects(mHolder, mShape.get(), mAreaType))
{ {
} }
@ -57,20 +78,4 @@ namespace DetourNavigator
|| result; || result;
return result; return result;
} }
std::vector<RecastMeshObject> makeChildrenObjects(const btCollisionShape& shape, const AreaType areaType)
{
if (shape.isCompound())
return makeChildrenObjects(static_cast<const btCompoundShape&>(shape), areaType);
else
return std::vector<RecastMeshObject>();
}
std::vector<RecastMeshObject> makeChildrenObjects(const btCompoundShape& shape, const AreaType areaType)
{
std::vector<RecastMeshObject> result;
for (int i = 0, num = shape.getNumChildShapes(); i < num; ++i)
result.emplace_back(*shape.getChildShape(i), shape.getChildTransform(i), areaType);
return result;
}
} }

@ -5,6 +5,9 @@
#include <LinearMath/btTransform.h> #include <LinearMath/btTransform.h>
#include <osg/ref_ptr>
#include <osg/Object>
#include <functional> #include <functional>
#include <vector> #include <vector>
@ -13,13 +16,34 @@ class btCompoundShape;
namespace DetourNavigator namespace DetourNavigator
{ {
class CollisionShape
{
public:
CollisionShape(osg::ref_ptr<const osg::Object> holder, const btCollisionShape& shape)
: mHolder(std::move(holder))
, mShape(shape)
{}
const osg::ref_ptr<const osg::Object>& getHolder() const { return mHolder; }
const btCollisionShape& getShape() const { return mShape; }
private:
osg::ref_ptr<const osg::Object> mHolder;
std::reference_wrapper<const btCollisionShape> mShape;
};
class RecastMeshObject class RecastMeshObject
{ {
public: public:
RecastMeshObject(const btCollisionShape& shape, const btTransform& transform, const AreaType areaType); RecastMeshObject(const CollisionShape& shape, const btTransform& transform, const AreaType areaType);
bool update(const btTransform& transform, const AreaType areaType); bool update(const btTransform& transform, const AreaType areaType);
const osg::ref_ptr<const osg::Object>& getHolder() const
{
return mHolder;
}
const btCollisionShape& getShape() const const btCollisionShape& getShape() const
{ {
return mShape; return mShape;
@ -36,16 +60,13 @@ namespace DetourNavigator
} }
private: private:
osg::ref_ptr<const osg::Object> mHolder;
std::reference_wrapper<const btCollisionShape> mShape; std::reference_wrapper<const btCollisionShape> mShape;
btTransform mTransform; btTransform mTransform;
AreaType mAreaType; AreaType mAreaType;
btVector3 mLocalScaling; btVector3 mLocalScaling;
std::vector<RecastMeshObject> mChildren; std::vector<RecastMeshObject> mChildren;
}; };
std::vector<RecastMeshObject> makeChildrenObjects(const btCollisionShape& shape, const AreaType areaType);
std::vector<RecastMeshObject> makeChildrenObjects(const btCompoundShape& shape, const AreaType areaType);
} }
#endif #endif

@ -12,14 +12,14 @@ namespace DetourNavigator
: mSettings(settings) : mSettings(settings)
{} {}
bool TileCachedRecastMeshManager::addObject(const ObjectId id, const btCollisionShape& shape, bool TileCachedRecastMeshManager::addObject(const ObjectId id, const CollisionShape& shape,
const btTransform& transform, const AreaType areaType) const btTransform& transform, const AreaType areaType)
{ {
std::vector<TilePosition> tilesPositions; std::vector<TilePosition> tilesPositions;
const auto border = getBorderSize(mSettings); const auto border = getBorderSize(mSettings);
{ {
auto tiles = mTiles.lock(); auto tiles = mTiles.lock();
getTilesPositions(shape, transform, mSettings, [&] (const TilePosition& tilePosition) getTilesPositions(shape.getShape(), transform, mSettings, [&] (const TilePosition& tilePosition)
{ {
if (addTile(id, shape, transform, areaType, tilePosition, border, tiles.get())) if (addTile(id, shape, transform, areaType, tilePosition, border, tiles.get()))
tilesPositions.push_back(tilePosition); tilesPositions.push_back(tilePosition);
@ -67,7 +67,7 @@ namespace DetourNavigator
const auto tiles = mTiles.lock(); const auto tiles = mTiles.lock();
for (auto& tile : *tiles) for (auto& tile : *tiles)
{ {
if (tile.second.addWater(cellPosition, cellSize, transform)) if (tile.second->addWater(cellPosition, cellSize, transform))
{ {
tilesPositions.push_back(tile.first); tilesPositions.push_back(tile.first);
result = true; result = true;
@ -86,9 +86,9 @@ namespace DetourNavigator
tileBounds.mMin -= osg::Vec2f(border, border); tileBounds.mMin -= osg::Vec2f(border, border);
tileBounds.mMax += osg::Vec2f(border, border); tileBounds.mMax += osg::Vec2f(border, border);
tile = tiles->insert(std::make_pair(tilePosition, tile = tiles->insert(std::make_pair(tilePosition,
CachedRecastMeshManager(mSettings, tileBounds, mTilesGeneration))).first; std::make_shared<CachedRecastMeshManager>(mSettings, tileBounds, mTilesGeneration))).first;
} }
if (tile->second.addWater(cellPosition, cellSize, transform)) if (tile->second->addWater(cellPosition, cellSize, transform))
{ {
tilesPositions.push_back(tilePosition); tilesPositions.push_back(tilePosition);
result = true; result = true;
@ -114,8 +114,8 @@ namespace DetourNavigator
const auto tile = tiles->find(tilePosition); const auto tile = tiles->find(tilePosition);
if (tile == tiles->end()) if (tile == tiles->end())
continue; continue;
const auto tileResult = tile->second.removeWater(cellPosition); const auto tileResult = tile->second->removeWater(cellPosition);
if (tile->second.isEmpty()) if (tile->second->isEmpty())
{ {
tiles->erase(tile); tiles->erase(tile);
++mTilesGeneration; ++mTilesGeneration;
@ -130,11 +130,17 @@ namespace DetourNavigator
std::shared_ptr<RecastMesh> TileCachedRecastMeshManager::getMesh(const TilePosition& tilePosition) std::shared_ptr<RecastMesh> TileCachedRecastMeshManager::getMesh(const TilePosition& tilePosition)
{ {
const auto tiles = mTiles.lock(); const auto manager = [&] () -> std::shared_ptr<CachedRecastMeshManager>
const auto it = tiles->find(tilePosition); {
if (it == tiles->end()) const auto tiles = mTiles.lock();
const auto it = tiles->find(tilePosition);
if (it == tiles->end())
return nullptr;
return it->second;
} ();
if (manager == nullptr)
return nullptr; return nullptr;
return it->second.getMesh(); return manager->getMesh();
} }
bool TileCachedRecastMeshManager::hasTile(const TilePosition& tilePosition) bool TileCachedRecastMeshManager::hasTile(const TilePosition& tilePosition)
@ -153,12 +159,12 @@ namespace DetourNavigator
const auto it = tiles->find(tilePosition); const auto it = tiles->find(tilePosition);
if (it == tiles->end()) if (it == tiles->end())
return; return;
it->second.reportNavMeshChange(recastMeshVersion, navMeshVersion); it->second->reportNavMeshChange(recastMeshVersion, navMeshVersion);
} }
bool TileCachedRecastMeshManager::addTile(const ObjectId id, const btCollisionShape& shape, bool TileCachedRecastMeshManager::addTile(const ObjectId id, const CollisionShape& 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) TilesMap& tiles)
{ {
auto tile = tiles.find(tilePosition); auto tile = tiles.find(tilePosition);
if (tile == tiles.end()) if (tile == tiles.end())
@ -167,26 +173,26 @@ namespace DetourNavigator
tileBounds.mMin -= osg::Vec2f(border, border); tileBounds.mMin -= osg::Vec2f(border, border);
tileBounds.mMax += osg::Vec2f(border, border); tileBounds.mMax += osg::Vec2f(border, border);
tile = tiles.insert(std::make_pair( tile = tiles.insert(std::make_pair(
tilePosition, CachedRecastMeshManager(mSettings, tileBounds, mTilesGeneration))).first; tilePosition, std::make_shared<CachedRecastMeshManager>(mSettings, tileBounds, mTilesGeneration))).first;
} }
return tile->second.addObject(id, shape, transform, areaType); return tile->second->addObject(id, shape, transform, areaType);
} }
bool TileCachedRecastMeshManager::updateTile(const ObjectId id, const btTransform& transform, bool TileCachedRecastMeshManager::updateTile(const ObjectId id, const btTransform& transform,
const AreaType areaType, const TilePosition& tilePosition, std::map<TilePosition, CachedRecastMeshManager>& tiles) const AreaType areaType, const TilePosition& tilePosition, TilesMap& tiles)
{ {
const auto tile = tiles.find(tilePosition); const auto tile = tiles.find(tilePosition);
return tile != tiles.end() && tile->second.updateObject(id, transform, areaType); return tile != tiles.end() && tile->second->updateObject(id, transform, areaType);
} }
std::optional<RemovedRecastMeshObject> TileCachedRecastMeshManager::removeTile(const ObjectId id, std::optional<RemovedRecastMeshObject> TileCachedRecastMeshManager::removeTile(const ObjectId id,
const TilePosition& tilePosition, std::map<TilePosition, CachedRecastMeshManager>& tiles) const TilePosition& tilePosition, TilesMap& tiles)
{ {
const auto tile = tiles.find(tilePosition); const auto tile = tiles.find(tilePosition);
if (tile == tiles.end()) if (tile == tiles.end())
return std::optional<RemovedRecastMeshObject>(); return std::optional<RemovedRecastMeshObject>();
const auto tileResult = tile->second.removeObject(id); const auto tileResult = tile->second->removeObject(id);
if (tile->second.isEmpty()) if (tile->second->isEmpty())
{ {
tiles.erase(tile); tiles.erase(tile);
++mTilesGeneration; ++mTilesGeneration;

@ -21,11 +21,11 @@ namespace DetourNavigator
public: public:
TileCachedRecastMeshManager(const Settings& settings); TileCachedRecastMeshManager(const Settings& settings);
bool addObject(const ObjectId id, const btCollisionShape& shape, const btTransform& transform, bool addObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform,
const AreaType areaType); const AreaType areaType);
template <class OnChangedTile> template <class OnChangedTile>
bool updateObject(const ObjectId id, const btCollisionShape& shape, const btTransform& transform, bool updateObject(const ObjectId id, const CollisionShape& shape, const btTransform& transform,
const AreaType areaType, OnChangedTile&& onChangedTile) const AreaType areaType, OnChangedTile&& onChangedTile)
{ {
const auto object = mObjectsTilesPositions.find(id); const auto object = mObjectsTilesPositions.find(id);
@ -55,7 +55,7 @@ namespace DetourNavigator
changed = true; changed = true;
} }
}; };
getTilesPositions(shape, transform, mSettings, onTilePosition); getTilesPositions(shape.getShape(), transform, mSettings, onTilePosition);
std::sort(newTiles.begin(), newTiles.end()); std::sort(newTiles.begin(), newTiles.end());
for (const auto& tile : currentTiles) for (const auto& tile : currentTiles)
{ {
@ -88,7 +88,7 @@ namespace DetourNavigator
void forEachTile(Function&& function) void forEachTile(Function&& function)
{ {
for (auto& [tilePosition, recastMeshManager] : *mTiles.lock()) for (auto& [tilePosition, recastMeshManager] : *mTiles.lock())
function(tilePosition, recastMeshManager); function(tilePosition, *recastMeshManager);
} }
std::size_t getRevision() const; std::size_t getRevision() const;
@ -96,22 +96,23 @@ namespace DetourNavigator
void reportNavMeshChange(const TilePosition& tilePosition, Version recastMeshVersion, Version navMeshVersion); void reportNavMeshChange(const TilePosition& tilePosition, Version recastMeshVersion, Version navMeshVersion);
private: private:
using TilesMap = std::map<TilePosition, std::shared_ptr<CachedRecastMeshManager>>;
const Settings& mSettings; const Settings& mSettings;
Misc::ScopeGuarded<std::map<TilePosition, CachedRecastMeshManager>> mTiles; Misc::ScopeGuarded<TilesMap> mTiles;
std::unordered_map<ObjectId, std::vector<TilePosition>> mObjectsTilesPositions; std::unordered_map<ObjectId, std::vector<TilePosition>> mObjectsTilesPositions;
std::map<osg::Vec2i, std::vector<TilePosition>> mWaterTilesPositions; std::map<osg::Vec2i, std::vector<TilePosition>> mWaterTilesPositions;
std::size_t mRevision = 0; std::size_t mRevision = 0;
std::size_t mTilesGeneration = 0; std::size_t mTilesGeneration = 0;
bool addTile(const ObjectId id, const btCollisionShape& shape, const btTransform& transform, bool addTile(const ObjectId id, const CollisionShape& shape, const btTransform& transform,
const AreaType areaType, const TilePosition& tilePosition, float border, const AreaType areaType, const TilePosition& tilePosition, float border, TilesMap& tiles);
std::map<TilePosition, CachedRecastMeshManager>& tiles);
bool updateTile(const ObjectId id, const btTransform& transform, const AreaType areaType, bool updateTile(const ObjectId id, const btTransform& transform, const AreaType areaType,
const TilePosition& tilePosition, std::map<TilePosition, CachedRecastMeshManager>& tiles); const TilePosition& tilePosition, TilesMap& tiles);
std::optional<RemovedRecastMeshObject> removeTile(const ObjectId id, const TilePosition& tilePosition, std::optional<RemovedRecastMeshObject> removeTile(const ObjectId id, const TilePosition& tilePosition,
std::map<TilePosition, CachedRecastMeshManager>& tiles); TilesMap& tiles);
}; };
} }

@ -100,6 +100,17 @@ boost::program_options::variables_map ConfigurationManager::separateComposingVar
void ConfigurationManager::mergeComposingVariables(boost::program_options::variables_map & first, boost::program_options::variables_map & second, boost::program_options::options_description& description) void ConfigurationManager::mergeComposingVariables(boost::program_options::variables_map & first, boost::program_options::variables_map & second, boost::program_options::options_description& description)
{ {
// There are a few places this assumes all variables are present in second, but it's never crashed in the wild, so it looks like that's guaranteed.
std::set<std::string> replacedVariables;
if (description.find_nothrow("replace", false))
{
auto replace = second["replace"];
if (!replace.defaulted() && !replace.empty())
{
std::vector<std::string> replaceVector = replace.as<Files::EscapeStringVector>().toStdStringVector();
replacedVariables.insert(replaceVector.begin(), replaceVector.end());
}
}
for (const auto& option : description.options()) for (const auto& option : description.options())
{ {
if (option->semantic()->is_composing()) if (option->semantic()->is_composing())
@ -113,6 +124,12 @@ void ConfigurationManager::mergeComposingVariables(boost::program_options::varia
continue; continue;
} }
if (replacedVariables.count(name))
{
firstPosition->second = second[name];
continue;
}
if (second[name].defaulted() || second[name].empty()) if (second[name].defaulted() || second[name].empty())
continue; continue;

@ -6,6 +6,7 @@
#include <BulletCollision/CollisionShapes/btBoxShape.h> #include <BulletCollision/CollisionShapes/btBoxShape.h>
#include <BulletCollision/CollisionShapes/btScaledBvhTriangleMeshShape.h> #include <BulletCollision/CollisionShapes/btScaledBvhTriangleMeshShape.h>
#include <BulletCollision/CollisionShapes/btCompoundShape.h> #include <BulletCollision/CollisionShapes/btCompoundShape.h>
#include <BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h>
namespace Resource namespace Resource
{ {
@ -75,6 +76,9 @@ btCollisionShape* BulletShape::duplicateCollisionShape(const btCollisionShape *s
return new btBoxShape(*boxshape); return new btBoxShape(*boxshape);
} }
if (shape->getShapeType() == TERRAIN_SHAPE_PROXYTYPE)
return new btHeightfieldTerrainShape(static_cast<const btHeightfieldTerrainShape&>(*shape));
throw std::logic_error(std::string("Unhandled Bullet shape duplication: ")+shape->getName()); throw std::logic_error(std::string("Unhandled Bullet shape duplication: ")+shape->getName());
} }

@ -884,8 +884,6 @@ namespace SceneUtil
std::string lightingMethodString = Settings::Manager::getString("lighting method", "Shaders"); std::string lightingMethodString = Settings::Manager::getString("lighting method", "Shaders");
auto lightingMethod = LightManager::getLightingMethodFromString(lightingMethodString); auto lightingMethod = LightManager::getLightingMethodFromString(lightingMethodString);
updateSettings();
static bool hasLoggedWarnings = false; static bool hasLoggedWarnings = false;
if (lightingMethod == LightingMethod::SingleUBO && !hasLoggedWarnings) if (lightingMethod == LightingMethod::SingleUBO && !hasLoggedWarnings)
@ -904,6 +902,8 @@ namespace SceneUtil
else else
initSingleUBO(targetLights); initSingleUBO(targetLights);
updateSettings();
getOrCreateStateSet()->addUniform(new osg::Uniform("PointLightCount", 0)); getOrCreateStateSet()->addUniform(new osg::Uniform("PointLightCount", 0));
addCullCallback(new LightManagerCullCallback(this)); addCullCallback(new LightManagerCullCallback(this));

Loading…
Cancel
Save