Browse Source

Total rewrite of slow MeshBuilder, cleanMesh() is now usably fast.

On 5-subdivided icosphere it performs 600 times better than previous
implementation.
vectorfields
Vladimír Vondruš 15 years ago
parent
commit
459e6f868e
  1. 299
      src/MeshBuilder.h
  2. 225
      src/Test/MeshBuilderTest.cpp
  3. 20
      src/Test/MeshBuilderTest.h

299
src/MeshBuilder.h

@ -21,6 +21,8 @@
#include <set> #include <set>
#include <map> #include <map>
#include <limits>
#include <unordered_map>
#include "Buffer.h" #include "Buffer.h"
#include "IndexedMesh.h" #include "IndexedMesh.h"
@ -39,20 +41,28 @@ but it must have at least operator== implemented.
*/ */
template<class Vertex> class MeshBuilder { template<class Vertex> class MeshBuilder {
public: public:
typedef size_t VertexPointer;
/** @brief Triangle face */ /** @brief Triangle face */
struct Face { struct Face {
/** @brief Implicit constructor */ /** @brief Implicit constructor */
Face() {} Face() {}
/** @brief Constructor */ /** @brief Constructor */
Face(Vertex* first, Vertex* second, Vertex* third) { Face(VertexPointer first, VertexPointer second, VertexPointer third) {
vertices[0] = first; vertices[0] = first;
vertices[1] = second; vertices[1] = second;
vertices[2] = third; vertices[2] = third;
} }
/** @brief Vertex data */ /** @brief Vertex data */
Vertex* vertices[3]; VertexPointer vertices[3];
bool operator==(const Face& other) const {
return other.vertices[0] == vertices[0] &&
other.vertices[1] == vertices[1] &&
other.vertices[2] == vertices[2];
}
}; };
/** /**
@ -65,36 +75,20 @@ template<class Vertex> class MeshBuilder {
/** /**
* @brief Clear mesh data * @brief Clear mesh data
* *
* Should be called after the mesh is built and the builder is not * The data are cleared automatically in destructor and when calling
* needed. This function is also automatically called in destructor or * setData(). If you don't want to use the data after building the mesh,
* when calling setData(). * destroy the MeshBuilder or call this function.
*/ */
void clear() { void clear() {
for(typename std::set<Vertex*>::iterator it = _vertices.begin(); it != _vertices.end(); ++it)
delete *it;
for(typename std::set<Face*>::iterator it = _faces.begin(); it != _faces.end(); ++it)
delete *it;
_vertices.clear(); _vertices.clear();
_faces.clear(); _faces.clear();
vertexFaces.clear();
} }
/** @brief Array with vertices */ /** @brief Array with vertices */
inline const std::set<Vertex*>& vertices() const { return _vertices; } inline const std::vector<Vertex>& vertices() const { return _vertices; }
/** @brief Array with fixed vertices */
std::vector<Vertex> fixedVertices() const {
std::vector<Vertex> vertices;
for(typename std::set<Vertex*>::const_iterator it = _vertices.begin(); it != _vertices.end(); ++it)
vertices.push_back(**it);
return vertices;
}
/** @brief Array with faces */ /** @brief Array with faces */
inline const std::set<Face*>& faces() const { return _faces; } inline const std::vector<Face>& faces() const { return _faces; }
/** /**
* @brief Set mesh data * @brief Set mesh data
@ -120,53 +114,20 @@ template<class Vertex> class MeshBuilder {
setData<GLuint>(vertices, indices, vertexCount, indexCount, GL_UNSIGNED_INT); setData<GLuint>(vertices, indices, vertexCount, indexCount, GL_UNSIGNED_INT);
} }
/** /** @brief Add vertex */
* @brief Remove face from the mesh inline VertexPointer addVertex(const Vertex& v) {
* @param face Pointer to face which to remove _vertices.push_back(v);
* return _vertices.size()-1;
* If face vertices are not shared with another face, they are removed
* too.
*/
void removeFace(Face* face) {
/* Remove face from vertexFaces */
for(int i = 0; i != 3; ++i) {
typename std::multimap<Vertex*, Face*>::iterator it = vertexFaces.lower_bound(face->vertices[i]);
typename std::multimap<Vertex*, Face*>::iterator end = vertexFaces.upper_bound(face->vertices[i]);
for(; it != end; ++it) if(it->second == face) {
vertexFaces.erase(it);
break;
}
/* If the vertex is not part of any face anymore, remove it too */
if(vertexFaces.count(face->vertices[i]) == 0) {
_vertices.erase(face->vertices[i]);
delete face->vertices[i];
}
}
/* Remove face from faces and delete it */
_faces.erase(face);
delete face;
} }
/** /** @brief Add face */
* @brief Add face inline void addFace(const Face& face) {
* @param face Pointer to face which to add _faces.push_back(face);
*/ }
void addFace(Face* face) {
/* Don't insert the same face twice */
if(_faces.find(face) != _faces.end()) return;
/* Add vertex to vertexData, if it is not yet there */
for(int i = 0; i != 3; ++i)
_vertices.insert(face->vertices[i]);
/* Add vertices to vertexFaces */
for(int i = 0; i != 3; ++i)
vertexFaces.insert(std::pair<Vertex*, Face*>(face->vertices[i], face));
/* Add face to face list */ /** @brief Add face */
_faces.insert(face); inline void addFace(VertexPointer first, VertexPointer second, VertexPointer third) {
_faces.push_back(Face(first, second, third));
} }
/** /**
@ -179,15 +140,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) {
/* Copy of current faces */ size_t faceCount = _faces.size();
std::set<Face*> f = faces(); _faces.reserve(_faces.size()*4);
/* Subdivide each face to four new */ /* Subdivide each face to four new */
for(typename std::set<Face*>::const_iterator face = f.begin(); face != f.end(); ++face) { for(size_t i = 0; i != faceCount; ++i) {
/* Interpolate each side */ /* Interpolate each side */
Vertex* newVertices[3]; VertexPointer newVertices[3];
for(int i = 0; i != 3; ++i) for(int j = 0; j != 3; ++j)
newVertices[i] = new Vertex(interpolator(*(*face)->vertices[i], *(*face)->vertices[(i+1)%3])); newVertices[j] = addVertex(interpolator(_vertices[_faces[i].vertices[j]], _vertices[_faces[i].vertices[(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)
@ -202,58 +163,88 @@ template<class Vertex> class MeshBuilder {
* / \ / \ * / \ / \
* orig 1 ----- new 1 ---- orig 2 * orig 1 ----- new 1 ---- orig 2
*/ */
addFace(new Face((*face)->vertices[0], newVertices[0], newVertices[2])); addFace(_faces[i].vertices[0], newVertices[0], newVertices[2]);
addFace(new Face(newVertices[0], (*face)->vertices[1], newVertices[1])); addFace(newVertices[0], _faces[i].vertices[1], newVertices[1]);
addFace(new Face(newVertices[2], newVertices[1], (*face)->vertices[2])); addFace(newVertices[2], newVertices[1], _faces[i].vertices[2]);
for(int i = 0; i != 3; ++i) for(size_t j = 0; j != 3; ++j)
(*face)->vertices[i] = newVertices[i]; _faces[i].vertices[j] = newVertices[j];
} }
} }
/** /**
* @brief Clean the mesh * @brief Clean the mesh
* @param epsilon Epsilon value, vertices nearer than this
* distance will be melt together.
* *
* Removes duplicate vertices from the mesh. * Removes duplicate vertices from the mesh. With template parameter
* @c vertexSize you can specify how many initial vertex fields are
* important (for example, when dealing with perspective in 3D space,
* only first three fields of otherwise 4D vertex are important).
*/ */
void cleanMesh() { template<size_t vertexSize = Vertex::Size> void cleanMesh(typename Vertex::Type epsilon = EPSILON) {
/* Vertices scheduled for deletion */ if(_faces.empty()) return;
std::vector<Vertex*> trashcan;
/* Get mesh bounds */
/* Foreach all vertices and for each find similar in the rest of the array */ Vertex min, max;
for(typename std::set<Vertex*>::iterator it = _vertices.begin(); it != _vertices.end(); ++it) { for(size_t i = 0; i != Vertex::Size; ++i) {
typename std::set<Vertex*>::iterator next = it; min[i] = std::numeric_limits<typename Vertex::Type>::max();
for(typename std::set<Vertex*>::iterator similarIt = ++next; similarIt != _vertices.end(); ++similarIt) { max[i] = std::numeric_limits<typename Vertex::Type>::min();
if(**it == **similarIt) { }
/* Range of faces sharing that similar vertex */ for(auto it = _vertices.cbegin(); it != _vertices.cend(); ++it)
typename std::multimap<Vertex*, Face*>::iterator begin = vertexFaces.lower_bound(*similarIt); for(size_t i = 0; i != vertexSize; ++i)
typename std::multimap<Vertex*, Face*>::iterator end = vertexFaces.upper_bound(*similarIt); if((*it)[i] < min[i])
min[i] = (*it)[i];
/* Updated array of faces, now sharing the original vertex */ else if((*it)[i] > max[i])
std::multimap<Vertex*, Face*> updatedFaces; max[i] = (*it)[i];
/* Replace similar vertex in faces with this */ /* Make epsilon so large that size_t can index all vertices inside
for(typename std::multimap<Vertex*, Face*>::iterator faceIt = begin; faceIt != end; ++faceIt) { mesh bounds. */
for(int i = 0; i != 3; ++i) if(*similarIt == faceIt->second->vertices[i]) { Vertex size = max-min;
faceIt->second->vertices[i] = *it; for(size_t i = 0; i != Vertex::Size; ++i)
} if(static_cast<typename Vertex::Type>(size[i]/std::numeric_limits<size_t>::max()) > epsilon)
epsilon = static_cast<typename Vertex::Type>(size[i]/std::numeric_limits<size_t>::max());
updatedFaces.insert(std::pair<Vertex*, Face*>(*it, faceIt->second));
} /* First go with original vertex coordinates, then move them by
epsilon/2 in each direction. */
/* Remove old faces, insert updated */ Vertex moved;
vertexFaces.erase(begin, end); for(size_t moving = 0; moving <= vertexSize; ++moving) {
vertexFaces.insert(updatedFaces.begin(), updatedFaces.end());
/* Under each index is pointer to face which contains given vertex
/* Schedule vertex for deletion */ and index of vertex in the face. */
trashcan.push_back(*similarIt); std::unordered_map<Math::Vector<size_t, vertexSize>, HashedVertex, IndexHash<vertexSize>> table;
/* Reserve space for all vertices */
table.reserve(_vertices.size());
/* Go through all faces' vertices */
for(auto it = _faces.begin(); it != _faces.end(); ++it) {
for(size_t i = 0; i != 3; ++i) {
/* Index of a vertex in vertexSize-dimensional table */
size_t index[vertexSize];
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
existing vertex */
HashedVertex v(it->vertices[i], table.size());
auto result = table.insert(std::pair<Math::Vector<size_t, vertexSize>, HashedVertex>(index, v));
it->vertices[i] = result.first->second.newVertexPointer;
} }
} }
}
/* Delete all scheduled vertices */ /* Shrink vertices array */
for(typename std::vector<Vertex*>::const_iterator it = trashcan.begin(); it != trashcan.end(); ++it) { std::vector<Vertex> vertices(table.size());
_vertices.erase(*it); for(auto it = table.cbegin(); it != table.cend(); ++it)
delete *it; vertices[it->second.newVertexPointer] = _vertices[it->second.oldVertexPointer];
std::swap(vertices, _vertices);
/* Move vertex coordinates by epsilon/2 in next direction */
if(moving != Vertex::Size) {
moved = Vertex();
moved[moving] = epsilon/2;
}
} }
} }
@ -296,59 +287,59 @@ template<class Vertex> class MeshBuilder {
} }
private: private:
std::set<Vertex*> _vertices; std::vector<Face> _faces;
std::multimap<Vertex*, Face*> vertexFaces; std::vector<Vertex> _vertices;
std::set<Face*> _faces;
typedef size_t FacePointer;
template<size_t vertexSize> class IndexHash {
public:
inline size_t operator()(const Math::Vector<size_t, vertexSize>& data) const {
size_t a = 0;
for(size_t i = 0; i != vertexSize; ++i)
a ^= data[i];
return a;
}
};
struct HashedVertex {
VertexPointer oldVertexPointer, newVertexPointer;
HashedVertex(VertexPointer oldVertexPointer, VertexPointer newVertexPointer): oldVertexPointer(oldVertexPointer), newVertexPointer(newVertexPointer) {}
};
template<class IndexType> void setData(const Vertex* vertexData, const IndexType* indices, GLsizei vertexCount, GLsizei indexCount, GLenum indexType) { template<class IndexType> void setData(const Vertex* vertexData, const IndexType* indices, GLsizei vertexCount, GLsizei indexCount, GLenum indexType) {
clear(); clear();
/* Map vertex indices to vertex pointers */ /* Map vertex indices to vertex pointers */
std::map<IndexType, Vertex*> vertexMapping; std::vector<Vertex> vertices;
for(IndexType i = 0; i != vertexCount; ++i) { vertices.reserve(vertexCount);
Vertex* v = new Vertex(vertexData[i]); for(IndexType i = 0; i != vertexCount; ++i)
_vertices.insert(v); addVertex(Vertex(vertexData[i]));
vertexMapping.insert(std::pair<IndexType, Vertex*>(i, v));
}
/* Faces array */ /* Faces array */
for(IndexType i = 0; i < indexCount; i += 3) { _faces.reserve(indexCount/3);
Face* f = new Face; for(IndexType i = 0; i < indexCount; i += 3)
for(int ii = 0; ii != 3; ++ii) { addFace(indices[i], indices[i+1], indices[i+2]);
f->vertices[ii] = vertexMapping[indices[i+ii]];
vertexFaces.insert(std::pair<Vertex*, Face*>(f->vertices[ii], f));
}
_faces.insert(f);
}
} }
template<class IndexType> void buildInternal(IndexedMesh* mesh, Buffer* vertexBuffer, Buffer::Usage vertexBufferUsage, Buffer::Usage indexBufferUsage, GLenum indexType) { template<class IndexType> void buildInternal(IndexedMesh* mesh, Buffer* vertexBuffer, Buffer::Usage vertexBufferUsage, Buffer::Usage indexBufferUsage, GLenum indexType) {
/* Update the mesh parameters */ /* Compress face array to index array */
mesh->setPrimitive(Mesh::Triangles);
mesh->setVertexCount(_vertices.size());
mesh->setIndexCount(_faces.size()*3);
mesh->setIndexType(indexType);
/* Convert vertex pointers to fixed vector and map vertex data
pointers to vertex indices */
std::vector<Vertex> vertices;
std::map<Vertex*, IndexType> indicesMapping;
IndexType i = 0;
for(typename std::set<Vertex*>::const_iterator it = _vertices.begin(); it != _vertices.end(); ++it) {
vertices.push_back(**it);
indicesMapping.insert(std::pair<Vertex*, IndexType>(*it, i++));
}
/* Create indices array */
std::vector<IndexType> indices; std::vector<IndexType> indices;
indices.reserve(_faces.size()*3); indices.reserve(_faces.size()*3);
for(typename std::set<Face*>::const_iterator it = _faces.begin(); it != _faces.end(); ++it) { for(auto it = _faces.cbegin(); it != _faces.cend(); ++it) {
for(int i = 0; i != 3; ++i) indices.push_back(it->vertices[0]);
indices.push_back(indicesMapping[(*it)->vertices[i]]); indices.push_back(it->vertices[1]);
indices.push_back(it->vertices[2]);
} }
/* Create mesh */ /* Update mesh parameters and fill it with data */
vertexBuffer->setData(sizeof(Vertex)*vertices.size(), vertices.data(), vertexBufferUsage); mesh->setPrimitive(Mesh::Triangles);
mesh->setVertexCount(_vertices.size());
mesh->setIndexCount(indices.size());
mesh->setIndexType(indexType);
vertexBuffer->setData(sizeof(Vertex)*_vertices.size(), _vertices.data(), vertexBufferUsage);
mesh->indexBuffer()->setData(sizeof(IndexType)*indices.size(), indices.data(), indexBufferUsage); mesh->indexBuffer()->setData(sizeof(IndexType)*indices.size(), indices.data(), indexBufferUsage);
} }
}; };

225
src/Test/MeshBuilderTest.cpp

@ -27,152 +27,159 @@ using namespace std;
namespace Magnum { namespace Test { namespace Magnum { namespace Test {
void MeshBuilderTest::setData() { void MeshBuilderTest::setData() {
MeshBuilder<int> builder; MeshBuilder<Vector1> builder;
int vertexData[] = { 1, 2, 3, 4 }; int vertexData[] = { 1, 2, 3, 4 };
GLubyte indices[] = { 0, 1, 2, 1, 2, 3 }; GLubyte indices[] = { 0, 1, 2, 1, 2, 3 };
builder.setData(vertexData, indices, 4, 6); builder.setData(reinterpret_cast<Vector1*>(vertexData), indices, 4, 6);
set<int*> vertices = builder.vertices(); vector<MeshBuilder<Vector1>::Face> faces = builder.faces();
QVERIFY(vertices.size() == 4);
int i = 1;
for(set<int*>::const_iterator it = vertices.begin(); it != vertices.end(); ++it)
QVERIFY(**it == i++);
set<MeshBuilder<int>::Face*> faces = builder.faces();
QVERIFY(faces.size() == 2); QVERIFY(faces.size() == 2);
set<MeshBuilder<int>::Face*>::const_iterator it = faces.begin(); vector<MeshBuilder<Vector1>::Face>::const_iterator it = faces.begin();
QVERIFY(*(*it)->vertices[0] == 1); QVERIFY(builder.vertices()[it->vertices[0]] == 1);
QVERIFY(*(*it)->vertices[1] == 2); QVERIFY(builder.vertices()[it->vertices[1]] == 2);
QVERIFY(*(*it)->vertices[2] == 3); QVERIFY(builder.vertices()[it->vertices[2]] == 3);
it++; it++;
QVERIFY(*(*it)->vertices[0] == 2); QVERIFY(builder.vertices()[it->vertices[0]] == 2);
QVERIFY(*(*it)->vertices[1] == 3); QVERIFY(builder.vertices()[it->vertices[1]] == 3);
QVERIFY(*(*it)->vertices[2] == 4); QVERIFY(builder.vertices()[it->vertices[2]] == 4);
}
void MeshBuilderTest::addFace() { vector<Vector1> vertices = builder.vertices();
MeshBuilder<int> builder; QVERIFY(vertices.size() == 4);
int* v1 = new int(1);
int* v2 = new int(2);
int* v3 = new int(3);
int* v4 = new int(4);
MeshBuilder<int>::Face* f1 = new MeshBuilder<int>::Face(v1, v2, v3);
builder.addFace(f1);
MeshBuilder<int>::Face* f2 = new MeshBuilder<int>::Face(v2, v3, v4);
builder.addFace(f2);
set<int*> vertices;
vertices.insert(v1);
vertices.insert(v2);
vertices.insert(v3);
vertices.insert(v4);
set<MeshBuilder<int>::Face*> faces;
faces.insert(f1);
faces.insert(f2);
QVERIFY(builder.vertices() == vertices); int i = 0;
QVERIFY(builder.faces() == faces); for(auto it = vertices.begin(); it != vertices.end(); ++it)
QVERIFY(*it == ++i);
} }
void MeshBuilderTest::removeFace() { void MeshBuilderTest::addFace() {
MeshBuilder<int> builder; Vector1 v1(1);
Vector1 v2(2);
int* v1 = new int(1); Vector1 v3(3);
int* v2 = new int(2); Vector1 v4(4);
int* v3 = new int(3); MeshBuilder<Vector1>::Face f1(0, 1, 2);
int* v4 = new int(4); MeshBuilder<Vector1>::Face f2(1, 2, 3);
MeshBuilder<int>::Face* f1 = new MeshBuilder<int>::Face(v1, v2, v3); MeshBuilder<Vector1> builder;
builder.addVertex(v1);
builder.addVertex(v2);
builder.addVertex(v3);
builder.addVertex(v4);
builder.addFace(f1); builder.addFace(f1);
MeshBuilder<int>::Face* f2 = new MeshBuilder<int>::Face(v2, v3, v4);
builder.addFace(f2); builder.addFace(f2);
MeshBuilder<int>::Face* f3 = new MeshBuilder<int>::Face(v2, v3, v1);
builder.addFace(f3);
/* Remove second face */
builder.removeFace(f2);
set<int*> vertices;
vertices.insert(v1);
vertices.insert(v2);
vertices.insert(v3);
set<MeshBuilder<int>::Face*> faces;
faces.insert(f1);
faces.insert(f3);
vector<Vector1> vertices;
vertices.push_back(v1);
vertices.push_back(v2);
vertices.push_back(v3);
vertices.push_back(v4);
QVERIFY(builder.vertices() == vertices); QVERIFY(builder.vertices() == vertices);
QVERIFY(builder.faces() == faces);
/* Remove third face (vertices shouldn't change) */ vector<MeshBuilder<Vector1>::Face> faces;
builder.removeFace(f3); faces.push_back(f1);
faces.erase(f3); faces.push_back(f2);
QVERIFY(builder.vertices() == vertices);
QVERIFY(builder.faces() == faces); QVERIFY(builder.faces() == faces);
} }
void MeshBuilderTest::cleanMesh() { void MeshBuilderTest::cleanMesh() {
MeshBuilder<int> builder; Vector1 v1(1);
Vector1 v2(2);
int* v1 = new int(1); Vector1 v3(1);
int* v2 = new int(2); Vector1 v4(4);
int* v3 = new int(1); MeshBuilder<Vector1>::Face f1(0, 1, 2);
int* v4 = new int(4); MeshBuilder<Vector1>::Face f2(1, 2, 3);
MeshBuilder<int>::Face* f1 = new MeshBuilder<int>::Face(v1, v2, v3); MeshBuilder<Vector1> builder;
builder.addVertex(v1);
builder.addVertex(v2);
builder.addVertex(v3);
builder.addVertex(v4);
builder.addFace(f1); builder.addFace(f1);
MeshBuilder<int>::Face* f2 = new MeshBuilder<int>::Face(v2, v3, v4);
builder.addFace(f2); builder.addFace(f2);
builder.cleanMesh(); builder.cleanMesh(1);
int* unique = (f1->vertices[2] == v1) ? v1 : v3;
set<int*> vertices;
vertices.insert(unique);
vertices.insert(v2);
vertices.insert(v4);
set<MeshBuilder<int>::Face*> faces;
faces.insert(f1);
faces.insert(f2);
/* Verify cleanup */ /* Verify cleanup */
QVERIFY(f1->vertices[2] == unique); vector<Vector1> vertices;
QVERIFY(f2->vertices[1] == unique); vertices.push_back(v1);
vertices.push_back(v2);
vertices.push_back(v4);
QVERIFY(builder.vertices() == vertices); QVERIFY(builder.vertices() == vertices);
QVERIFY(builder.faces() == faces);
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() {
MeshBuilder<int> builder; Vector1 v1(0);
Vector1 v2(2);
int* v1 = new int(0); Vector1 v3(6);
int* v2 = new int(2); Vector1 v4(8);
int* v3 = new int(6); MeshBuilder<Vector1>::Face f1(0, 1, 2);
int* v4 = new int(8); MeshBuilder<Vector1>::Face f2(1, 2, 3);
MeshBuilder<int>::Face* f1 = new MeshBuilder<int>::Face(v1, v2, v3); MeshBuilder<Vector1> builder;
builder.addVertex(v1);
builder.addVertex(v2);
builder.addVertex(v3);
builder.addVertex(v4);
builder.addFace(f1); builder.addFace(f1);
MeshBuilder<int>::Face* f2 = new MeshBuilder<int>::Face(v2, v3, v4);
builder.addFace(f2); builder.addFace(f2);
builder.subdivide(interpolator); builder.subdivide(interpolator);
QVERIFY(builder.vertices().size() == 10);
QVERIFY(builder.faces().size() == 8); QVERIFY(builder.faces().size() == 8);
/* This also effectively checks the data, as the vertices should be exactly QVERIFY(builder.vertices().size() == 10);
0, 1, 2, .. 8 */ QVERIFY(builder.vertices()[0] == Vector1(0));
builder.cleanMesh(); 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);
/* Vertices 0, 1, 2, 3, 4, 5, 6, 7, 8 */
QVERIFY(builder.vertices().size() == 9); QVERIFY(builder.vertices().size() == 9);
QVERIFY(builder.faces().size() == 8);
} }
}} }}

20
src/Test/MeshBuilderTest.h

@ -25,12 +25,28 @@ class MeshBuilderTest: public QObject {
private slots: private slots:
void setData(); void setData();
void addFace(); void addFace();
void removeFace();
void cleanMesh(); void cleanMesh();
void subdivide(); void subdivide();
private: private:
inline static int interpolator(int a, int b) { return (a+b)/2; } class Vector1 {
public:
static const size_t Size = 1;
typedef int Type;
Vector1(): data(0) {}
Vector1(int i): data(i) {}
int operator[](size_t i) const { return data; }
int& operator[](size_t i) { return data; }
bool operator==(int i) const { return i == data; }
bool operator==(Vector1 i) const { return i.data == data; }
Vector1 operator-(Vector1 i) const { return data-i.data; }
private:
int data;
};
inline static Vector1 interpolator(Vector1 a, Vector1 b) { return (a[0]+b[0])/2; }
}; };
}} }}

Loading…
Cancel
Save