You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
339 lines
14 KiB
339 lines
14 KiB
// David Eberly, Geometric Tools, Redmond WA 98052
|
|
// Copyright (c) 1998-2021
|
|
// Distributed under the Boost Software License, Version 1.0.
|
|
// https://www.boost.org/LICENSE_1_0.txt
|
|
// https://www.geometrictools.com/License/Boost/LICENSE_1_0.txt
|
|
// Version: 4.0.2020.11.16
|
|
|
|
#pragma once
|
|
|
|
#include <Mathematics/Logger.h>
|
|
#include <algorithm>
|
|
#include <map>
|
|
#include <vector>
|
|
|
|
// UniqueVerticesTriangles is a helper class that provides support for several
|
|
// mesh generation and mesh reduction operations. The vertices have type
|
|
// VertexType, which must have a less-than comparison predicate because
|
|
// duplicate vertices are eliminated in the operations.
|
|
//
|
|
// 1. Generate an indexed triangle representation from an array of
|
|
// triples of VertexType. Each triple represents the vertices of
|
|
// a triangle. Presumably, the triangles share vertices. The output
|
|
// is an array of unique VertexType objects (a vertex pool) and an
|
|
// array of triples of indices into the pool, each triple
|
|
// representing a triangle.
|
|
//
|
|
// 2. Remove duplicate vertices from a vertex pool used by an indexed
|
|
// triangle representation. A new vertex pool of unique vertices is
|
|
// generated and the indexed triangles are modified to be indices into
|
|
// this vertex pool.
|
|
//
|
|
// 3. Remove unused vertices from a vertex pool used by an indexed triangle
|
|
// representation. A new vertex pool of unique vertices is generated and
|
|
// the indexed triangles are modified to be indices into the new vertex
|
|
// pool.
|
|
//
|
|
// 4. Remove duplicate and unused vertices from a vertex pool, a combination
|
|
// of the operations in #2 and #3.
|
|
|
|
// Uncomment this preprocessor symbol to validate the preconditions of the
|
|
// inputs of the class member functions.
|
|
#define GTL_VALIDATE_UNIQUE_VERTICES_TRIANGLES
|
|
|
|
namespace gte
|
|
{
|
|
template <typename VertexType>
|
|
class // [[deprecated("Use UniqueVerticesSimplices<VertexType,IndexType,3> instead.")]]
|
|
UniqueVerticesTriangles
|
|
{
|
|
public:
|
|
// The class has no state, so the constructors, destructors, copy
|
|
// semantics and move semantics are all the defaults generated by the
|
|
// compiler.
|
|
|
|
// See #1 in the comments at the beginning of this file. The
|
|
// preconditions are
|
|
// 1. inVertices.size() is a positive multiple of 3
|
|
// The postconditions are
|
|
// 1. outVertices has unique vertices
|
|
// 2. outIndices.size() = inVertices.size()
|
|
// 3. 0 <= outIndices[i] < outVertices.size()
|
|
void GenerateIndexedTriangles(
|
|
std::vector<VertexType> const& inVertices,
|
|
std::vector<VertexType>& outVertices,
|
|
std::vector<int>& outIndices)
|
|
{
|
|
#if defined(GTL_VALIDATE_UNIQUE_VERTICES_TRIANGLES)
|
|
LogAssert(inVertices.size() > 0 && inVertices.size() % 3 == 0,
|
|
"Invalid number of vertices.");
|
|
#endif
|
|
outIndices.resize(inVertices.size());
|
|
RemoveDuplicates(inVertices, outVertices, outIndices.data());
|
|
}
|
|
|
|
// See #1 in the comments at the beginning of this file. The
|
|
// preconditions are
|
|
// 1. inVertices.size() is a positive multiple of 3
|
|
// The postconditions are
|
|
// 1. outVertices has unique vertices
|
|
// 2. outTriangles.size() = inVertices.size() / 3
|
|
// 3. 0 <= outTriangles[i][j] < outVertices.size()
|
|
void GenerateIndexedTriangles(
|
|
std::vector<VertexType> const& inVertices,
|
|
std::vector<VertexType>& outVertices,
|
|
std::vector<std::array<int, 3>>& outTriangles)
|
|
{
|
|
#if defined(GTL_VALIDATE_UNIQUE_VERTICES_TRIANGLES)
|
|
LogAssert(inVertices.size() > 0 && inVertices.size() % 3 == 0,
|
|
"Invalid number of vertices.");
|
|
#endif
|
|
outTriangles.resize(inVertices.size() / 3);
|
|
int* outIndices = reinterpret_cast<int*>(outTriangles.data());
|
|
RemoveDuplicates(inVertices, outVertices, outIndices);
|
|
}
|
|
|
|
// See #2 in the comments at the beginning of the file. The
|
|
// preconditions are
|
|
// 1. inVertices.size() is positive
|
|
// 2. inIndices.size() is a positive multiple of 3
|
|
// 3. 0 <= inIndices[i] < inVertices.size()
|
|
// The postconditions are
|
|
// 1. outVertices has unique vertices
|
|
// 2. outIndices.size() = inIndices.size()
|
|
// 3. 0 <= outIndices[i] < outVertices.size()
|
|
void RemoveDuplicateVertices(
|
|
std::vector<VertexType> const& inVertices,
|
|
std::vector<int> const& inIndices,
|
|
std::vector<VertexType>& outVertices,
|
|
std::vector<int>& outIndices)
|
|
{
|
|
#if defined(GTL_VALIDATE_UNIQUE_VERTICES_TRIANGLES)
|
|
LogAssert(inVertices.size() > 0, "Invalid number of vertices.");
|
|
LogAssert(inIndices.size() > 0 && inIndices.size() % 3 == 0,
|
|
"Invalid number of indices.");
|
|
int const numVertices = static_cast<int>(inVertices.size());
|
|
for (auto index : inIndices)
|
|
{
|
|
LogAssert(0 <= index && index < numVertices, "Invalid index.");
|
|
}
|
|
#endif
|
|
|
|
std::vector<int> inToOutMapping(inVertices.size());
|
|
RemoveDuplicates(inVertices, outVertices, inToOutMapping.data());
|
|
|
|
outIndices.resize(inIndices.size());
|
|
for (size_t i = 0; i < inIndices.size(); ++i)
|
|
{
|
|
outIndices[i] = inToOutMapping[inIndices[i]];
|
|
}
|
|
}
|
|
|
|
// See #2 in the comments at the beginning of the file. The
|
|
// preconditions are
|
|
// 1. inVertices.size() is positive
|
|
// 2. inTriangles.size() is positive
|
|
// 3. 0 <= inTriangles[i][j] < inVertices.size()
|
|
// The postconditions are
|
|
// 1. outVertices has unique vertices
|
|
// 2. outTriangles.size() = inTriangles.size()
|
|
// 3. 0 <= outTriangles[i][j] < outVertices.size()
|
|
void RemoveDuplicateVertices(
|
|
std::vector<VertexType> const& inVertices,
|
|
std::vector<std::array<int, 3>> const& inTriangles,
|
|
std::vector<VertexType>& outVertices,
|
|
std::vector<std::array<int, 3>>& outTriangles)
|
|
{
|
|
#if defined(GTL_VALIDATE_UNIQUE_VERTICES_TRIANGLES)
|
|
LogAssert(inVertices.size() > 0, "Invalid number of vertices.");
|
|
LogAssert(inTriangles.size() > 0, "Invalid number of triangles.");
|
|
int const numVertices = static_cast<int>(inVertices.size());
|
|
for (auto const& triangle : inTriangles)
|
|
{
|
|
for (size_t j = 0; j < 3; ++j)
|
|
{
|
|
LogAssert(0 <= triangle[j] && triangle[j] < numVertices,
|
|
"Invalid index.");
|
|
}
|
|
}
|
|
#endif
|
|
|
|
std::vector<int> inToOutMapping(inVertices.size());
|
|
RemoveDuplicates(inVertices, outVertices, inToOutMapping.data());
|
|
|
|
size_t const numTriangles = inTriangles.size();
|
|
outTriangles.resize(numTriangles);
|
|
for (size_t t = 0; t < numTriangles; ++t)
|
|
{
|
|
for (size_t j = 0; j < 3; ++j)
|
|
{
|
|
outTriangles[t][j] = inToOutMapping[inTriangles[t][j]];
|
|
}
|
|
}
|
|
}
|
|
|
|
// See #3 in the comments at the beginning of the file. The
|
|
// preconditions are
|
|
// 1. inVertices.size() is positive
|
|
// 2. inIndices.size() is a positive multiple of 3
|
|
// 3. 0 <= inIndices[i] < inVertices.size()
|
|
// The postconditions are
|
|
// 1. outVertices.size() is positive
|
|
// 2. outIndices.size() = inIndices.size()
|
|
// 3. 0 <= outIndices[i] < outVertices.size()
|
|
// 4. each outVertices[j] occurs at least once in outIndices
|
|
void RemoveUnusedVertices(
|
|
std::vector<VertexType> const& inVertices,
|
|
std::vector<int> const& inIndices,
|
|
std::vector<VertexType>& outVertices,
|
|
std::vector<int>& outIndices)
|
|
{
|
|
#if defined(GTL_VALIDATE_UNIQUE_VERTICES_TRIANGLES)
|
|
LogAssert(inVertices.size() > 0, "Invalid number of vertices.");
|
|
LogAssert(inIndices.size() > 0 && inIndices.size() % 3 == 0,
|
|
"Invalid number of indices.");
|
|
int const numVertices = static_cast<int>(inVertices.size());
|
|
for (auto index : inIndices)
|
|
{
|
|
LogAssert(0 <= index && index < numVertices, "Invalid index.");
|
|
}
|
|
#endif
|
|
outIndices.resize(inIndices.size());
|
|
RemoveUnused(inVertices, inIndices.size(), inIndices.data(),
|
|
outVertices, outIndices.data());
|
|
}
|
|
|
|
// See #3 in the comments at the beginning of the file. The
|
|
// preconditions are
|
|
// 1. inVertices.size() is positive
|
|
// 2. inTriangles.size() is positive
|
|
// 3. 0 <= outTriangles[i][j] < inVertices.size()
|
|
// The postconditions are
|
|
// 1. outVertices.size() is positive
|
|
// 2. outTriangles.size() = inTriangles.size()
|
|
// 3. 0 <= outTriangles[i][j] < outVertices.size()
|
|
// 4. each outVertices[j] occurs at least once in outTriangles
|
|
void RemoveUnusedVertices(
|
|
std::vector<VertexType> const& inVertices,
|
|
std::vector<std::array<int, 3>> const& inTriangles,
|
|
std::vector<VertexType> & outVertices,
|
|
std::vector<std::array<int, 3>> & outTriangles)
|
|
{
|
|
#if defined(GTL_VALIDATE_UNIQUE_VERTICES_TRIANGLES)
|
|
LogAssert(inVertices.size() > 0, "Invalid number of vertices.");
|
|
LogAssert(inTriangles.size() > 0, "Invalid number of triangles.");
|
|
int const numVertices = static_cast<int>(inVertices.size());
|
|
for (auto const& triangle : inTriangles)
|
|
{
|
|
for (size_t j = 0; j < 3; ++j)
|
|
{
|
|
LogAssert(0 <= triangle[j] && triangle[j] < numVertices,
|
|
"Invalid index.");
|
|
}
|
|
}
|
|
#endif
|
|
outTriangles.resize(inTriangles.size());
|
|
size_t const numInIndices = 3 * inTriangles.size();
|
|
int const* inIndices = reinterpret_cast<int const*>(inTriangles.data());
|
|
int* outIndices = reinterpret_cast<int*>(outTriangles.data());
|
|
RemoveUnused(inVertices, numInIndices, inIndices, outVertices, outIndices);
|
|
}
|
|
|
|
// See #4 and the preconditions for RemoveDuplicateVertices and for
|
|
// RemoveUnusedVertices.
|
|
void RemoveDuplicateAndUnusedVertices(
|
|
std::vector<VertexType> const& inVertices,
|
|
std::vector<int> const& inIndices,
|
|
std::vector<VertexType>& outVertices,
|
|
std::vector<int>& outIndices)
|
|
{
|
|
std::vector<VertexType> tempVertices;
|
|
std::vector<int> tempIndices;
|
|
RemoveDuplicateVertices(inVertices, inIndices, tempVertices, tempIndices);
|
|
RemoveUnusedVertices(tempVertices, tempIndices, outVertices, outIndices);
|
|
}
|
|
|
|
// See #4 and the preconditions for RemoveDuplicateVertices and for
|
|
// RemoveUnusedVertices.
|
|
void RemoveDuplicateAndUnusedVertices(
|
|
std::vector<VertexType> const& inVertices,
|
|
std::vector<std::array<int, 3>> const& inTriangles,
|
|
std::vector<VertexType>& outVertices,
|
|
std::vector<std::array<int, 3>>& outTriangles)
|
|
{
|
|
std::vector<VertexType> tempVertices;
|
|
std::vector<std::array<int, 3>> tempTriangles;
|
|
RemoveDuplicateVertices(inVertices, inTriangles, tempVertices, tempTriangles);
|
|
RemoveUnusedVertices(tempVertices, tempTriangles, outVertices, outTriangles);
|
|
}
|
|
|
|
private:
|
|
void RemoveDuplicates(
|
|
std::vector<VertexType> const& inVertices,
|
|
std::vector<VertexType>& outVertices,
|
|
int* inToOutMapping)
|
|
{
|
|
// Construct the unique vertices.
|
|
size_t const numInVertices = inVertices.size();
|
|
size_t numOutVertices = 0;
|
|
std::map<VertexType, int> vmap;
|
|
for (size_t i = 0; i < numInVertices; ++i, ++inToOutMapping)
|
|
{
|
|
auto const iter = vmap.find(inVertices[i]);
|
|
if (iter != vmap.end())
|
|
{
|
|
// The vertex is a duplicate of one inserted earlier into
|
|
// the map. Its index i will be modified to that of the
|
|
// first-found vertex.
|
|
*inToOutMapping = iter->second;
|
|
}
|
|
else
|
|
{
|
|
// The vertex occurs for the first time.
|
|
vmap.insert(std::make_pair(inVertices[i],
|
|
static_cast<int>(numOutVertices)));
|
|
*inToOutMapping = static_cast<int>(numOutVertices);
|
|
++numOutVertices;
|
|
}
|
|
}
|
|
|
|
// Pack the unique vertices into an array.
|
|
outVertices.resize(numOutVertices);
|
|
for (auto const& element : vmap)
|
|
{
|
|
outVertices[element.second] = element.first;
|
|
}
|
|
}
|
|
|
|
void RemoveUnused(
|
|
std::vector<VertexType> const& inVertices,
|
|
size_t const numInIndices,
|
|
int const* inIndices,
|
|
std::vector<VertexType>& outVertices,
|
|
int* outIndices)
|
|
{
|
|
std::set<int> usedIndices;
|
|
for (size_t i = 0; i < numInIndices; ++i)
|
|
{
|
|
usedIndices.insert(inIndices[i]);
|
|
}
|
|
|
|
// Locate the used vertices and pack them into an array.
|
|
outVertices.resize(usedIndices.size());
|
|
size_t numOutVertices = 0;
|
|
std::map<int, int> vmap;
|
|
for (auto oldIndex : usedIndices)
|
|
{
|
|
outVertices[numOutVertices] = inVertices[oldIndex];
|
|
vmap.insert(std::make_pair(oldIndex, static_cast<int>(numOutVertices)));
|
|
++numOutVertices;
|
|
}
|
|
|
|
// Reassign the old indices to the new indices.
|
|
for (size_t i = 0; i < numInIndices; ++i)
|
|
{
|
|
outIndices[i] = vmap.find(inIndices[i])->second;
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|