Magnum::MeshTools namespace

Mesh tools.

Contents

Tools for generating, optimizing and cleaning meshes.

This library is built if WITH_MESHTOOLS is enabled when building Magnum. To use this library with CMake, you need to request the MeshTools component of the Magnum package and link to the Magnum::MeshTools target:

find_package(Magnum REQUIRED MeshTools)

# ...
target_link_libraries(your-app Magnum::MeshTools)

Note that functionality depending on GL APIs is available only if Magnum is built with both WITH_GL and TARGET_GL enabled (which is done by default).

See Downloading and building and Usage with CMake for more information.

Functions

auto combineIndexArrays(const std::vector<UnsignedInt>& interleavedArrays, UnsignedInt stride) -> std::pair<std::vector<UnsignedInt>, std::vector<UnsignedInt>>
Combine interleaved index arrays.
auto combineIndexArrays(const std::vector<std::reference_wrapper<std::vector<UnsignedInt>>>& arrays) -> std::vector<UnsignedInt>
Combine index arrays.
auto combineIndexArrays(std::initializer_list<std::reference_wrapper<std::vector<UnsignedInt>>> arrays) -> std::vector<UnsignedInt>
template<class ... T>
auto combineIndexedArrays(const std::pair<const std::vector<UnsignedInt>&, std::vector<T>&>&... indexedArrays) -> std::vector<UnsignedInt>
Combine indexed arrays.
auto compile(const Trade::MeshData2D& meshData, GL::BufferUsage usage) -> std::tuple<GL::Mesh, std::unique_ptr<GL::Buffer>, std::unique_ptr<GL::Buffer>>
Compile 2D mesh data.
auto compile(const Trade::MeshData3D& meshData, GL::BufferUsage usage) -> std::tuple<GL::Mesh, std::unique_ptr<GL::Buffer>, std::unique_ptr<GL::Buffer>>
Compile 3D mesh data.
auto compressIndices(const std::vector<UnsignedInt>& indices) -> std::tuple<Containers::Array<char>, MeshIndexType, UnsignedInt, UnsignedInt>
Compress vertex indices.
template<class T>
auto compressIndicesAs(const std::vector<UnsignedInt>& indices) -> Containers::Array<T>
Compress vertex indices as given type.
template<class T>
auto duplicate(const std::vector<UnsignedInt>& indices, const std::vector<T>& data) -> std::vector<T>
Duplicate data using index array.
void flipFaceWinding(std::vector<UnsignedInt>& indices)
Flip face winding.
void flipNormals(std::vector<Vector3>& normals)
Flip mesh normals.
void flipNormals(std::vector<UnsignedInt>& indices, std::vector<Vector3>& normals)
Flip mesh normals and face winding.
auto fullScreenTriangle(GL::Version version) -> std::pair<std::unique_ptr<GL::Buffer>, GL::Mesh>
Create full screen triangle mesh.
auto fullScreenTriangle() -> std::pair<std::unique_ptr<GL::Buffer>, GL::Mesh>
auto generateFlatNormals(const std::vector<UnsignedInt>& indices, const std::vector<Vector3>& positions) -> std::tuple<std::vector<UnsignedInt>, std::vector<Vector3>>
Generate flat normals.
template<class T, class ... U>
auto interleave(const T& first, const U&... next) -> Containers::Array<char>
Interleave vertex attributes.
template<class T, class ... U>
void interleaveInto(Containers::ArrayView<char> buffer, const T& first, const U&... next)
Interleave vertex attributes into existing buffer.
template<class Vector>
auto removeDuplicates(std::vector<Vector>& data, typename Vector::Type epsilon = Math::TypeTraits<typename Vector::Type>::epsilon()) -> std::vector<UnsignedInt>
Remove duplicate floating-point vector data from given array.
template<class Vertex, class Interpolator>
void subdivide(std::vector<UnsignedInt>& indices, std::vector<Vertex>& vertices, Interpolator interpolator)
Subdivide the mesh.
void tipsify(std::vector<UnsignedInt>& indices, UnsignedInt vertexCount, std::size_t cacheSize)
Tipsify the mesh.
template<class T, class U>
void transformVectorsInPlace(const Math::Quaternion<T>& normalizedQuaternion, U& vectors)
Transform vectors in-place using given transformation.
template<class T, class U>
void transformVectorsInPlace(const Math::Complex<T>& complex, U& vectors)
template<class T, class U>
void transformVectorsInPlace(const Math::Matrix3<T>& matrix, U& vectors)
template<class T, class U>
void transformVectorsInPlace(const Math::Matrix4<T>& matrix, U& vectors)
template<class T, class U>
auto transformVectors(const T& transformation, U vectors) -> U
Transform vectors using given transformation.
template<class T, class U>
void transformPointsInPlace(const Math::DualQuaternion<T>& normalizedDualQuaternion, U& points)
Transform points in-place using given transformation.
template<class T, class U>
void transformPointsInPlace(const Math::DualComplex<T>& dualComplex, U& points)
template<class T, class U>
void transformPointsInPlace(const Math::Matrix3<T>& matrix, U& points)
template<class T, class U>
void transformPointsInPlace(const Math::Matrix4<T>& matrix, U& points)
template<class T, class U>
auto transformPoints(const T& transformation, U vectors) -> U
Transform points using given transformation.

Function documentation

std::pair<std::vector<UnsignedInt>, std::vector<UnsignedInt>> Magnum::MeshTools::combineIndexArrays(const std::vector<UnsignedInt>& interleavedArrays, UnsignedInt stride)

Combine interleaved index arrays.

Unlike above, this function takes one interleaved array instead of separate index arrays. Continuing with the above example, you would call this function with the following array (odd value is vertex index, even is normal index, stride is thus 2):

0 1 2 3 5 4 0 1 0 4 1 6 3 1 2 3 2 1

Similarly to above this function will return the following combined index array as first pair value:

0 1 2 0 3 4 5 1 6

And second pair value is the cleaned up interleaved array:

0 1 2 3 5 4 0 4 1 6 3 1 2 1

std::vector<UnsignedInt> Magnum::MeshTools::combineIndexArrays(const std::vector<std::reference_wrapper<std::vector<UnsignedInt>>>& arrays)

Combine index arrays.

Parameters
arrays in/out Index arrays to combine. These arrays are updated in-place to contain unique combinations of the original indices.
Returns Resulting combined index array

Creates new combined index array and updates the original ones with translation to new ones. For example, when you have position and normal array, each indexed with separate indices and you want to index both of them with single index array:

a b c d e f         // positions
A B C D E F G       // normals

0 2 5 0 0 1 3 2 2   // position indices
1 3 4 1 4 6 1 3 1   // normal indices

In particular, first triangle in the mesh will have positions a c f and normals B D E. You can see that not all combinations are unique and also that there are some vertices unused. When you pass the two index arrays above to this function, the following combined index array is returned:

0 1 2 0 3 4 5 1 6

And the original arrays are cleaned up to have only unique combinations:

0 2 5 0 1 3 2
1 3 4 4 6 1 1

You can use these as translation table to create new vertex and normal arrays which can be then indexed with the combined index array:

a c f a b d c
B D E E G B B

Again, first triangle in the mesh will have positions a c f and normals B D E.

This function calls combineIndexArrays(const std::vector<UnsignedInt>&, UnsignedInt) internally. See also combineIndexedArrays() which does the vertex data reordering automatically.

std::vector<UnsignedInt> Magnum::MeshTools::combineIndexArrays(std::initializer_list<std::reference_wrapper<std::vector<UnsignedInt>>> arrays)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

template<class ... T>
std::vector<UnsignedInt> Magnum::MeshTools::combineIndexedArrays(const std::pair<const std::vector<UnsignedInt>&, std::vector<T>&>&... indexedArrays)

Combine indexed arrays.

Parameters
indexedArrays in/out Index and attribute arrays
Returns Array with resulting indices

Creates new combined index array and reorders original attribute arrays so they can be indexed with the new single index array.

The index array must be passed as const reference (to avoid copying) and attribute array as reference, so it can be replaced with combined data. To avoid explicit verbose specification of tuple type, you can write it with help of some STL functions like shown below. Also if one index array is shared by more than one attribute array, just pass the index array more times. Example:

std::vector<UnsignedInt> vertexIndices;
std::vector<Vector3> positions;
std::vector<UnsignedInt> normalTextureIndices;
std::vector<Vector3> normals;
std::vector<Vector2> textureCoordinates;

std::vector<UnsignedInt> indices = MeshTools::combineIndexedArrays(
    std::make_pair(std::cref(vertexIndices), std::ref(positions)),
    std::make_pair(std::cref(normalTextureIndices), std::ref(normals)),
    std::make_pair(std::cref(normalTextureIndices), std::ref(textureCoordinates))
);

See combineIndexArrays() documentation for more information about the procedure.

std::tuple<GL::Mesh, std::unique_ptr<GL::Buffer>, std::unique_ptr<GL::Buffer>> Magnum::MeshTools::compile(const Trade::MeshData2D& meshData, GL::BufferUsage usage)

Compile 2D mesh data.

Configures mesh for Shaders::Generic2D shader with vertex buffer and possibly also index buffer, if the mesh is indexed. Positions are bound to Shaders::Generic2D::Position attribute. If the mesh contains texture coordinates, they are bound to Shaders::Generic2D::TextureCoordinates attribute. No data compression or index optimization (except for index buffer packing) is done. The usage parameter is used for both vertex and index buffer.

The second returned buffer may be nullptr if the mesh is not indexed.

This is just a convenience function for creating generic meshes, you might want to use interleave() and compressIndices() functions instead for greater flexibility.

std::tuple<GL::Mesh, std::unique_ptr<GL::Buffer>, std::unique_ptr<GL::Buffer>> Magnum::MeshTools::compile(const Trade::MeshData3D& meshData, GL::BufferUsage usage)

Compile 3D mesh data.

Configures mesh for Shaders::Generic3D shader with vertex buffer and possibly also index buffer, if the mesh is indexed. Positions are bound to Shaders::Generic3D::Position attribute. If the mesh contains normals, they are bound to Shaders::Generic3D::Normal attribute, texture coordinates are bound to Shaders::Generic2D::TextureCoordinates attribute. No data compression or index optimization (except for index buffer packing) is done. The usage parameter is used for both vertex and index buffer.

The second returned buffer may be nullptr if the mesh is not indexed.

This is just a convenience function for creating generic meshes, you might want to use interleave() and compressIndices() functions instead for greater flexibility.

std::tuple<Containers::Array<char>, MeshIndexType, UnsignedInt, UnsignedInt> Magnum::MeshTools::compressIndices(const std::vector<UnsignedInt>& indices)

Compress vertex indices.

Parameters
indices Index array
Returns Index range, type and compressed index array

This function takes index array and outputs them compressed to smallest possible size. For example when your indices have maximum number 463, it's wasteful to store them in array of 32bit integers, array of 16bit integers is sufficient.

Example usage:

std::vector<UnsignedInt> indices;

Containers::Array<char> indexData;
MeshIndexType indexType;
UnsignedInt indexStart, indexEnd;
std::tie(indexData, indexType, indexStart, indexEnd) =
    MeshTools::compressIndices(indices);

GL::Buffer indexBuffer;
indexBuffer.setData(indexData, GL::BufferUsage::StaticDraw);

GL::Mesh mesh;
mesh.setCount(indices.size())
    .setIndexBuffer(indexBuffer, 0, indexType, indexStart, indexEnd);

template<class T>
Containers::Array<T> Magnum::MeshTools::compressIndicesAs(const std::vector<UnsignedInt>& indices)

Compress vertex indices as given type.

The type can be either UnsignedByte, UnsignedShort or UnsignedInt. Values in the index array are expected to be representable with given type.

Example usage:

std::vector<UnsignedInt> indices;
Containers::Array<UnsignedShort> indexData =
    MeshTools::compressIndicesAs<UnsignedShort>(indices);

template<class T>
std::vector<T> Magnum::MeshTools::duplicate(const std::vector<UnsignedInt>& indices, const std::vector<T>& data)

Duplicate data using index array.

Converts indexed array to non-indexed, for example data {a, b, c, d} with index array {1, 1, 0, 3, 2, 2} will be converted to {b, b, a, d, c, c}.

void Magnum::MeshTools::flipFaceWinding(std::vector<UnsignedInt>& indices)

Flip face winding.

Parameters
indices in/out Index array to operate on

The same as flipNormals(std::vector<UnsignedInt>&, std::vector<Vector3>&), but flips only face winding.

void Magnum::MeshTools::flipNormals(std::vector<Vector3>& normals)

Flip mesh normals.

Parameters
normals in/out Normal array to operate on

The same as flipNormals(std::vector<UnsignedInt>&, std::vector<Vector3>&), but flips only normals, not face winding.

void Magnum::MeshTools::flipNormals(std::vector<UnsignedInt>& indices, std::vector<Vector3>& normals)

Flip mesh normals and face winding.

Parameters
indices in/out Index array to operate on
normals in/out Normal array to operate on

Flips normal vectors and face winding in index array for face culling to work properly too. See also flipNormals(std::vector<Vector3>&) and flipFaceWinding(), which flip normals or face winding only.

std::pair<std::unique_ptr<GL::Buffer>, GL::Mesh> Magnum::MeshTools::fullScreenTriangle(GL::Version version)

Create full screen triangle mesh.

Returns pre-configured mesh along with vertex buffer which can be used for full-screen post-processing effects. The mesh is single triangle covering whole screen area ( $ (-1, -1) - (1, 1) $ on both dimensions) and provides only vertex positions, as other attributes (such as texture coordinates) can be computed from them. The vertex positions are, in order:

\[ \begin{pmatrix} -1 \\ 1 \end{pmatrix}, \begin{pmatrix} -1 \\ -3 \end{pmatrix}, \begin{pmatrix} 3 \\ 1 \end{pmatrix} \]

Based on version parameter, on OpenGL 2.1 and OpenGL ES 2.0 the vertex positions are passed explicitly as attribute 0, contained in the buffer. On OpenGL 3.0+ and OpenGL ES 3.0+ the mesh is attribute-less and the vertex positions can be computed using gl_VertexID builtin shader variable, thus nullptr is returned instead of vertex buffer.

Computing positions in vertex shader in a portable way might be done like this. For OpenGL 2.1 and OpenGL ES 2.0 you then need to bind the location of position attribute to 0.

#if (!defined(GL_ES) && __VERSION__ >= 130) || (defined(GL_ES) && __VERSION__ >= 300)
#define NEW_GLSL
#endif

#ifndef NEW_GLSL
attribute lowp vec4 position;
#endif

void main() {
    #ifdef NEW_GLSL
    gl_Position = vec4((gl_VertexID == 2) ?  3.0 : -1.0,
                       (gl_VertexID == 1) ? -3.0 :  1.0, 0.0, 1.0);
    #else
    gl_Position = position;
    #endif
}

std::pair<std::unique_ptr<GL::Buffer>, GL::Mesh> Magnum::MeshTools::fullScreenTriangle()

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

This function implicitly uses current context version.

std::tuple<std::vector<UnsignedInt>, std::vector<Vector3>> Magnum::MeshTools::generateFlatNormals(const std::vector<UnsignedInt>& indices, const std::vector<Vector3>& positions)

Generate flat normals.

Parameters
indices Array of triangle face indices
positions Array of vertex positions
Returns Normal indices and vectors

For each face generates one normal vector, removes duplicates before returning. Example usage:

std::vector<UnsignedInt> vertexIndices;
std::vector<Vector3> positions;

std::vector<UnsignedInt> normalIndices;
std::vector<Vector3> normals;
std::tie(normalIndices, normals) =
    MeshTools::generateFlatNormals(vertexIndices, positions);

You can then use combineIndexedArrays() to combine normal and vertex array to use the same indices.

template<class T, class ... U>
Containers::Array<char> Magnum::MeshTools::interleave(const T& first, const U&... next)

Interleave vertex attributes.

This function takes list of attribute arrays and returns them interleaved, so data for each attribute are in continuous place in memory.

Example usage:

std::vector<Vector3> positions;
std::vector<Vector2> textureCoordinates;

GL::Buffer vertexBuffer;
vertexBuffer.setData(MeshTools::interleave(positions, textureCoordinates), GL::BufferUsage::StaticDraw);

GL::Mesh mesh;
mesh.setCount(positions.size())
    .addVertexBuffer(vertexBuffer, 0, MyShader::Position{}, MyShader::TextureCoordinates{});

It's often desirable to align data for one vertex on 32bit boundaries. To achieve that, you can specify gaps between the attributes:

std::vector<Vector4> positions;
std::vector<UnsignedShort> weights;
std::vector<Color3ub> vertexColors;

auto data = MeshTools::interleave(positions, weights, 2, vertexColors, 1);

All gap bytes are set zero. This way vertex stride is 24 bytes, without gaps it would be 21 bytes, causing possible performance loss.

template<class T, class ... U>
void Magnum::MeshTools::interleaveInto(Containers::ArrayView<char> buffer, const T& first, const U&... next)

Interleave vertex attributes into existing buffer.

Unlike interleave() this function interleaves the data into existing buffer and leaves gaps untouched instead of zero-initializing them. This function can thus be used for interleaving data depending on runtime parameters.

template<class Vector>
std::vector<UnsignedInt> Magnum::MeshTools::removeDuplicates(std::vector<Vector>& data, typename Vector::Type epsilon = Math::TypeTraits<typename Vector::Type>::epsilon())

Remove duplicate floating-point vector data from given array.

Parameters
data in/out Input data array
epsilon in Epsilon value, vertices nearer than this distance will be melt together
Returns Index array and unique data

Removes duplicate data from the array by collapsing them into buckets of size epsilon. First vector in given bucket is used, other ones are thrown away, no interpolation is done. Note that this function is meant to be used for floating-point data (or generally with non-zero epsilon), for discrete data the usual sorting method is much more efficient.

If you want to remove duplicate data from already indexed array, first remove duplicates as if the array wasn't indexed at all and then use duplicate() to combine the two index arrays:

std::vector<UnsignedInt> indices;
std::vector<Vector3> positions;

indices = MeshTools::duplicate(indices, MeshTools::removeDuplicates(positions));

Removing duplicates in multiple indcidental arrays is also possible — first remove duplicates in each array separately and then use combineIndexedArrays() to combine the resulting index arrays to single index array and reorder the data accordingly:

std::vector<Vector3> positions;
std::vector<Vector2> texCoords;

std::vector<UnsignedInt> positionIndices = MeshTools::removeDuplicates(positions);
std::vector<UnsignedInt> texCoordIndices = MeshTools::removeDuplicates(texCoords);

std::vector<UnsignedInt> indices = MeshTools::combineIndexedArrays(
    std::make_pair(std::cref(positionIndices), std::ref(positions)),
    std::make_pair(std::cref(texCoordIndices), std::ref(texCoords))
);

template<class Vertex, class Interpolator>
void Magnum::MeshTools::subdivide(std::vector<UnsignedInt>& indices, std::vector<Vertex>& vertices, Interpolator interpolator)

Subdivide the mesh.

Template parameters
Vertex Vertex data type
Interpolator See interpolator function parameter
Parameters
indices in/out Index array to operate on
vertices in/out Vertex array to operate on
interpolator Functor or function pointer which interpolates two adjacent vertices: Vertex interpolator(Vertex a, Vertex b)

Goes through all triangle faces and subdivides them into four new. Removing duplicate vertices in the mesh is up to user.

void Magnum::MeshTools::tipsify(std::vector<UnsignedInt>& indices, UnsignedInt vertexCount, std::size_t cacheSize)

Tipsify the mesh.

Parameters
indices in/out Indices array to operate on
vertexCount in Vertex count
cacheSize in Post-transform vertex cache size

Optimizes the mesh for vertex-bound applications by rearranging its index array for beter usage of post-transform vertex cache. Algorithm used: Pedro V. Sander, Diego Nehab, and Joshua Barczak — Fast Triangle Reordering for Vertex Locality and Reduced Overdraw, SIGGRAPH 2007, http://gfx.cs.princeton.edu/pubs/Sander_2007_%3ETR/index.php*.

template<class T, class U>
void Magnum::MeshTools::transformVectorsInPlace(const Math::Quaternion<T>& normalizedQuaternion, U& vectors)

Transform vectors in-place using given transformation.

Usable for one-time mesh transformations that would otherwise negatively affect dependent objects, such as (uneven) scaling. Accepts any forward-iterable type with compatible vector type as vectors. Expects that Quaternion is normalized, no further requirements are for other transformation representations.

Unlike in transformPointsInPlace(), the transformation does not involve translation.

Example usage:

std::vector<Vector3> vectors;
auto transformation = Quaternion::rotation(35.0_degf, Vector3::yAxis());
MeshTools::transformVectorsInPlace(transformation, vectors);

template<class T, class U>
void Magnum::MeshTools::transformVectorsInPlace(const Math::Complex<T>& complex, U& vectors)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

template<class T, class U>
void Magnum::MeshTools::transformVectorsInPlace(const Math::Matrix3<T>& matrix, U& vectors)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

template<class T, class U>
void Magnum::MeshTools::transformVectorsInPlace(const Math::Matrix4<T>& matrix, U& vectors)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

template<class T, class U>
U Magnum::MeshTools::transformVectors(const T& transformation, U vectors)

Transform vectors using given transformation.

Returns transformed vectors instead of modifying them in-place. See transformVectorsInPlace() for more information.

template<class T, class U>
void Magnum::MeshTools::transformPointsInPlace(const Math::DualQuaternion<T>& normalizedDualQuaternion, U& points)

Transform points in-place using given transformation.

Usable for one-time mesh transformations that would otherwise negatively affect dependent objects, such as (uneven) scaling. Accepts any forward-iterable type with compatible vector type as vectors. Expects that DualQuaternion is normalized, no further requirements are for other transformation representations.

Unlike in transformVectorsInPlace(), the transformation also involves translation.

Example usage:

std::vector<Vector3> points;
auto transformation =
    DualQuaternion::rotation(35.0_degf, Vector3::yAxis())*
    DualQuaternion::translation({0.5f, -1.0f, 3.0f});
MeshTools::transformPointsInPlace(transformation, points);

template<class T, class U>
void Magnum::MeshTools::transformPointsInPlace(const Math::DualComplex<T>& dualComplex, U& points)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

template<class T, class U>
void Magnum::MeshTools::transformPointsInPlace(const Math::Matrix3<T>& matrix, U& points)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

template<class T, class U>
void Magnum::MeshTools::transformPointsInPlace(const Math::Matrix4<T>& matrix, U& points)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

template<class T, class U>
U Magnum::MeshTools::transformPoints(const T& transformation, U vectors)

Transform points using given transformation.

Returns transformed points instead of modifying them in-place. See transformPointsInPlace() for more information.