mirror of https://github.com/mosra/magnum.git
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.
145 lines
5.8 KiB
145 lines
5.8 KiB
#ifndef Magnum_MeshTools_Clean_h |
|
#define Magnum_MeshTools_Clean_h |
|
/* |
|
Copyright © 2010, 2011, 2012 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 Function Magnum::MeshTools::clean() |
|
*/ |
|
|
|
#include <unordered_map> |
|
#include <limits> |
|
|
|
#include "Utility/MurmurHash2.h" |
|
#include "TypeTraits.h" |
|
|
|
namespace Magnum { namespace MeshTools { |
|
|
|
#ifndef DOXYGEN_GENERATING_OUTPUT |
|
namespace Implementation { |
|
|
|
template<class Vertex, size_t vertexSize = Vertex::Size> class Clean { |
|
public: |
|
inline Clean(std::vector<unsigned int>& indices, std::vector<Vertex>& vertices): indices(indices), vertices(vertices) {} |
|
|
|
void operator()(typename Vertex::Type epsilon = TypeTraits<typename Vertex::Type>::epsilon()) { |
|
if(indices.empty()) return; |
|
|
|
/* Get mesh bounds */ |
|
Vertex min, max; |
|
for(size_t i = 0; i != Vertex::Size; ++i) { |
|
min[i] = std::numeric_limits<typename Vertex::Type>::max(); |
|
max[i] = std::numeric_limits<typename Vertex::Type>::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<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()); |
|
|
|
/* 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<Math::Vector<vertexSize, size_t>, HashedVertex, IndexHash> table; |
|
|
|
/* Reserve space for all vertices */ |
|
table.reserve(vertices.size()); |
|
|
|
/* Go through all faces' vertices */ |
|
for(auto it = indices.begin(); it != indices.end(); ++it) { |
|
/* Index of a vertex in vertexSize-dimensional table */ |
|
size_t index[vertexSize]; |
|
for(size_t ii = 0; ii != vertexSize; ++ii) |
|
index[ii] = (vertices[*it][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, table.size()); |
|
auto result = table.insert(std::pair<Math::Vector<vertexSize, size_t>, HashedVertex>(Math::Vector<vertexSize, size_t>::from(index), v)); |
|
*it = result.first->second.newIndex; |
|
} |
|
|
|
/* Shrink vertices array */ |
|
std::vector<Vertex> newVertices(table.size()); |
|
for(auto it = table.cbegin(); it != table.cend(); ++it) |
|
newVertices[it->second.newIndex] = vertices[it->second.oldIndex]; |
|
std::swap(newVertices, vertices); |
|
|
|
/* Move vertex coordinates by epsilon/2 in next direction */ |
|
if(moving != Vertex::Size) { |
|
moved = Vertex(); |
|
moved[moving] = epsilon/2; |
|
} |
|
} |
|
} |
|
|
|
private: |
|
class IndexHash { |
|
public: |
|
inline size_t operator()(const Math::Vector<vertexSize, size_t>& data) const { |
|
return *reinterpret_cast<const size_t*>(Corrade::Utility::MurmurHash2()(reinterpret_cast<const char*>(&data), sizeof(data)).byteArray()); |
|
} |
|
}; |
|
|
|
struct HashedVertex { |
|
unsigned int oldIndex, newIndex; |
|
|
|
HashedVertex(unsigned int oldIndex, unsigned int newIndex): oldIndex(oldIndex), newIndex(newIndex) {} |
|
}; |
|
|
|
std::vector<unsigned int>& indices; |
|
std::vector<Vertex>& vertices; |
|
}; |
|
|
|
} |
|
#endif |
|
|
|
/** @ingroup mesh |
|
@brief %Clean the mesh |
|
@tparam Vertex Vertex data type |
|
@tparam vertexSize 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) |
|
@param[in,out] indices Index array to operate on |
|
@param[in,out] vertices Vertex array to operate on |
|
@param[in] epsilon Epsilon value, vertices nearer than this distance will |
|
be melt together. |
|
|
|
Removes duplicate vertices from the mesh. |
|
|
|
@todo Different (no cycle) implementation for integral vertices |
|
@todo Interpolate vertices, not collapse them to first in the cell |
|
@todo Ability to specify other attributes for interpolation |
|
*/ |
|
template<class Vertex, size_t vertexSize = Vertex::Size> inline void clean(std::vector<unsigned int>& indices, std::vector<Vertex>& vertices, typename Vertex::Type epsilon = TypeTraits<typename Vertex::Type>::epsilon()) { |
|
Implementation::Clean<Vertex, vertexSize>(indices, vertices)(epsilon); |
|
} |
|
|
|
}} |
|
|
|
#endif
|
|
|