1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-01-26 09:35:28 +00:00
elsid af7059373c
Make RecastMesh independent from the order of RecastMeshBuilder calls
To make sure RecastMesh objects are equal if built with the same data but in
different order. Will be used later when there will be more than one place
building RecasMesh objects.
2021-07-26 00:22:20 +02:00

436 lines
20 KiB
C++

#include "operators.hpp"
#include <components/detournavigator/recastmeshbuilder.hpp>
#include <components/detournavigator/settings.hpp>
#include <components/detournavigator/recastmesh.hpp>
#include <components/detournavigator/exceptions.hpp>
#include <BulletCollision/CollisionShapes/btBoxShape.h>
#include <BulletCollision/CollisionShapes/btBvhTriangleMeshShape.h>
#include <BulletCollision/CollisionShapes/btTriangleMesh.h>
#include <BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h>
#include <BulletCollision/CollisionShapes/btCompoundShape.h>
#include <DetourCommon.h>
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <array>
namespace DetourNavigator
{
static inline bool operator ==(const RecastMesh::Water& lhs, const RecastMesh::Water& rhs)
{
return lhs.mCellSize == rhs.mCellSize && lhs.mTransform == rhs.mTransform;
}
}
namespace
{
using namespace testing;
using namespace DetourNavigator;
struct DetourNavigatorRecastMeshBuilderTest : Test
{
Settings mSettings;
TileBounds mBounds;
const std::size_t mGeneration = 0;
const std::size_t mRevision = 0;
DetourNavigatorRecastMeshBuilderTest()
{
mSettings.mRecastScaleFactor = 1.0f;
mBounds.mMin = osg::Vec2f(-std::numeric_limits<float>::max() * std::numeric_limits<float>::epsilon(),
-std::numeric_limits<float>::max() * std::numeric_limits<float>::epsilon());
mBounds.mMax = osg::Vec2f(std::numeric_limits<float>::max() * std::numeric_limits<float>::epsilon(),
std::numeric_limits<float>::max() * std::numeric_limits<float>::epsilon());
}
};
TEST_F(DetourNavigatorRecastMeshBuilderTest, create_for_empty_should_return_empty)
{
RecastMeshBuilder builder(mSettings, mBounds);
const auto recastMesh = std::move(builder).create(mGeneration, mRevision);
EXPECT_EQ(recastMesh->getMesh().getVertices(), std::vector<float>());
EXPECT_EQ(recastMesh->getMesh().getIndices(), std::vector<int>());
EXPECT_EQ(recastMesh->getMesh().getAreaTypes(), std::vector<AreaType>());
}
TEST_F(DetourNavigatorRecastMeshBuilderTest, add_bhv_triangle_mesh_shape)
{
btTriangleMesh mesh;
mesh.addTriangle(btVector3(-1, -1, 0), btVector3(-1, 1, 0), btVector3(1, -1, 0));
btBvhTriangleMeshShape shape(&mesh, true);
RecastMeshBuilder builder(mSettings, mBounds);
builder.addObject(static_cast<const btCollisionShape&>(shape), btTransform::getIdentity(), AreaType_ground);
const auto recastMesh = std::move(builder).create(mGeneration, mRevision);
EXPECT_EQ(recastMesh->getMesh().getVertices(), std::vector<float>({
-1, 0, -1,
-1, 0, 1,
1, 0, -1,
})) << recastMesh->getMesh().getVertices();
EXPECT_EQ(recastMesh->getMesh().getIndices(), std::vector<int>({2, 1, 0}));
EXPECT_EQ(recastMesh->getMesh().getAreaTypes(), std::vector<AreaType>({AreaType_ground}));
}
TEST_F(DetourNavigatorRecastMeshBuilderTest, add_transformed_bhv_triangle_mesh_shape)
{
btTriangleMesh mesh;
mesh.addTriangle(btVector3(-1, -1, 0), btVector3(-1, 1, 0), btVector3(1, -1, 0));
btBvhTriangleMeshShape shape(&mesh, true);
RecastMeshBuilder builder(mSettings, mBounds);
builder.addObject(
static_cast<const btCollisionShape&>(shape),
btTransform(btMatrix3x3::getIdentity().scaled(btVector3(1, 2, 3)), btVector3(1, 2, 3)),
AreaType_ground
);
const auto recastMesh = std::move(builder).create(mGeneration, mRevision);
EXPECT_EQ(recastMesh->getMesh().getVertices(), std::vector<float>({
0, 3, 0,
0, 3, 4,
2, 3, 0,
})) << recastMesh->getMesh().getVertices();
EXPECT_EQ(recastMesh->getMesh().getIndices(), std::vector<int>({2, 1, 0}));
EXPECT_EQ(recastMesh->getMesh().getAreaTypes(), std::vector<AreaType>({AreaType_ground}));
}
TEST_F(DetourNavigatorRecastMeshBuilderTest, add_heightfield_terrian_shape)
{
const std::array<btScalar, 4> heightfieldData {{0, 0, 0, 0}};
btHeightfieldTerrainShape shape(2, 2, heightfieldData.data(), 1, 0, 0, 2, PHY_FLOAT, false);
RecastMeshBuilder builder(mSettings, mBounds);
builder.addObject(static_cast<const btCollisionShape&>(shape), btTransform::getIdentity(), AreaType_ground);
const auto recastMesh = std::move(builder).create(mGeneration, mRevision);
EXPECT_EQ(recastMesh->getMesh().getVertices(), std::vector<float>({
-0.5, 0, -0.5,
-0.5, 0, 0.5,
0.5, 0, -0.5,
0.5, 0, 0.5,
}));
EXPECT_EQ(recastMesh->getMesh().getIndices(), std::vector<int>({0, 1, 2, 2, 1, 3}));
EXPECT_EQ(recastMesh->getMesh().getAreaTypes(), std::vector<AreaType>({AreaType_ground, AreaType_ground}));
}
TEST_F(DetourNavigatorRecastMeshBuilderTest, add_box_shape_should_produce_12_triangles)
{
btBoxShape shape(btVector3(1, 1, 2));
RecastMeshBuilder builder(mSettings, mBounds);
builder.addObject(static_cast<const btCollisionShape&>(shape), btTransform::getIdentity(), AreaType_ground);
const auto recastMesh = std::move(builder).create(mGeneration, mRevision);
EXPECT_EQ(recastMesh->getMesh().getVertices(), std::vector<float>({
-1, -2, -1,
-1, -2, 1,
-1, 2, -1,
-1, 2, 1,
1, -2, -1,
1, -2, 1,
1, 2, -1,
1, 2, 1,
})) << recastMesh->getMesh().getVertices();
EXPECT_EQ(recastMesh->getMesh().getIndices(), std::vector<int>({
0, 1, 3,
0, 2, 6,
0, 4, 5,
1, 5, 7,
2, 3, 7,
3, 2, 0,
4, 6, 7,
5, 1, 0,
6, 4, 0,
7, 3, 1,
7, 5, 4,
7, 6, 2,
})) << recastMesh->getMesh().getIndices();
EXPECT_EQ(recastMesh->getMesh().getAreaTypes(), std::vector<AreaType>(12, AreaType_ground));
}
TEST_F(DetourNavigatorRecastMeshBuilderTest, add_compound_shape)
{
btTriangleMesh mesh1;
mesh1.addTriangle(btVector3(-1, -1, 0), btVector3(-1, 1, 0), btVector3(1, -1, 0));
btBvhTriangleMeshShape triangle1(&mesh1, true);
btBoxShape box(btVector3(1, 1, 2));
btTriangleMesh mesh2;
mesh2.addTriangle(btVector3(1, 1, 0), btVector3(-1, 1, 0), btVector3(1, -1, 0));
btBvhTriangleMeshShape triangle2(&mesh2, true);
btCompoundShape shape;
shape.addChildShape(btTransform::getIdentity(), &triangle1);
shape.addChildShape(btTransform::getIdentity(), &box);
shape.addChildShape(btTransform::getIdentity(), &triangle2);
RecastMeshBuilder builder(mSettings, mBounds);
builder.addObject(
static_cast<const btCollisionShape&>(shape),
btTransform::getIdentity(),
AreaType_ground
);
const auto recastMesh = std::move(builder).create(mGeneration, mRevision);
EXPECT_EQ(recastMesh->getMesh().getVertices(), std::vector<float>({
-1, -2, -1,
-1, -2, 1,
-1, 0, -1,
-1, 0, 1,
-1, 2, -1,
-1, 2, 1,
1, -2, -1,
1, -2, 1,
1, 0, -1,
1, 0, 1,
1, 2, -1,
1, 2, 1,
})) << recastMesh->getMesh().getVertices();
EXPECT_EQ(recastMesh->getMesh().getIndices(), std::vector<int>({
0, 1, 5,
0, 4, 10,
0, 6, 7,
1, 7, 11,
4, 5, 11,
5, 4, 0,
6, 10, 11,
7, 1, 0,
8, 3, 2,
8, 3, 9,
10, 6, 0,
11, 5, 1,
11, 7, 6,
11, 10, 4,
})) << recastMesh->getMesh().getIndices();
EXPECT_EQ(recastMesh->getMesh().getAreaTypes(), std::vector<AreaType>(14, AreaType_ground));
}
TEST_F(DetourNavigatorRecastMeshBuilderTest, add_transformed_compound_shape)
{
btTriangleMesh mesh;
mesh.addTriangle(btVector3(-1, -1, 0), btVector3(-1, 1, 0), btVector3(1, -1, 0));
btBvhTriangleMeshShape triangle(&mesh, true);
btCompoundShape shape;
shape.addChildShape(btTransform::getIdentity(), &triangle);
RecastMeshBuilder builder(mSettings, mBounds);
builder.addObject(
static_cast<const btCollisionShape&>(shape),
btTransform(btMatrix3x3::getIdentity().scaled(btVector3(1, 2, 3)), btVector3(1, 2, 3)),
AreaType_ground
);
const auto recastMesh = std::move(builder).create(mGeneration, mRevision);
EXPECT_EQ(recastMesh->getMesh().getVertices(), std::vector<float>({
0, 3, 0,
0, 3, 4,
2, 3, 0,
})) << recastMesh->getMesh().getVertices();
EXPECT_EQ(recastMesh->getMesh().getIndices(), std::vector<int>({2, 1, 0}));
EXPECT_EQ(recastMesh->getMesh().getAreaTypes(), std::vector<AreaType>({AreaType_ground}));
}
TEST_F(DetourNavigatorRecastMeshBuilderTest, add_transformed_compound_shape_with_transformed_bhv_triangle_shape)
{
btTriangleMesh mesh;
mesh.addTriangle(btVector3(-1, -1, 0), btVector3(-1, 1, 0), btVector3(1, -1, 0));
btBvhTriangleMeshShape triangle(&mesh, true);
btCompoundShape shape;
shape.addChildShape(btTransform(btMatrix3x3::getIdentity().scaled(btVector3(1, 2, 3)), btVector3(1, 2, 3)),
&triangle);
RecastMeshBuilder builder(mSettings, mBounds);
builder.addObject(
static_cast<const btCollisionShape&>(shape),
btTransform(btMatrix3x3::getIdentity().scaled(btVector3(1, 2, 3)), btVector3(1, 2, 3)),
AreaType_ground
);
const auto recastMesh = std::move(builder).create(mGeneration, mRevision);
EXPECT_EQ(recastMesh->getMesh().getVertices(), std::vector<float>({
1, 12, 2,
1, 12, 10,
3, 12, 2,
})) << recastMesh->getMesh().getVertices();
EXPECT_EQ(recastMesh->getMesh().getIndices(), std::vector<int>({2, 1, 0}));
EXPECT_EQ(recastMesh->getMesh().getAreaTypes(), std::vector<AreaType>({AreaType_ground}));
}
TEST_F(DetourNavigatorRecastMeshBuilderTest, without_bounds_add_bhv_triangle_shape_should_not_filter_by_bounds)
{
btTriangleMesh mesh;
mesh.addTriangle(btVector3(-1, -1, 0), btVector3(-1, 1, 0), btVector3(1, -1, 0));
mesh.addTriangle(btVector3(-3, -3, 0), btVector3(-3, -2, 0), btVector3(-2, -3, 0));
btBvhTriangleMeshShape shape(&mesh, true);
RecastMeshBuilder builder(mSettings, mBounds);
builder.addObject(
static_cast<const btCollisionShape&>(shape),
btTransform::getIdentity(),
AreaType_ground
);
const auto recastMesh = std::move(builder).create(mGeneration, mRevision);
EXPECT_EQ(recastMesh->getMesh().getVertices(), std::vector<float>({
-3, 0, -3,
-3, 0, -2,
-2, 0, -3,
-1, 0, -1,
-1, 0, 1,
1, 0, -1,
})) << recastMesh->getMesh().getVertices();
EXPECT_EQ(recastMesh->getMesh().getIndices(), std::vector<int>({2, 1, 0, 5, 4, 3}));
EXPECT_EQ(recastMesh->getMesh().getAreaTypes(), std::vector<AreaType>(2, AreaType_ground));
}
TEST_F(DetourNavigatorRecastMeshBuilderTest, with_bounds_add_bhv_triangle_shape_should_filter_by_bounds)
{
mSettings.mRecastScaleFactor = 0.1f;
mBounds.mMin = osg::Vec2f(-3, -3) * mSettings.mRecastScaleFactor;
mBounds.mMax = osg::Vec2f(-2, -2) * mSettings.mRecastScaleFactor;
btTriangleMesh mesh;
mesh.addTriangle(btVector3(-1, -1, 0), btVector3(-1, 1, 0), btVector3(1, -1, 0));
mesh.addTriangle(btVector3(-3, -3, 0), btVector3(-3, -2, 0), btVector3(-2, -3, 0));
btBvhTriangleMeshShape shape(&mesh, true);
RecastMeshBuilder builder(mSettings, mBounds);
builder.addObject(
static_cast<const btCollisionShape&>(shape),
btTransform::getIdentity(),
AreaType_ground
);
const auto recastMesh = std::move(builder).create(mGeneration, mRevision);
EXPECT_EQ(recastMesh->getMesh().getVertices(), std::vector<float>({
-0.3f, 0, -0.3f,
-0.3f, 0, -0.2f,
-0.2f, 0, -0.3f,
})) << recastMesh->getMesh().getVertices();
EXPECT_EQ(recastMesh->getMesh().getIndices(), std::vector<int>({2, 1, 0}));
EXPECT_EQ(recastMesh->getMesh().getAreaTypes(), std::vector<AreaType>({AreaType_ground}));
}
TEST_F(DetourNavigatorRecastMeshBuilderTest, with_bounds_add_rotated_by_x_bhv_triangle_shape_should_filter_by_bounds)
{
mBounds.mMin = osg::Vec2f(-5, -5);
mBounds.mMax = osg::Vec2f(5, -2);
btTriangleMesh mesh;
mesh.addTriangle(btVector3(0, -1, -1), btVector3(0, -1, -1), btVector3(0, 1, -1));
mesh.addTriangle(btVector3(0, -3, -3), btVector3(0, -3, -2), btVector3(0, -2, -3));
btBvhTriangleMeshShape shape(&mesh, true);
RecastMeshBuilder builder(mSettings, mBounds);
builder.addObject(
static_cast<const btCollisionShape&>(shape),
btTransform(btQuaternion(btVector3(1, 0, 0),
static_cast<btScalar>(-osg::PI_4))),
AreaType_ground
);
const auto recastMesh = std::move(builder).create(mGeneration, mRevision);
EXPECT_THAT(recastMesh->getMesh().getVertices(), Pointwise(FloatNear(1e-5), std::vector<float>({
0, -0.707106769084930419921875, -3.535533905029296875,
0, 4.44089209850062616169452667236328125e-16, -4.24264049530029296875,
0, 0.707106769084930419921875, -3.535533905029296875,
}))) << recastMesh->getMesh().getVertices();
EXPECT_EQ(recastMesh->getMesh().getIndices(), std::vector<int>({0, 2, 1}));
EXPECT_EQ(recastMesh->getMesh().getAreaTypes(), std::vector<AreaType>({AreaType_ground}));
}
TEST_F(DetourNavigatorRecastMeshBuilderTest, with_bounds_add_rotated_by_y_bhv_triangle_shape_should_filter_by_bounds)
{
mBounds.mMin = osg::Vec2f(-5, -5);
mBounds.mMax = osg::Vec2f(-3, 5);
btTriangleMesh mesh;
mesh.addTriangle(btVector3(-1, 0, -1), btVector3(-1, 0, 1), btVector3(1, 0, -1));
mesh.addTriangle(btVector3(-3, 0, -3), btVector3(-3, 0, -2), btVector3(-2, 0, -3));
btBvhTriangleMeshShape shape(&mesh, true);
RecastMeshBuilder builder(mSettings, mBounds);
builder.addObject(
static_cast<const btCollisionShape&>(shape),
btTransform(btQuaternion(btVector3(0, 1, 0),
static_cast<btScalar>(osg::PI_4))),
AreaType_ground
);
const auto recastMesh = std::move(builder).create(mGeneration, mRevision);
EXPECT_THAT(recastMesh->getMesh().getVertices(), Pointwise(FloatNear(1e-5), std::vector<float>({
-4.24264049530029296875, 4.44089209850062616169452667236328125e-16, 0,
-3.535533905029296875, -0.707106769084930419921875, 0,
-3.535533905029296875, 0.707106769084930419921875, 0,
}))) << recastMesh->getMesh().getVertices();
EXPECT_EQ(recastMesh->getMesh().getIndices(), std::vector<int>({1, 2, 0}));
EXPECT_EQ(recastMesh->getMesh().getAreaTypes(), std::vector<AreaType>({AreaType_ground}));
}
TEST_F(DetourNavigatorRecastMeshBuilderTest, with_bounds_add_rotated_by_z_bhv_triangle_shape_should_filter_by_bounds)
{
mBounds.mMin = osg::Vec2f(-5, -5);
mBounds.mMax = osg::Vec2f(-1, -1);
btTriangleMesh mesh;
mesh.addTriangle(btVector3(-1, -1, 0), btVector3(-1, 1, 0), btVector3(1, -1, 0));
mesh.addTriangle(btVector3(-3, -3, 0), btVector3(-3, -2, 0), btVector3(-2, -3, 0));
btBvhTriangleMeshShape shape(&mesh, true);
RecastMeshBuilder builder(mSettings, mBounds);
builder.addObject(
static_cast<const btCollisionShape&>(shape),
btTransform(btQuaternion(btVector3(0, 0, 1),
static_cast<btScalar>(osg::PI_4))),
AreaType_ground
);
const auto recastMesh = std::move(builder).create(mGeneration, mRevision);
EXPECT_THAT(recastMesh->getMesh().getVertices(), Pointwise(FloatNear(1e-5), std::vector<float>({
-1.41421353816986083984375, 0, -1.1102230246251565404236316680908203125e-16,
1.1102230246251565404236316680908203125e-16, 0, -1.41421353816986083984375,
1.41421353816986083984375, 0, 1.1102230246251565404236316680908203125e-16,
}))) << recastMesh->getMesh().getVertices();
EXPECT_EQ(recastMesh->getMesh().getIndices(), std::vector<int>({2, 0, 1}));
EXPECT_EQ(recastMesh->getMesh().getAreaTypes(), std::vector<AreaType>({AreaType_ground}));
}
TEST_F(DetourNavigatorRecastMeshBuilderTest, flags_values_should_be_corresponding_to_added_objects)
{
btTriangleMesh mesh1;
mesh1.addTriangle(btVector3(-1, -1, 0), btVector3(-1, 1, 0), btVector3(1, -1, 0));
btBvhTriangleMeshShape shape1(&mesh1, true);
btTriangleMesh mesh2;
mesh2.addTriangle(btVector3(-3, -3, 0), btVector3(-3, -2, 0), btVector3(-2, -3, 0));
btBvhTriangleMeshShape shape2(&mesh2, true);
RecastMeshBuilder builder(mSettings, mBounds);
builder.addObject(
static_cast<const btCollisionShape&>(shape1),
btTransform::getIdentity(),
AreaType_ground
);
builder.addObject(
static_cast<const btCollisionShape&>(shape2),
btTransform::getIdentity(),
AreaType_null
);
const auto recastMesh = std::move(builder).create(mGeneration, mRevision);
EXPECT_EQ(recastMesh->getMesh().getVertices(), std::vector<float>({
-3, 0, -3,
-3, 0, -2,
-2, 0, -3,
-1, 0, -1,
-1, 0, 1,
1, 0, -1,
})) << recastMesh->getMesh().getVertices();
EXPECT_EQ(recastMesh->getMesh().getIndices(), std::vector<int>({2, 1, 0, 5, 4, 3}));
EXPECT_EQ(recastMesh->getMesh().getAreaTypes(), std::vector<AreaType>({AreaType_null, AreaType_ground}));
}
TEST_F(DetourNavigatorRecastMeshBuilderTest, add_water_then_get_water_should_return_it)
{
RecastMeshBuilder builder(mSettings, mBounds);
builder.addWater(1000, btTransform(btMatrix3x3::getIdentity(), btVector3(100, 200, 300)));
const auto recastMesh = std::move(builder).create(mGeneration, mRevision);
EXPECT_EQ(recastMesh->getWater(), std::vector<RecastMesh::Water>({
RecastMesh::Water {1000, btTransform(btMatrix3x3::getIdentity(), btVector3(100, 200, 300))}
}));
}
TEST_F(DetourNavigatorRecastMeshBuilderTest, add_bhv_triangle_mesh_shape_with_duplicated_vertices)
{
btTriangleMesh mesh;
mesh.addTriangle(btVector3(-1, -1, 0), btVector3(-1, 1, 0), btVector3(1, -1, 0));
mesh.addTriangle(btVector3(1, 1, 0), btVector3(-1, 1, 0), btVector3(1, -1, 0));
btBvhTriangleMeshShape shape(&mesh, true);
RecastMeshBuilder builder(mSettings, mBounds);
builder.addObject(static_cast<const btCollisionShape&>(shape), btTransform::getIdentity(), AreaType_ground);
const auto recastMesh = std::move(builder).create(mGeneration, mRevision);
EXPECT_EQ(recastMesh->getMesh().getVertices(), std::vector<float>({
-1, 0, -1,
-1, 0, 1,
1, 0, -1,
1, 0, 1,
})) << recastMesh->getMesh().getVertices();
EXPECT_EQ(recastMesh->getMesh().getIndices(), std::vector<int>({2, 1, 0, 2, 1, 3}));
EXPECT_EQ(recastMesh->getMesh().getAreaTypes(), std::vector<AreaType>({AreaType_ground, AreaType_ground}));
}
}