From 459e6f868e6d9b3511dbbc501a13cdf7dda775e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 22 Aug 2011 20:27:34 +0200 Subject: [PATCH] Total rewrite of slow MeshBuilder, cleanMesh() is now usably fast. On 5-subdivided icosphere it performs 600 times better than previous implementation. --- src/MeshBuilder.h | 299 +++++++++++++++++------------------ src/Test/MeshBuilderTest.cpp | 225 +++++++++++++------------- src/Test/MeshBuilderTest.h | 20 ++- 3 files changed, 279 insertions(+), 265 deletions(-) diff --git a/src/MeshBuilder.h b/src/MeshBuilder.h index 6a04b65ac..199ef1c57 100644 --- a/src/MeshBuilder.h +++ b/src/MeshBuilder.h @@ -21,6 +21,8 @@ #include #include +#include +#include #include "Buffer.h" #include "IndexedMesh.h" @@ -39,20 +41,28 @@ but it must have at least operator== implemented. */ template class MeshBuilder { public: + typedef size_t VertexPointer; + /** @brief Triangle face */ struct Face { /** @brief Implicit constructor */ Face() {} /** @brief Constructor */ - Face(Vertex* first, Vertex* second, Vertex* third) { + Face(VertexPointer first, VertexPointer second, VertexPointer third) { vertices[0] = first; vertices[1] = second; vertices[2] = third; } /** @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 MeshBuilder { /** * @brief Clear mesh data * - * Should be called after the mesh is built and the builder is not - * needed. This function is also automatically called in destructor or - * when calling setData(). + * The data are cleared automatically in destructor and when calling + * setData(). If you don't want to use the data after building the mesh, + * destroy the MeshBuilder or call this function. */ void clear() { - for(typename std::set::iterator it = _vertices.begin(); it != _vertices.end(); ++it) - delete *it; - - for(typename std::set::iterator it = _faces.begin(); it != _faces.end(); ++it) - delete *it; - _vertices.clear(); _faces.clear(); - vertexFaces.clear(); } /** @brief Array with vertices */ - inline const std::set& vertices() const { return _vertices; } - - /** @brief Array with fixed vertices */ - std::vector fixedVertices() const { - std::vector vertices; - for(typename std::set::const_iterator it = _vertices.begin(); it != _vertices.end(); ++it) - vertices.push_back(**it); - - return vertices; - } + inline const std::vector& vertices() const { return _vertices; } /** @brief Array with faces */ - inline const std::set& faces() const { return _faces; } + inline const std::vector& faces() const { return _faces; } /** * @brief Set mesh data @@ -120,53 +114,20 @@ template class MeshBuilder { setData(vertices, indices, vertexCount, indexCount, GL_UNSIGNED_INT); } - /** - * @brief Remove face from the mesh - * @param face Pointer to face which to remove - * - * 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::iterator it = vertexFaces.lower_bound(face->vertices[i]); - typename std::multimap::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 vertex */ + inline VertexPointer addVertex(const Vertex& v) { + _vertices.push_back(v); + return _vertices.size()-1; } - /** - * @brief Add face - * @param face Pointer to face which to add - */ - 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(face->vertices[i], face)); + /** @brief Add face */ + inline void addFace(const Face& face) { + _faces.push_back(face); + } - /* Add face to face list */ - _faces.insert(face); + /** @brief Add face */ + inline void addFace(VertexPointer first, VertexPointer second, VertexPointer third) { + _faces.push_back(Face(first, second, third)); } /** @@ -179,15 +140,15 @@ template class MeshBuilder { * Cleaning the mesh is up to user. */ template void subdivide(Interpolator interpolator) { - /* Copy of current faces */ - std::set f = faces(); + size_t faceCount = _faces.size(); + _faces.reserve(_faces.size()*4); /* Subdivide each face to four new */ - for(typename std::set::const_iterator face = f.begin(); face != f.end(); ++face) { + for(size_t i = 0; i != faceCount; ++i) { /* Interpolate each side */ - Vertex* newVertices[3]; - for(int i = 0; i != 3; ++i) - newVertices[i] = new Vertex(interpolator(*(*face)->vertices[i], *(*face)->vertices[(i+1)%3])); + VertexPointer newVertices[3]; + for(int j = 0; j != 3; ++j) + 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) @@ -202,58 +163,88 @@ template class MeshBuilder { * / \ / \ * orig 1 ----- new 1 ---- orig 2 */ - addFace(new Face((*face)->vertices[0], newVertices[0], newVertices[2])); - addFace(new Face(newVertices[0], (*face)->vertices[1], newVertices[1])); - addFace(new Face(newVertices[2], newVertices[1], (*face)->vertices[2])); - for(int i = 0; i != 3; ++i) - (*face)->vertices[i] = newVertices[i]; + addFace(_faces[i].vertices[0], newVertices[0], newVertices[2]); + addFace(newVertices[0], _faces[i].vertices[1], newVertices[1]); + addFace(newVertices[2], newVertices[1], _faces[i].vertices[2]); + for(size_t j = 0; j != 3; ++j) + _faces[i].vertices[j] = newVertices[j]; } } /** * @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() { - /* Vertices scheduled for deletion */ - std::vector trashcan; - - /* Foreach all vertices and for each find similar in the rest of the array */ - for(typename std::set::iterator it = _vertices.begin(); it != _vertices.end(); ++it) { - typename std::set::iterator next = it; - for(typename std::set::iterator similarIt = ++next; similarIt != _vertices.end(); ++similarIt) { - if(**it == **similarIt) { - /* Range of faces sharing that similar vertex */ - typename std::multimap::iterator begin = vertexFaces.lower_bound(*similarIt); - typename std::multimap::iterator end = vertexFaces.upper_bound(*similarIt); - - /* Updated array of faces, now sharing the original vertex */ - std::multimap updatedFaces; - - /* Replace similar vertex in faces with this */ - for(typename std::multimap::iterator faceIt = begin; faceIt != end; ++faceIt) { - for(int i = 0; i != 3; ++i) if(*similarIt == faceIt->second->vertices[i]) { - faceIt->second->vertices[i] = *it; - } - - updatedFaces.insert(std::pair(*it, faceIt->second)); - } - - /* Remove old faces, insert updated */ - vertexFaces.erase(begin, end); - vertexFaces.insert(updatedFaces.begin(), updatedFaces.end()); - - /* Schedule vertex for deletion */ - trashcan.push_back(*similarIt); + template void cleanMesh(typename Vertex::Type epsilon = EPSILON) { + if(_faces.empty()) return; + + /* Get mesh bounds */ + Vertex min, max; + for(size_t i = 0; i != Vertex::Size; ++i) { + min[i] = std::numeric_limits::max(); + max[i] = std::numeric_limits::min(); + } + for(auto it = _vertices.cbegin(); it != _vertices.cend(); ++it) + for(size_t i = 0; i != vertexSize; ++i) + if((*it)[i] < min[i]) + min[i] = (*it)[i]; + else if((*it)[i] > max[i]) + max[i] = (*it)[i]; + + /* Make epsilon so large that size_t can index all vertices inside + mesh bounds. */ + Vertex size = max-min; + for(size_t i = 0; i != Vertex::Size; ++i) + if(static_cast(size[i]/std::numeric_limits::max()) > epsilon) + epsilon = static_cast(size[i]/std::numeric_limits::max()); + + /* First go with original vertex coordinates, then move them by + epsilon/2 in each direction. */ + Vertex moved; + for(size_t moving = 0; moving <= vertexSize; ++moving) { + + /* Under each index is pointer to face which contains given vertex + and index of vertex in the face. */ + std::unordered_map, HashedVertex, IndexHash> 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, HashedVertex>(index, v)); + it->vertices[i] = result.first->second.newVertexPointer; } } - } - /* Delete all scheduled vertices */ - for(typename std::vector::const_iterator it = trashcan.begin(); it != trashcan.end(); ++it) { - _vertices.erase(*it); - delete *it; + /* Shrink vertices array */ + std::vector vertices(table.size()); + for(auto it = table.cbegin(); it != table.cend(); ++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 MeshBuilder { } private: - std::set _vertices; - std::multimap vertexFaces; - std::set _faces; + std::vector _faces; + std::vector _vertices; + + typedef size_t FacePointer; + + template class IndexHash { + public: + inline size_t operator()(const Math::Vector& 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 void setData(const Vertex* vertexData, const IndexType* indices, GLsizei vertexCount, GLsizei indexCount, GLenum indexType) { clear(); /* Map vertex indices to vertex pointers */ - std::map vertexMapping; - for(IndexType i = 0; i != vertexCount; ++i) { - Vertex* v = new Vertex(vertexData[i]); - _vertices.insert(v); - vertexMapping.insert(std::pair(i, v)); - } + std::vector vertices; + vertices.reserve(vertexCount); + for(IndexType i = 0; i != vertexCount; ++i) + addVertex(Vertex(vertexData[i])); /* Faces array */ - for(IndexType i = 0; i < indexCount; i += 3) { - Face* f = new Face; - for(int ii = 0; ii != 3; ++ii) { - f->vertices[ii] = vertexMapping[indices[i+ii]]; - vertexFaces.insert(std::pair(f->vertices[ii], f)); - } - _faces.insert(f); - } + _faces.reserve(indexCount/3); + for(IndexType i = 0; i < indexCount; i += 3) + addFace(indices[i], indices[i+1], indices[i+2]); } template void buildInternal(IndexedMesh* mesh, Buffer* vertexBuffer, Buffer::Usage vertexBufferUsage, Buffer::Usage indexBufferUsage, GLenum indexType) { - /* Update the mesh parameters */ - 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 vertices; - std::map indicesMapping; - IndexType i = 0; - for(typename std::set::const_iterator it = _vertices.begin(); it != _vertices.end(); ++it) { - vertices.push_back(**it); - indicesMapping.insert(std::pair(*it, i++)); - } - - /* Create indices array */ + /* Compress face array to index array */ std::vector indices; indices.reserve(_faces.size()*3); - for(typename std::set::const_iterator it = _faces.begin(); it != _faces.end(); ++it) { - for(int i = 0; i != 3; ++i) - indices.push_back(indicesMapping[(*it)->vertices[i]]); + for(auto it = _faces.cbegin(); it != _faces.cend(); ++it) { + indices.push_back(it->vertices[0]); + indices.push_back(it->vertices[1]); + indices.push_back(it->vertices[2]); } - /* Create mesh */ - vertexBuffer->setData(sizeof(Vertex)*vertices.size(), vertices.data(), vertexBufferUsage); + /* Update mesh parameters and fill it with data */ + 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); } }; diff --git a/src/Test/MeshBuilderTest.cpp b/src/Test/MeshBuilderTest.cpp index 732a674ed..7843aa53a 100644 --- a/src/Test/MeshBuilderTest.cpp +++ b/src/Test/MeshBuilderTest.cpp @@ -27,152 +27,159 @@ using namespace std; namespace Magnum { namespace Test { void MeshBuilderTest::setData() { - MeshBuilder builder; + MeshBuilder builder; int vertexData[] = { 1, 2, 3, 4 }; GLubyte indices[] = { 0, 1, 2, 1, 2, 3 }; - builder.setData(vertexData, indices, 4, 6); + builder.setData(reinterpret_cast(vertexData), indices, 4, 6); - set vertices = builder.vertices(); - QVERIFY(vertices.size() == 4); - - int i = 1; - for(set::const_iterator it = vertices.begin(); it != vertices.end(); ++it) - QVERIFY(**it == i++); - - set::Face*> faces = builder.faces(); + vector::Face> faces = builder.faces(); QVERIFY(faces.size() == 2); - set::Face*>::const_iterator it = faces.begin(); - QVERIFY(*(*it)->vertices[0] == 1); - QVERIFY(*(*it)->vertices[1] == 2); - QVERIFY(*(*it)->vertices[2] == 3); + vector::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(*(*it)->vertices[0] == 2); - QVERIFY(*(*it)->vertices[1] == 3); - QVERIFY(*(*it)->vertices[2] == 4); -} + QVERIFY(builder.vertices()[it->vertices[0]] == 2); + QVERIFY(builder.vertices()[it->vertices[1]] == 3); + QVERIFY(builder.vertices()[it->vertices[2]] == 4); -void MeshBuilderTest::addFace() { - MeshBuilder builder; - - int* v1 = new int(1); - int* v2 = new int(2); - int* v3 = new int(3); - int* v4 = new int(4); - - MeshBuilder::Face* f1 = new MeshBuilder::Face(v1, v2, v3); - builder.addFace(f1); - MeshBuilder::Face* f2 = new MeshBuilder::Face(v2, v3, v4); - builder.addFace(f2); - - set vertices; - vertices.insert(v1); - vertices.insert(v2); - vertices.insert(v3); - vertices.insert(v4); - - set::Face*> faces; - faces.insert(f1); - faces.insert(f2); + vector vertices = builder.vertices(); + QVERIFY(vertices.size() == 4); - QVERIFY(builder.vertices() == vertices); - QVERIFY(builder.faces() == faces); + int i = 0; + for(auto it = vertices.begin(); it != vertices.end(); ++it) + QVERIFY(*it == ++i); } -void MeshBuilderTest::removeFace() { - MeshBuilder builder; - - int* v1 = new int(1); - int* v2 = new int(2); - int* v3 = new int(3); - int* v4 = new int(4); - - MeshBuilder::Face* f1 = new MeshBuilder::Face(v1, v2, v3); +void MeshBuilderTest::addFace() { + Vector1 v1(1); + Vector1 v2(2); + Vector1 v3(3); + Vector1 v4(4); + MeshBuilder::Face f1(0, 1, 2); + MeshBuilder::Face f2(1, 2, 3); + + MeshBuilder builder; + builder.addVertex(v1); + builder.addVertex(v2); + builder.addVertex(v3); + builder.addVertex(v4); builder.addFace(f1); - MeshBuilder::Face* f2 = new MeshBuilder::Face(v2, v3, v4); builder.addFace(f2); - MeshBuilder::Face* f3 = new MeshBuilder::Face(v2, v3, v1); - builder.addFace(f3); - - /* Remove second face */ - builder.removeFace(f2); - - set vertices; - vertices.insert(v1); - vertices.insert(v2); - vertices.insert(v3); - - set::Face*> faces; - faces.insert(f1); - faces.insert(f3); + vector vertices; + vertices.push_back(v1); + vertices.push_back(v2); + vertices.push_back(v3); + vertices.push_back(v4); QVERIFY(builder.vertices() == vertices); - QVERIFY(builder.faces() == faces); - /* Remove third face (vertices shouldn't change) */ - builder.removeFace(f3); - faces.erase(f3); - QVERIFY(builder.vertices() == vertices); + vector::Face> faces; + faces.push_back(f1); + faces.push_back(f2); QVERIFY(builder.faces() == faces); } void MeshBuilderTest::cleanMesh() { - MeshBuilder builder; - - int* v1 = new int(1); - int* v2 = new int(2); - int* v3 = new int(1); - int* v4 = new int(4); - - MeshBuilder::Face* f1 = new MeshBuilder::Face(v1, v2, v3); + Vector1 v1(1); + Vector1 v2(2); + Vector1 v3(1); + Vector1 v4(4); + MeshBuilder::Face f1(0, 1, 2); + MeshBuilder::Face f2(1, 2, 3); + + MeshBuilder builder; + builder.addVertex(v1); + builder.addVertex(v2); + builder.addVertex(v3); + builder.addVertex(v4); builder.addFace(f1); - MeshBuilder::Face* f2 = new MeshBuilder::Face(v2, v3, v4); builder.addFace(f2); - builder.cleanMesh(); - int* unique = (f1->vertices[2] == v1) ? v1 : v3; - - set vertices; - vertices.insert(unique); - vertices.insert(v2); - vertices.insert(v4); - - set::Face*> faces; - faces.insert(f1); - faces.insert(f2); + builder.cleanMesh(1); /* Verify cleanup */ - QVERIFY(f1->vertices[2] == unique); - QVERIFY(f2->vertices[1] == unique); + vector vertices; + vertices.push_back(v1); + vertices.push_back(v2); + vertices.push_back(v4); QVERIFY(builder.vertices() == vertices); - QVERIFY(builder.faces() == faces); + + vector::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() { - MeshBuilder builder; - - int* v1 = new int(0); - int* v2 = new int(2); - int* v3 = new int(6); - int* v4 = new int(8); - - MeshBuilder::Face* f1 = new MeshBuilder::Face(v1, v2, v3); + Vector1 v1(0); + Vector1 v2(2); + Vector1 v3(6); + Vector1 v4(8); + MeshBuilder::Face f1(0, 1, 2); + MeshBuilder::Face f2(1, 2, 3); + + MeshBuilder builder; + builder.addVertex(v1); + builder.addVertex(v2); + builder.addVertex(v3); + builder.addVertex(v4); builder.addFace(f1); - MeshBuilder::Face* f2 = new MeshBuilder::Face(v2, v3, v4); builder.addFace(f2); builder.subdivide(interpolator); - - QVERIFY(builder.vertices().size() == 10); QVERIFY(builder.faces().size() == 8); - /* This also effectively checks the data, as the vertices should be exactly - 0, 1, 2, .. 8 */ - builder.cleanMesh(); + QVERIFY(builder.vertices().size() == 10); + QVERIFY(builder.vertices()[0] == Vector1(0)); + 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.faces().size() == 8); } }} diff --git a/src/Test/MeshBuilderTest.h b/src/Test/MeshBuilderTest.h index e446502a5..e0a5f76f8 100644 --- a/src/Test/MeshBuilderTest.h +++ b/src/Test/MeshBuilderTest.h @@ -25,12 +25,28 @@ class MeshBuilderTest: public QObject { private slots: void setData(); void addFace(); - void removeFace(); void cleanMesh(); void subdivide(); 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; } }; }}