mirror of https://github.com/mosra/magnum.git
Browse Source
This should have been there for ages already. Not added almost five years after MeshData became a thing, ffs.pull/659/head
13 changed files with 642 additions and 16 deletions
@ -0,0 +1,411 @@ |
|||||||
|
/* |
||||||
|
This file is part of Magnum. |
||||||
|
|
||||||
|
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, |
||||||
|
2020, 2021, 2022, 2023, 2024 |
||||||
|
Vladimír Vondruš <mosra@centrum.cz> |
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a |
||||||
|
copy of this software and associated documentation files (the "Software"), |
||||||
|
to deal in the Software without restriction, including without limitation |
||||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense, |
||||||
|
and/or sell copies of the Software, and to permit persons to whom the |
||||||
|
Software is furnished to do so, subject to the following conditions: |
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included |
||||||
|
in all copies or substantial portions of the Software. |
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
||||||
|
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
||||||
|
DEALINGS IN THE SOFTWARE. |
||||||
|
*/ |
||||||
|
|
||||||
|
namespace Magnum { |
||||||
|
/** @page meshtools Mesh processing tools |
||||||
|
@brief Overview of algorithms and utilities in the @ref MeshTools namespace |
||||||
|
|
||||||
|
@m_keywords{MeshTools} |
||||||
|
|
||||||
|
@tableofcontents |
||||||
|
@m_footernavigation |
||||||
|
|
||||||
|
The @ref MeshTools namespace provides a broad set of tools for transforming, |
||||||
|
filtering, optimizing and merging mesh data, operating both on high-level |
||||||
|
@ref Trade::MeshData instances as well as directly on concrete data views. |
||||||
|
|
||||||
|
@todoc subdivide isn't mentioned because it's useless, neither is |
||||||
|
fullScreenTriangle |
||||||
|
@todoc normal generation and flipping once it's more than "sorry this is shit" |
||||||
|
|
||||||
|
@section meshtools-create Creating MeshData instances from scratch |
||||||
|
|
||||||
|
When it's desirable to create a @ref Trade::MeshData instance from scratch, for |
||||||
|
example from runtime-generated data, @ref MeshTools::interleave(MeshPrimitive, const Trade::MeshIndexData&, Containers::ArrayView<const Trade::MeshAttributeData>) "MeshTools::interleave()" |
||||||
|
is the most convenient way. |
||||||
|
|
||||||
|
@snippet MeshTools.cpp interleave-meshdata |
||||||
|
|
||||||
|
Interleaving isn't the only possible layout option --- you can also |
||||||
|
@ref Trade-MeshData-populating "construct the MeshData instance directly". It's |
||||||
|
more involved, but gives you an ability to have any packing you need. |
||||||
|
|
||||||
|
Note that, however, a @ref Trade::MeshData instance isn't *required* in many |
||||||
|
cases --- most @ref MeshTools algorithms, including for example normal |
||||||
|
generation or duplicate removal, have alternatives that operate directly on |
||||||
|
plain data arrays, and wrapping those in a @ref Trade::MeshData instance just |
||||||
|
to call a function may be a needless complication. On the other hand, having a |
||||||
|
@ref Trade::MeshData instance may be beneficial when it's needed to abstract |
||||||
|
away optional vertex attributes or when the types or layout can be arbitrary. |
||||||
|
The only interface that unconditionally relies on @ref Trade::MeshData is the |
||||||
|
@ref Trade::AbstractSceneConverter, for example when you want to export a mesh |
||||||
|
to a file or when you want to call an external library to perform advanced |
||||||
|
tasks on the data. |
||||||
|
|
||||||
|
@section meshtools-gpu Uploading a mesh to the GPU |
||||||
|
|
||||||
|
The @ref MeshTools::compile() utility creates a @ref GL::Mesh instance out of |
||||||
|
an arbitrary @ref Trade::MeshData, binding builtin attributes listed in the |
||||||
|
@ref Trade::MeshData enum to known locations defined in @ref Shaders::GenericGL. |
||||||
|
|
||||||
|
@snippet Trade.cpp MeshData-gpu-opengl |
||||||
|
|
||||||
|
For more control over index / vertex data storage and handling custom |
||||||
|
attributes, the @ref MeshTools::compile(const Trade::MeshData&, GL::Buffer&, GL::Buffer&) |
||||||
|
overload can be used. Additionally, there's @ref MeshTools::compileLines() with |
||||||
|
specialized handling for line meshes to be rendered with @ref Shaders::LineGL, |
||||||
|
and the @ref MeshTools::compiledPerVertexJointCount() utility gives back a |
||||||
|
count of primary and secondary per-vertex joints for setting up an appropriate |
||||||
|
@ref shaders-usage-skinning "skinning shader". |
||||||
|
|
||||||
|
@section meshtools-optimization Data layout optimization |
||||||
|
|
||||||
|
Mesh import in @ref Trade::AbstractImporter subclasses is commonly done so that |
||||||
|
the input layout is preserved as much as possible, and without performing |
||||||
|
non-essential operations. Similarly, meshes coming from the @ref Primitives |
||||||
|
library preference common, unsurprising formats over the most efficient |
||||||
|
representation. Thus, depending on quality of the input data, the target use |
||||||
|
case and whether given mesh is used for further processing or rendering, there |
||||||
|
are various optimization possibilities. |
||||||
|
|
||||||
|
@subsection meshtools-optimization-interleave Interleaving vertex data |
||||||
|
|
||||||
|
Assuming a mesh is processed vertex-by-vertex with all attributes used, which |
||||||
|
is the common case for both CPU- and GPU-side operation, the best memory layout |
||||||
|
is interleaving the data so that attributes for a particular vertex are next to |
||||||
|
each other in memory. |
||||||
|
|
||||||
|
@snippet MeshTools.cpp meshtools-interleave |
||||||
|
|
||||||
|
Interleaving is however the default behavior in most importers, and most |
||||||
|
@ref MeshTools algorithms produce interleaved layouts by default as well. |
||||||
|
@ref MeshTools::interleave(const Trade::MeshData&, Containers::ArrayView<const Trade::MeshAttributeData>, InterleaveFlags) "MeshTools::interleave()" |
||||||
|
is thus implicitly a passthrough in case the data is already interleaved, so |
||||||
|
it's often desirable to pass a r-value there like shown above, which causes it |
||||||
|
to be just moved through if nothing needs to be done. |
||||||
|
|
||||||
|
For interleaving of raw data arrays with types known at compile time, there's |
||||||
|
@ref MeshTools::interleave(const T&, const U&... next), or its non-allocating |
||||||
|
@ref MeshTools::interleaveInto() variant. If the vertex layout is a concrete |
||||||
|
@cpp struct @ce, another way is for example using |
||||||
|
@relativeref{Corrade,Utility::copy()} to members slices made with |
||||||
|
@relativeref{Corrade,Containers::StridedArrayView::slice(U T::*) const}. |
||||||
|
|
||||||
|
@subsection meshtools-optimization-pack-indices Packing index data |
||||||
|
|
||||||
|
Especially when importing data from text-based formats such as OBJ, index |
||||||
|
buffers have full 32-bit values, which is not always necessary. With |
||||||
|
@ref MeshTools::compressIndices(const Trade::MeshData&, MeshIndexType) "MeshTools::compressIndices()" the index buffer gets reduced to a smaller type. |
||||||
|
Because vertex data are untouched by this operation, it's again desirable to |
||||||
|
pass a r-value in, which causes vertex data to be moved through instead of |
||||||
|
copying: |
||||||
|
|
||||||
|
@snippet MeshTools.cpp meshtools-compressindices |
||||||
|
|
||||||
|
You can also use the same function to unpack already-packed index data back to |
||||||
|
a larger type, by passing an appropriate @ref MeshIndexType as the second |
||||||
|
argument. |
||||||
|
|
||||||
|
Index packing can be also done directly on an index array using |
||||||
|
@ref MeshTools::compressIndices(const Containers::StridedArrayView1D<const UnsignedInt>&, MeshIndexType, Long). |
||||||
|
There's no non-allocating variant in this case, because one would have to do |
||||||
|
another pass over the index array to figure out the target array size first. If |
||||||
|
you want to perform packing to a concrete type, use one of the |
||||||
|
@ref Math::castInto() overloads. |
||||||
|
|
||||||
|
@subsection meshtools-optimization-cache Vertex transform cache optimization |
||||||
|
|
||||||
|
The @ref MeshTools::tipsify() utility reorders the index buffer in a way that |
||||||
|
tries to maximize use of GPU vertex cache, resulting in possibly faster |
||||||
|
rendering. It's however recommended to use the |
||||||
|
@relativeref{Trade,MeshOptimizerSceneConverter} plugin instead if possible. It |
||||||
|
contains a set of state-of-the-art algorithms and by default performs a |
||||||
|
non-destructive sequence of optimizations that make the mesh faster to render |
||||||
|
without affecting appearance in any way. |
||||||
|
|
||||||
|
@snippet MeshTools.cpp meshtools-meshoptimizer |
||||||
|
|
||||||
|
See documentation of @ref Trade::AbstractSceneConverter for more information |
||||||
|
about using the plugin interface. |
||||||
|
|
||||||
|
@m_class{m-note m-success} |
||||||
|
|
||||||
|
@par |
||||||
|
If you're dealing with meshes loaded from files, you can call |
||||||
|
the @ref magnum-sceneconverter "magnum-sceneconverter" utility with |
||||||
|
`-C MeshOptimizerSceneConverter` to perform these optimizations offline. |
||||||
|
|
||||||
|
@section meshtools-index Index buffer generation |
||||||
|
|
||||||
|
A mesh can be non-indexed, meaning that e.g. each three vertices form a |
||||||
|
triangle, it can have an index buffer of an arbitrary type, or it can be formed |
||||||
|
from strips or fans. While such flexibility allows to pick a representation |
||||||
|
that best fits given topology or use case, it can be a burden for algorithms |
||||||
|
that need to work with arbitrary input meshes. The @ref MeshTools::generateIndices() |
||||||
|
helper takes an arbitrary mesh and produces an instance that always has a |
||||||
|
32-bit index buffer and has one of the base primitive types such as |
||||||
|
@ref MeshPrimitive::Triangles, @relativeref{MeshPrimitive,Lines} or |
||||||
|
@relativeref{MeshPrimitive,Points}, so it can then be then passed straight to |
||||||
|
an algorithm that expects indexed primitives. |
||||||
|
|
||||||
|
@snippet MeshTools.cpp meshtools-generateindices |
||||||
|
|
||||||
|
Ultimately, if a non-indexed list of primitives is expected, the mesh can be |
||||||
|
subsequently passed through @ref MeshTools::duplicate(const Trade::MeshData&, Containers::ArrayView<const Trade::MeshAttributeData>) "MeshTools::duplicate()". |
||||||
|
|
||||||
|
Besides the variant taking a @ref Trade::MeshData, there are low-level |
||||||
|
@ref MeshTools::generateLineStripIndices(), |
||||||
|
@relativeref{MeshTools,generateLineLoopIndices()}, |
||||||
|
@relativeref{MeshTools,generateTriangleStripIndices()} and |
||||||
|
@relativeref{MeshTools,generateTriangleFanIndices()} utilities producing an |
||||||
|
index buffer corresponding to a primitive strip, loop or fan. To complete the |
||||||
|
offering, @relativeref{MeshTools,generateTrivialIndices()} outputs a |
||||||
|
@cpp 0, 1, 2, 3, 4, 5, ... @ce sequence if an index buffer needs to be added to |
||||||
|
a mesh that otherwise doesn't need it. For all of these there are |
||||||
|
non-allocating @relativeref{MeshTools,generateLineStripIndicesInto()} etc. |
||||||
|
variants as well. |
||||||
|
|
||||||
|
Finally, @ref MeshTools::generateQuadIndices() / |
||||||
|
@relativeref{MeshTools,generateQuadIndicesInto()} creates a triangle index |
||||||
|
buffer for a list of (indexed) quads. It additionally takes vertex positions to |
||||||
|
favor edges that don't create overlapping or too thin triangles. |
||||||
|
|
||||||
|
@htmlinclude triangulate.svg |
||||||
|
|
||||||
|
@section meshtools-vertex Vertex data transformation |
||||||
|
|
||||||
|
The @ref MeshTools::transform2D(), @relativeref{MeshTools,transform3D()} and |
||||||
|
@relativeref{MeshTools,transformTextureCoordinates2D()} functions can be used |
||||||
|
to bake a transformation into vertex positions, tangent space and texture |
||||||
|
coordinates. One use case is preparing a set of meshes to be joined together, |
||||||
|
baking their transform hierarchy directly into the data, or for example making |
||||||
|
a mesh ready to be used with a renderer that doesn't support passing scaling or |
||||||
|
texture coordinate transformation as a parameter. |
||||||
|
|
||||||
|
@snippet MeshTools.cpp meshtools-transform |
||||||
|
|
||||||
|
If the mesh has the to-be-transformed attributes in a floating-point format, |
||||||
|
i.e. not packed in any way, the function can operate directly on the data |
||||||
|
itself without making a copy. For that reason, if the original unmodified |
||||||
|
instance isn't needed afterwards anymore, it's again useful to pass a r-value |
||||||
|
in, as shown in the snippet. Alternatively, the |
||||||
|
@relativeref{MeshTools,transform2DInPlace()}, |
||||||
|
@relativeref{MeshTools,transform3DInPlace()} and |
||||||
|
@relativeref{MeshTools,transformTextureCoordinates2DInPlace()} variants operate |
||||||
|
in-place, not modifying the attribute layout in any way, but have with |
||||||
|
additional restrictions on the attribute types. |
||||||
|
|
||||||
|
@todoc mention the data overloads, once they're not laughable inline wrappers |
||||||
|
over singular Math APIs |
||||||
|
|
||||||
|
@section meshtools-concatenate Joining multiple meshes together |
||||||
|
|
||||||
|
While models usually contain multiple smaller meshes because it makes editing |
||||||
|
easier, for rendering it's often better to batch them together. The |
||||||
|
@ref MeshTools::concatenate() function concatenates several input meshes into |
||||||
|
a single one. Then, if all the input meshes were using the same material and |
||||||
|
were already in their final transform relative to each other, you can render or |
||||||
|
further process them as a whole: |
||||||
|
|
||||||
|
@snippet MeshTools-gl.cpp meshtools-concatenate |
||||||
|
|
||||||
|
If not, their relative order is preserved in the output, so you can for example |
||||||
|
render each individual piece separately by passing an appropriate index range |
||||||
|
to @ref GL::Mesh::setIndexOffset() and @relativeref{GL::Mesh,setCount()}, |
||||||
|
or by making @ref GL::MeshView instances and rendering those instead: |
||||||
|
|
||||||
|
@snippet MeshTools-gl.cpp meshtools-concatenate-offsets |
||||||
|
|
||||||
|
Meshes joined this way can make use of various rendering optimizations, see |
||||||
|
@ref shaders-usage-multidraw for the shader-side details. There's also a |
||||||
|
@ref MeshTools::concatenateInto() variant that reuses a @ref Trade::MeshData |
||||||
|
instance with previously allocated buffers to support use cases where meshes |
||||||
|
are repeatedly batched on-the-fly. |
||||||
|
|
||||||
|
@m_class{m-note m-success} |
||||||
|
|
||||||
|
@par |
||||||
|
Joining all meshes in a file with scene hierarchy baked in can be also done |
||||||
|
using the `--concatenate-meshes` option of the |
||||||
|
@ref magnum-sceneconverter "magnum-sceneconverter" utility. |
||||||
|
|
||||||
|
@section meshtools-attributes-insert Inserting additional attributes into an existing mesh |
||||||
|
|
||||||
|
The @ref MeshTools::interleave(const Trade::MeshData&, Containers::ArrayView<const Trade::MeshAttributeData>, InterleaveFlags) "MeshTools::interleave()" |
||||||
|
API shown above can be also used to insert additional attributes to an existing |
||||||
|
mesh. The following snippet takes a cube primitive and copies an external |
||||||
|
vertex color attribute alongside existing attributes: |
||||||
|
|
||||||
|
@snippet MeshTools.cpp meshtools-interleave-insert |
||||||
|
|
||||||
|
It's also possible to add just an uninitialized attribute placeholder, |
||||||
|
specifying just the desired type, and copy the data to it later using |
||||||
|
@ref Trade-MeshData-access-mutable "mutable MeshData attribute access": |
||||||
|
|
||||||
|
@m_class{m-console-wrap} |
||||||
|
|
||||||
|
@snippet MeshTools.cpp meshtools-interleave-insert-placeholder |
||||||
|
|
||||||
|
Similar functionality is available also in the above-mentioned |
||||||
|
@ref MeshTools::duplicate(const Trade::MeshData&, Containers::ArrayView<const Trade::MeshAttributeData>) "MeshTools::duplicate()" |
||||||
|
API, with the difference that the attribute gets inserted only after a |
||||||
|
duplication based on an index buffer is performed. This is useful for example |
||||||
|
when it's desirable to add a value that's different for each vertex: |
||||||
|
|
||||||
|
@snippet MeshTools.cpp meshtools-duplicate-insert |
||||||
|
|
||||||
|
In case you need to insert attributes that differ not per vertex but per face, |
||||||
|
@ref MeshTools::combineFaceAttributes(const Trade::MeshData&, Containers::ArrayView<const Trade::MeshAttributeData>) "MeshTools::combineFaceAttributes()" |
||||||
|
can be used: |
||||||
|
|
||||||
|
@snippet MeshTools.cpp combineFaceAttributes |
||||||
|
|
||||||
|
Finally, for scenarios where a mesh has per-attribute index buffers, such as is |
||||||
|
the case when directly importing data from OBJ files, |
||||||
|
@ref MeshTools::combineIndexedAttributes() can be used to combine them into a |
||||||
|
mesh with a single index buffer. |
||||||
|
|
||||||
|
@section meshtools-attributes-filter Filtering mesh attributes |
||||||
|
|
||||||
|
The inverse of attribute insertion is possible with |
||||||
|
@ref MeshTools::filterAttributes(), @relativeref{MeshTools,filterOnlyAttributes()} |
||||||
|
and @relativeref{MeshTools,filterExceptAttributes()}. In this case however, the |
||||||
|
operation affects just the metadata and the result is a *non-owning reference* |
||||||
|
to data in the original mesh with just the attributes that passed the filter. |
||||||
|
In other words, the vertex data stay unchanged, there's just nothing |
||||||
|
referencing the data for attributes that were filtered away. |
||||||
|
|
||||||
|
@snippet MeshTools.cpp meshtools-filter |
||||||
|
|
||||||
|
This avoids a needless copy in cases the result is passed to other algorithms |
||||||
|
that perform further operations on the data. If filtering is the final step, |
||||||
|
pass the result to @ref MeshTools::interleave(const Trade::MeshData&, Containers::ArrayView<const Trade::MeshAttributeData>, InterleaveFlags) "MeshTools::interleave()" |
||||||
|
without @ref MeshTools::InterleaveFlag::PreserveInterleavedAttributes set to |
||||||
|
create a copy that contains only the remaining attributes: |
||||||
|
|
||||||
|
@snippet MeshTools.cpp meshtools-filter-unsparse |
||||||
|
|
||||||
|
@section meshtools-duplicates Duplicate vertex removal, duplication based on an index buffer |
||||||
|
|
||||||
|
The @ref MeshTools::removeDuplicates(const Trade::MeshData&) "MeshTools::removeDuplicates()" |
||||||
|
function returns a @ref Trade::MeshData instance that has contains only unique |
||||||
|
vertices, and has an index buffer that maps them back to their original |
||||||
|
locations. Besides cleaning up messy models the function can be also used for |
||||||
|
converting non-indexed meshes (imported from STL files, for example) to |
||||||
|
indexed. Sometimes bit-exact comparison isn't enough however, and the |
||||||
|
@ref MeshTools::removeDuplicatesFuzzy(const Trade::MeshData&, Float, Double) "MeshTools::removeDuplicatesFuzzy()" |
||||||
|
variant instead applies a fuzzy comparison to all floating-point attributes. |
||||||
|
|
||||||
|
@snippet MeshTools.cpp meshtools-removeduplicates |
||||||
|
|
||||||
|
The fuzzy thresholds are adjustable and setting them to higher values can |
||||||
|
perform rudimentary mesh simplification, but for a robust behavior with higher |
||||||
|
simplification ratios it's recommended to use the |
||||||
|
@relativeref{Trade,MeshOptimizerSceneConverter} simplification feature instead. |
||||||
|
Here for example attempting to reduce the mesh index count by a factor of 10: |
||||||
|
|
||||||
|
@snippet MeshTools.cpp meshtools-meshoptimizer-simplify |
||||||
|
|
||||||
|
<b></b> |
||||||
|
|
||||||
|
@m_class{m-note m-success} |
||||||
|
|
||||||
|
@par |
||||||
|
The @ref magnum-sceneconverter "magnum-sceneconverter" utility can perform |
||||||
|
duplicate vertex removal in all meshes in a file using |
||||||
|
`--remove-duplicate-vertices` or `--remove-duplicate-vertices-fuzzy`. A |
||||||
|
MeshOptimizer simplification equivalent to the above snippet is doable with |
||||||
|
`-C MeshOptimizerSceneConverter -c simplify,simplifyTargetIndexCountThreshold=0.1`. |
||||||
|
|
||||||
|
Internally, the duplicate vertex removal is implemented using |
||||||
|
@ref MeshTools::removeDuplicatesInPlace(const Containers::StridedArrayView2D<char>&), |
||||||
|
@ref MeshTools::removeDuplicatesFuzzyInPlace(const Containers::StridedArrayView2D<Float>&, Float) |
||||||
|
and their non-in-place, and non-allocating `*Into()` variants. These functions |
||||||
|
return an index array that maps from the original data to the deduplicated |
||||||
|
locations. Commonly, the index array eventually becomes an index buffer of the |
||||||
|
resulting mesh, or one uses the @ref MeshTools::removeDuplicatesIndexedInPlace() |
||||||
|
/ @ref MeshTools::removeDuplicatesFuzzyIndexedInPlace() variants if there's an |
||||||
|
existing buffer in the first place. |
||||||
|
|
||||||
|
Another use case for the index buffer returned by these functions is to perform |
||||||
|
an operation of on the deduplicated data and then apply the updates back to the |
||||||
|
original. One such scenario is for example with soft body simulation, where a |
||||||
|
simulation engine requires a watertight indexed mesh containing only positions. |
||||||
|
Rendering however usually needs at least normals as well, and potentially |
||||||
|
texture coordinates, vertex colors and others. Assuming an indexed mesh with |
||||||
|
arbitrary attributes, a watertight mesh consisting of just indexed positions |
||||||
|
would be made like this: |
||||||
|
|
||||||
|
@snippet MeshTools.cpp meshtools-removeduplicates-position-only |
||||||
|
|
||||||
|
@todoc Fix duplicate / duplicateInto to not need those massive casts, ffs |
||||||
|
|
||||||
|
The `positionIndices` are made in two steps instead of using |
||||||
|
@ref MeshTools::removeDuplicatesIndexedInPlace() because the `indexMapping`, |
||||||
|
without being mixed with the original index buffer, needs to be preserved for a |
||||||
|
later use. Once a simulation updates the positions, they get copied back to the |
||||||
|
original mesh: |
||||||
|
|
||||||
|
@snippet MeshTools.cpp meshtools-removeduplicates-position-only-copy |
||||||
|
|
||||||
|
@section meshtools-bounding-volume Bounding volume calculation |
||||||
|
|
||||||
|
The @ref MeshTools::boundingRange() and |
||||||
|
@ref MeshTools::boundingSphereBouncingBubble() utilities can be used to |
||||||
|
calculate a bounding volume for a given list of vertex positions, for example |
||||||
|
to use for culling. Because their output is just a single value, they take a |
||||||
|
position view directly and don't have any convenience variant operating on a |
||||||
|
@ref Trade::MeshData. The most straightforward way is to pass |
||||||
|
@ref Trade::MeshData::positions3DAsArray() to them, see the |
||||||
|
@ref Trade-MeshData-access "MeshData data access documentation" for more |
||||||
|
details and alternative approaches that don't allocate a temporary array. |
||||||
|
|
||||||
|
@section meshtools-helpers Memory ownership helpers |
||||||
|
|
||||||
|
Much like all other heavier data structures in Magnum, a @ref Trade::MeshData |
||||||
|
is move-only, to prevent accidental copies when passing it around. If a copy is |
||||||
|
desirable and it cannot be made as a side effect of some other operation, |
||||||
|
@ref MeshTools::copy() can be used. |
||||||
|
|
||||||
|
Another use case for it is creating a self-contained @ref Trade::MeshData |
||||||
|
instance --- in some cases, such as for example with @ref Primitives::cubeSolid() |
||||||
|
or with memory-mapped files, you may get back a @ref Trade::MeshData that |
||||||
|
references external data, instead of owning them, to avoid unnecessary copies. |
||||||
|
Such instances usually cannot be modified in-place and one has to ensure that |
||||||
|
the memoory they reference stays in scope. Calling @ref MeshTools::copy() on |
||||||
|
such an instance makes a self-contained copy that owns the data and can be |
||||||
|
modified. For example, turning the cube primitive into a skybox with normals |
||||||
|
flipped inwards: |
||||||
|
|
||||||
|
@snippet MeshTools.cpp meshtools-copy |
||||||
|
|
||||||
|
An inverse to @ref MeshTools::copy() is @ref MeshTools::reference(). It makes a |
||||||
|
non-owning reference to data contained in another @ref Trade::MeshData. It's |
||||||
|
mainly useful for tool internals, for example to implement common handling for |
||||||
|
@cpp const Trade::MeshData& @ce and @cpp Trade::MeshData& @ce. |
||||||
|
|
||||||
|
*/ |
||||||
|
} |
||||||
Loading…
Reference in new issue