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.

318 lines
12 KiB

#ifndef Magnum_MeshBuilder_h
#define Magnum_MeshBuilder_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.
*/
/** @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 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 */
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