mirror of
https://github.com/OpenMW/openmw.git
synced 2025-05-22 17:11:29 +00:00
Merge branch 'fix_navigator_update' into 'master'
Fix navmesh update on player changing tile See merge request OpenMW/openmw!3756
This commit is contained in:
commit
c84386aa4b
7 changed files with 299 additions and 31 deletions
|
@ -1,3 +1,4 @@
|
||||||
|
#include <components/detournavigator/debug.hpp>
|
||||||
#include <components/detournavigator/gettilespositions.hpp>
|
#include <components/detournavigator/gettilespositions.hpp>
|
||||||
#include <components/detournavigator/settings.hpp>
|
#include <components/detournavigator/settings.hpp>
|
||||||
|
|
||||||
|
@ -86,4 +87,79 @@ namespace
|
||||||
|
|
||||||
EXPECT_THAT(mTilesPositions, ElementsAre(TilePosition(0, 0)));
|
EXPECT_THAT(mTilesPositions, ElementsAre(TilePosition(0, 0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct TilesPositionsRangeParams
|
||||||
|
{
|
||||||
|
TilesPositionsRange mA;
|
||||||
|
TilesPositionsRange mB;
|
||||||
|
TilesPositionsRange mResult;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DetourNavigatorGetIntersectionTest : TestWithParam<TilesPositionsRangeParams>
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_P(DetourNavigatorGetIntersectionTest, should_return_expected_result)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(getIntersection(GetParam().mA, GetParam().mB), GetParam().mResult);
|
||||||
|
EXPECT_EQ(getIntersection(GetParam().mB, GetParam().mA), GetParam().mResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
const TilesPositionsRangeParams getIntersectionParams[] = {
|
||||||
|
{ .mA = TilesPositionsRange{}, .mB = TilesPositionsRange{}, .mResult = TilesPositionsRange{} },
|
||||||
|
{
|
||||||
|
.mA = TilesPositionsRange{ .mBegin = TilePosition{ 0, 0 }, .mEnd = TilePosition{ 2, 2 } },
|
||||||
|
.mB = TilesPositionsRange{ .mBegin = TilePosition{ 1, 1 }, .mEnd = TilePosition{ 3, 3 } },
|
||||||
|
.mResult = TilesPositionsRange{ .mBegin = TilePosition{ 1, 1 }, .mEnd = TilePosition{ 2, 2 } },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.mA = TilesPositionsRange{ .mBegin = TilePosition{ 0, 0 }, .mEnd = TilePosition{ 1, 1 } },
|
||||||
|
.mB = TilesPositionsRange{ .mBegin = TilePosition{ 2, 2 }, .mEnd = TilePosition{ 3, 3 } },
|
||||||
|
.mResult = TilesPositionsRange{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.mA = TilesPositionsRange{ .mBegin = TilePosition{ 0, 0 }, .mEnd = TilePosition{ 1, 1 } },
|
||||||
|
.mB = TilesPositionsRange{ .mBegin = TilePosition{ 1, 1 }, .mEnd = TilePosition{ 2, 2 } },
|
||||||
|
.mResult = TilesPositionsRange{ .mBegin = TilePosition{ 1, 1 }, .mEnd = TilePosition{ 1, 1 } },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.mA = TilesPositionsRange{ .mBegin = TilePosition{ 0, 0 }, .mEnd = TilePosition{ 1, 1 } },
|
||||||
|
.mB = TilesPositionsRange{ .mBegin = TilePosition{ 0, 2 }, .mEnd = TilePosition{ 3, 3 } },
|
||||||
|
.mResult = TilesPositionsRange{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.mA = TilesPositionsRange{ .mBegin = TilePosition{ 0, 0 }, .mEnd = TilePosition{ 1, 1 } },
|
||||||
|
.mB = TilesPositionsRange{ .mBegin = TilePosition{ 2, 0 }, .mEnd = TilePosition{ 3, 3 } },
|
||||||
|
.mResult = TilesPositionsRange{},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_SUITE_P(
|
||||||
|
GetIntersectionParams, DetourNavigatorGetIntersectionTest, ValuesIn(getIntersectionParams));
|
||||||
|
|
||||||
|
struct DetourNavigatorGetUnionTest : TestWithParam<TilesPositionsRangeParams>
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_P(DetourNavigatorGetUnionTest, should_return_expected_result)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(getUnion(GetParam().mA, GetParam().mB), GetParam().mResult);
|
||||||
|
EXPECT_EQ(getUnion(GetParam().mB, GetParam().mA), GetParam().mResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
const TilesPositionsRangeParams getUnionParams[] = {
|
||||||
|
{ .mA = TilesPositionsRange{}, .mB = TilesPositionsRange{}, .mResult = TilesPositionsRange{} },
|
||||||
|
{
|
||||||
|
.mA = TilesPositionsRange{ .mBegin = TilePosition{ 0, 0 }, .mEnd = TilePosition{ 2, 2 } },
|
||||||
|
.mB = TilesPositionsRange{ .mBegin = TilePosition{ 1, 1 }, .mEnd = TilePosition{ 3, 3 } },
|
||||||
|
.mResult = TilesPositionsRange{ .mBegin = TilePosition{ 0, 0 }, .mEnd = TilePosition{ 3, 3 } },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.mA = TilesPositionsRange{ .mBegin = TilePosition{ 0, 0 }, .mEnd = TilePosition{ 1, 1 } },
|
||||||
|
.mB = TilesPositionsRange{ .mBegin = TilePosition{ 1, 1 }, .mEnd = TilePosition{ 2, 2 } },
|
||||||
|
.mResult = TilesPositionsRange{ .mBegin = TilePosition{ 0, 0 }, .mEnd = TilePosition{ 2, 2 } },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_SUITE_P(GetUnionParams, DetourNavigatorGetUnionTest, ValuesIn(getUnionParams));
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,8 @@ namespace
|
||||||
using namespace DetourNavigator;
|
using namespace DetourNavigator;
|
||||||
using namespace DetourNavigator::Tests;
|
using namespace DetourNavigator::Tests;
|
||||||
|
|
||||||
|
constexpr int heightfieldTileSize = ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1);
|
||||||
|
|
||||||
struct DetourNavigatorNavigatorTest : Test
|
struct DetourNavigatorNavigatorTest : Test
|
||||||
{
|
{
|
||||||
Settings mSettings = makeSettings();
|
Settings mSettings = makeSettings();
|
||||||
|
@ -53,7 +55,6 @@ namespace
|
||||||
AreaCosts mAreaCosts;
|
AreaCosts mAreaCosts;
|
||||||
Loading::Listener mListener;
|
Loading::Listener mListener;
|
||||||
const osg::Vec2i mCellPosition{ 0, 0 };
|
const osg::Vec2i mCellPosition{ 0, 0 };
|
||||||
const int mHeightfieldTileSize = ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1);
|
|
||||||
const float mEndTolerance = 0;
|
const float mEndTolerance = 0;
|
||||||
const btTransform mTransform{ btMatrix3x3::getIdentity(), btVector3(256, 256, 0) };
|
const btTransform mTransform{ btMatrix3x3::getIdentity(), btVector3(256, 256, 0) };
|
||||||
const ObjectTransform mObjectTransform{ ESM::Position{ { 256, 256, 0 }, { 0, 0, 0 } }, 0.0f };
|
const ObjectTransform mObjectTransform{ ESM::Position{ { 256, 256, 0 }, { 0, 0, 0 } }, 0.0f };
|
||||||
|
@ -129,7 +130,7 @@ namespace
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
T& shape() { return static_cast<T&>(*mInstance->mCollisionShape); }
|
T& shape() const { return static_cast<T&>(*mInstance->mCollisionShape); }
|
||||||
const osg::ref_ptr<const Resource::BulletShapeInstance>& instance() const { return mInstance; }
|
const osg::ref_ptr<const Resource::BulletShapeInstance>& instance() const { return mInstance; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -167,7 +168,7 @@ namespace
|
||||||
TEST_F(DetourNavigatorNavigatorTest, update_then_find_path_should_return_path)
|
TEST_F(DetourNavigatorNavigatorTest, update_then_find_path_should_return_path)
|
||||||
{
|
{
|
||||||
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
||||||
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
|
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||||
|
|
||||||
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
||||||
auto updateGuard = mNavigator->makeUpdateGuard();
|
auto updateGuard = mNavigator->makeUpdateGuard();
|
||||||
|
@ -189,7 +190,7 @@ namespace
|
||||||
TEST_F(DetourNavigatorNavigatorTest, find_path_to_the_start_position_should_contain_single_point)
|
TEST_F(DetourNavigatorNavigatorTest, find_path_to_the_start_position_should_contain_single_point)
|
||||||
{
|
{
|
||||||
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
||||||
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
|
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||||
|
|
||||||
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
||||||
auto updateGuard = mNavigator->makeUpdateGuard();
|
auto updateGuard = mNavigator->makeUpdateGuard();
|
||||||
|
@ -211,7 +212,7 @@ namespace
|
||||||
mSettings, std::make_unique<NavMeshDb>(":memory:", std::numeric_limits<std::uint64_t>::max())));
|
mSettings, std::make_unique<NavMeshDb>(":memory:", std::numeric_limits<std::uint64_t>::max())));
|
||||||
|
|
||||||
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
||||||
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
|
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||||
|
|
||||||
CollisionShapeInstance compound(std::make_unique<btCompoundShape>());
|
CollisionShapeInstance compound(std::make_unique<btCompoundShape>());
|
||||||
compound.shape().addChildShape(
|
compound.shape().addChildShape(
|
||||||
|
@ -256,7 +257,7 @@ namespace
|
||||||
TEST_F(DetourNavigatorNavigatorTest, update_changed_object_should_change_navmesh)
|
TEST_F(DetourNavigatorNavigatorTest, update_changed_object_should_change_navmesh)
|
||||||
{
|
{
|
||||||
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
||||||
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
|
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||||
|
|
||||||
CollisionShapeInstance compound(std::make_unique<btCompoundShape>());
|
CollisionShapeInstance compound(std::make_unique<btCompoundShape>());
|
||||||
compound.shape().addChildShape(
|
compound.shape().addChildShape(
|
||||||
|
@ -335,7 +336,7 @@ namespace
|
||||||
TEST_F(DetourNavigatorNavigatorTest, only_one_heightfield_per_cell_is_allowed)
|
TEST_F(DetourNavigatorNavigatorTest, only_one_heightfield_per_cell_is_allowed)
|
||||||
{
|
{
|
||||||
const HeightfieldSurface surface1 = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
const HeightfieldSurface surface1 = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
||||||
const int cellSize1 = mHeightfieldTileSize * (surface1.mSize - 1);
|
const int cellSize1 = heightfieldTileSize * (surface1.mSize - 1);
|
||||||
|
|
||||||
const std::array<float, 5 * 5> heightfieldData2{ {
|
const std::array<float, 5 * 5> heightfieldData2{ {
|
||||||
-25, -25, -25, -25, -25, // row 0
|
-25, -25, -25, -25, -25, // row 0
|
||||||
|
@ -345,7 +346,7 @@ namespace
|
||||||
-25, -25, -25, -25, -25, // row 4
|
-25, -25, -25, -25, -25, // row 4
|
||||||
} };
|
} };
|
||||||
const HeightfieldSurface surface2 = makeSquareHeightfieldSurface(heightfieldData2);
|
const HeightfieldSurface surface2 = makeSquareHeightfieldSurface(heightfieldData2);
|
||||||
const int cellSize2 = mHeightfieldTileSize * (surface2.mSize - 1);
|
const int cellSize2 = heightfieldTileSize * (surface2.mSize - 1);
|
||||||
|
|
||||||
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
||||||
mNavigator->addHeightfield(mCellPosition, cellSize1, surface1, nullptr);
|
mNavigator->addHeightfield(mCellPosition, cellSize1, surface1, nullptr);
|
||||||
|
@ -412,7 +413,7 @@ namespace
|
||||||
0, -50, -100, -100, -100, // row 4
|
0, -50, -100, -100, -100, // row 4
|
||||||
} };
|
} };
|
||||||
const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData);
|
const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData);
|
||||||
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
|
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||||
|
|
||||||
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
||||||
mNavigator->addWater(mCellPosition, cellSize, 300, nullptr);
|
mNavigator->addWater(mCellPosition, cellSize, 300, nullptr);
|
||||||
|
@ -446,7 +447,7 @@ namespace
|
||||||
0, 0, 0, 0, 0, 0, 0, // row 6
|
0, 0, 0, 0, 0, 0, 0, // row 6
|
||||||
} };
|
} };
|
||||||
const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData);
|
const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData);
|
||||||
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
|
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||||
|
|
||||||
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
||||||
mNavigator->addWater(mCellPosition, cellSize, -25, nullptr);
|
mNavigator->addWater(mCellPosition, cellSize, -25, nullptr);
|
||||||
|
@ -480,7 +481,7 @@ namespace
|
||||||
0, 0, 0, 0, 0, 0, 0, // row 6
|
0, 0, 0, 0, 0, 0, 0, // row 6
|
||||||
} };
|
} };
|
||||||
const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData);
|
const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData);
|
||||||
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
|
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||||
|
|
||||||
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
||||||
mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr);
|
mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr);
|
||||||
|
@ -513,7 +514,7 @@ namespace
|
||||||
0, 0, 0, 0, 0, 0, 0, // row 6
|
0, 0, 0, 0, 0, 0, 0, // row 6
|
||||||
} };
|
} };
|
||||||
const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData);
|
const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData);
|
||||||
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
|
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||||
|
|
||||||
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
||||||
mNavigator->addWater(mCellPosition, cellSize, -25, nullptr);
|
mNavigator->addWater(mCellPosition, cellSize, -25, nullptr);
|
||||||
|
@ -566,7 +567,7 @@ namespace
|
||||||
TEST_F(DetourNavigatorNavigatorTest, update_heightfield_remove_and_update_then_find_path_should_return_path)
|
TEST_F(DetourNavigatorNavigatorTest, update_heightfield_remove_and_update_then_find_path_should_return_path)
|
||||||
{
|
{
|
||||||
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
||||||
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
|
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||||
|
|
||||||
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
||||||
mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr);
|
mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr);
|
||||||
|
@ -602,7 +603,7 @@ namespace
|
||||||
0, -25, -100, -100, -100, -100, // row 5
|
0, -25, -100, -100, -100, -100, // row 5
|
||||||
} };
|
} };
|
||||||
const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData);
|
const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData);
|
||||||
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
|
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||||
|
|
||||||
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
||||||
mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr);
|
mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr);
|
||||||
|
@ -629,7 +630,7 @@ namespace
|
||||||
mSettings, std::make_unique<NavMeshDb>(":memory:", std::numeric_limits<std::uint64_t>::max())));
|
mSettings, std::make_unique<NavMeshDb>(":memory:", std::numeric_limits<std::uint64_t>::max())));
|
||||||
|
|
||||||
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
||||||
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
|
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||||
const btVector3 shift = getHeightfieldShift(mCellPosition, cellSize, surface.mMinHeight, surface.mMaxHeight);
|
const btVector3 shift = getHeightfieldShift(mCellPosition, cellSize, surface.mMinHeight, surface.mMaxHeight);
|
||||||
|
|
||||||
std::vector<CollisionShapeInstance<btBoxShape>> boxes;
|
std::vector<CollisionShapeInstance<btBoxShape>> boxes;
|
||||||
|
@ -718,7 +719,7 @@ namespace
|
||||||
TEST_F(DetourNavigatorNavigatorTest, update_then_raycast_should_return_position)
|
TEST_F(DetourNavigatorNavigatorTest, update_then_raycast_should_return_position)
|
||||||
{
|
{
|
||||||
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
||||||
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
|
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||||
|
|
||||||
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
||||||
mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr);
|
mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr);
|
||||||
|
@ -737,7 +738,7 @@ namespace
|
||||||
update_for_oscillating_object_that_does_not_change_navmesh_should_not_trigger_navmesh_update)
|
update_for_oscillating_object_that_does_not_change_navmesh_should_not_trigger_navmesh_update)
|
||||||
{
|
{
|
||||||
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
||||||
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
|
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||||
|
|
||||||
CollisionShapeInstance oscillatingBox(std::make_unique<btBoxShape>(btVector3(20, 20, 20)));
|
CollisionShapeInstance oscillatingBox(std::make_unique<btBoxShape>(btVector3(20, 20, 20)));
|
||||||
const btVector3 oscillatingBoxShapePosition(288, 288, 400);
|
const btVector3 oscillatingBoxShapePosition(288, 288, 400);
|
||||||
|
@ -777,7 +778,7 @@ namespace
|
||||||
TEST_F(DetourNavigatorNavigatorTest, should_provide_path_over_flat_heightfield)
|
TEST_F(DetourNavigatorNavigatorTest, should_provide_path_over_flat_heightfield)
|
||||||
{
|
{
|
||||||
const HeightfieldPlane plane{ 100 };
|
const HeightfieldPlane plane{ 100 };
|
||||||
const int cellSize = mHeightfieldTileSize * 4;
|
const int cellSize = heightfieldTileSize * 4;
|
||||||
|
|
||||||
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
||||||
mNavigator->addHeightfield(mCellPosition, cellSize, plane, nullptr);
|
mNavigator->addHeightfield(mCellPosition, cellSize, plane, nullptr);
|
||||||
|
@ -796,7 +797,7 @@ namespace
|
||||||
TEST_F(DetourNavigatorNavigatorTest, for_not_reachable_destination_find_path_should_provide_partial_path)
|
TEST_F(DetourNavigatorNavigatorTest, for_not_reachable_destination_find_path_should_provide_partial_path)
|
||||||
{
|
{
|
||||||
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
||||||
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
|
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||||
|
|
||||||
CollisionShapeInstance compound(std::make_unique<btCompoundShape>());
|
CollisionShapeInstance compound(std::make_unique<btCompoundShape>());
|
||||||
compound.shape().addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(204, -204, 0)),
|
compound.shape().addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(204, -204, 0)),
|
||||||
|
@ -822,7 +823,7 @@ namespace
|
||||||
TEST_F(DetourNavigatorNavigatorTest, end_tolerance_should_extent_available_destinations)
|
TEST_F(DetourNavigatorNavigatorTest, end_tolerance_should_extent_available_destinations)
|
||||||
{
|
{
|
||||||
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
||||||
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
|
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||||
|
|
||||||
CollisionShapeInstance compound(std::make_unique<btCompoundShape>());
|
CollisionShapeInstance compound(std::make_unique<btCompoundShape>());
|
||||||
compound.shape().addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(204, -204, 0)),
|
compound.shape().addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(204, -204, 0)),
|
||||||
|
@ -948,7 +949,7 @@ namespace
|
||||||
TEST_F(DetourNavigatorNavigatorTest, find_nearest_nav_mesh_position_should_return_nav_mesh_position)
|
TEST_F(DetourNavigatorNavigatorTest, find_nearest_nav_mesh_position_should_return_nav_mesh_position)
|
||||||
{
|
{
|
||||||
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
||||||
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
|
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||||
|
|
||||||
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
||||||
auto updateGuard = mNavigator->makeUpdateGuard();
|
auto updateGuard = mNavigator->makeUpdateGuard();
|
||||||
|
@ -966,7 +967,7 @@ namespace
|
||||||
TEST_F(DetourNavigatorNavigatorTest, find_nearest_nav_mesh_position_should_return_nullopt_when_too_far)
|
TEST_F(DetourNavigatorNavigatorTest, find_nearest_nav_mesh_position_should_return_nullopt_when_too_far)
|
||||||
{
|
{
|
||||||
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
||||||
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
|
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||||
|
|
||||||
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
||||||
auto updateGuard = mNavigator->makeUpdateGuard();
|
auto updateGuard = mNavigator->makeUpdateGuard();
|
||||||
|
@ -984,7 +985,7 @@ namespace
|
||||||
TEST_F(DetourNavigatorNavigatorTest, find_nearest_nav_mesh_position_should_return_nullopt_when_flags_do_not_match)
|
TEST_F(DetourNavigatorNavigatorTest, find_nearest_nav_mesh_position_should_return_nullopt_when_flags_do_not_match)
|
||||||
{
|
{
|
||||||
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
const HeightfieldSurface surface = makeSquareHeightfieldSurface(defaultHeightfieldData);
|
||||||
const int cellSize = mHeightfieldTileSize * (surface.mSize - 1);
|
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||||
|
|
||||||
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
||||||
auto updateGuard = mNavigator->makeUpdateGuard();
|
auto updateGuard = mNavigator->makeUpdateGuard();
|
||||||
|
@ -998,4 +999,142 @@ namespace
|
||||||
EXPECT_EQ(findNearestNavMeshPosition(*mNavigator, mAgentBounds, position, searchAreaHalfExtents, Flag_swim),
|
EXPECT_EQ(findNearestNavMeshPosition(*mNavigator, mAgentBounds, position, searchAreaHalfExtents, Flag_swim),
|
||||||
std::nullopt);
|
std::nullopt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct DetourNavigatorUpdateTest : TestWithParam<std::function<void(Navigator&)>>
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<TilePosition> getUsedTiles(const NavMeshCacheItem& navMesh)
|
||||||
|
{
|
||||||
|
std::vector<TilePosition> result;
|
||||||
|
navMesh.forEachUsedTile([&](const TilePosition& position, const auto&...) { result.push_back(position); });
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(DetourNavigatorUpdateTest, update_should_change_covered_area_when_player_moves)
|
||||||
|
{
|
||||||
|
Loading::Listener listener;
|
||||||
|
Settings settings = makeSettings();
|
||||||
|
settings.mMaxTilesNumber = 5;
|
||||||
|
NavigatorImpl navigator(settings, nullptr);
|
||||||
|
const AgentBounds agentBounds{ CollisionShapeType::Aabb, { 29, 29, 66 } };
|
||||||
|
ASSERT_TRUE(navigator.addAgent(agentBounds));
|
||||||
|
|
||||||
|
GetParam()(navigator);
|
||||||
|
|
||||||
|
{
|
||||||
|
auto updateGuard = navigator.makeUpdateGuard();
|
||||||
|
navigator.update(osg::Vec3f(3000, 3000, 0), updateGuard.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
navigator.wait(WaitConditionType::allJobsDone, &listener);
|
||||||
|
|
||||||
|
{
|
||||||
|
const auto navMesh = navigator.getNavMesh(agentBounds);
|
||||||
|
ASSERT_NE(navMesh, nullptr);
|
||||||
|
|
||||||
|
const TilePosition expectedTiles[] = { { 3, 4 }, { 4, 3 }, { 4, 4 }, { 4, 5 }, { 5, 4 } };
|
||||||
|
const auto usedTiles = getUsedTiles(*navMesh->lockConst());
|
||||||
|
EXPECT_THAT(usedTiles, UnorderedElementsAreArray(expectedTiles)) << usedTiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto updateGuard = navigator.makeUpdateGuard();
|
||||||
|
navigator.update(osg::Vec3f(4000, 3000, 0), updateGuard.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
navigator.wait(WaitConditionType::allJobsDone, &listener);
|
||||||
|
|
||||||
|
{
|
||||||
|
const auto navMesh = navigator.getNavMesh(agentBounds);
|
||||||
|
ASSERT_NE(navMesh, nullptr);
|
||||||
|
|
||||||
|
const TilePosition expectedTiles[] = { { 4, 4 }, { 5, 3 }, { 5, 4 }, { 5, 5 }, { 6, 4 } };
|
||||||
|
const auto usedTiles = getUsedTiles(*navMesh->lockConst());
|
||||||
|
EXPECT_THAT(usedTiles, UnorderedElementsAreArray(expectedTiles)) << usedTiles;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct AddHeightfieldSurface
|
||||||
|
{
|
||||||
|
static constexpr std::size_t sSize = 65;
|
||||||
|
static constexpr float sHeights[sSize * sSize]{};
|
||||||
|
|
||||||
|
void operator()(Navigator& navigator) const
|
||||||
|
{
|
||||||
|
const osg::Vec2i cellPosition(0, 0);
|
||||||
|
const HeightfieldSurface surface{
|
||||||
|
.mHeights = sHeights,
|
||||||
|
.mSize = sSize,
|
||||||
|
.mMinHeight = -1,
|
||||||
|
.mMaxHeight = 1,
|
||||||
|
};
|
||||||
|
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||||
|
navigator.addHeightfield(cellPosition, cellSize, surface, nullptr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AddHeightfieldPlane
|
||||||
|
{
|
||||||
|
void operator()(Navigator& navigator) const
|
||||||
|
{
|
||||||
|
const osg::Vec2i cellPosition(0, 0);
|
||||||
|
const HeightfieldPlane plane{ .mHeight = 0 };
|
||||||
|
const int cellSize = 8192;
|
||||||
|
navigator.addHeightfield(cellPosition, cellSize, plane, nullptr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AddWater
|
||||||
|
{
|
||||||
|
void operator()(Navigator& navigator) const
|
||||||
|
{
|
||||||
|
const osg::Vec2i cellPosition(0, 0);
|
||||||
|
const float level = 0;
|
||||||
|
const int cellSize = 8192;
|
||||||
|
navigator.addWater(cellPosition, cellSize, level, nullptr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AddObject
|
||||||
|
{
|
||||||
|
const float mSize = 8192;
|
||||||
|
CollisionShapeInstance<btBoxShape> mBox{ std::make_unique<btBoxShape>(btVector3(mSize, mSize, 1)) };
|
||||||
|
const ObjectTransform mTransform{
|
||||||
|
.mPosition = ESM::Position{ .pos = { 0, 0, 0 }, .rot{ 0, 0, 0 } },
|
||||||
|
.mScale = 1.0f,
|
||||||
|
};
|
||||||
|
|
||||||
|
void operator()(Navigator& navigator) const
|
||||||
|
{
|
||||||
|
navigator.addObject(ObjectId(&mBox.shape()), ObjectShapes(mBox.instance(), mTransform),
|
||||||
|
btTransform::getIdentity(), nullptr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AddAll
|
||||||
|
{
|
||||||
|
AddHeightfieldSurface mAddHeightfieldSurface;
|
||||||
|
AddHeightfieldPlane mAddHeightfieldPlane;
|
||||||
|
AddWater mAddWater;
|
||||||
|
AddObject mAddObject;
|
||||||
|
|
||||||
|
void operator()(Navigator& navigator) const
|
||||||
|
{
|
||||||
|
mAddHeightfieldSurface(navigator);
|
||||||
|
mAddHeightfieldPlane(navigator);
|
||||||
|
mAddWater(navigator);
|
||||||
|
mAddObject(navigator);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::function<void(Navigator&)> addNavMeshData[] = {
|
||||||
|
AddHeightfieldSurface{},
|
||||||
|
AddHeightfieldPlane{},
|
||||||
|
AddWater{},
|
||||||
|
AddObject{},
|
||||||
|
AddAll{},
|
||||||
|
};
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_SUITE_P(DifferentNavMeshData, DetourNavigatorUpdateTest, ValuesIn(addNavMeshData));
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,12 +42,24 @@ namespace testing
|
||||||
<< ", " << value.y() << ", " << value.z() << ')';
|
<< ", " << value.y() << ", " << value.z() << ')';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline testing::Message& Message::operator<<(const osg::Vec2i& value)
|
||||||
|
{
|
||||||
|
return (*this) << "{" << value.x() << ", " << value.y() << '}';
|
||||||
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline testing::Message& Message::operator<<(const Wrapper<osg::Vec3f>& value)
|
inline testing::Message& Message::operator<<(const Wrapper<osg::Vec3f>& value)
|
||||||
{
|
{
|
||||||
return (*this) << value.mValue;
|
return (*this) << value.mValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline testing::Message& Message::operator<<(const Wrapper<osg::Vec2i>& value)
|
||||||
|
{
|
||||||
|
return (*this) << value.mValue;
|
||||||
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline testing::Message& Message::operator<<(const Wrapper<float>& value)
|
inline testing::Message& Message::operator<<(const Wrapper<float>& value)
|
||||||
{
|
{
|
||||||
|
@ -72,6 +84,12 @@ namespace testing
|
||||||
return writeRange(*this, value, 1);
|
return writeRange(*this, value, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline testing::Message& Message::operator<<(const std::vector<osg::Vec2i>& value)
|
||||||
|
{
|
||||||
|
return writeRange(*this, value, 1);
|
||||||
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline testing::Message& Message::operator<<(const std::vector<float>& value)
|
inline testing::Message& Message::operator<<(const std::vector<float>& value)
|
||||||
{
|
{
|
||||||
|
|
|
@ -76,4 +76,13 @@ namespace DetourNavigator
|
||||||
return {};
|
return {};
|
||||||
return TilesPositionsRange{ TilePosition(beginX, beginY), TilePosition(endX, endY) };
|
return TilesPositionsRange{ TilePosition(beginX, beginY), TilePosition(endX, endY) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TilesPositionsRange getUnion(const TilesPositionsRange& a, const TilesPositionsRange& b) noexcept
|
||||||
|
{
|
||||||
|
const int beginX = std::min(a.mBegin.x(), b.mBegin.x());
|
||||||
|
const int endX = std::max(a.mEnd.x(), b.mEnd.x());
|
||||||
|
const int beginY = std::min(a.mBegin.y(), b.mBegin.y());
|
||||||
|
const int endY = std::max(a.mEnd.y(), b.mEnd.y());
|
||||||
|
return TilesPositionsRange{ .mBegin = TilePosition(beginX, beginY), .mEnd = TilePosition(endX, endY) };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,8 @@ namespace DetourNavigator
|
||||||
}
|
}
|
||||||
|
|
||||||
TilesPositionsRange getIntersection(const TilesPositionsRange& a, const TilesPositionsRange& b) noexcept;
|
TilesPositionsRange getIntersection(const TilesPositionsRange& a, const TilesPositionsRange& b) noexcept;
|
||||||
|
|
||||||
|
TilesPositionsRange getUnion(const TilesPositionsRange& a, const TilesPositionsRange& b) noexcept;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -166,6 +166,7 @@ namespace DetourNavigator
|
||||||
return;
|
return;
|
||||||
mLastRecastMeshManagerRevision = mRecastMeshManager.getRevision();
|
mLastRecastMeshManagerRevision = mRecastMeshManager.getRevision();
|
||||||
mPlayerTile = playerTile;
|
mPlayerTile = playerTile;
|
||||||
|
mRecastMeshManager.setRange(makeRange(playerTile, mSettings.mMaxTilesNumber), guard);
|
||||||
const auto changedTiles = mRecastMeshManager.takeChangedTiles(guard);
|
const auto changedTiles = mRecastMeshManager.takeChangedTiles(guard);
|
||||||
const TilesPositionsRange range = mRecastMeshManager.getLimitedObjectsRange();
|
const TilesPositionsRange range = mRecastMeshManager.getLimitedObjectsRange();
|
||||||
for (const auto& [agentBounds, cached] : mCache)
|
for (const auto& [agentBounds, cached] : mCache)
|
||||||
|
|
|
@ -54,6 +54,15 @@ namespace DetourNavigator
|
||||||
private:
|
private:
|
||||||
const std::optional<std::unique_lock<Mutex>> mImpl;
|
const std::optional<std::unique_lock<Mutex>> mImpl;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
TilesPositionsRange getIndexRange(const auto& index)
|
||||||
|
{
|
||||||
|
const auto bounds = index.bounds();
|
||||||
|
return TilesPositionsRange{
|
||||||
|
.mBegin = makeTilePosition(bounds.min_corner()),
|
||||||
|
.mEnd = makeTilePosition(bounds.max_corner()) + TilePosition(1, 1),
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TileCachedRecastMeshManager::TileCachedRecastMeshManager(const RecastSettings& settings)
|
TileCachedRecastMeshManager::TileCachedRecastMeshManager(const RecastSettings& settings)
|
||||||
|
@ -104,14 +113,28 @@ namespace DetourNavigator
|
||||||
|
|
||||||
TilesPositionsRange TileCachedRecastMeshManager::getLimitedObjectsRange() const
|
TilesPositionsRange TileCachedRecastMeshManager::getLimitedObjectsRange() const
|
||||||
{
|
{
|
||||||
if (mObjects.empty())
|
std::optional<TilesPositionsRange> result;
|
||||||
return {};
|
if (!mWater.empty())
|
||||||
const auto bounds = mObjectIndex.bounds();
|
result = getIndexRange(mWaterIndex);
|
||||||
const TilesPositionsRange objectsRange{
|
if (!mHeightfields.empty())
|
||||||
.mBegin = makeTilePosition(bounds.min_corner()),
|
{
|
||||||
.mEnd = makeTilePosition(bounds.max_corner()) + TilePosition(1, 1),
|
const TilesPositionsRange range = getIndexRange(mHeightfieldIndex);
|
||||||
};
|
if (result.has_value())
|
||||||
return getIntersection(mRange, objectsRange);
|
result = getUnion(*result, range);
|
||||||
|
else
|
||||||
|
result = range;
|
||||||
|
}
|
||||||
|
if (!mObjects.empty())
|
||||||
|
{
|
||||||
|
const TilesPositionsRange range = getIndexRange(mObjectIndex);
|
||||||
|
if (result.has_value())
|
||||||
|
result = getUnion(*result, range);
|
||||||
|
else
|
||||||
|
result = range;
|
||||||
|
}
|
||||||
|
if (result.has_value())
|
||||||
|
return getIntersection(mRange, *result);
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
void TileCachedRecastMeshManager::setWorldspace(std::string_view worldspace, const UpdateGuard* guard)
|
void TileCachedRecastMeshManager::setWorldspace(std::string_view worldspace, const UpdateGuard* guard)
|
||||||
|
|
Loading…
Reference in a new issue