Browse Source

Cleaning overengineered MeshBuilder.

* Removed VertexPointer typedef, as size_t is somewhere 64bit and
   OpenGL doesn't have corresponding data type. Using unsigned int
   everywhere instead, to avoid confusion.
 * Removed Face structure altogether, using vertex indices directly.
 * Greatly reduced size of unit tests, also thanks to C++11
   std::initializer_list feature.
pull/279/head
Vladimír Vondruš 15 years ago
parent
commit
c11e691053
  1. 134
      src/MeshBuilder.h
  2. 2
      src/Primitives/Icosphere.h
  3. 160
      src/Test/MeshBuilderTest.cpp

134
src/MeshBuilder.h

@ -42,31 +42,6 @@ vertices etc.).
*/ */
template<class Vertex> class MeshBuilder { template<class Vertex> class MeshBuilder {
public: public:
typedef size_t VertexPointer; /**< @brief Type for indexing vertices */
/** @brief Triangle face */
struct Face {
/** @brief Implicit constructor */
Face() {}
/** @brief Constructor */
Face(VertexPointer first, VertexPointer second, VertexPointer third) {
vertices[0] = first;
vertices[1] = second;
vertices[2] = third;
}
/** @brief Vertex data */
VertexPointer vertices[3];
/** @brief Comparison operator */
bool operator==(const Face& other) const {
return other.vertices[0] == vertices[0] &&
other.vertices[1] == vertices[1] &&
other.vertices[2] == vertices[2];
}
};
/** /**
* @brief Destructor * @brief Destructor
* *
@ -83,22 +58,20 @@ template<class Vertex> class MeshBuilder {
*/ */
void clear() { void clear() {
_vertices.clear(); _vertices.clear();
_faces.clear(); _indices.clear();
} }
/** @brief Vertex count */ /** @brief Vertex count */
inline size_t vertexCount() const { return _vertices.size(); } inline unsigned int vertexCount() const { return _vertices.size(); }
/** @brief Array with vertices */ /** @brief Array with vertices */
inline const std::vector<Vertex>& vertices() const { return _vertices; } inline const std::vector<Vertex>& vertices() const { return _vertices; }
inline std::vector<Vertex>& vertices() { return _vertices; } /**< @copydoc vertices() const */
/** @brief Face count */ /** @brief Index count */
inline size_t faceCount() const { return _faces.size(); } inline unsigned int indexCount() const { return _indices.size(); }
/** @brief Array with faces */ /** @brief Array with indices */
inline const std::vector<Face>& faces() const { return _faces; } inline const std::vector<unsigned int>& indices() const { return _indices; }
inline std::vector<Face>& faces() { return _faces; } /**< @copydoc faces() const */
/** /**
* @brief Set mesh data * @brief Set mesh data
@ -110,36 +83,33 @@ template<class Vertex> class MeshBuilder {
* Replaces mesh builder data with given data. Type of indices is * Replaces mesh builder data with given data. Type of indices is
* detected from given pointer. * detected from given pointer.
*/ */
template<class IndexType> void setData(const Vertex* vertexData, const IndexType* indices, GLsizei vertexCount, GLsizei indexCount) { template<class IndexType> void setData(const Vertex* vertexData, const IndexType* indices, unsigned int vertexCount, unsigned int indexCount) {
clear(); clear();
/* Map vertex indices to vertex pointers */ /* Vertex array */
std::vector<Vertex> vertices; std::vector<Vertex> vertices;
vertices.reserve(vertexCount); vertices.reserve(vertexCount);
/* Using TypeTraits::IndexType to ensure we have allowed type for indexing */ /* Using TypeTraits::IndexType to ensure we have allowed type for indexing */
for(typename TypeTraits<IndexType>::IndexType i = 0; i != vertexCount; ++i) for(typename TypeTraits<IndexType>::IndexType i = 0; i != vertexCount; ++i)
addVertex(Vertex(vertexData[i])); addVertex(Vertex(vertexData[i]));
/* Faces array */ /* Index array */
_faces.reserve(indexCount/3); _indices.reserve(indexCount);
for(size_t i = 0; i < static_cast<size_t>(indexCount); i += 3) for(unsigned int i = 0; i != indexCount; ++i)
addFace(indices[i], indices[i+1], indices[i+2]); _indices.push_back(indices[i]);
} }
/** @brief Add vertex */ /** @brief Add vertex */
inline VertexPointer addVertex(const Vertex& v) { inline unsigned int addVertex(const Vertex& v) {
_vertices.push_back(v); _vertices.push_back(v);
return _vertices.size()-1; return _vertices.size()-1;
} }
/** @brief Add face */ /** @brief Add face */
inline void addFace(const Face& face) { inline void addFace(unsigned int first, unsigned int second, unsigned int third) {
_faces.push_back(face); _indices.push_back(first);
} _indices.push_back(second);
_indices.push_back(third);
/** @brief Add face */
inline void addFace(VertexPointer first, VertexPointer second, VertexPointer third) {
_faces.push_back(Face(first, second, third));
} }
/** /**
@ -152,15 +122,15 @@ template<class Vertex> class MeshBuilder {
* Cleaning the mesh is up to user. * Cleaning the mesh is up to user.
*/ */
template<class Interpolator> void subdivide(Interpolator interpolator) { template<class Interpolator> void subdivide(Interpolator interpolator) {
size_t faceCount = _faces.size(); size_t indexCount = _indices.size();
_faces.reserve(_faces.size()*4); _indices.reserve(_indices.size()*4);
/* Subdivide each face to four new */ /* Subdivide each face to four new */
for(size_t i = 0; i != faceCount; ++i) { for(size_t i = 0; i != indexCount; i += 3) {
/* Interpolate each side */ /* Interpolate each side */
VertexPointer newVertices[3]; unsigned int newVertices[3];
for(int j = 0; j != 3; ++j) for(int j = 0; j != 3; ++j)
newVertices[j] = addVertex(interpolator(_vertices[_faces[i].vertices[j]], _vertices[_faces[i].vertices[(j+1)%3]])); newVertices[j] = addVertex(interpolator(_vertices[_indices[i+j]], _vertices[_indices[i+(j+1)%3]]));
/* /*
* Add three new faces (0, 1, 3) and update original (2) * Add three new faces (0, 1, 3) and update original (2)
@ -175,11 +145,11 @@ template<class Vertex> class MeshBuilder {
* / \ / \ * / \ / \
* orig 1 ----- new 1 ---- orig 2 * orig 1 ----- new 1 ---- orig 2
*/ */
addFace(_faces[i].vertices[0], newVertices[0], newVertices[2]); addFace(_indices[i], newVertices[0], newVertices[2]);
addFace(newVertices[0], _faces[i].vertices[1], newVertices[1]); addFace(newVertices[0], _indices[i+1], newVertices[1]);
addFace(newVertices[2], newVertices[1], _faces[i].vertices[2]); addFace(newVertices[2], newVertices[1], _indices[i+2]);
for(size_t j = 0; j != 3; ++j) for(size_t j = 0; j != 3; ++j)
_faces[i].vertices[j] = newVertices[j]; _indices[i+j] = newVertices[j];
} }
} }
@ -194,7 +164,7 @@ template<class Vertex> class MeshBuilder {
* only first three fields of otherwise 4D vertex are important). * only first three fields of otherwise 4D vertex are important).
*/ */
template<size_t vertexSize = Vertex::Size> void cleanMesh(typename Vertex::Type epsilon = EPSILON) { template<size_t vertexSize = Vertex::Size> void cleanMesh(typename Vertex::Type epsilon = EPSILON) {
if(_faces.empty()) return; if(_indices.empty()) return;
/* Get mesh bounds */ /* Get mesh bounds */
Vertex min, max; Vertex min, max;
@ -229,27 +199,24 @@ template<class Vertex> class MeshBuilder {
table.reserve(_vertices.size()); table.reserve(_vertices.size());
/* Go through all faces' vertices */ /* Go through all faces' vertices */
for(auto it = _faces.begin(); it != _faces.end(); ++it) { for(auto it = _indices.begin(); it != _indices.end(); ++it) {
for(size_t i = 0; i != 3; ++i) { /* Index of a vertex in vertexSize-dimensional table */
size_t index[vertexSize];
/* Index of a vertex in vertexSize-dimensional table */ for(size_t ii = 0; ii != vertexSize; ++ii)
size_t index[vertexSize]; index[ii] = (_vertices[*it][ii]+moved[ii]-min[ii])/epsilon;
for(size_t ii = 0; ii != vertexSize; ++ii)
index[ii] = (_vertices[it->vertices[i]][ii]+moved[ii]-min[ii])/epsilon; /* Try inserting the vertex into table, if it already
exists, change vertex pointer of the face to already
/* Try inserting the vertex into table, if it already existing vertex */
exists, change vertex pointer of the face to already HashedVertex v(*it, table.size());
existing vertex */ auto result = table.insert(std::pair<Math::Vector<size_t, vertexSize>, HashedVertex>(index, v));
HashedVertex v(it->vertices[i], table.size()); *it = result.first->second.newIndex;
auto result = table.insert(std::pair<Math::Vector<size_t, vertexSize>, HashedVertex>(index, v));
it->vertices[i] = result.first->second.newVertexPointer;
}
} }
/* Shrink vertices array */ /* Shrink vertices array */
std::vector<Vertex> vertices(table.size()); std::vector<Vertex> vertices(table.size());
for(auto it = table.cbegin(); it != table.cend(); ++it) for(auto it = table.cbegin(); it != table.cend(); ++it)
vertices[it->second.newVertexPointer] = _vertices[it->second.oldVertexPointer]; vertices[it->second.newIndex] = _vertices[it->second.oldIndex];
std::swap(vertices, _vertices); std::swap(vertices, _vertices);
/* Move vertex coordinates by epsilon/2 in next direction */ /* Move vertex coordinates by epsilon/2 in next direction */
@ -278,7 +245,7 @@ template<class Vertex> class MeshBuilder {
mesh->setPrimitive(Mesh::Triangles); mesh->setPrimitive(Mesh::Triangles);
mesh->setVertexCount(_vertices.size()); mesh->setVertexCount(_vertices.size());
vertexBuffer->setData(sizeof(Vertex)*_vertices.size(), _vertices.data(), vertexBufferUsage); vertexBuffer->setData(sizeof(Vertex)*_vertices.size(), _vertices.data(), vertexBufferUsage);
SizeBasedCall<IndexBuilder>(_vertices.size())(mesh, _faces, indexBufferUsage); SizeBasedCall<IndexBuilder>(_vertices.size())(mesh, _indices, indexBufferUsage);
} }
/** /**
@ -297,11 +264,9 @@ template<class Vertex> class MeshBuilder {
} }
private: private:
std::vector<Face> _faces; std::vector<unsigned int> _indices;
std::vector<Vertex> _vertices; std::vector<Vertex> _vertices;
typedef size_t FacePointer;
template<size_t vertexSize> class IndexHash { template<size_t vertexSize> class IndexHash {
public: public:
inline size_t operator()(const Math::Vector<size_t, vertexSize>& data) const { inline size_t operator()(const Math::Vector<size_t, vertexSize>& data) const {
@ -313,23 +278,20 @@ template<class Vertex> class MeshBuilder {
}; };
struct HashedVertex { struct HashedVertex {
VertexPointer oldVertexPointer, newVertexPointer; unsigned int oldIndex, newIndex;
HashedVertex(VertexPointer oldVertexPointer, VertexPointer newVertexPointer): oldVertexPointer(oldVertexPointer), newVertexPointer(newVertexPointer) {} HashedVertex(unsigned int oldIndex, unsigned int newIndex): oldIndex(oldIndex), newIndex(newIndex) {}
}; };
struct IndexBuilder { struct IndexBuilder {
template<class IndexType> static void run(IndexedMesh* mesh, const std::vector<Face>& faces, Buffer::Usage indexBufferUsage) { template<class IndexType> static void run(IndexedMesh* mesh, const std::vector<unsigned int>& _indices, Buffer::Usage indexBufferUsage) {
/* Compress face array to index array. Using /* Compress face array to index array. Using
TypeTraits::IndexType to ensure we have allowed type for TypeTraits::IndexType to ensure we have allowed type for
indexing */ indexing */
std::vector<typename TypeTraits<IndexType>::IndexType> indices; std::vector<typename TypeTraits<IndexType>::IndexType> indices;
indices.reserve(faces.size()*3); indices.reserve(_indices.size());
for(auto it = faces.cbegin(); it != faces.cend(); ++it) { for(auto it = _indices.cbegin(); it != _indices.cend(); ++it)
indices.push_back(it->vertices[0]); indices.push_back(*it);
indices.push_back(it->vertices[1]);
indices.push_back(it->vertices[2]);
}
/* Update mesh parameters and fill index buffer */ /* Update mesh parameters and fill index buffer */
mesh->setIndexCount(indices.size()); mesh->setIndexCount(indices.size());

2
src/Primitives/Icosphere.h

@ -49,7 +49,7 @@ template<size_t subdivisions> class Icosphere: public AbstractPrimitive<typename
inline Mesh::Primitive primitive() const { return Mesh::Triangles; } inline Mesh::Primitive primitive() const { return Mesh::Triangles; }
inline size_t vertexCount() const { return builder()->vertexCount(); } inline size_t vertexCount() const { return builder()->vertexCount(); }
inline size_t indexCount() const { return builder()->faceCount()*3; } inline size_t indexCount() const { return builder()->indexCount(); }
inline void build(IndexedMesh* mesh, Buffer* vertexBuffer) { inline void build(IndexedMesh* mesh, Buffer* vertexBuffer) {
/* mesh is prepared by the builder, no need to call prepareMesh */ /* mesh is prepared by the builder, no need to call prepareMesh */

160
src/Test/MeshBuilderTest.cpp

@ -16,7 +16,6 @@
#include "MeshBuilderTest.h" #include "MeshBuilderTest.h"
#include <QtTest/QTest> #include <QtTest/QTest>
#include <QtCore/QDebug>
#include "MeshBuilder.h" #include "MeshBuilder.h"
@ -29,152 +28,57 @@ namespace Magnum { namespace Test {
void MeshBuilderTest::setData() { void MeshBuilderTest::setData() {
MeshBuilder<Vector1> builder; MeshBuilder<Vector1> builder;
int vertexData[] = { 1, 2, 3, 4 }; Vector1 vertexData[] = { 1, 2, 3, 4 };
GLubyte indices[] = { 0, 1, 2, 1, 2, 3 }; GLubyte indexData[] = { 0, 1, 2, 1, 2, 3 };
builder.setData(reinterpret_cast<Vector1*>(vertexData), indices, 4, 6); builder.setData(vertexData, indexData, 4, 6);
vector<MeshBuilder<Vector1>::Face> faces = builder.faces(); QVERIFY((builder.vertices() == vector<Vector1>{1, 2, 3, 4}));
QVERIFY(faces.size() == 2); QVERIFY((builder.indices() == vector<unsigned int>{0, 1, 2, 1, 2, 3}));
vector<MeshBuilder<Vector1>::Face>::const_iterator it = faces.begin();
QVERIFY(builder.vertices()[it->vertices[0]] == 1);
QVERIFY(builder.vertices()[it->vertices[1]] == 2);
QVERIFY(builder.vertices()[it->vertices[2]] == 3);
it++;
QVERIFY(builder.vertices()[it->vertices[0]] == 2);
QVERIFY(builder.vertices()[it->vertices[1]] == 3);
QVERIFY(builder.vertices()[it->vertices[2]] == 4);
vector<Vector1> vertices = builder.vertices();
QVERIFY(vertices.size() == 4);
int i = 0;
for(auto it = vertices.begin(); it != vertices.end(); ++it)
QVERIFY(*it == ++i);
} }
void MeshBuilderTest::addFace() { void MeshBuilderTest::addFace() {
Vector1 v1(1);
Vector1 v2(2);
Vector1 v3(3);
Vector1 v4(4);
MeshBuilder<Vector1>::Face f1(0, 1, 2);
MeshBuilder<Vector1>::Face f2(1, 2, 3);
MeshBuilder<Vector1> builder; MeshBuilder<Vector1> builder;
builder.addVertex(v1); builder.addVertex(1);
builder.addVertex(v2); builder.addVertex(2);
builder.addVertex(v3); builder.addVertex(3);
builder.addVertex(v4); builder.addVertex(4);
builder.addFace(f1); builder.addFace(0, 1, 2);
builder.addFace(f2); builder.addFace(1, 2, 3);
vector<Vector1> vertices; QVERIFY((builder.vertices() == vector<Vector1>{1, 2, 3, 4}));
vertices.push_back(v1); QVERIFY((builder.indices() == vector<unsigned int>{0, 1, 2, 1, 2, 3}));
vertices.push_back(v2);
vertices.push_back(v3);
vertices.push_back(v4);
QVERIFY(builder.vertices() == vertices);
vector<MeshBuilder<Vector1>::Face> faces;
faces.push_back(f1);
faces.push_back(f2);
QVERIFY(builder.faces() == faces);
} }
void MeshBuilderTest::cleanMesh() { void MeshBuilderTest::cleanMesh() {
Vector1 v1(1);
Vector1 v2(2);
Vector1 v3(1);
Vector1 v4(4);
MeshBuilder<Vector1>::Face f1(0, 1, 2);
MeshBuilder<Vector1>::Face f2(1, 2, 3);
MeshBuilder<Vector1> builder; MeshBuilder<Vector1> builder;
builder.addVertex(v1); builder.addVertex(1);
builder.addVertex(v2); builder.addVertex(2);
builder.addVertex(v3); builder.addVertex(1);
builder.addVertex(v4); builder.addVertex(4);
builder.addFace(f1); builder.addFace(0, 1, 2);
builder.addFace(f2); builder.addFace(1, 2, 3);
builder.cleanMesh(1); builder.cleanMesh(1);
/* Verify cleanup */ /* Verify cleanup */
vector<Vector1> vertices; QVERIFY((builder.vertices() == vector<Vector1>{1, 2, 4}));
vertices.push_back(v1); QVERIFY((builder.indices() == vector<unsigned int>{0, 1, 0, 1, 0, 2}));
vertices.push_back(v2);
vertices.push_back(v4);
QVERIFY(builder.vertices() == vertices);
vector<MeshBuilder<Vector1>::Face> faces;
faces.push_back(f1);
faces.push_back(f2);
QVERIFY(builder.faces()[0].vertices[0] == 0);
QVERIFY(builder.faces()[0].vertices[1] == 1);
QVERIFY(builder.faces()[0].vertices[2] == 0);
QVERIFY(builder.faces()[1].vertices[0] == 1);
QVERIFY(builder.faces()[1].vertices[1] == 0);
QVERIFY(builder.faces()[1].vertices[2] == 2);
QVERIFY(f1.vertices[2] == f2.vertices[1]);
} }
void MeshBuilderTest::subdivide() { void MeshBuilderTest::subdivide() {
Vector1 v1(0);
Vector1 v2(2);
Vector1 v3(6);
Vector1 v4(8);
MeshBuilder<Vector1>::Face f1(0, 1, 2);
MeshBuilder<Vector1>::Face f2(1, 2, 3);
MeshBuilder<Vector1> builder; MeshBuilder<Vector1> builder;
builder.addVertex(v1); builder.addVertex(0);
builder.addVertex(v2); builder.addVertex(2);
builder.addVertex(v3); builder.addVertex(6);
builder.addVertex(v4); builder.addVertex(8);
builder.addFace(f1); builder.addFace(0, 1, 2);
builder.addFace(f2); builder.addFace(1, 2, 3);
builder.subdivide(interpolator); builder.subdivide(interpolator);
QVERIFY(builder.faces().size() == 8); QVERIFY(builder.indices().size() == 24);
QVERIFY(builder.vertices().size() == 10); QVERIFY((builder.vertices() == vector<Vector1>{0, 2, 6, 8, 1, 4, 3, 4, 7, 5}));
QVERIFY(builder.vertices()[0] == Vector1(0)); QVERIFY((builder.indices() == vector<unsigned int>{4, 5, 6, 7, 8, 9, 0, 4, 6, 4, 1, 5, 6, 5, 2, 1, 7, 9, 7, 2, 8, 9, 8, 3}));
QVERIFY(builder.vertices()[1] == Vector1(2));
QVERIFY(builder.vertices()[2] == Vector1(6));
QVERIFY(builder.vertices()[3] == Vector1(8));
QVERIFY(builder.vertices()[4] == Vector1(1));
QVERIFY(builder.vertices()[5] == Vector1(4));
QVERIFY(builder.vertices()[6] == Vector1(3));
QVERIFY(builder.vertices()[7] == Vector1(4));
QVERIFY(builder.vertices()[8] == Vector1(7));
QVERIFY(builder.vertices()[9] == Vector1(5));
QVERIFY(builder.faces()[0].vertices[0] == 4);
QVERIFY(builder.faces()[0].vertices[1] == 5);
QVERIFY(builder.faces()[0].vertices[2] == 6);
QVERIFY(builder.faces()[1].vertices[0] == 7);
QVERIFY(builder.faces()[1].vertices[1] == 8);
QVERIFY(builder.faces()[1].vertices[2] == 9);
QVERIFY(builder.faces()[2].vertices[0] == 0);
QVERIFY(builder.faces()[2].vertices[1] == 4);
QVERIFY(builder.faces()[2].vertices[2] == 6);
QVERIFY(builder.faces()[3].vertices[0] == 4);
QVERIFY(builder.faces()[3].vertices[1] == 1);
QVERIFY(builder.faces()[3].vertices[2] == 5);
QVERIFY(builder.faces()[4].vertices[0] == 6);
QVERIFY(builder.faces()[4].vertices[1] == 5);
QVERIFY(builder.faces()[4].vertices[2] == 2);
QVERIFY(builder.faces()[5].vertices[0] == 1);
QVERIFY(builder.faces()[5].vertices[1] == 7);
QVERIFY(builder.faces()[5].vertices[2] == 9);
QVERIFY(builder.faces()[6].vertices[0] == 7);
QVERIFY(builder.faces()[6].vertices[1] == 2);
QVERIFY(builder.faces()[6].vertices[2] == 8);
QVERIFY(builder.faces()[7].vertices[0] == 9);
QVERIFY(builder.faces()[7].vertices[1] == 8);
QVERIFY(builder.faces()[7].vertices[2] == 3);
builder.cleanMesh(1); builder.cleanMesh(1);

Loading…
Cancel
Save