Optimize recast mesh size by vertex deduplication

pull/578/head
elsid 5 years ago
parent bf228746c9
commit 00197e1cd9
No known key found for this signature in database
GPG Key ID: B845CB9FEE18AB40

@ -23,17 +23,23 @@ namespace DetourNavigator
namespace
{
template <class T>
struct Wrapper {
struct Wrapper
{
const T& mValue;
};
template <class Range>
inline testing::Message& writeRange(testing::Message& message, const Range& range)
inline testing::Message& writeRange(testing::Message& message, const Range& range, std::size_t newLine)
{
message << "{\n";
message << "{";
std::size_t i = 0;
for (const auto& v : range)
message << Wrapper<typename std::decay<decltype(v)>::type> {v} << ",\n";
return message << "}";
{
if (i++ % newLine == 0)
message << "\n";
message << Wrapper<typename std::decay<decltype(v)>::type> {v} << ", ";
}
return message << "\n}";
}
}
@ -60,22 +66,34 @@ namespace testing
return (*this) << std::setprecision(std::numeric_limits<float>::max_exponent10) << value.mValue;
}
template <>
inline testing::Message& Message::operator <<(const Wrapper<int>& value)
{
return (*this) << value.mValue;
}
template <>
inline testing::Message& Message::operator <<(const std::deque<osg::Vec3f>& value)
{
return writeRange(*this, value);
return writeRange(*this, value, 1);
}
template <>
inline testing::Message& Message::operator <<(const std::vector<osg::Vec3f>& value)
{
return writeRange(*this, value);
return writeRange(*this, value, 1);
}
template <>
inline testing::Message& Message::operator <<(const std::vector<float>& value)
{
return writeRange(*this, value);
return writeRange(*this, value, 3);
}
template <>
inline testing::Message& Message::operator <<(const std::vector<int>& value)
{
return writeRange(*this, value, 3);
}
}

@ -104,11 +104,9 @@ namespace
-0.5, 0, -0.5,
-0.5, 0, 0.5,
0.5, 0, -0.5,
0.5, 0, -0.5,
-0.5, 0, 0.5,
0.5, 0, 0.5,
}));
EXPECT_EQ(recastMesh->getIndices(), std::vector<int>({0, 1, 2, 3, 4, 5}));
EXPECT_EQ(recastMesh->getIndices(), std::vector<int>({0, 1, 2, 2, 1, 3}));
EXPECT_EQ(recastMesh->getAreaTypes(), std::vector<AreaType>({AreaType_ground, AreaType_ground}));
}
@ -127,7 +125,7 @@ namespace
-1, -2, 1,
1, -2, -1,
-1, -2, -1,
}));
})) << recastMesh->getVertices();
EXPECT_EQ(recastMesh->getIndices(), std::vector<int>({
0, 2, 3,
3, 1, 0,
@ -141,7 +139,7 @@ namespace
2, 6, 7,
7, 6, 4,
4, 5, 7,
}));
})) << recastMesh->getIndices();
EXPECT_EQ(recastMesh->getAreaTypes(), std::vector<AreaType>(12, AreaType_ground));
}
@ -166,37 +164,35 @@ namespace
);
const auto recastMesh = builder.create(mGeneration, mRevision);
EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({
1, 0, -1,
-1, 0, 1,
-1, -2, -1,
-1, -2, 1,
-1, 0, -1,
1, 2, 1,
-1, 2, 1,
1, 2, -1,
-1, 0, 1,
-1, 2, -1,
1, -2, 1,
-1, -2, 1,
-1, 2, 1,
1, -2, -1,
-1, -2, -1,
1, -2, 1,
1, 0, -1,
-1, 0, 1,
1, 0, 1,
}));
1, 2, -1,
1, 2, 1,
})) << recastMesh->getVertices();
EXPECT_EQ(recastMesh->getIndices(), std::vector<int>({
0, 1, 2,
3, 5, 6,
6, 4, 3,
3, 7, 9,
9, 5, 3,
3, 4, 8,
8, 7, 3,
10, 8, 4,
4, 6, 10,
10, 6, 5,
5, 9, 10,
10, 9, 7,
7, 8, 10,
11, 12, 13,
}));
8, 3, 2,
11, 10, 4,
4, 5, 11,
11, 7, 6,
6, 10, 11,
11, 5, 1,
1, 7, 11,
0, 1, 5,
5, 4, 0,
0, 4, 10,
10, 6, 0,
0, 6, 7,
7, 1, 0,
8, 3, 9,
})) << recastMesh->getIndices();
EXPECT_EQ(recastMesh->getAreaTypes(), std::vector<AreaType>(14, AreaType_ground));
}
@ -413,4 +409,24 @@ namespace
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 = builder.create(mGeneration, mRevision);
EXPECT_EQ(recastMesh->getVertices(), std::vector<float>({
-1, 0, -1,
-1, 0, 1,
1, 0, -1,
1, 0, 1,
})) << recastMesh->getVertices();
EXPECT_EQ(recastMesh->getIndices(), std::vector<int>({2, 1, 0, 2, 1, 3}));
EXPECT_EQ(recastMesh->getAreaTypes(), std::vector<AreaType>({AreaType_ground, AreaType_ground}));
}
}

@ -17,11 +17,50 @@
#include <LinearMath/btAabbUtil2.h>
#include <algorithm>
#include <tuple>
namespace DetourNavigator
{
using BulletHelpers::makeProcessTriangleCallback;
namespace
{
void optimizeRecastMesh(std::vector<int>& indices, std::vector<float>& vertices)
{
std::vector<std::tuple<float, float, float>> uniqueVertices;
uniqueVertices.reserve(vertices.size() / 3);
for (std::size_t i = 0, n = vertices.size() / 3; i < n; ++i)
uniqueVertices.emplace_back(vertices[i * 3], vertices[i * 3 + 1], vertices[i * 3 + 2]);
std::sort(uniqueVertices.begin(), uniqueVertices.end());
const auto end = std::unique(uniqueVertices.begin(), uniqueVertices.end());
uniqueVertices.erase(end, uniqueVertices.end());
if (uniqueVertices.size() == vertices.size() / 3)
return;
for (std::size_t i = 0, n = indices.size(); i < n; ++i)
{
const auto index = indices[i];
const auto vertex = std::make_tuple(vertices[index * 3], vertices[index * 3 + 1], vertices[index * 3 + 2]);
const auto it = std::lower_bound(uniqueVertices.begin(), uniqueVertices.end(), vertex);
assert(it != uniqueVertices.end());
assert(*it == vertex);
indices[i] = std::distance(uniqueVertices.begin(), it);
}
vertices.resize(uniqueVertices.size() * 3);
for (std::size_t i = 0, n = uniqueVertices.size(); i < n; ++i)
{
vertices[i * 3] = std::get<0>(uniqueVertices[i]);
vertices[i * 3 + 1] = std::get<1>(uniqueVertices[i]);
vertices[i * 3 + 2] = std::get<2>(uniqueVertices[i]);
}
}
}
RecastMeshBuilder::RecastMeshBuilder(const Settings& settings, const TileBounds& bounds)
: mSettings(settings)
, mBounds(bounds)
@ -112,8 +151,9 @@ namespace DetourNavigator
mWater.push_back(RecastMesh::Water {cellSize, transform});
}
std::shared_ptr<RecastMesh> RecastMeshBuilder::create(std::size_t generation, std::size_t revision) const
std::shared_ptr<RecastMesh> RecastMeshBuilder::create(std::size_t generation, std::size_t revision)
{
optimizeRecastMesh(mIndices, mVertices);
return std::make_shared<RecastMesh>(generation, revision, mIndices, mVertices, mAreaTypes,
mWater, mSettings.get().mTrianglesPerChunk);
}

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

Loading…
Cancel
Save