Browse Source

Class for building meshes from scratch or prefabricated data.

vectorfields
Vladimír Vondruš 16 years ago
parent
commit
c30872a471
  1. 308
      src/MeshBuilder.h
  2. 1
      src/Test/CMakeLists.txt
  3. 153
      src/Test/MeshBuilderTest.cpp
  4. 34
      src/Test/MeshBuilderTest.h

308
src/MeshBuilder.h

@ -0,0 +1,308 @@
#ifndef Magnum_MeshBuilder_h
#define Magnum_MeshBuilder_h
/*
Copyright © 2010 Vladimír Vondruš <mosra@centrum.cz>
This file is part of Magnum.
Magnum is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 3
only, as published by the Free Software Foundation.
Magnum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License version 3 for more details.
*/
/** @file
* @brief Class Magnum::MeshBuilder
*/
#include <set>
#include <map>
#include "Buffer.h"
#include "IndexedMesh.h"
namespace Magnum {
/**
@brief Mesh builder
Class for building meshes with triangle primitive from scratch or from
prefabricated data and modifying them (adding/removing faces, cleaning duplicate
vertices etc.).
Vertex template can be absolutely anything (single integer, structure, class)
but it must have at least operator== implemented.
*/
template<class Vertex> class MeshBuilder {
public:
/** @brief Triangle face */
struct Face {
/** @brief Implicit constructor */
Face() {}
/** @brief Constructor */
Face(Vertex* first, Vertex* second, Vertex* third) {
vertices[0] = first;
vertices[1] = second;
vertices[2] = third;
}
/** @brief Vertex data */
Vertex* vertices[3];
};
/**
* @brief Destructor
*
* Removed all vertices and faces.
*/
inline virtual ~MeshBuilder() { clear(); }
/**
* @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().
*/
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();
_faces.clear();
vertexFaces.clear();
}
/** @brief Array with vertices */
inline const std::set<Vertex*>& vertices() const { return _vertices; }
/** @brief Array with faces */
inline const std::set<Face*>& faces() const { return _faces; }
/**
* @brief Set mesh data
* @param vertices Vertices
* @param indices Vertex indices
* @param vertexCount Vertex count
* @param indexCount Index count
*
* Replaces mesh builder data with given data. Type of indices is
* detected from given pointer.
*/
inline void setData(const Vertex* vertices, const GLubyte* indices, GLsizei vertexCount, GLsizei indexCount) {
setData<GLubyte>(vertices, indices, vertexCount, indexCount, GL_UNSIGNED_BYTE);
}
/** @copydoc setData() */
inline void setData(const Vertex* vertices, const GLushort* indices, GLsizei vertexCount, GLsizei indexCount) {
setData<GLushort>(vertices, indices, vertexCount, indexCount, GL_UNSIGNED_SHORT);
}
/** @copydoc setData() */
inline void setData(const Vertex* vertices, const GLuint* indices, GLsizei vertexCount, GLsizei indexCount) {
setData<GLuint>(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<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
* @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<Vertex*, Face*>(face->vertices[i], face));
/* Add face to face list */
_faces.insert(face);
}
/**
* @brief Clean the mesh
*
* Removes duplicate vertices from the mesh.
*/
void cleanMesh() {
/* Vertices scheduled for deletion */
std::vector<Vertex*> trashcan;
/* Foreach all vertices and for each find similar in the rest of the array */
for(typename std::set<Vertex*>::iterator it = _vertices.begin(); it != _vertices.end(); ++it) {
typename std::set<Vertex*>::iterator next = it;
for(typename std::set<Vertex*>::iterator similarIt = ++next; similarIt != _vertices.end(); ++similarIt) {
if(**it == **similarIt) {
/* Range of faces sharing that similar vertex */
typename std::multimap<Vertex*, Face*>::iterator begin = vertexFaces.lower_bound(*similarIt);
typename std::multimap<Vertex*, Face*>::iterator end = vertexFaces.upper_bound(*similarIt);
/* Updated array of faces, now sharing the original vertex */
std::multimap<Vertex*, Face*> updatedFaces;
/* Replace similar vertex in faces with this */
for(typename std::multimap<Vertex*, Face*>::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<Vertex*, Face*>(*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);
}
}
}
/* Delete all scheduled vertices */
for(typename std::vector<Vertex*>::const_iterator it = trashcan.begin(); it != trashcan.end(); ++it) {
_vertices.erase(*it);
delete *it;
}
}
/**
* @brief Build indexed mesh and fill existing buffers with it
* @param mesh Mesh. The mesh primitive is set to
* Mesh::Triangles, if it is not already, vertex and index count
* is updated to values from the builder.
* @param vertexBuffer Vertex buffer created as interleaved
* with Mesh::addBuffer(). Otherwise the behaviour is undefined.
* @param vertexBufferUsage Usage of the vertex buffer.
* @param indexBufferUsage Usage of the index buffer.
*
* Builds indexed mesh from the data and fills given mesh buffers with
* them.
* @note The mesh is @b not cleaned before building.
*/
void build(IndexedMesh* mesh, Buffer* vertexBuffer, Buffer::Usage vertexBufferUsage, Buffer::Usage indexBufferUsage) {
if(_faces.size()*3 <= 0xFF)
buildInternal<GLubyte>(mesh, vertexBuffer, vertexBufferUsage, indexBufferUsage, GL_UNSIGNED_BYTE);
else if(_faces.size()*3 <= 0xFFFF)
buildInternal<GLushort>(mesh, vertexBuffer, vertexBufferUsage, indexBufferUsage, GL_UNSIGNED_SHORT);
else
buildInternal<GLuint>(mesh, vertexBuffer, vertexBufferUsage, indexBufferUsage, GL_UNSIGNED_INT);
}
/**
* @brief Build indexed data and create new mesh from them
* @param vertexBufferUsage Usage of the vertex buffer.
* @param indexBufferUsage Usage of the index buffer.
*
* @see build(IndexedMesh*, Buffer*, Buffer::Usage, Buffer::Usage)
*/
IndexedMesh* build(Buffer::Usage vertexBufferUsage, Buffer::Usage indexBufferUsage) {
IndexedMesh mesh = new IndexedMesh(Mesh::Triangles, 0, 0, GL_UNSIGNED_BYTE);
Buffer* vertexBuffer = mesh.addBuffer(true);
build(mesh, vertexBuffer, vertexBufferUsage, indexBufferUsage);
return mesh;
}
private:
std::set<Vertex*> _vertices;
std::multimap<Vertex*, Face*> vertexFaces;
std::set<Face*> _faces;
template<class IndexType> void setData(const Vertex* vertexData, const IndexType* indices, GLsizei vertexCount, GLsizei indexCount, GLenum indexType) {
clear();
/* Map vertex indices to vertex pointers */
std::map<IndexType, Vertex*> vertexMapping;
for(IndexType i = 0; i != vertexCount; ++i) {
Vertex* v = new Vertex(vertexData[i]);
_vertices.insert(v);
vertexMapping.insert(std::pair<IndexType, Vertex*>(i, v));
}
/* 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<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) {
/* 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<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;
indices.reserve(_faces.size()*3);
for(typename std::set<Face*>::const_iterator it = _faces.begin(); it != _faces.end(); ++it) {
for(int i = 0; i != 3; ++i)
indices.push_back(indicesMapping[(*it)->vertices[i]]);
}
/* Create mesh */
vertexBuffer->setData(sizeof(Vertex)*vertices.size(), vertices.data(), vertexBufferUsage);
mesh->indexBuffer()->setData(sizeof(IndexType)*indices.size(), indices.data(), indexBufferUsage);
}
};
}
#endif

1
src/Test/CMakeLists.txt

@ -1,3 +1,4 @@
magnum_add_test(ObjectTest ObjectTest.h ObjectTest.cpp Magnum) magnum_add_test(ObjectTest ObjectTest.h ObjectTest.cpp Magnum)
magnum_add_test(CameraTest CameraTest.h CameraTest.cpp Magnum) magnum_add_test(CameraTest CameraTest.h CameraTest.cpp Magnum)
magnum_add_test(SceneTest SceneTest.h SceneTest.cpp Magnum) magnum_add_test(SceneTest SceneTest.h SceneTest.cpp Magnum)
magnum_add_test(MeshBuilderTest MeshBuilderTest.h MeshBuilderTest.cpp Magnum)

153
src/Test/MeshBuilderTest.cpp

@ -0,0 +1,153 @@
/*
Copyright © 2010, 2011 Vladimír Vondruš <mosra@centrum.cz>
This file is part of Magnum.
Magnum is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 3
only, as published by the Free Software Foundation.
Magnum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License version 3 for more details.
*/
#include "MeshBuilderTest.h"
#include <QtTest/QTest>
#include <QtCore/QDebug>
#include "MeshBuilder.h"
QTEST_APPLESS_MAIN(Magnum::Test::MeshBuilderTest)
using namespace std;
namespace Magnum { namespace Test {
void MeshBuilderTest::setData() {
MeshBuilder<int> builder;
int vertexData[] = { 1, 2, 3, 4 };
GLubyte indices[] = { 0, 1, 2, 1, 2, 3 };
builder.setData(vertexData, indices, 4, 6);
set<int*> vertices = builder.vertices();
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);
set<MeshBuilder<int>::Face*>::const_iterator it = faces.begin();
QVERIFY(*(*it)->vertices[0] == 1);
QVERIFY(*(*it)->vertices[1] == 2);
QVERIFY(*(*it)->vertices[2] == 3);
it++;
QVERIFY(*(*it)->vertices[0] == 2);
QVERIFY(*(*it)->vertices[1] == 3);
QVERIFY(*(*it)->vertices[2] == 4);
}
void MeshBuilderTest::addFace() {
MeshBuilder<int> builder;
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);
QVERIFY(builder.faces() == faces);
}
void MeshBuilderTest::removeFace() {
MeshBuilder<int> builder;
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);
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);
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);
QVERIFY(builder.faces() == faces);
}
void MeshBuilderTest::cleanMesh() {
MeshBuilder<int> builder;
int* v1 = new int(1);
int* v2 = new int(2);
int* v3 = new int(1);
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);
builder.cleanMesh();
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 */
QVERIFY(f1->vertices[2] == unique);
QVERIFY(f2->vertices[1] == unique);
QVERIFY(builder.vertices() == vertices);
QVERIFY(builder.faces() == faces);
}
}}

34
src/Test/MeshBuilderTest.h

@ -0,0 +1,34 @@
#ifndef Magnum_Test_MeshBuilderTest_h
#define Magnum_Test_MeshBuilderTest_h
/*
Copyright © 2010, 2011 Vladimír Vondruš <mosra@centrum.cz>
This file is part of Magnum.
Magnum is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 3
only, as published by the Free Software Foundation.
Magnum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License version 3 for more details.
*/
#include <QtCore/QObject>
namespace Magnum { namespace Test {
class MeshBuilderTest: public QObject {
Q_OBJECT
private slots:
void setData();
void addFace();
void removeFace();
void cleanMesh();
};
}}
#endif
Loading…
Cancel
Save