|
|
#ifndef Magnum_Trade_MeshData_h |
|
|
#define Magnum_Trade_MeshData_h |
|
|
/* |
|
|
This file is part of Magnum. |
|
|
|
|
|
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019 |
|
|
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. |
|
|
*/ |
|
|
|
|
|
/** @file |
|
|
* @brief Class @ref Magnum::Trade::MeshData, @ref Magnum::Trade::MeshIndexData, @ref Magnum::Trade::MeshAttributeData, enum @ref Magnum::Trade::MeshAttribute, function @ref Magnum::Trade::isMeshAttributeCustom(), @ref Magnum::Trade::meshAttributeCustom() |
|
|
* @m_since_latest |
|
|
*/ |
|
|
|
|
|
#include <Corrade/Containers/Array.h> |
|
|
#include <Corrade/Containers/StridedArrayView.h> |
|
|
|
|
|
#include "Magnum/Mesh.h" |
|
|
#include "Magnum/VertexFormat.h" |
|
|
#include "Magnum/Trade/Data.h" |
|
|
#include "Magnum/Trade/Trade.h" |
|
|
|
|
|
namespace Magnum { namespace Trade { |
|
|
|
|
|
/** |
|
|
@brief Mesh attribute name |
|
|
@m_since_latest |
|
|
|
|
|
@see @ref MeshData, @ref MeshAttributeData, @ref VertexFormat, |
|
|
@ref AbstractImporter::meshAttributeForName(), |
|
|
@ref AbstractImporter::meshAttributeName() |
|
|
*/ |
|
|
/* 16 bits because 8 bits is not enough to cover all potential per-edge, |
|
|
per-face, per-instance and per-meshlet attributes */ |
|
|
enum class MeshAttribute: UnsignedShort { |
|
|
/* 0 reserved for an invalid value (returned from |
|
|
AbstractImporter::meshAttributeForName()) */ |
|
|
|
|
|
/** |
|
|
* Position. Type is usually @ref VertexFormat::Vector2 for 2D and |
|
|
* @ref VertexFormat::Vector3 for 3D, but can be also any of |
|
|
* @ref VertexFormat::Vector2h, @ref VertexFormat::Vector3h, |
|
|
* @ref VertexFormat::Vector2ub, @ref VertexFormat::Vector2ubNormalized, |
|
|
* @ref VertexFormat::Vector2b, @ref VertexFormat::Vector2bNormalized, |
|
|
* @ref VertexFormat::Vector2us, @ref VertexFormat::Vector2usNormalized, |
|
|
* @ref VertexFormat::Vector2s, @ref VertexFormat::Vector2sNormalized, |
|
|
* @ref VertexFormat::Vector3ub, @ref VertexFormat::Vector3ubNormalized, |
|
|
* @ref VertexFormat::Vector3b, @ref VertexFormat::Vector3bNormalized, |
|
|
* @ref VertexFormat::Vector3us, @ref VertexFormat::Vector3usNormalized, |
|
|
* @ref VertexFormat::Vector3s or @ref VertexFormat::Vector3sNormalized. |
|
|
* Corresponds to @ref Shaders::Generic::Position. |
|
|
* @see @ref MeshData::positions2DAsArray(), |
|
|
* @ref MeshData::positions3DAsArray() |
|
|
*/ |
|
|
Position = 1, |
|
|
|
|
|
/** |
|
|
* Tangent, optionally including bitangent sign. In the first case the type |
|
|
* is usually @ref VertexFormat::Vector3, but can be also |
|
|
* @ref VertexFormat::Vector3h, @ref VertexFormat::Vector3bNormalized or |
|
|
* @ref VertexFormat::Vector3sNormalized; in the second case the type is |
|
|
* @ref VertexFormat::Vector4 (or @ref VertexFormat::Vector4h, |
|
|
* @ref VertexFormat::Vector4bNormalized, |
|
|
* @ref VertexFormat::Vector4sNormalized) and the fourth component is a |
|
|
* sign value (@cpp -1.0f @ce or @cpp +1.0f @ce) defining handedness of the |
|
|
* tangent basis. Reconstruct the @ref MeshAttribute::Bitangent can be then |
|
|
* done like this: |
|
|
* |
|
|
* @snippet MagnumTrade.cpp MeshAttribute-bitangent-from-tangent |
|
|
* |
|
|
* Corresponds to @ref Shaders::Generic::Tangent. |
|
|
* @see @ref MeshData::tangentsAsArray(), |
|
|
* @ref MeshData::bitangentSignsAsArray() |
|
|
*/ |
|
|
Tangent, |
|
|
|
|
|
/** |
|
|
* Bitangent. Type is usually @ref VertexFormat::Vector3, but can be also |
|
|
* @ref VertexFormat::Vector3h, @ref VertexFormat::Vector3bNormalized or |
|
|
* @ref VertexFormat::Vector3sNormalized. For better storage efficiency, |
|
|
* the bitangent can be also reconstructed from the normal and tangent, see |
|
|
* @ref MeshAttribute::Tangent for more information. |
|
|
* @see @ref MeshData::bitangentsAsArray() |
|
|
*/ |
|
|
Bitangent, |
|
|
|
|
|
/** |
|
|
* Normal. Type is usually @ref VertexFormat::Vector3, but can be also |
|
|
* @ref VertexFormat::Vector3h. @ref VertexFormat::Vector3bNormalized or |
|
|
* @ref VertexFormat::Vector3sNormalized. Corresponds to |
|
|
* @ref Shaders::Generic::Normal. |
|
|
* @see @ref MeshData::normalsAsArray() |
|
|
*/ |
|
|
Normal, |
|
|
|
|
|
/** |
|
|
* Texture coordinates. Type is usually @ref VertexFormat::Vector2 for |
|
|
* 2D coordinates, but can be also any of @ref VertexFormat::Vector2h, |
|
|
* @ref VertexFormat::Vector2ub, @ref VertexFormat::Vector2ubNormalized, |
|
|
* @ref VertexFormat::Vector2b, @ref VertexFormat::Vector2bNormalized, |
|
|
* @ref VertexFormat::Vector2us, @ref VertexFormat::Vector2usNormalized, |
|
|
* @ref VertexFormat::Vector2s or @ref VertexFormat::Vector2sNormalized. |
|
|
* Corresponds to @ref Shaders::Generic::TextureCoordinates. |
|
|
* @see @ref MeshData::textureCoordinates2DAsArray() |
|
|
*/ |
|
|
TextureCoordinates, |
|
|
|
|
|
/** |
|
|
* Vertex color. Type is usually @ref VertexFormat::Vector3 or |
|
|
* @ref VertexFormat::Vector4, but can be also any of |
|
|
* @ref VertexFormat::Vector3h, @ref VertexFormat::Vector4h, |
|
|
* @ref VertexFormat::Vector3ubNormalized, |
|
|
* @ref VertexFormat::Vector3usNormalized, |
|
|
* @ref VertexFormat::Vector4ubNormalized or |
|
|
* @ref VertexFormat::Vector4usNormalized. Corresponds to |
|
|
* @ref Shaders::Generic::Color3 or @ref Shaders::Generic::Color4. |
|
|
* @see @ref MeshData::colorsAsArray() |
|
|
*/ |
|
|
Color, |
|
|
|
|
|
/** |
|
|
* This and all higher values are for importer-specific attributes. Can be |
|
|
* of any type. See documentation of a particular importer for details. |
|
|
* @see @ref isMeshAttributeCustom(MeshAttribute) |
|
|
* @ref meshAttributeCustom(MeshAttribute), |
|
|
* @ref meshAttributeCustom(UnsignedShort) |
|
|
*/ |
|
|
Custom = 32768 |
|
|
}; |
|
|
|
|
|
/** |
|
|
@debugoperatorenum{MeshAttribute} |
|
|
@m_since_latest |
|
|
*/ |
|
|
MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, MeshAttribute value); |
|
|
|
|
|
/** |
|
|
@brief Whether a mesh attribute is custom |
|
|
@m_since_latest |
|
|
|
|
|
Returns @cpp true @ce if @p name has a value larger or equal to |
|
|
@ref MeshAttribute::Custom, @cpp false @ce otherwise. |
|
|
@see @ref meshAttributeCustom(UnsignedShort), |
|
|
@ref meshAttributeCustom(MeshAttribute) |
|
|
*/ |
|
|
constexpr bool isMeshAttributeCustom(MeshAttribute name) { |
|
|
return UnsignedShort(name) >= UnsignedShort(MeshAttribute::Custom); |
|
|
} |
|
|
|
|
|
/** |
|
|
@brief Create a custom mesh attribute |
|
|
@m_since_latest |
|
|
|
|
|
Returns a custom mesh attribute with index @p id. The index is expected to be |
|
|
less than the value of @ref MeshAttribute::Custom. Use |
|
|
@ref meshAttributeCustom(MeshAttribute) to get the index back. |
|
|
*/ |
|
|
/* Constexpr so it's usable for creating compile-time MeshAttributeData |
|
|
instances */ |
|
|
constexpr MeshAttribute meshAttributeCustom(UnsignedShort id) { |
|
|
return CORRADE_CONSTEXPR_ASSERT(id < UnsignedShort(MeshAttribute::Custom), |
|
|
"Trade::meshAttributeCustom(): index" << id << "too large"), |
|
|
MeshAttribute(UnsignedShort(MeshAttribute::Custom) + id); |
|
|
} |
|
|
|
|
|
/** |
|
|
@brief Get index of a custom mesh attribute |
|
|
@m_since_latest |
|
|
|
|
|
Inverse to @ref meshAttributeCustom(UnsignedShort). Expects that the attribute |
|
|
is custom. |
|
|
@see @ref isMeshAttributeCustom() |
|
|
*/ |
|
|
constexpr UnsignedShort meshAttributeCustom(MeshAttribute name) { |
|
|
return CORRADE_CONSTEXPR_ASSERT(isMeshAttributeCustom(name), |
|
|
"Trade::meshAttributeCustom():" << name << "is not custom"), |
|
|
UnsignedShort(name) - UnsignedShort(MeshAttribute::Custom); |
|
|
} |
|
|
|
|
|
/** |
|
|
@brief Mesh index data |
|
|
@m_since_latest |
|
|
|
|
|
Convenience type for populating @ref MeshData, see its documentation for an |
|
|
introduction. |
|
|
|
|
|
@section Trade-MeshIndexData-usage Usage |
|
|
|
|
|
The most straightforward usage is constructing the instance from a view on the |
|
|
index array. The @ref MeshIndexType gets inferred from the view type: |
|
|
|
|
|
@snippet MagnumTrade.cpp MeshIndexData-usage |
|
|
|
|
|
Alternatively, you can pass a typeless @cpp const void @ce view and supply |
|
|
@ref MeshIndexType explicitly, or a contiguous 2D view and let the class detect |
|
|
the actual index type from second dimension size. Note that the class accepts |
|
|
only contiguous views and not @ref Corrade::Containers::StridedArrayView, |
|
|
following limitations of GPU index buffers that also have to be contiguous. |
|
|
@see @ref MeshAttributeData |
|
|
*/ |
|
|
class MAGNUM_TRADE_EXPORT MeshIndexData { |
|
|
public: |
|
|
/** @brief Construct for a non-indexed mesh */ |
|
|
explicit MeshIndexData(std::nullptr_t = nullptr) noexcept: _type{} {} |
|
|
|
|
|
/** |
|
|
* @brief Construct with a runtime-specified index type |
|
|
* @param type Index type |
|
|
* @param data Index data |
|
|
* |
|
|
* The @p data size is expected to correspond to given @p type (e.g., |
|
|
* for @ref MeshIndexType::UnsignedInt the @p data array size should |
|
|
* be divisible by 4). If you know the @p type at compile time, you can |
|
|
* use one of the @ref MeshIndexData(Containers::ArrayView<const UnsignedByte>), |
|
|
* @ref MeshIndexData(Containers::ArrayView<const UnsignedShort>) or |
|
|
* @ref MeshIndexData(Containers::ArrayView<const UnsignedInt>) |
|
|
* constructors, which infer the index type automatically. |
|
|
* |
|
|
* If @p data is empty, the mesh will be treated as indexed but with |
|
|
* zero indices. To create a non-indexed mesh, use the |
|
|
* @ref MeshIndexData(std::nullptr_t) constructor. |
|
|
*/ |
|
|
explicit MeshIndexData(MeshIndexType type, Containers::ArrayView<const void> data) noexcept; |
|
|
|
|
|
/** @brief Construct with unsigned byte indices */ |
|
|
constexpr explicit MeshIndexData(Containers::ArrayView<const UnsignedByte> data) noexcept: _type{MeshIndexType::UnsignedByte}, _data{data} {} |
|
|
|
|
|
/** @brief Construct with unsigned short indices */ |
|
|
constexpr explicit MeshIndexData(Containers::ArrayView<const UnsignedShort> data) noexcept: _type{MeshIndexType::UnsignedShort}, _data{data} {} |
|
|
|
|
|
/** @brief Construct with unsigned int indices */ |
|
|
constexpr explicit MeshIndexData(Containers::ArrayView<const UnsignedInt> data) noexcept: _type{MeshIndexType::UnsignedInt}, _data{data} {} |
|
|
|
|
|
/** |
|
|
* @brief Constructor |
|
|
* |
|
|
* Expects that @p data is contiguous and size of the second dimension |
|
|
* is either 1, 2 or 4, corresponding to one of the @ref MeshIndexType |
|
|
* values. |
|
|
*/ |
|
|
explicit MeshIndexData(const Containers::StridedArrayView2D<const char>& data) noexcept; |
|
|
|
|
|
/** @brief Index type */ |
|
|
constexpr MeshIndexType type() const { return _type; } |
|
|
|
|
|
/** @brief Type-erased index data */ |
|
|
constexpr Containers::ArrayView<const void> data() const { return _data; } |
|
|
|
|
|
private: |
|
|
friend MeshData; |
|
|
MeshIndexType _type; |
|
|
/* Void so the constructors can be constexpr */ |
|
|
Containers::ArrayView<const void> _data; |
|
|
}; |
|
|
|
|
|
/** |
|
|
@brief Mesh attribute data |
|
|
@m_since_latest |
|
|
|
|
|
Convenience type for populating @ref MeshData, see its documentation for an |
|
|
introduction. Additionally usable in various @ref MeshTools algorithms such as |
|
|
@ref MeshTools::duplicate(const Trade::MeshData& data, Containers::ArrayView<const Trade::MeshAttributeData>) |
|
|
or @ref MeshTools::interleave(const Trade::MeshData& data, Containers::ArrayView<const Trade::MeshAttributeData>). |
|
|
|
|
|
@section Trade-MeshAttributeData-usage Usage |
|
|
|
|
|
The most straightforward usage is constructing an instance from a pair of |
|
|
@ref MeshAttribute and a strided view. The @ref VertexFormat gets inferred from |
|
|
the view type: |
|
|
|
|
|
@snippet MagnumTrade.cpp MeshAttributeData-usage |
|
|
|
|
|
Alternatively, you can pass a typeless @cpp const void @ce view and supply |
|
|
@ref VertexFormat explicitly, or a 2D view. |
|
|
|
|
|
@subsection Trade-MeshAttributeData-usage-offset-only Offset-only attribute data |
|
|
|
|
|
If the actual attribute data location is not known yet, the instance can be |
|
|
created as "offset-only", meaning the actual view gets created only later when |
|
|
passed to a @ref MeshData instance with a concrete vertex data array. This is |
|
|
useful for example when vertex layout is static (and thus can be defined at |
|
|
compile time), but the actual data is allocated / populated at runtime: |
|
|
|
|
|
@snippet MagnumTrade.cpp MeshAttributeData-usage-offset-only |
|
|
|
|
|
Note that @ref MeshTools algorithms generally don't accept offset-only |
|
|
@ref MeshAttributeData instances except when passed through a @ref MeshData |
|
|
instance. |
|
|
|
|
|
@section Trade-MeshAttributeData-custom-vertex-format Custom vertex formats |
|
|
|
|
|
Apart from custom @ref MeshAttribute names, shown in |
|
|
@ref Trade-MeshData-populating-custom, @ref VertexFormat can be extended with |
|
|
implementation-specific formats as well. Formats that don't have a generic |
|
|
@ref VertexFormat equivalent can be created using @ref vertexFormatWrap(), |
|
|
however note that most APIs and @ref MeshTools functions can't work with those |
|
|
as their size or contents can't be known: |
|
|
|
|
|
@snippet MagnumTrade.cpp MeshAttributeData-custom-vertex-format |
|
|
|
|
|
@see @ref MeshIndexData |
|
|
*/ |
|
|
class MAGNUM_TRADE_EXPORT MeshAttributeData { |
|
|
public: |
|
|
/** |
|
|
* @brief Default constructor |
|
|
* |
|
|
* Leaves contents at unspecified values. Provided as a convenience for |
|
|
* initialization of the attribute array for @ref MeshData, expected to |
|
|
* be replaced with concrete values later. |
|
|
*/ |
|
|
constexpr explicit MeshAttributeData() noexcept: _data{}, _vertexCount{}, _format{}, _stride{}, _name{}, _arraySize{}, _isOffsetOnly{false} {} |
|
|
|
|
|
/** |
|
|
* @brief Type-erased constructor |
|
|
* @param name Attribute name |
|
|
* @param format Vertex format |
|
|
* @param data Attribute data |
|
|
* |
|
|
* Expects that @p data stride is large enough to fit @p type and that |
|
|
* @p type corresponds to @p name. |
|
|
*/ |
|
|
explicit MeshAttributeData(MeshAttribute name, VertexFormat format, const Containers::StridedArrayView1D<const void>& data) noexcept: MeshAttributeData{name, format, 0, data} {} |
|
|
|
|
|
/** |
|
|
* @brief Type-erased constructor for an array attribute |
|
|
* @param name Attribute name |
|
|
* @param format Vertex format |
|
|
* @param arraySize Array size |
|
|
* @param data Attribute data |
|
|
* |
|
|
* Expects that @p data stride is large enough to fit @p type, @p type |
|
|
* corresponds to @p name and @p arraySize is zero for builtin |
|
|
* attributes. Passing @cpp 0 @ce to @p arraySize is equivalent to |
|
|
* calling the above overload. |
|
|
*/ |
|
|
explicit MeshAttributeData(MeshAttribute name, VertexFormat format, UnsignedShort arraySize, const Containers::StridedArrayView1D<const void>& data) noexcept; |
|
|
|
|
|
/** |
|
|
* @brief Constructor |
|
|
* @param name Attribute name |
|
|
* @param format Vertex format |
|
|
* @param data Attribute data |
|
|
* |
|
|
* Expects that the second dimension of @p data is contiguous and its |
|
|
* size matches @p type; and that @p type corresponds to @p name. |
|
|
*/ |
|
|
explicit MeshAttributeData(MeshAttribute name, VertexFormat format, const Containers::StridedArrayView2D<const char>& data) noexcept: MeshAttributeData{name, format, 0, data} {} |
|
|
|
|
|
/** @overload */ |
|
|
explicit MeshAttributeData(MeshAttribute name, VertexFormat format, std::nullptr_t) noexcept: MeshAttributeData{name, format, 0, nullptr, nullptr} {} |
|
|
|
|
|
/** |
|
|
* @brief Construct an array attribute |
|
|
* @param name Attribute name |
|
|
* @param format Vertex format |
|
|
* @param arraySize Array size |
|
|
* @param data Attribute data |
|
|
* |
|
|
* Expects that the second dimension of @p data is contiguous and its |
|
|
* size matches @p type and @p arraSize, that @p type corresponds to |
|
|
* @p name and @p arraySize is zero for builtin attributes. Passing |
|
|
* @cpp 0 @ce to @p arraySize is equivalent to calling the above |
|
|
* overload. |
|
|
*/ |
|
|
explicit MeshAttributeData(MeshAttribute name, VertexFormat format, UnsignedShort arraySize, const Containers::StridedArrayView2D<const char>& data) noexcept; |
|
|
|
|
|
/** |
|
|
* @brief Constructor |
|
|
* @param name Attribute name |
|
|
* @param data Attribute data |
|
|
* |
|
|
* Detects @ref VertexFormat based on @p T and calls |
|
|
* @ref MeshAttributeData(MeshAttribute, VertexFormat, const Containers::StridedArrayView1D<const void>&). |
|
|
* For most types known by Magnum, the detected @ref VertexFormat is of |
|
|
* the same name as the type (so e.g. @ref Magnum::Vector3ui "Vector3ui" |
|
|
* gets recognized as @ref VertexFormat::Vector3ui), with the |
|
|
* following exceptions: |
|
|
* |
|
|
* - @ref Color3ub is recognized as |
|
|
* @ref VertexFormat::Vector3ubNormalized |
|
|
* - @ref Color3us is recognized as |
|
|
* @ref VertexFormat::Vector3usNormalized |
|
|
* - @ref Color4ub is recognized as |
|
|
* @ref VertexFormat::Vector4ubNormalized |
|
|
* - @ref Color4us is recognized as |
|
|
* @ref VertexFormat::Vector4usNormalized |
|
|
* - @ref Matrix2x2b is recognized as |
|
|
* @ref VertexFormat::Matrix2x2bNormalized (and similarly for |
|
|
* other matrix sizes) |
|
|
* - @ref Matrix2x2s is recognized as |
|
|
* @ref VertexFormat::Matrix2x2sNormalized (and similarly for |
|
|
* other matrix sizes) |
|
|
* |
|
|
* This also means that if you have a @ref Magnum::Vector2s "Vector2s", |
|
|
* for example, and want to pick a |
|
|
* @ref VertexFormat::Vector2sNormalized instead of the |
|
|
* (autodetected) @ref VertexFormat::Vector2s, you need to specify |
|
|
* it explicitly --- there's no way the library can infer this from the |
|
|
* type alone, except for the color types above (which are generally |
|
|
* always normalized). |
|
|
*/ |
|
|
template<class T> constexpr explicit MeshAttributeData(MeshAttribute name, const Containers::StridedArrayView1D<T>& data) noexcept; |
|
|
|
|
|
/** @overload */ |
|
|
template<class T> constexpr explicit MeshAttributeData(MeshAttribute name, const Containers::ArrayView<T>& data) noexcept: MeshAttributeData{name, Containers::stridedArrayView(data)} {} |
|
|
|
|
|
/** |
|
|
* @brief Construct an array attribute |
|
|
* @param name Attribute name |
|
|
* @param data Attribute data |
|
|
* |
|
|
* Detects @ref VertexFormat based on @p T and calls |
|
|
* @ref MeshAttributeData(MeshAttribute, VertexFormat, UnsignedShort, const Containers::StridedArrayView1D<const void>&) |
|
|
* with the second dimension size passed to @p arraySize. Expects that |
|
|
* the second dimension is contiguous. At the moment only custom |
|
|
* attributes can be arrays, which means this function can't be used |
|
|
* with a builtin @p name. See @ref MeshAttributeData(MeshAttribute, const Containers::StridedArrayView1D<T>&) |
|
|
* for details about @ref VertexFormat detection. |
|
|
*/ |
|
|
template<class T> constexpr explicit MeshAttributeData(MeshAttribute name, const Containers::StridedArrayView2D<T>& data) noexcept; |
|
|
|
|
|
/** |
|
|
* @brief Construct an offset-only attribute |
|
|
* @param name Attribute name |
|
|
* @param format Attribute format |
|
|
* @param offset Attribute data offset |
|
|
* @param vertexCount Attribute vertex count |
|
|
* @param stride Attribute stride |
|
|
* @param arraySize Array size. Use @cpp 0 @ce for non-array |
|
|
* attributes. |
|
|
* |
|
|
* Instances created this way refer to an offset in unspecified |
|
|
* external vertex data instead of containing the data view directly. |
|
|
* Useful when the location of the vertex data array is not known at |
|
|
* attribute construction time. Expects that @p arraySize is zero for |
|
|
* builtin attributes. Note that instances created this way can't be |
|
|
* used in most @ref MeshTools algorithms. |
|
|
* |
|
|
* Additionally, for even more flexibility, the @p vertexCount can be |
|
|
* overriden at @ref MeshData construction time, however all attributes |
|
|
* are still required to have the same vertex count to catch accidents. |
|
|
* @see @ref isOffsetOnly(), @ref arraySize(), |
|
|
* @ref data(Containers::ArrayView<const void>) const |
|
|
*/ |
|
|
explicit constexpr MeshAttributeData(MeshAttribute name, VertexFormat format, std::size_t offset, UnsignedInt vertexCount, std::ptrdiff_t stride, UnsignedShort arraySize = 0) noexcept; |
|
|
|
|
|
/** |
|
|
* @brief Construct a pad value |
|
|
* |
|
|
* Usable in various @ref MeshTools algorithms to insert padding |
|
|
* between interleaved attributes. Negative values can be used to alias |
|
|
* multiple different attributes onto each other. Not meant to be |
|
|
* passed to @ref MeshData. |
|
|
* @see @ref stride() |
|
|
*/ |
|
|
constexpr explicit MeshAttributeData(Int padding): _data{nullptr}, _vertexCount{0}, _format{}, _stride{ |
|
|
(CORRADE_CONSTEXPR_ASSERT(padding >= -32768 && padding <= 32767, |
|
|
"Trade::MeshAttributeData: at most 32k padding supported, got" << padding), Short(padding)) |
|
|
}, _name{}, _arraySize{}, _isOffsetOnly{false} {} |
|
|
|
|
|
/** |
|
|
* @brief If the attribute is offset-only |
|
|
* |
|
|
* Returns @cpp true @ce if the attribute doesn't contain the data view |
|
|
* directly, but instead refers to unspecified external vertex data. |
|
|
* @see @ref data(Containers::ArrayView<const void>) const, |
|
|
* @ref MeshAttributeData(MeshAttribute, VertexFormat, std::size_t, UnsignedInt, std::ptrdiff_t, UnsignedShort) |
|
|
*/ |
|
|
constexpr bool isOffsetOnly() const { return _isOffsetOnly; } |
|
|
|
|
|
/** @brief Attribute name */ |
|
|
constexpr MeshAttribute name() const { return _name; } |
|
|
|
|
|
/** @brief Attribute format */ |
|
|
constexpr VertexFormat format() const { return _format; } |
|
|
|
|
|
/** |
|
|
* @brief Attribute offset |
|
|
* |
|
|
* If the attribute is offset-only, returns the offset directly, |
|
|
* otherwise uses the @p vertexData parameter to calculate the offset. |
|
|
* @see @ref isOffsetOnly() |
|
|
*/ |
|
|
std::size_t offset(Containers::ArrayView<const void> vertexData) const { |
|
|
return _isOffsetOnly ? _data.offset : reinterpret_cast<const char*>(_data.pointer) - reinterpret_cast<const char*>(vertexData.data()); |
|
|
} |
|
|
|
|
|
/** |
|
|
* @brief Attribute stride |
|
|
* |
|
|
* Can be negative for pad values, never negative for real attributes. |
|
|
* @see @ref MeshAttributeData(Int) |
|
|
*/ |
|
|
constexpr Short stride() const { return _stride; } |
|
|
|
|
|
/** @brief Attribute array size */ |
|
|
constexpr UnsignedShort arraySize() const { return _arraySize; } |
|
|
|
|
|
/** |
|
|
* @brief Type-erased attribute data |
|
|
* |
|
|
* Expects that the attribute is not offset-only, in that case use the |
|
|
* @ref data(Containers::ArrayView<const void>) const overload |
|
|
* instead. |
|
|
* @see @ref isOffsetOnly() |
|
|
*/ |
|
|
constexpr Containers::StridedArrayView1D<const void> data() const { |
|
|
return Containers::StridedArrayView1D<const void>{ |
|
|
/* We're *sure* the view is correct, so faking the view size */ |
|
|
/** @todo better ideas for the StridedArrayView API? */ |
|
|
{_data.pointer, ~std::size_t{}}, _vertexCount, |
|
|
(CORRADE_CONSTEXPR_ASSERT(!_isOffsetOnly, "Trade::MeshAttributeData::data(): the attribute is a relative offset, supply a data array"), _stride)}; |
|
|
} |
|
|
|
|
|
/** |
|
|
* @brief Type-erased attribute data for an offset-only attribute |
|
|
* |
|
|
* If the attribute is not offset-only, the @p vertexData parameter is |
|
|
* ignored. |
|
|
* @see @ref isOffsetOnly(), @ref data() const |
|
|
*/ |
|
|
Containers::StridedArrayView1D<const void> data(Containers::ArrayView<const void> vertexData) const { |
|
|
return Containers::StridedArrayView1D<const void>{ |
|
|
/* We're *sure* the view is correct, so faking the view size */ |
|
|
/** @todo better ideas for the StridedArrayView API? */ |
|
|
vertexData, _isOffsetOnly ? reinterpret_cast<const char*>(vertexData.data()) + _data.offset : _data.pointer, _vertexCount, _stride}; |
|
|
} |
|
|
|
|
|
private: |
|
|
constexpr explicit MeshAttributeData(MeshAttribute name, VertexFormat format, UnsignedShort arraySize, const Containers::StridedArrayView1D<const void>& data, std::nullptr_t) noexcept; |
|
|
|
|
|
friend MeshData; |
|
|
union Data { |
|
|
/* FFS C++ why this doesn't JUST WORK goddamit?! */ |
|
|
constexpr Data(const void* pointer = nullptr): pointer{pointer} {} |
|
|
constexpr Data(std::size_t offset): offset{offset} {} |
|
|
|
|
|
const void* pointer; |
|
|
std::size_t offset; |
|
|
} _data; |
|
|
/* Vertex count in MeshData is currently 32-bit, so this doesn't need |
|
|
to be 64-bit either */ |
|
|
UnsignedInt _vertexCount; |
|
|
VertexFormat _format; |
|
|
/* According to https://opengl.gpuinfo.org/displaycapability.php?name=GL_MAX_VERTEX_ATTRIB_STRIDE, |
|
|
current largest reported stride is 4k so 32k should be enough */ |
|
|
Short _stride; |
|
|
MeshAttribute _name; |
|
|
UnsignedShort _arraySize; |
|
|
bool _isOffsetOnly; |
|
|
/* 1 byte free for more stuff on 64b (23, aligned to 24) and on 32b |
|
|
(19, aligned to 20) */ |
|
|
}; |
|
|
|
|
|
/** @relatesalso MeshAttributeData |
|
|
@brief Create a non-owning array of @ref MeshAttributeData items |
|
|
@m_since_latest |
|
|
|
|
|
Useful when you have the attribute definitions statically defined (for example |
|
|
when the vertex data themselves are already defined at compile time) and don't |
|
|
want to allocate just to pass those to @ref MeshData. |
|
|
*/ |
|
|
Containers::Array<MeshAttributeData> MAGNUM_TRADE_EXPORT meshAttributeDataNonOwningArray(Containers::ArrayView<const MeshAttributeData> view); |
|
|
|
|
|
/** |
|
|
@brief Mesh data |
|
|
@m_since_latest |
|
|
|
|
|
Provides access to mesh vertex and index data, together with additional |
|
|
information such as primitive type. Populated instances of this class are |
|
|
returned from @ref AbstractImporter::mesh() and from particular functions in |
|
|
the @ref Primitives library. |
|
|
|
|
|
@section Trade-MeshData-usage Basic usage |
|
|
|
|
|
The simplest usage is through the convenience functions @ref positions2DAsArray(), |
|
|
@ref positions3DAsArray(), @ref tangentsAsArray(), @ref bitangentsAsArray(), |
|
|
@ref normalsAsArray(), @ref tangentsAsArray(), @ref textureCoordinates2DAsArray() |
|
|
and @ref colorsAsArray(). Each of these takes an index (as there can be |
|
|
multiple sets of texture coordinates, for example) and you're expected to check |
|
|
for attribute presence first with either @ref hasAttribute() or |
|
|
@ref attributeCount(MeshAttribute) const: |
|
|
|
|
|
@snippet MagnumTrade.cpp MeshData-usage |
|
|
|
|
|
@section Trade-MeshData-usage-advanced Advanced usage |
|
|
|
|
|
The @ref positions2DAsArray(), ... functions shown above always return a |
|
|
newly-allocated @ref Corrade::Containers::Array instance with a clearly defined |
|
|
type that's large enough to represent most data. While that's fine for many use |
|
|
cases, sometimes you may want to minimize the import time of a large model or |
|
|
the imported data may be already in a well-optimized layout and format that you |
|
|
want to preserve. The @ref MeshData class internally stores a contiguous blob |
|
|
of data, which you can directly upload, and then use provided metadata to let |
|
|
the GPU know of the format and layout. Because there's a lot of possible types |
|
|
of each attribute (floats, packed integers, ...), the @ref GL::DynamicAttribute |
|
|
can accept a pair of @ref GL::Attribute defined by the shader and the actual |
|
|
@ref VertexFormat, figuring out all properties such as component count and |
|
|
element data type without having to explicitly handle all relevant types: |
|
|
|
|
|
@snippet MagnumTrade.cpp MeshData-usage-advanced |
|
|
|
|
|
@section Trade-MeshData-usage-compile Using MeshTools::compile() |
|
|
|
|
|
For a quick yet efficient way to upload all data and configure a mesh for all |
|
|
known attributes that are present, @ref MeshTools::compile() can be used. |
|
|
Compared to the above, it's just an oneliner: |
|
|
|
|
|
@snippet MagnumTrade.cpp MeshData-usage-compile |
|
|
|
|
|
Compared to configuring the mesh manually you may lose a bit of flexibility, |
|
|
especially when you need to set up custom attributes or modify the data after. |
|
|
See @ref MeshTools::compile(const Trade::MeshData&, GL::Buffer&, GL::Buffer&) |
|
|
for a possible solution. |
|
|
|
|
|
@section Trade-MeshData-usage-mutable Mutable data access |
|
|
|
|
|
The interfaces implicitly provide @cpp const @ce views on the contained index |
|
|
and vertex data through the @ref indexData(), @ref vertexData(), |
|
|
@ref indices() and @ref attribute() accessors. This is done because in general |
|
|
case the data can also refer to a memory-mapped file or constant memory. In |
|
|
cases when it's desirable to modify the data in-place, there's the |
|
|
@ref mutableIndexData(), @ref mutableVertexData(), @ref mutableIndices() and |
|
|
@ref mutableAttribute() set of functions. To use these, you need to check that |
|
|
the data are mutable using @ref indexDataFlags() or @ref vertexDataFlags() |
|
|
first. The following snippet applies a transformation to the mesh data: |
|
|
|
|
|
@snippet MagnumTrade.cpp MeshData-usage-mutable |
|
|
|
|
|
@section Trade-MeshData-populating Populating an instance |
|
|
|
|
|
A @ref MeshData instance by default takes over the ownership of an |
|
|
@ref Corrade::Containers::Array containing the vertex / index data together |
|
|
with a @ref MeshIndexData instance and a list of @ref MeshAttributeData |
|
|
describing various index and vertex properties. For example, an interleaved |
|
|
indexed mesh with 3D positions and RGBA colors would look like this --- |
|
|
and variants with just vertex data or just index data or neither are possible |
|
|
too: |
|
|
|
|
|
@snippet MagnumTrade.cpp MeshData-populating |
|
|
|
|
|
In cases where you want the @ref MeshData instance to only refer to external |
|
|
data without taking ownership (for example in a memory-mapped file, constant |
|
|
memory etc., instead of moving in an @ref Corrade::Containers::Array you pass |
|
|
@ref DataFlags describing if the data is mutable or not together with an |
|
|
@ref Corrade::Containers::ArrayView. A variant of the above where the index |
|
|
data is constant and vertex data mutable, both referenced externally: |
|
|
|
|
|
@snippet MagnumTrade.cpp MeshData-populating-non-owned |
|
|
|
|
|
@subsection Trade-MeshData-populating-custom Custom mesh attributes |
|
|
|
|
|
To allow for greater flexibility, a @ref MeshData instance can describe not |
|
|
just attributes that are predefined in the @ref MeshAttribute enum, but also |
|
|
custom attributes, created with @ref meshAttributeCustom(). For example, the |
|
|
snippet below describes a custom per-face structure that exposes faces as |
|
|
higher-order polygons combining multiple triangles together ---in this case, |
|
|
each face has an array of 15 IDs, which is exposed as a 2D array: |
|
|
|
|
|
@snippet MagnumTrade.cpp MeshData-populating-custom |
|
|
|
|
|
Later, the (array) attributes can be retrieved back using the same custom |
|
|
identifiers --- note the use of @cpp [] @ce to get back a 2D array again: |
|
|
|
|
|
@snippet MagnumTrade.cpp MeshData-populating-custom-retrieve |
|
|
|
|
|
When a custom attribute is exposed through @ref AbstractImporter, it's possible |
|
|
to map custom @ref MeshAttribute values to human-readable string names using |
|
|
@ref AbstractImporter::meshAttributeName() and |
|
|
@ref AbstractImporter::meshAttributeForName(). Using @ref meshPrimitiveWrap() |
|
|
you can also supply implementation-specific values that are not available in |
|
|
the generic @ref MeshPrimitive enum, similarly see also |
|
|
@ref Trade-MeshAttributeData-custom-vertex-format for details on |
|
|
implementation-specific @ref VertexFormat values. |
|
|
@see @ref AbstractImporter::mesh() |
|
|
*/ |
|
|
class MAGNUM_TRADE_EXPORT MeshData { |
|
|
public: |
|
|
enum: UnsignedInt { |
|
|
/** |
|
|
* Implicit vertex count. When passed to a constructor, indicates |
|
|
* that vertex count should be taken from attribute data views. |
|
|
*/ |
|
|
ImplicitVertexCount = ~UnsignedInt{} |
|
|
}; |
|
|
|
|
|
/** |
|
|
* @brief Construct an indexed mesh data |
|
|
* @param primitive Primitive |
|
|
* @param indexData Index data |
|
|
* @param indices Index data description |
|
|
* @param vertexData Vertex data |
|
|
* @param attributes Description of all vertex attribute data |
|
|
* @param vertexCount Vertex count. If set to |
|
|
* @ref ImplicitVertexCount, vertex count is taken from data views |
|
|
* passed to @p attributes (in which case there has to be at least |
|
|
* one). |
|
|
* @param importerState Importer-specific state |
|
|
* |
|
|
* The @p indices are expected to point to a sub-range of @p indexData. |
|
|
* The @p attributes are expected to reference (sparse) sub-ranges of |
|
|
* @p vertexData. If the mesh has no attributes, the @p indices are |
|
|
* expected to be valid (but can be empty). If you want to create an |
|
|
* attribute-less non-indexed mesh, use |
|
|
* @ref MeshData(MeshPrimitive, UnsignedInt, const void*) to specify |
|
|
* desired vertex count. |
|
|
* |
|
|
* The @ref indexDataFlags() / @ref vertexDataFlags() are implicitly |
|
|
* set to a combination of @ref DataFlag::Owned and |
|
|
* @ref DataFlag::Mutable. For non-owned data use the |
|
|
* @ref MeshData(MeshPrimitive, DataFlags, Containers::ArrayView<const void>, const MeshIndexData&, DataFlags, Containers::ArrayView<const void>, Containers::Array<MeshAttributeData>&&, UnsignedInt, const void*) |
|
|
* constructor or its variants instead. |
|
|
*/ |
|
|
explicit MeshData(MeshPrimitive primitive, Containers::Array<char>&& indexData, const MeshIndexData& indices, Containers::Array<char>&& vertexData, Containers::Array<MeshAttributeData>&& attributes, UnsignedInt vertexCount = ImplicitVertexCount, const void* importerState = nullptr) noexcept; |
|
|
|
|
|
/** @overload */ |
|
|
/* Not noexcept because allocation happens inside */ |
|
|
explicit MeshData(MeshPrimitive primitive, Containers::Array<char>&& indexData, const MeshIndexData& indices, Containers::Array<char>&& vertexData, std::initializer_list<MeshAttributeData> attributes, UnsignedInt vertexCount = ImplicitVertexCount, const void* importerState = nullptr); |
|
|
|
|
|
/** |
|
|
* @brief Construct indexed mesh data with non-owned index and vertex data |
|
|
* @param primitive Primitive |
|
|
* @param indexDataFlags Index data flags |
|
|
* @param indexData View on index data |
|
|
* @param indices Index data description |
|
|
* @param vertexDataFlags Vertex data flags |
|
|
* @param vertexData View on vertex data |
|
|
* @param attributes Description of all vertex attribute data |
|
|
* @param vertexCount Vertex count. If set to |
|
|
* @ref ImplicitVertexCount, vertex count is taken from data views |
|
|
* passed to @p attributes (in which case there has to be at least |
|
|
* one). |
|
|
* @param importerState Importer-specific state |
|
|
* |
|
|
* Compared to @ref MeshData(MeshPrimitive, Containers::Array<char>&&, const MeshIndexData&, Containers::Array<char>&&, Containers::Array<MeshAttributeData>&&, UnsignedInt, const void*) |
|
|
* creates an instance that doesn't own the passed vertex and index |
|
|
* data. The @p indexDataFlags / @p vertexDataFlags parameters can |
|
|
* contain @ref DataFlag::Mutable to indicate the external data can be |
|
|
* modified, and is expected to *not* have @ref DataFlag::Owned set. |
|
|
*/ |
|
|
explicit MeshData(MeshPrimitive primitive, DataFlags indexDataFlags, Containers::ArrayView<const void> indexData, const MeshIndexData& indices, DataFlags vertexDataFlags, Containers::ArrayView<const void> vertexData, Containers::Array<MeshAttributeData>&& attributes, UnsignedInt vertexCount = ImplicitVertexCount, const void* importerState = nullptr) noexcept; |
|
|
|
|
|
/** @overload */ |
|
|
/* Not noexcept because allocation happens inside */ |
|
|
explicit MeshData(MeshPrimitive primitive, DataFlags indexDataFlags, Containers::ArrayView<const void> indexData, const MeshIndexData& indices, DataFlags vertexDataFlags, Containers::ArrayView<const void> vertexData, std::initializer_list<MeshAttributeData> attributes, UnsignedInt vertexCount = ImplicitVertexCount, const void* importerState = nullptr); |
|
|
|
|
|
/** |
|
|
* @brief Construct indexed mesh data with non-owned index data |
|
|
* @param primitive Primitive |
|
|
* @param indexDataFlags Index data flags |
|
|
* @param indexData View on index data |
|
|
* @param indices Index data description |
|
|
* @param vertexData Vertex data |
|
|
* @param attributes Description of all vertex attribute data |
|
|
* @param vertexCount Vertex count. If set to |
|
|
* @ref ImplicitVertexCount, vertex count is taken from data views |
|
|
* passed to @p attributes (in which case there has to be at least |
|
|
* one). |
|
|
* @param importerState Importer-specific state |
|
|
* |
|
|
* Compared to @ref MeshData(MeshPrimitive, Containers::Array<char>&&, const MeshIndexData&, Containers::Array<char>&&, Containers::Array<MeshAttributeData>&&, UnsignedInt, const void*) |
|
|
* creates an instance that doesn't own the passed index data. The |
|
|
* @p indexDataFlags parameter can contain @ref DataFlag::Mutable to |
|
|
* indicate the external data can be modified, and is expected to *not* |
|
|
* have @ref DataFlag::Owned set. The @ref vertexDataFlags() are |
|
|
* implicitly set to a combination of @ref DataFlag::Owned and |
|
|
* @ref DataFlag::Mutable. |
|
|
*/ |
|
|
explicit MeshData(MeshPrimitive primitive, DataFlags indexDataFlags, Containers::ArrayView<const void> indexData, const MeshIndexData& indices, Containers::Array<char>&& vertexData, Containers::Array<MeshAttributeData>&& attributes, UnsignedInt vertexCount = ImplicitVertexCount, const void* importerState = nullptr) noexcept; |
|
|
|
|
|
/** @overload */ |
|
|
/* Not noexcept because allocation happens inside */ |
|
|
explicit MeshData(MeshPrimitive primitive, DataFlags indexDataFlags, Containers::ArrayView<const void> indexData, const MeshIndexData& indices, Containers::Array<char>&& vertexData, std::initializer_list<MeshAttributeData> attributes, UnsignedInt vertexCount = ImplicitVertexCount, const void* importerState = nullptr); |
|
|
|
|
|
/** |
|
|
* @brief Construct indexed mesh data with non-owned vertex data |
|
|
* @param primitive Primitive |
|
|
* @param indexData Index data |
|
|
* @param indices Index data description |
|
|
* @param vertexDataFlags Vertex data flags |
|
|
* @param vertexData View on vertex data |
|
|
* @param attributes Description of all vertex attribute data |
|
|
* @param vertexCount Vertex count. If set to |
|
|
* @ref ImplicitVertexCount, vertex count is taken from data views |
|
|
* passed to @p attributes (in which case there has to be at least |
|
|
* one). |
|
|
* @param importerState Importer-specific state |
|
|
* |
|
|
* Compared to @ref MeshData(MeshPrimitive, Containers::Array<char>&&, const MeshIndexData&, Containers::Array<char>&&, Containers::Array<MeshAttributeData>&&, UnsignedInt, const void*) |
|
|
* creates an instance that doesn't own the passed vertex data. The |
|
|
* @p vertexDataFlags parameter can contain @ref DataFlag::Mutable to |
|
|
* indicate the external data can be modified, and is expected to *not* |
|
|
* have @ref DataFlag::Owned set. The @ref indexDataFlags() are |
|
|
* implicitly set to a combination of @ref DataFlag::Owned and |
|
|
* @ref DataFlag::Mutable. |
|
|
*/ |
|
|
explicit MeshData(MeshPrimitive primitive, Containers::Array<char>&& indexData, const MeshIndexData& indices, DataFlags vertexDataFlags, Containers::ArrayView<const void> vertexData, Containers::Array<MeshAttributeData>&& attributes, UnsignedInt vertexCount = ImplicitVertexCount, const void* importerState = nullptr) noexcept; |
|
|
|
|
|
/** @overload */ |
|
|
/* Not noexcept because allocation happens inside */ |
|
|
explicit MeshData(MeshPrimitive primitive, Containers::Array<char>&& indexData, const MeshIndexData& indices, DataFlags vertexDataFlags, Containers::ArrayView<const void> vertexData, std::initializer_list<MeshAttributeData> attributes, UnsignedInt vertexCount = ImplicitVertexCount, const void* importerState = nullptr); |
|
|
|
|
|
/** |
|
|
* @brief Construct a non-indexed mesh data |
|
|
* @param primitive Primitive |
|
|
* @param vertexData Vertex data |
|
|
* @param attributes Description of all vertex attribute data |
|
|
* @param vertexCount Vertex count. If set to |
|
|
* @ref ImplicitVertexCount, vertex count is taken from data views |
|
|
* passed to @p attributes (in which case there has to be at least |
|
|
* one). |
|
|
* @param importerState Importer-specific state |
|
|
* |
|
|
* Same as calling @ref MeshData(MeshPrimitive, Containers::Array<char>&&, const MeshIndexData&, Containers::Array<char>&&, Containers::Array<MeshAttributeData>&&, UnsignedInt, const void*) |
|
|
* with default-constructed @p indexData and @p indices arguments. |
|
|
* |
|
|
* The @ref vertexDataFlags() are implicitly set to a combination of |
|
|
* @ref DataFlag::Owned and @ref DataFlag::Mutable. For consistency, |
|
|
* the @ref indexDataFlags() are implicitly set to a combination of |
|
|
* @ref DataFlag::Owned and @ref DataFlag::Mutable, even though there |
|
|
* isn't any data to own or to mutate. For non-owned data use the |
|
|
* @ref MeshData(MeshPrimitive, DataFlags, Containers::ArrayView<const void>, Containers::Array<MeshAttributeData>&&, UnsignedInt, const void*) |
|
|
* constructor instead. |
|
|
*/ |
|
|
explicit MeshData(MeshPrimitive primitive, Containers::Array<char>&& vertexData, Containers::Array<MeshAttributeData>&& attributes, UnsignedInt vertexCount = ImplicitVertexCount, const void* importerState = nullptr) noexcept; |
|
|
|
|
|
/** @overload */ |
|
|
/* Not noexcept because allocation happens inside */ |
|
|
explicit MeshData(MeshPrimitive primitive, Containers::Array<char>&& vertexData, std::initializer_list<MeshAttributeData> attributes, UnsignedInt vertexCount = ImplicitVertexCount, const void* importerState = nullptr); |
|
|
|
|
|
/** |
|
|
* @brief Construct a non-owned non-indexed mesh data |
|
|
* @param primitive Primitive |
|
|
* @param vertexDataFlags Vertex data flags |
|
|
* @param vertexData View on vertex data |
|
|
* @param attributes Description of all vertex attribute data |
|
|
* @param vertexCount Vertex count. If set to |
|
|
* @ref ImplicitVertexCount, vertex count is taken from data views |
|
|
* passed to @p attributes (in which case there has to be at least |
|
|
* one). |
|
|
* @param importerState Importer-specific state |
|
|
* |
|
|
* Compared to @ref MeshData(MeshPrimitive, Containers::Array<char>&&, Containers::Array<MeshAttributeData>&&, UnsignedInt, const void*) |
|
|
* creates an instance that doesn't own the passed data. The |
|
|
* @p vertexDataFlags parameter can contain @ref DataFlag::Mutable to |
|
|
* indicate the external data can be modified, and is expected to *not* |
|
|
* have @ref DataFlag::Owned set. For consistency, the |
|
|
* @ref indexDataFlags() are implicitly set to a combination of |
|
|
* @ref DataFlag::Owned and @ref DataFlag::Mutable, even though there |
|
|
* isn't any data to own or to mutate. |
|
|
*/ |
|
|
explicit MeshData(MeshPrimitive primitive, DataFlags vertexDataFlags, Containers::ArrayView<const void> vertexData, Containers::Array<MeshAttributeData>&& attributes, UnsignedInt vertexCount = ImplicitVertexCount, const void* importerState = nullptr) noexcept; |
|
|
|
|
|
/** @overload */ |
|
|
/* Not noexcept because allocation happens inside */ |
|
|
explicit MeshData(MeshPrimitive primitive, DataFlags vertexDataFlags, Containers::ArrayView<const void> vertexData, std::initializer_list<MeshAttributeData> attributes, UnsignedInt vertexCount = ImplicitVertexCount, const void* importerState = nullptr); |
|
|
|
|
|
/** |
|
|
* @brief Construct an attribute-less indexed mesh data |
|
|
* @param primitive Primitive |
|
|
* @param indexData Index data |
|
|
* @param indices Index data description |
|
|
* @param vertexCount Vertex count. Passing @ref ImplicitVertexCount |
|
|
* is not allowed in this overload. |
|
|
* @param importerState Importer-specific state |
|
|
* |
|
|
* Same as calling @ref MeshData(MeshPrimitive, Containers::Array<char>&&, const MeshIndexData&, Containers::Array<char>&&, Containers::Array<MeshAttributeData>&&, UnsignedInt, const void*) |
|
|
* with default-constructed @p vertexData and @p attributes arguments. |
|
|
* The @p indices are expected to be valid (but can be empty). If you |
|
|
* want to create an attribute-less non-indexed mesh, use |
|
|
* @ref MeshData(MeshPrimitive, UnsignedInt, const void*) to specify |
|
|
* desired vertex count. |
|
|
* |
|
|
* The @ref indexDataFlags() are implicitly set to a combination of |
|
|
* @ref DataFlag::Owned and @ref DataFlag::Mutable. For consistency, |
|
|
* the @ref vertexDataFlags() are implicitly set to a combination of |
|
|
* @ref DataFlag::Owned and @ref DataFlag::Mutable, even though there |
|
|
* isn't any data to own or to mutate. For non-owned data use the |
|
|
* @ref MeshData(MeshPrimitive, DataFlags, Containers::ArrayView<const void>, const MeshIndexData&, UnsignedInt, const void*) |
|
|
* constructor instead. |
|
|
*/ |
|
|
explicit MeshData(MeshPrimitive primitive, Containers::Array<char>&& indexData, const MeshIndexData& indices, UnsignedInt vertexCount, const void* importerState = nullptr) noexcept; |
|
|
|
|
|
/** |
|
|
* @brief Construct a non-owned attribute-less indexed mesh data |
|
|
* @param primitive Primitive |
|
|
* @param indexDataFlags Index data flags |
|
|
* @param indexData View on index data |
|
|
* @param indices Index data description |
|
|
* @param vertexCount Vertex count. Passing |
|
|
* @ref ImplicitVertexCount is not allowed in this overload. |
|
|
* @param importerState Importer-specific state |
|
|
* |
|
|
* Compared to @ref MeshData(MeshPrimitive, Containers::Array<char>&&, const MeshIndexData&, UnsignedInt, const void*) |
|
|
* creates an instance that doesn't own the passed data. The |
|
|
* @p indexDataFlags parameter can contain @ref DataFlag::Mutable to |
|
|
* indicate the external data can be modified, and is expected to *not* |
|
|
* have @ref DataFlag::Owned set. For consistency, the |
|
|
* @ref vertexDataFlags() are implicitly set to a combination of |
|
|
* @ref DataFlag::Owned and @ref DataFlag::Mutable, even though there |
|
|
* isn't any data to own or to mutate. |
|
|
*/ |
|
|
explicit MeshData(MeshPrimitive primitive, DataFlags indexDataFlags, Containers::ArrayView<const void> indexData, const MeshIndexData& indices, UnsignedInt vertexCount, const void* importerState = nullptr) noexcept; |
|
|
|
|
|
/** |
|
|
* @brief Construct an index-less attribute-less mesh data |
|
|
* @param primitive Primitive |
|
|
* @param vertexCount Vertex count. Passing @ref ImplicitVertexCount |
|
|
* is not allowed in this overload. |
|
|
* @param importerState Importer-specific state |
|
|
* |
|
|
* Useful in case the drawing is fully driven by a shader. For |
|
|
* consistency, the @ref indexDataFlags() / @ref vertexDataFlags() are |
|
|
* implicitly set to a combination of @ref DataFlag::Owned and |
|
|
* @ref DataFlag::Mutable, even though there isn't any data to own or |
|
|
* to mutate. |
|
|
*/ |
|
|
explicit MeshData(MeshPrimitive primitive, UnsignedInt vertexCount, const void* importerState = nullptr) noexcept; |
|
|
|
|
|
~MeshData(); |
|
|
|
|
|
/** @brief Copying is not allowed */ |
|
|
MeshData(const MeshData&) = delete; |
|
|
|
|
|
/** @brief Move constructor */ |
|
|
MeshData(MeshData&&) noexcept; |
|
|
|
|
|
/** @brief Copying is not allowed */ |
|
|
MeshData& operator=(const MeshData&) = delete; |
|
|
|
|
|
/** @brief Move assignment */ |
|
|
MeshData& operator=(MeshData&&) noexcept; |
|
|
|
|
|
/** |
|
|
* @brief Index data flags |
|
|
* |
|
|
* @see @ref releaseIndexData(), @ref mutableIndexData(), |
|
|
* @ref mutableIndices() |
|
|
*/ |
|
|
DataFlags indexDataFlags() const { return _indexDataFlags; } |
|
|
|
|
|
/** |
|
|
* @brief Vertex data flags |
|
|
* |
|
|
* @see @ref releaseVertexData(), @ref mutableVertexData(), |
|
|
* @ref mutableAttribute() |
|
|
*/ |
|
|
DataFlags vertexDataFlags() const { return _vertexDataFlags; } |
|
|
|
|
|
/** @brief Primitive */ |
|
|
MeshPrimitive primitive() const { return _primitive; } |
|
|
|
|
|
/** |
|
|
* @brief Raw index data |
|
|
* |
|
|
* Returns @cpp nullptr @ce if the mesh is not indexed. |
|
|
* @see @ref isIndexed(), @ref indexCount(), @ref indexType(), |
|
|
* @ref indices(), @ref mutableIndexData(), @ref releaseIndexData() |
|
|
*/ |
|
|
Containers::ArrayView<const char> indexData() const & { return _indexData; } |
|
|
|
|
|
/** @brief Taking a view to a r-value instance is not allowed */ |
|
|
Containers::ArrayView<const char> indexData() const && = delete; |
|
|
|
|
|
/** |
|
|
* @brief Mutable raw index data |
|
|
* |
|
|
* Like @ref indexData(), but returns a non-const view. Expects that |
|
|
* the mesh is mutable. |
|
|
* @see @ref indexDataFlags() |
|
|
*/ |
|
|
Containers::ArrayView<char> mutableIndexData() &; |
|
|
|
|
|
/** @brief Taking a view to a r-value instance is not allowed */ |
|
|
Containers::ArrayView<char> mutableIndexData() && = delete; |
|
|
|
|
|
/** |
|
|
* @brief Raw attribute metadata |
|
|
* |
|
|
* Returns the raw data that are used as a base for all `attribute*()` |
|
|
* accessors, or @cpp nullptr @ce if the mesh has no attributes. In |
|
|
* most cases you don't want to access those directly, but rather use |
|
|
* the @ref attribute(), @ref attributeName(), @ref attributeFormat(), |
|
|
* @ref attributeOffset(), @ref attributeStride() etc. accessors. |
|
|
* Compared to those and to @ref attributeData(UnsignedInt) const, the |
|
|
* @ref MeshAttributeData instances returned by this function may have |
|
|
* different data pointers, and some of them might be offset-only --- |
|
|
* use this function only if you *really* know what are you doing. |
|
|
* @see @ref MeshAttributeData::isOffsetOnly() |
|
|
*/ |
|
|
Containers::ArrayView<const MeshAttributeData> attributeData() const & { return _attributes; } |
|
|
|
|
|
/** @brief Taking a view to a r-value instance is not allowed */ |
|
|
Containers::ArrayView<const MeshAttributeData> attributeData() && = delete; |
|
|
|
|
|
/** |
|
|
* @brief Raw vertex data |
|
|
* |
|
|
* Contains data for all vertex attributes. Returns @cpp nullptr @ce if |
|
|
* the mesh has no attributes. |
|
|
* @see @ref attributeCount(), @ref attributeName(), |
|
|
* @ref attributeFormat(), @ref attribute(), |
|
|
* @ref mutableVertexData(), @ref releaseVertexData() |
|
|
*/ |
|
|
Containers::ArrayView<const char> vertexData() const & { return _vertexData; } |
|
|
|
|
|
/** @brief Taking a view to a r-value instance is not allowed */ |
|
|
Containers::ArrayView<const char> vertexData() const && = delete; |
|
|
|
|
|
/** |
|
|
* @brief Mutable raw vertex data |
|
|
* |
|
|
* Like @ref vertexData(), but returns a non-const view. Expects that |
|
|
* the mesh is mutable. |
|
|
* @see @ref vertexDataFlags() |
|
|
*/ |
|
|
Containers::ArrayView<char> mutableVertexData() &; |
|
|
|
|
|
/** @brief Taking a view to a r-value instance is not allowed */ |
|
|
Containers::ArrayView<char> mutableVertexData() && = delete; |
|
|
|
|
|
/** @brief Whether the mesh is indexed */ |
|
|
bool isIndexed() const { return _indexType != MeshIndexType{}; } |
|
|
|
|
|
/** |
|
|
* @brief Index count |
|
|
* |
|
|
* Count of elements in the @ref indices() array. Expects that the |
|
|
* mesh is indexed; returned value is always non-zero. See also |
|
|
* @ref vertexCount() which returns count of elements in every |
|
|
* @ref attribute() array, and @ref attributeCount() which returns |
|
|
* count of different per-vertex attribute arrays. |
|
|
* @see @ref isIndexed(), @ref indexType() |
|
|
*/ |
|
|
UnsignedInt indexCount() const; |
|
|
|
|
|
/** |
|
|
* @brief Index type |
|
|
* |
|
|
* Expects that the mesh is indexed. |
|
|
* @see @ref isIndexed(), @ref attributeFormat() |
|
|
*/ |
|
|
MeshIndexType indexType() const; |
|
|
|
|
|
/** |
|
|
* @brief Index offset |
|
|
* |
|
|
* Byte offset of the first index from the beginning of the |
|
|
* @ref indexData(), or a byte difference between pointers returned |
|
|
* from @ref indexData() and @ref indices(). Expects that the mesh is |
|
|
* indexed. |
|
|
* @see @ref attributeOffset() |
|
|
*/ |
|
|
std::size_t indexOffset() const; |
|
|
|
|
|
/** |
|
|
* @brief Mesh indices |
|
|
* |
|
|
* The view is guaranteed to be contiguous and its second dimension |
|
|
* represents the actual data type (its size is equal to type size). |
|
|
* Use the templated overload below to get the indices in a concrete |
|
|
* type. |
|
|
* @see @ref Corrade::Containers::StridedArrayView::isContiguous() |
|
|
*/ |
|
|
Containers::StridedArrayView2D<const char> indices() const; |
|
|
|
|
|
/** |
|
|
* @brief Mutable mesh indices |
|
|
* |
|
|
* Like @ref indices() const, but returns a mutable view. Expects that |
|
|
* the mesh is mutable. |
|
|
* @see @ref indexDataFlags() |
|
|
*/ |
|
|
Containers::StridedArrayView2D<char> mutableIndices(); |
|
|
|
|
|
/** |
|
|
* @brief Mesh indices in a concrete type |
|
|
* |
|
|
* Expects that the mesh is indexed and that @p T corresponds to |
|
|
* @ref indexType(). You can also use the non-templated |
|
|
* @ref indicesAsArray() accessor to get indices converted to 32-bit, |
|
|
* but note that such operation involves extra allocation and data |
|
|
* conversion. |
|
|
* @see @ref isIndexed(), @ref attribute(), @ref mutableIndices() |
|
|
*/ |
|
|
template<class T> Containers::ArrayView<const T> indices() const; |
|
|
|
|
|
/** |
|
|
* @brief Mutable mesh indices in a concrete type |
|
|
* |
|
|
* Like @ref indices() const, but returns a mutable view. Expects that |
|
|
* the mesh is mutable. |
|
|
* @see @ref indexDataFlags() |
|
|
*/ |
|
|
template<class T> Containers::ArrayView<T> mutableIndices(); |
|
|
|
|
|
/** |
|
|
* @brief Mesh vertex count |
|
|
* |
|
|
* Count of elements in every attribute array returned by |
|
|
* @ref attribute() (or, in case of an attribute-less mesh, the |
|
|
* desired vertex count). See also @ref indexCount() which returns |
|
|
* count of elements in the @ref indices() array, and |
|
|
* @ref attributeCount() which returns count of different per-vertex |
|
|
* attribute arrays. |
|
|
*/ |
|
|
UnsignedInt vertexCount() const { return _vertexCount; } |
|
|
|
|
|
/** |
|
|
* @brief Attribute array count |
|
|
* |
|
|
* Count of different per-vertex attribute arrays, or @cpp 0 @ce for an |
|
|
* attribute-less mesh. See also @ref indexCount() which returns count |
|
|
* of elements in the @ref indices() array and @ref vertexCount() which |
|
|
* returns count of elements in every @ref attribute() array. |
|
|
* @see @ref attributeCount(MeshAttribute) const |
|
|
*/ |
|
|
UnsignedInt attributeCount() const { return _attributes.size(); } |
|
|
|
|
|
/** |
|
|
* @brief Raw attribute data |
|
|
* |
|
|
* Returns the raw data that are used as a base for all `attribute*()` |
|
|
* accessors. In most cases you don't want to access those directly, |
|
|
* but rather use the @ref attribute(), @ref attributeName(), |
|
|
* @ref attributeFormat(), @ref attributeOffset(), |
|
|
* @ref attributeStride() etc. accessors. |
|
|
* |
|
|
* Useful mainly for passing particular attributes unchanged directly |
|
|
* to @ref MeshTools algorithms --- unlike with @ref attributeData() |
|
|
* and @ref releaseAttributeData(), returned instances are guaranteed |
|
|
* to always have an absolute data pointer (i.e., |
|
|
* @ref MeshAttributeData::isOffsetOnly() always returning |
|
|
* @cpp false @ce). The @p id is expected to be smaller than |
|
|
* @ref attributeCount() const. |
|
|
* @see @ref MeshTools::interleavedData() |
|
|
*/ |
|
|
MeshAttributeData attributeData(UnsignedInt id) const; |
|
|
|
|
|
/** |
|
|
* @brief Attribute name |
|
|
* |
|
|
* The @p id is expected to be smaller than @ref attributeCount() const. |
|
|
* @see @ref attributeFormat(), @ref isMeshAttributeCustom(), |
|
|
* @ref AbstractImporter::meshAttributeForName(), |
|
|
* @ref AbstractImporter::meshAttributeName() |
|
|
*/ |
|
|
MeshAttribute attributeName(UnsignedInt id) const; |
|
|
|
|
|
/** |
|
|
* @brief Attribute format |
|
|
* |
|
|
* The @p id is expected to be smaller than @ref attributeCount() const. |
|
|
* You can also use @ref attributeFormat(MeshAttribute, UnsignedInt) const |
|
|
* to directly get a type of given named attribute. |
|
|
* @see @ref attributeName(), @ref indexType() |
|
|
*/ |
|
|
VertexFormat attributeFormat(UnsignedInt id) const; |
|
|
|
|
|
/** |
|
|
* @brief Attribute offset |
|
|
* |
|
|
* Byte offset of the first element of given attribute from the |
|
|
* beginning of the @ref vertexData() array, or a byte difference |
|
|
* between pointers returned from @ref vertexData() and a particular |
|
|
* @ref attribute(). The @p id is expected to be smaller than |
|
|
* @ref attributeCount() const. You can also use |
|
|
* @ref attributeOffset(MeshAttribute, UnsignedInt) const to |
|
|
* directly get an offset of given named attribute. |
|
|
* @see @ref indexOffset(), @ref MeshTools::isInterleaved() |
|
|
*/ |
|
|
std::size_t attributeOffset(UnsignedInt id) const; |
|
|
|
|
|
/** |
|
|
* @brief Attribute stride |
|
|
* |
|
|
* Stride between consecutive elements of given attribute in the |
|
|
* @ref vertexData() array. The @p id is expected to be smaller |
|
|
* than @ref attributeCount() const. You can also use |
|
|
* @ref attributeStride(MeshAttribute, UnsignedInt) const to |
|
|
* directly get a stride of given named attribute. |
|
|
* @see @ref MeshTools::isInterleaved() |
|
|
*/ |
|
|
UnsignedInt attributeStride(UnsignedInt id) const; |
|
|
|
|
|
/** |
|
|
* @brief Attribute array size |
|
|
* |
|
|
* In case given attribute is an array (the equivalent of e.g. |
|
|
* @cpp int[30] @ce), returns array size, otherwise returns @cpp 0 @ce. |
|
|
* At the moment only custom attributes can be arrays, no builtin |
|
|
* @ref MeshAttribute is an array attribute. You can also use |
|
|
* @ref attributeArraySize(MeshAttribute, UnsignedInt) const to |
|
|
* directly get array size of given named attribute. |
|
|
* |
|
|
* Note that this is different from vertex count, which is exposed |
|
|
* through @ref vertexCount(), and is an orthogonal concept to having |
|
|
* multiple attributes of the same name (for example two sets of |
|
|
* texture coordinates), which is exposed through |
|
|
* @ref attributeCount(MeshAttribute) const. See |
|
|
* @ref Trade-MeshData-populating-custom for an example. |
|
|
* @see @ref isMeshAttributeCustom() |
|
|
*/ |
|
|
UnsignedShort attributeArraySize(UnsignedInt id) const; |
|
|
|
|
|
/** |
|
|
* @brief Whether the mesh has given attribute |
|
|
* |
|
|
* @see @ref attributeCount(MeshAttribute) const |
|
|
*/ |
|
|
bool hasAttribute(MeshAttribute name) const { |
|
|
return attributeCount(name); |
|
|
} |
|
|
|
|
|
/** |
|
|
* @brief Count of given named attribute |
|
|
* |
|
|
* Unlike @ref attributeCount() const this returns count for given |
|
|
* attribute name --- for example a mesh can have more than one set of |
|
|
* texture coordinates. |
|
|
* @see @ref hasAttribute() |
|
|
*/ |
|
|
UnsignedInt attributeCount(MeshAttribute name) const; |
|
|
|
|
|
/** |
|
|
* @brief Absolute ID of a named attribute |
|
|
* |
|
|
* The @p id is expected to be smaller than |
|
|
* @ref attributeCount(MeshAttribute) const. |
|
|
*/ |
|
|
UnsignedInt attributeId(MeshAttribute name, UnsignedInt id = 0) const; |
|
|
|
|
|
/** |
|
|
* @brief Format of a named attribute |
|
|
* |
|
|
* The @p id is expected to be smaller than |
|
|
* @ref attributeCount(MeshAttribute) const. |
|
|
* @see @ref attributeFormat(UnsignedInt) const |
|
|
*/ |
|
|
VertexFormat attributeFormat(MeshAttribute name, UnsignedInt id = 0) const; |
|
|
|
|
|
/** |
|
|
* @brief Offset of a named attribute |
|
|
* |
|
|
* Byte offset of the first element of given named attribute from the |
|
|
* beginning of the @ref vertexData() array. The @p id is expected to |
|
|
* be smaller than @ref attributeCount(MeshAttribute) const. |
|
|
* @see @ref attributeOffset(UnsignedInt) const |
|
|
*/ |
|
|
std::size_t attributeOffset(MeshAttribute name, UnsignedInt id = 0) const; |
|
|
|
|
|
/** |
|
|
* @brief Stride of a named attribute |
|
|
* |
|
|
* Stride between consecutive elements of given named attribute in the |
|
|
* @ref vertexData() array. The @p id is expected to be smaller than |
|
|
* @ref attributeCount(MeshAttribute) const. |
|
|
* @see @ref attributeStride(UnsignedInt) const |
|
|
*/ |
|
|
UnsignedInt attributeStride(MeshAttribute name, UnsignedInt id = 0) const; |
|
|
|
|
|
/** |
|
|
* @brief Array size of a named attribute |
|
|
* |
|
|
* The @p id is expected to be smaller than |
|
|
* @ref attributeCount(MeshAttribute) const. Note that this is |
|
|
* different from vertex count, and is an orthogonal concept to having |
|
|
* multiple attributes of the same name --- see |
|
|
* @ref attributeArraySize(UnsignedInt) const for more information. |
|
|
*/ |
|
|
UnsignedShort attributeArraySize(MeshAttribute name, UnsignedInt id = 0) const; |
|
|
|
|
|
/** |
|
|
* @brief Data for given attribute |
|
|
* |
|
|
* The @p id is expected to be smaller than @ref attributeCount() const. |
|
|
* The second dimension represents the actual data type (its size is |
|
|
* equal to format size for known @ref VertexFormat values and to |
|
|
* attribute stride for implementation-specific values) and is |
|
|
* guaranteed to be contiguous. Use the templated overload below to get |
|
|
* the attribute in a concrete type. |
|
|
* @see @ref Corrade::Containers::StridedArrayView::isContiguous(), |
|
|
* @ref isVertexFormatImplementationSpecific() |
|
|
*/ |
|
|
Containers::StridedArrayView2D<const char> attribute(UnsignedInt id) const; |
|
|
|
|
|
/** |
|
|
* @brief Mutable data for given attribute |
|
|
* |
|
|
* Like @ref attribute(UnsignedInt) const, but returns a mutable view. |
|
|
* Expects that the mesh is mutable. |
|
|
* @see @ref vertexDataFlags() |
|
|
*/ |
|
|
Containers::StridedArrayView2D<char> mutableAttribute(UnsignedInt id); |
|
|
|
|
|
/** |
|
|
* @brief Data for given attribute in a concrete type |
|
|
* |
|
|
* The @p id is expected to be smaller than @ref attributeCount() const |
|
|
* and @p T is expected to correspond to |
|
|
* @ref attributeFormat(UnsignedInt) const. Expects that the vertex |
|
|
* format is *not* implementation-specific, in that case you can only |
|
|
* access the attribute via the typeless @ref attribute(UnsignedInt) const |
|
|
* above. The attribute is also expected to not be an array, in that |
|
|
* case you need to use the overload below by using @cpp T[] @ce |
|
|
* instead of @cpp T @ce. You can also use the non-templated |
|
|
* @ref positions2DAsArray(), @ref positions3DAsArray(), |
|
|
* @ref tangentsAsArray(), @ref bitangentSignsAsArray(), |
|
|
* @ref bitangentsAsArray(), @ref normalsAsArray(), |
|
|
* @ref textureCoordinates2DAsArray() and @ref colorsAsArray() |
|
|
* accessors to get common attributes converted to usual types, but |
|
|
* note that these operations involve extra allocation and data |
|
|
* conversion. |
|
|
* @see @ref attribute(MeshAttribute, UnsignedInt) const, |
|
|
* @ref mutableAttribute(MeshAttribute, UnsignedInt), |
|
|
* @ref isVertexFormatImplementationSpecific(), |
|
|
* @ref attributeArraySize() |
|
|
*/ |
|
|
template<class T, class = typename std::enable_if<!std::is_array<T>::value>::type> Containers::StridedArrayView1D<const T> attribute(UnsignedInt id) const; |
|
|
|
|
|
/** |
|
|
* @brief Data for given array attribute in a concrete type |
|
|
* |
|
|
* Same as above, except that it works with array attributes instead |
|
|
* --- you're expected to select this overload by passing @cpp T[] @ce |
|
|
* instead of @cpp T @ce. The second dimension is guaranteed to be |
|
|
* contiguous and have the same size as reported by |
|
|
* @ref attributeArraySize() for given attribute. |
|
|
*/ |
|
|
template<class T, class = typename std::enable_if<std::is_array<T>::value>::type> Containers::StridedArrayView2D<const typename std::remove_extent<T>::type> attribute(UnsignedInt id) const; |
|
|
|
|
|
/** |
|
|
* @brief Mutable data for given attribute in a concrete type |
|
|
* |
|
|
* Like @ref attribute(UnsignedInt) const, but returns a mutable view. |
|
|
* Expects that the mesh is mutable. |
|
|
* @see @ref vertexDataFlags() |
|
|
*/ |
|
|
template<class T, class = typename std::enable_if<!std::is_array<T>::value>::type> Containers::StridedArrayView1D<T> mutableAttribute(UnsignedInt id); |
|
|
|
|
|
/** |
|
|
* @brief Mutable data for given array attribute in a concrete type |
|
|
* |
|
|
* Same as above, except that it works with array attributes instead |
|
|
* --- you're expected to select this overload by passing @cpp T[] @ce |
|
|
* instead of @cpp T @ce. The second dimension is guaranteed to be |
|
|
* contiguous and have the same size as reported by |
|
|
* @ref attributeArraySize() for given attribute. |
|
|
*/ |
|
|
template<class T, class = typename std::enable_if<std::is_array<T>::value>::type> Containers::StridedArrayView2D<typename std::remove_extent<T>::type> mutableAttribute(UnsignedInt id); |
|
|
|
|
|
/** |
|
|
* @brief Data for given named attribute |
|
|
* |
|
|
* The @p id is expected to be smaller than |
|
|
* @ref attributeCount(MeshAttribute) const. The second dimension |
|
|
* represents the actual data type (its size is equal to format size |
|
|
* for known @ref VertexFormat values and to attribute stride for |
|
|
* implementation-specific values) and is guaranteed to be contiguous. |
|
|
* Use the templated overload below to get the attribute in a concrete |
|
|
* type. |
|
|
* @see @ref attribute(UnsignedInt) const, |
|
|
* @ref mutableAttribute(MeshAttribute, UnsignedInt), |
|
|
* @ref Corrade::Containers::StridedArrayView::isContiguous(), |
|
|
* @ref isVertexFormatImplementationSpecific() |
|
|
*/ |
|
|
Containers::StridedArrayView2D<const char> attribute(MeshAttribute name, UnsignedInt id = 0) const; |
|
|
|
|
|
/** |
|
|
* @brief Mutable data for given named attribute |
|
|
* |
|
|
* Like @ref attribute(MeshAttribute, UnsignedInt) const, but returns a |
|
|
* mutable view. Expects that the mesh is mutable. |
|
|
* @see @ref vertexDataFlags() |
|
|
*/ |
|
|
Containers::StridedArrayView2D<char> mutableAttribute(MeshAttribute name, UnsignedInt id = 0); |
|
|
|
|
|
/** |
|
|
* @brief Data for given named attribute in a concrete type |
|
|
* |
|
|
* The @p id is expected to be smaller than |
|
|
* @ref attributeCount(MeshAttribute) const and @p T is expected to |
|
|
* correspond to @ref attributeFormat(MeshAttribute, UnsignedInt) const. |
|
|
* Expects that the vertex format is *not* implementation-specific, in |
|
|
* that case you can only access the attribute via the typeless |
|
|
* @ref attribute(MeshAttribute, UnsignedInt) const above. You can also |
|
|
* use the non-templated @ref positions2DAsArray(), |
|
|
* @ref positions3DAsArray(), @ref normalsAsArray(), |
|
|
* @ref textureCoordinates2DAsArray() and @ref colorsAsArray() |
|
|
* accessors to get common attributes converted to usual types, but |
|
|
* note that these operations involve extra data conversion and an |
|
|
* allocation. |
|
|
* @see @ref attribute(UnsignedInt) const, |
|
|
* @ref mutableAttribute(MeshAttribute, UnsignedInt), |
|
|
* @ref isVertexFormatImplementationSpecific() |
|
|
*/ |
|
|
template<class T, class = typename std::enable_if<!std::is_array<T>::value>::type> Containers::StridedArrayView1D<const T> attribute(MeshAttribute name, UnsignedInt id = 0) const; |
|
|
|
|
|
/** |
|
|
* @brief Data for given named array attribute in a concrete type |
|
|
* |
|
|
* Same as above, except that it works with array attributes instead |
|
|
* --- you're expected to select this overload by passing @cpp T[] @ce |
|
|
* instead of @cpp T @ce. The second dimension is guaranteed to be |
|
|
* contiguous and have the same size as reported by |
|
|
* @ref attributeArraySize() for given attribute. |
|
|
*/ |
|
|
template<class T, class = typename std::enable_if<std::is_array<T>::value>::type> Containers::StridedArrayView2D<const typename std::remove_extent<T>::type> attribute(MeshAttribute name, UnsignedInt id = 0) const; |
|
|
|
|
|
/** |
|
|
* @brief Mutable data for given named attribute in a concrete type |
|
|
* |
|
|
* Like @ref attribute(MeshAttribute, UnsignedInt) const, but returns a |
|
|
* mutable view. Expects that the mesh is mutable. |
|
|
* @see @ref vertexDataFlags() |
|
|
*/ |
|
|
template<class T, class = typename std::enable_if<!std::is_array<T>::value>::type> Containers::StridedArrayView1D<T> mutableAttribute(MeshAttribute name, UnsignedInt id = 0); |
|
|
|
|
|
/** |
|
|
* @brief Mutable data for given named array attribute in a concrete type |
|
|
* |
|
|
* Same as above, except that it works with array attributes instead |
|
|
* --- you're expected to select this overload by passing @cpp T[] @ce |
|
|
* instead of @cpp T @ce. The second dimension is guaranteed to be |
|
|
* contiguous and have the same size as reported by |
|
|
* @ref attributeArraySize() for given attribute. |
|
|
*/ |
|
|
template<class T, class = typename std::enable_if<std::is_array<T>::value>::type> Containers::StridedArrayView2D<typename std::remove_extent<T>::type> mutableAttribute(MeshAttribute name, UnsignedInt id = 0); |
|
|
|
|
|
/** |
|
|
* @brief Indices as 32-bit integers |
|
|
* |
|
|
* Convenience alternative to the templated @ref indices(). Converts |
|
|
* the index array from an arbitrary underlying type and returns it in |
|
|
* a newly-allocated array. |
|
|
* @see @ref indicesInto() |
|
|
*/ |
|
|
Containers::Array<UnsignedInt> indicesAsArray() const; |
|
|
|
|
|
/** |
|
|
* @brief Positions as 32-bit integers into a pre-allocated view |
|
|
* |
|
|
* Like @ref indicesAsArray(), but puts the result into @p destination |
|
|
* instead of allocating a new array. Expects that @p destination is |
|
|
* sized to contain exactly all data. |
|
|
* @see @ref indexCount() |
|
|
*/ |
|
|
void indicesInto(Containers::StridedArrayView1D<UnsignedInt> destination) const; |
|
|
|
|
|
/** |
|
|
* @brief Positions as 2D float vectors |
|
|
* |
|
|
* Convenience alternative to @ref attribute(MeshAttribute, UnsignedInt) const |
|
|
* with @ref MeshAttribute::Position as the first argument. Converts |
|
|
* the position array from an arbitrary underlying type and returns it |
|
|
* in a newly-allocated array. If the underlying type is |
|
|
* three-component, the last component is dropped. Expects that the |
|
|
* vertex format is *not* implementation-specific, in that case you can |
|
|
* only access the attribute via the typeless @ref attribute(MeshAttribute, UnsignedInt) const. |
|
|
* @see @ref positions2DInto(), @ref attributeFormat(), |
|
|
* @ref isVertexFormatImplementationSpecific() |
|
|
*/ |
|
|
Containers::Array<Vector2> positions2DAsArray(UnsignedInt id = 0) const; |
|
|
|
|
|
/** |
|
|
* @brief Positions as 2D float vectors into a pre-allocated view |
|
|
* |
|
|
* Like @ref positions2DAsArray(), but puts the result into |
|
|
* @p destination instead of allocating a new array. Expects that |
|
|
* @p destination is sized to contain exactly all data. |
|
|
* @see @ref vertexCount() |
|
|
*/ |
|
|
void positions2DInto(Containers::StridedArrayView1D<Vector2> destination, UnsignedInt id = 0) const; |
|
|
|
|
|
/** |
|
|
* @brief Positions as 3D float vectors |
|
|
* |
|
|
* Convenience alternative to @ref attribute(MeshAttribute, UnsignedInt) const |
|
|
* with @ref MeshAttribute::Position as the first argument. Converts |
|
|
* the position array from an arbitrary underlying type and returns it |
|
|
* in a newly-allocated array. If the underlying type is two-component, |
|
|
* the Z component is set to @cpp 0.0f @ce. Expects that the vertex |
|
|
* format is *not* implementation-specific, in that case you can only |
|
|
* access the attribute via the typeless @ref attribute(MeshAttribute, UnsignedInt) const. |
|
|
* @see @ref positions3DInto(), @ref attributeFormat(), |
|
|
* @ref isVertexFormatImplementationSpecific() |
|
|
*/ |
|
|
Containers::Array<Vector3> positions3DAsArray(UnsignedInt id = 0) const; |
|
|
|
|
|
/** |
|
|
* @brief Positions as 3D float vectors into a pre-allocated view |
|
|
* |
|
|
* Like @ref positions3DAsArray(), but puts the result into |
|
|
* @p destination instead of allocating a new array. Expects that |
|
|
* @p destination is sized to contain exactly all data. |
|
|
* @see @ref vertexCount() |
|
|
*/ |
|
|
void positions3DInto(Containers::StridedArrayView1D<Vector3> destination, UnsignedInt id = 0) const; |
|
|
|
|
|
/** |
|
|
* @brief Tangents as 3D float vectors |
|
|
* |
|
|
* Convenience alternative to @ref attribute(MeshAttribute, UnsignedInt) const |
|
|
* with @ref MeshAttribute::Tangent as the first argument. Converts the |
|
|
* tangent array from an arbitrary underlying type and returns it in a |
|
|
* newly-allocated array. Expects that the vertex format is *not* |
|
|
* implementation-specific, in that case you can only access the |
|
|
* attribute via the typeless @ref attribute(MeshAttribute, UnsignedInt) const. |
|
|
* |
|
|
* If the tangents contain a fourth component with bitangent direction, |
|
|
* it's ignored here --- use @ref bitangentSignsAsArray() to get those |
|
|
* instead. You can also use @ref tangentsInto() together with |
|
|
* @ref bitangentSignsInto() to put them both in a single array. |
|
|
* @see @ref bitangentsAsArray(), @ref normalsAsArray(), |
|
|
* @ref attributeFormat(), |
|
|
* @ref isVertexFormatImplementationSpecific() |
|
|
*/ |
|
|
Containers::Array<Vector3> tangentsAsArray(UnsignedInt id = 0) const; |
|
|
|
|
|
/** |
|
|
* @brief Tangents as 3D float vectors into a pre-allocated view |
|
|
* |
|
|
* Like @ref tangentsAsArray(), but puts the result into @p destination |
|
|
* instead of allocating a new array. Expects that @p destination is |
|
|
* sized to contain exactly all data. |
|
|
* @see @ref vertexCount() |
|
|
*/ |
|
|
void tangentsInto(Containers::StridedArrayView1D<Vector3> destination, UnsignedInt id = 0) const; |
|
|
|
|
|
/** |
|
|
* @brief Bitangent signs as floats |
|
|
* |
|
|
* Counterpart to @ref tangentsAsArray() returning value of the fourth |
|
|
* component. Expects that the type of @ref MeshAttribute::Tangent is |
|
|
* four-component. You can also use @ref tangentsInto() together with |
|
|
* @ref bitangentSignsInto() to put them both in a single array. |
|
|
* @see @ref tangentsAsArray(), @ref bitangentsAsArray(), |
|
|
* @ref normalsAsArray(), @ref attributeFormat(), |
|
|
* @ref isVertexFormatImplementationSpecific() |
|
|
*/ |
|
|
Containers::Array<Float> bitangentSignsAsArray(UnsignedInt id = 0) const; |
|
|
|
|
|
/** |
|
|
* @brief Bitangent signs as floats into a pre-allocated view |
|
|
* |
|
|
* Like @ref bitangentsAsArray(), but puts the result into |
|
|
* @p destination instead of allocating a new array. Expects that |
|
|
* @p destination is sized to contain exactly all data. |
|
|
* @see @ref vertexCount() |
|
|
*/ |
|
|
void bitangentSignsInto(Containers::StridedArrayView1D<Float> destination, UnsignedInt id = 0) const; |
|
|
|
|
|
/** |
|
|
* @brief Bitangents as 3D float vectors |
|
|
* |
|
|
* Convenience alternative to @ref attribute(MeshAttribute, UnsignedInt) const |
|
|
* with @ref MeshAttribute::Bitangent as the first argument. Converts |
|
|
* the bitangent array from an arbitrary underlying type and returns it |
|
|
* in a newly-allocated array. Expects that the vertex format is *not* |
|
|
* implementation-specific, in that case you can only access the |
|
|
* attribute via the typeless @ref attribute(MeshAttribute, UnsignedInt) const. |
|
|
* |
|
|
* Note that in some cases the bitangents aren't provided directly but |
|
|
* calculated from normals and four-component tangents. In that case |
|
|
* you'll need to get bitangent signs via @ref bitangentSignsAsArray() |
|
|
* and calculate the bitangents as shown in the documentation of |
|
|
* @ref MeshAttribute::Tangent. |
|
|
* @see @ref bitangentsInto(), @ref tangentsAsArray(), |
|
|
* @ref normalsAsArray(), @ref attributeFormat(), |
|
|
* @ref isVertexFormatImplementationSpecific() |
|
|
*/ |
|
|
Containers::Array<Vector3> bitangentsAsArray(UnsignedInt id = 0) const; |
|
|
|
|
|
/** |
|
|
* @brief Bitangents as 3D float vectors into a pre-allocated view |
|
|
* |
|
|
* Like @ref bitangentsAsArray(), but puts the result into |
|
|
* @p destination instead of allocating a new array. Expects that |
|
|
* @p destination is sized to contain exactly all data. |
|
|
* @see @ref vertexCount() |
|
|
*/ |
|
|
void bitangentsInto(Containers::StridedArrayView1D<Vector3> destination, UnsignedInt id = 0) const; |
|
|
|
|
|
/** |
|
|
* @brief Normals as 3D float vectors |
|
|
* |
|
|
* Convenience alternative to @ref attribute(MeshAttribute, UnsignedInt) const |
|
|
* with @ref MeshAttribute::Normal as the first argument. Converts the |
|
|
* normal array from an arbitrary underlying type and returns it in a |
|
|
* newly-allocated array. Expects that the vertex format is *not* |
|
|
* implementation-specific, in that case you can only access the |
|
|
* attribute via the typeless @ref attribute(MeshAttribute, UnsignedInt) const. |
|
|
* @see @ref normalsInto(), @ref tangentsAsArray(), |
|
|
* @ref bitangentsAsArray(), @ref attributeFormat(), |
|
|
* @ref isVertexFormatImplementationSpecific() |
|
|
*/ |
|
|
Containers::Array<Vector3> normalsAsArray(UnsignedInt id = 0) const; |
|
|
|
|
|
/** |
|
|
* @brief Normals as 3D float vectors into a pre-allocated view |
|
|
* |
|
|
* Like @ref normalsAsArray(), but puts the result into @p destination |
|
|
* instead of allocating a new array. Expects that @p destination is |
|
|
* sized to contain exactly all data. |
|
|
* @see @ref vertexCount() |
|
|
*/ |
|
|
void normalsInto(Containers::StridedArrayView1D<Vector3> destination, UnsignedInt id = 0) const; |
|
|
|
|
|
/** |
|
|
* @brief Texture coordinates as 2D float vectors |
|
|
* |
|
|
* Convenience alternative to @ref attribute(MeshAttribute, UnsignedInt) const |
|
|
* with @ref MeshAttribute::TextureCoordinates as the first argument. |
|
|
* Converts the texture coordinate array from an arbitrary underlying |
|
|
* type and returns it in a newly-allocated array. Expects that the |
|
|
* vertex format is *not* implementation-specific, in that case you can |
|
|
* only access the attribute via the typeless |
|
|
* @ref attribute(MeshAttribute, UnsignedInt) const. |
|
|
* @see @ref textureCoordinates2DInto(), @ref attributeFormat(), |
|
|
* @ref isVertexFormatImplementationSpecific() |
|
|
*/ |
|
|
Containers::Array<Vector2> textureCoordinates2DAsArray(UnsignedInt id = 0) const; |
|
|
|
|
|
/** |
|
|
* @brief Texture coordinates as 2D float vectors into a pre-allocated view |
|
|
* |
|
|
* Like @ref textureCoordinates2DAsArray(), but puts the result into |
|
|
* @p destination instead of allocating a new array. Expects that |
|
|
* @p destination is sized to contain exactly all data. |
|
|
* @see @ref vertexCount() |
|
|
*/ |
|
|
void textureCoordinates2DInto(Containers::StridedArrayView1D<Vector2> destination, UnsignedInt id = 0) const; |
|
|
|
|
|
/** |
|
|
* @brief Colors as RGBA floats |
|
|
* |
|
|
* Convenience alternative to @ref attribute(MeshAttribute, UnsignedInt) const |
|
|
* with @ref MeshAttribute::Color as the first argument. Converts the |
|
|
* color array from an arbitrary underlying type and returns it in a |
|
|
* newly-allocated array. If the underlying type is three-component, |
|
|
* the alpha component is set to @cpp 1.0f @ce. Expects that the vertex |
|
|
* format is *not* implementation-specific, in that case you can only |
|
|
* access the attribute via the typeless @ref attribute(MeshAttribute, UnsignedInt) const. |
|
|
* @see @ref colorsInto(), @ref attributeFormat(), |
|
|
* @ref isVertexFormatImplementationSpecific() |
|
|
*/ |
|
|
Containers::Array<Color4> colorsAsArray(UnsignedInt id = 0) const; |
|
|
|
|
|
/** |
|
|
* @brief Colors as RGBA floats into a pre-allocated view |
|
|
* |
|
|
* Like @ref colorsAsArray(), but puts the result into @p destination |
|
|
* instead of allocating a new array. Expects that @p destination is |
|
|
* sized to contain exactly all data. |
|
|
* @see @ref vertexCount() |
|
|
*/ |
|
|
void colorsInto(Containers::StridedArrayView1D<Color4> destination, UnsignedInt id = 0) const; |
|
|
|
|
|
/** |
|
|
* @brief Release index data storage |
|
|
* |
|
|
* Releases the ownership of the index data array and resets internal |
|
|
* index-related state to default. The mesh then behaves like |
|
|
* it has zero indices (but it can still have a non-zero vertex count), |
|
|
* however @ref indexData() still return a zero-sized non-null array so |
|
|
* index offset calculation continues to work as expected. |
|
|
* |
|
|
* Note that the returned array has a custom no-op deleter when the |
|
|
* data are not owned by the mesh, and while the returned array type is |
|
|
* mutable, the actual memory might be not. |
|
|
* @see @ref indexData(), @ref indexDataFlags() |
|
|
*/ |
|
|
Containers::Array<char> releaseIndexData(); |
|
|
|
|
|
/** |
|
|
* @brief Release attribute data storage |
|
|
* |
|
|
* Releases the ownership of the attribute data array and resets |
|
|
* internal attribute-related state to default. The mesh then behaves |
|
|
* like if it has no attributes (but it can still have a non-zero |
|
|
* vertex count). Note that the returned array has a custom no-op |
|
|
* deleter when the data are not owned by the mesh, and while the |
|
|
* returned array type is mutable, the actual memory might be not. |
|
|
* Additionally, the returned @ref MeshAttributeData instances |
|
|
* may have different data pointers and sizes than what's returned by |
|
|
* the @ref attribute() and @ref attributeData(UnsignedInt) const |
|
|
* accessors as some of them might be offset-only --- use this function |
|
|
* only if you *really* know what are you doing. |
|
|
* @see @ref attributeData(), @ref MeshAttributeData::isOffsetOnly() |
|
|
*/ |
|
|
Containers::Array<MeshAttributeData> releaseAttributeData(); |
|
|
|
|
|
/** |
|
|
* @brief Release vertex data storage |
|
|
* |
|
|
* Releases the ownership of the vertex data array and resets internal |
|
|
* attribute-related state to default. The mesh then behaves like it |
|
|
* has zero vertices (but it can still have a non-zero amount of |
|
|
* attributes), however @ref vertexData() will still return a zero- |
|
|
* sized non-null array so attribute offset calculation continues to |
|
|
* work as expected. |
|
|
* |
|
|
* Note that the returned array has a custom no-op deleter when the |
|
|
* data are not owned by the mesh, and while the returned array type is |
|
|
* mutable, the actual memory might be not. |
|
|
* @see @ref vertexData(), @ref vertexDataFlags() |
|
|
*/ |
|
|
Containers::Array<char> releaseVertexData(); |
|
|
|
|
|
/** |
|
|
* @brief Importer-specific state |
|
|
* |
|
|
* See @ref AbstractImporter::importerState() for more information. |
|
|
*/ |
|
|
const void* importerState() const { return _importerState; } |
|
|
|
|
|
private: |
|
|
/* For custom deleter checks. Not done in the constructors here because |
|
|
the restriction is pointless when used outside of plugin |
|
|
implementations. */ |
|
|
friend AbstractImporter; |
|
|
|
|
|
/* Internal helper that doesn't assert, unlike attributeId() */ |
|
|
UnsignedInt attributeFor(MeshAttribute name, UnsignedInt id) const; |
|
|
|
|
|
/* Like attribute(), but returning just a 1D view */ |
|
|
Containers::StridedArrayView1D<const void> attributeDataViewInternal(const MeshAttributeData& attribute) const; |
|
|
|
|
|
#ifndef CORRADE_NO_ASSERT |
|
|
template<class T> bool checkAttributeTypeCompatibility(const MeshAttributeData& attribute, const char* prefix) const; |
|
|
#endif |
|
|
|
|
|
/* GPUs don't currently support more than 32-bit index types / vertex |
|
|
counts so this should be enough. Sanity check: |
|
|
https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkIndexType.html */ |
|
|
UnsignedInt _vertexCount; |
|
|
MeshIndexType _indexType; |
|
|
MeshPrimitive _primitive; |
|
|
DataFlags _indexDataFlags, _vertexDataFlags; |
|
|
const void* _importerState; |
|
|
Containers::Array<char> _indexData, _vertexData; |
|
|
Containers::Array<MeshAttributeData> _attributes; |
|
|
/* MeshIndexData are "unpacked" in order to avoid excessive padding */ |
|
|
Containers::ArrayView<const char> _indices; |
|
|
}; |
|
|
|
|
|
#if !defined(CORRADE_NO_ASSERT) || defined(CORRADE_GRACEFUL_ASSERT) |
|
|
namespace Implementation { |
|
|
/* LCOV_EXCL_START */ |
|
|
template<class T> constexpr MeshIndexType meshIndexTypeFor() { |
|
|
/* C++ why there isn't an obvious way to do such a thing?! */ |
|
|
static_assert(sizeof(T) == 0, "unsupported index type"); |
|
|
return {}; |
|
|
} |
|
|
template<> constexpr MeshIndexType meshIndexTypeFor<UnsignedByte>() { return MeshIndexType::UnsignedByte; } |
|
|
template<> constexpr MeshIndexType meshIndexTypeFor<UnsignedShort>() { return MeshIndexType::UnsignedShort; } |
|
|
template<> constexpr MeshIndexType meshIndexTypeFor<UnsignedInt>() { return MeshIndexType::UnsignedInt; } |
|
|
|
|
|
/* Implicit mapping from a format to enum (1:1) */ |
|
|
template<class T> constexpr VertexFormat vertexFormatFor() { |
|
|
/* C++ why there isn't an obvious way to do such a thing?! */ |
|
|
static_assert(sizeof(T) == 0, "unsupported attribute type"); |
|
|
return {}; |
|
|
} |
|
|
#ifndef DOXYGEN_GENERATING_OUTPUT |
|
|
#define _c(format) \ |
|
|
template<> constexpr VertexFormat vertexFormatFor<format>() { return VertexFormat::format; } |
|
|
_c(Float) |
|
|
_c(Half) |
|
|
_c(Double) |
|
|
_c(UnsignedByte) |
|
|
_c(Byte) |
|
|
_c(UnsignedShort) |
|
|
_c(Short) |
|
|
_c(UnsignedInt) |
|
|
_c(Int) |
|
|
_c(Vector2) |
|
|
_c(Vector2h) |
|
|
_c(Vector2d) |
|
|
_c(Vector2ub) |
|
|
_c(Vector2b) |
|
|
_c(Vector2us) |
|
|
_c(Vector2s) |
|
|
_c(Vector2ui) |
|
|
_c(Vector2i) |
|
|
_c(Vector3) |
|
|
_c(Vector3h) |
|
|
_c(Vector3d) |
|
|
_c(Vector3ub) |
|
|
_c(Vector3b) |
|
|
_c(Vector3us) |
|
|
_c(Vector3s) |
|
|
_c(Vector3ui) |
|
|
_c(Vector3i) |
|
|
_c(Vector4) |
|
|
_c(Vector4h) |
|
|
_c(Vector4d) |
|
|
_c(Vector4ub) |
|
|
_c(Vector4b) |
|
|
_c(Vector4us) |
|
|
_c(Vector4s) |
|
|
_c(Vector4ui) |
|
|
_c(Vector4i) |
|
|
#define _cn(format) \ |
|
|
template<> constexpr VertexFormat vertexFormatFor<format>() { return VertexFormat::format ## Normalized; } |
|
|
_c(Matrix2x2) |
|
|
_c(Matrix2x2h) |
|
|
_c(Matrix2x2d) |
|
|
_cn(Matrix2x2b) |
|
|
_cn(Matrix2x2s) |
|
|
_c(Matrix2x3) |
|
|
_c(Matrix2x3h) |
|
|
_c(Matrix2x3d) |
|
|
_cn(Matrix2x3b) |
|
|
_cn(Matrix2x3s) |
|
|
_c(Matrix2x4) |
|
|
_c(Matrix2x4h) |
|
|
_c(Matrix2x4d) |
|
|
_cn(Matrix2x4b) |
|
|
_cn(Matrix2x4s) |
|
|
_c(Matrix3x2) |
|
|
_c(Matrix3x2h) |
|
|
_c(Matrix3x2d) |
|
|
_cn(Matrix3x2b) |
|
|
_cn(Matrix3x2s) |
|
|
_c(Matrix3x3) |
|
|
_c(Matrix3x3h) |
|
|
_c(Matrix3x3d) |
|
|
_cn(Matrix3x3b) |
|
|
_cn(Matrix3x3s) |
|
|
_c(Matrix3x4) |
|
|
_c(Matrix3x4h) |
|
|
_c(Matrix3x4d) |
|
|
_cn(Matrix3x4b) |
|
|
_cn(Matrix3x4s) |
|
|
_c(Matrix4x2) |
|
|
_c(Matrix4x2h) |
|
|
_c(Matrix4x2d) |
|
|
_cn(Matrix4x2b) |
|
|
_cn(Matrix4x2s) |
|
|
_c(Matrix4x3) |
|
|
_c(Matrix4x3h) |
|
|
_c(Matrix4x3d) |
|
|
_cn(Matrix4x3b) |
|
|
_cn(Matrix4x3s) |
|
|
_c(Matrix4x4) |
|
|
_c(Matrix4x4h) |
|
|
_c(Matrix4x4d) |
|
|
_cn(Matrix4x4b) |
|
|
_cn(Matrix4x4s) |
|
|
#undef _c |
|
|
#undef _cn |
|
|
#endif |
|
|
template<> constexpr VertexFormat vertexFormatFor<Color3>() { return VertexFormat::Vector3; } |
|
|
template<> constexpr VertexFormat vertexFormatFor<Color3h>() { return VertexFormat::Vector3h; } |
|
|
template<> constexpr VertexFormat vertexFormatFor<Color3ub>() { return VertexFormat::Vector3ubNormalized; } |
|
|
template<> constexpr VertexFormat vertexFormatFor<Color3us>() { return VertexFormat::Vector3usNormalized; } |
|
|
template<> constexpr VertexFormat vertexFormatFor<Color4>() { return VertexFormat::Vector4; } |
|
|
template<> constexpr VertexFormat vertexFormatFor<Color4h>() { return VertexFormat::Vector4h; } |
|
|
template<> constexpr VertexFormat vertexFormatFor<Color4ub>() { return VertexFormat::Vector4ubNormalized; } |
|
|
template<> constexpr VertexFormat vertexFormatFor<Color4us>() { return VertexFormat::Vector4usNormalized; } |
|
|
|
|
|
/* Check if enum is compatible with a format (1:n). Mostly just 1:1 mapping |
|
|
tho, so reusing vertexFormatFor(), with a few exceptions. */ |
|
|
template<class T> constexpr bool isVertexFormatCompatible(VertexFormat type) { |
|
|
return vertexFormatFor<T>() == type; |
|
|
} |
|
|
#ifndef DOXYGEN_GENERATING_OUTPUT |
|
|
#define _c(format_) \ |
|
|
template<> constexpr bool isVertexFormatCompatible<format_>(VertexFormat format) { \ |
|
|
return format == VertexFormat::format_ || \ |
|
|
format == VertexFormat::format_ ## Normalized; \ |
|
|
} |
|
|
_c(UnsignedByte) |
|
|
_c(Byte) |
|
|
_c(UnsignedShort) |
|
|
_c(Short) |
|
|
_c(Vector2ub) |
|
|
_c(Vector2b) |
|
|
_c(Vector2us) |
|
|
_c(Vector2s) |
|
|
_c(Vector3ub) |
|
|
_c(Vector3b) |
|
|
_c(Vector3us) |
|
|
_c(Vector3s) |
|
|
_c(Vector4ub) |
|
|
_c(Vector4b) |
|
|
_c(Vector4us) |
|
|
_c(Vector4s) |
|
|
/* For Color[34]u[sb] we expect the format to be normalized, which is |
|
|
handled by vertexFormatFor() properly already */ |
|
|
template<> constexpr bool isVertexFormatCompatible<Matrix2x4h>(VertexFormat format) { |
|
|
return format == VertexFormat::Matrix2x4h || |
|
|
format == VertexFormat::Matrix2x3hAligned; |
|
|
} |
|
|
template<> constexpr bool isVertexFormatCompatible<Matrix2x4b>(VertexFormat format) { |
|
|
return format == VertexFormat::Matrix2x4bNormalized || |
|
|
format == VertexFormat::Matrix2x2bNormalizedAligned || |
|
|
format == VertexFormat::Matrix2x3bNormalizedAligned; |
|
|
} |
|
|
template<> constexpr bool isVertexFormatCompatible<Matrix2x4s>(VertexFormat format) { |
|
|
return format == VertexFormat::Matrix2x4sNormalized || |
|
|
format == VertexFormat::Matrix2x3sNormalizedAligned; |
|
|
} |
|
|
template<> constexpr bool isVertexFormatCompatible<Matrix3x4h>(VertexFormat format) { |
|
|
return format == VertexFormat::Matrix3x4h || |
|
|
format == VertexFormat::Matrix3x3hAligned; |
|
|
} |
|
|
template<> constexpr bool isVertexFormatCompatible<Matrix3x4b>(VertexFormat format) { |
|
|
return format == VertexFormat::Matrix3x4bNormalized || |
|
|
format == VertexFormat::Matrix3x2bNormalizedAligned || |
|
|
format == VertexFormat::Matrix3x3bNormalizedAligned; |
|
|
} |
|
|
template<> constexpr bool isVertexFormatCompatible<Matrix3x4s>(VertexFormat format) { |
|
|
return format == VertexFormat::Matrix3x4sNormalized || |
|
|
format == VertexFormat::Matrix3x3sNormalizedAligned; |
|
|
} |
|
|
template<> constexpr bool isVertexFormatCompatible<Matrix4x4h>(VertexFormat format) { |
|
|
return format == VertexFormat::Matrix4x4h || |
|
|
format == VertexFormat::Matrix4x3hAligned; |
|
|
} |
|
|
template<> constexpr bool isVertexFormatCompatible<Matrix4x4b>(VertexFormat format) { |
|
|
return format == VertexFormat::Matrix4x4bNormalized || |
|
|
format == VertexFormat::Matrix4x2bNormalizedAligned || |
|
|
format == VertexFormat::Matrix4x3bNormalizedAligned; |
|
|
} |
|
|
template<> constexpr bool isVertexFormatCompatible<Matrix4x4s>(VertexFormat format) { |
|
|
return format == VertexFormat::Matrix4x4sNormalized || |
|
|
format == VertexFormat::Matrix4x3sNormalizedAligned; |
|
|
} |
|
|
#undef _c |
|
|
#endif |
|
|
/* LCOV_EXCL_STOP */ |
|
|
|
|
|
constexpr bool isVertexFormatCompatibleWithAttribute(MeshAttribute name, VertexFormat format) { |
|
|
/* Double types intentionally not supported for any builtin attributes |
|
|
right now -- only for custom types */ |
|
|
return |
|
|
/* Implementation-specific formats can be used for any attribute |
|
|
(tho the access capabilities will be reduced) */ |
|
|
isVertexFormatImplementationSpecific(format) || |
|
|
/* Named attributes are restricted so we can decode them */ |
|
|
(name == MeshAttribute::Position && |
|
|
(format == VertexFormat::Vector2 || |
|
|
format == VertexFormat::Vector2h || |
|
|
format == VertexFormat::Vector2ub || |
|
|
format == VertexFormat::Vector2ubNormalized || |
|
|
format == VertexFormat::Vector2b || |
|
|
format == VertexFormat::Vector2bNormalized || |
|
|
format == VertexFormat::Vector2us || |
|
|
format == VertexFormat::Vector2usNormalized || |
|
|
format == VertexFormat::Vector2s || |
|
|
format == VertexFormat::Vector2sNormalized || |
|
|
format == VertexFormat::Vector3 || |
|
|
format == VertexFormat::Vector3h || |
|
|
format == VertexFormat::Vector3ub || |
|
|
format == VertexFormat::Vector3ubNormalized || |
|
|
format == VertexFormat::Vector3b || |
|
|
format == VertexFormat::Vector3bNormalized || |
|
|
format == VertexFormat::Vector3us || |
|
|
format == VertexFormat::Vector3usNormalized || |
|
|
format == VertexFormat::Vector3s || |
|
|
format == VertexFormat::Vector3sNormalized)) || |
|
|
(name == MeshAttribute::Tangent && |
|
|
(format == VertexFormat::Vector3 || |
|
|
format == VertexFormat::Vector3h || |
|
|
format == VertexFormat::Vector3bNormalized || |
|
|
format == VertexFormat::Vector3sNormalized || |
|
|
format == VertexFormat::Vector4 || |
|
|
format == VertexFormat::Vector4h || |
|
|
format == VertexFormat::Vector4bNormalized || |
|
|
format == VertexFormat::Vector4sNormalized)) || |
|
|
((name == MeshAttribute::Bitangent || name == MeshAttribute::Normal) && |
|
|
(format == VertexFormat::Vector3 || |
|
|
format == VertexFormat::Vector3h || |
|
|
format == VertexFormat::Vector3bNormalized || |
|
|
format == VertexFormat::Vector3sNormalized)) || |
|
|
(name == MeshAttribute::Color && |
|
|
(format == VertexFormat::Vector3 || |
|
|
format == VertexFormat::Vector3h || |
|
|
format == VertexFormat::Vector3ubNormalized || |
|
|
format == VertexFormat::Vector3usNormalized || |
|
|
format == VertexFormat::Vector4 || |
|
|
format == VertexFormat::Vector4h || |
|
|
format == VertexFormat::Vector4ubNormalized || |
|
|
format == VertexFormat::Vector4usNormalized)) || |
|
|
(name == MeshAttribute::TextureCoordinates && |
|
|
(format == VertexFormat::Vector2 || |
|
|
format == VertexFormat::Vector2h || |
|
|
format == VertexFormat::Vector2ub || |
|
|
format == VertexFormat::Vector2ubNormalized || |
|
|
format == VertexFormat::Vector2b || |
|
|
format == VertexFormat::Vector2bNormalized || |
|
|
format == VertexFormat::Vector2us || |
|
|
format == VertexFormat::Vector2usNormalized || |
|
|
format == VertexFormat::Vector2s || |
|
|
format == VertexFormat::Vector2sNormalized)) || |
|
|
/* Custom attributes can be anything */ |
|
|
isMeshAttributeCustom(name); |
|
|
} |
|
|
|
|
|
constexpr bool isAttributeArrayAllowed(MeshAttribute name) { |
|
|
return isMeshAttributeCustom(name); |
|
|
} |
|
|
} |
|
|
#endif |
|
|
|
|
|
constexpr MeshAttributeData::MeshAttributeData(const MeshAttribute name, const VertexFormat format, const UnsignedShort arraySize, const Containers::StridedArrayView1D<const void>& data, std::nullptr_t) noexcept: |
|
|
_data{data.data()}, _vertexCount{UnsignedInt(data.size())}, _format{format}, |
|
|
/** @todo support zero / negative stride? would be hard to transfer to GL */ |
|
|
_stride{(CORRADE_CONSTEXPR_ASSERT(!(UnsignedInt(data.stride()) & 0xffff8000), |
|
|
"Trade::MeshAttributeData: expected stride to be positive and at most 32k, got" << data.stride()), |
|
|
Short(data.stride())) |
|
|
}, _name{(CORRADE_CONSTEXPR_ASSERT(Implementation::isVertexFormatCompatibleWithAttribute(name, format), |
|
|
"Trade::MeshAttributeData:" << format << "is not a valid format for" << name), name) |
|
|
}, _arraySize{(CORRADE_CONSTEXPR_ASSERT(!arraySize || Implementation::isAttributeArrayAllowed(name), |
|
|
"Trade::MeshAttributeData:" << name << "can't be an array attribute"), arraySize) |
|
|
}, _isOffsetOnly{(CORRADE_CONSTEXPR_ASSERT(!arraySize || !isVertexFormatImplementationSpecific(format), |
|
|
"Trade::MeshAttributeData: array attributes can't have an implementation-specific format"), false)} {} |
|
|
|
|
|
constexpr MeshAttributeData::MeshAttributeData(const MeshAttribute name, const VertexFormat format, const std::size_t offset, const UnsignedInt vertexCount, const std::ptrdiff_t stride, UnsignedShort arraySize) noexcept: |
|
|
_data{offset}, _vertexCount{vertexCount}, _format{format}, |
|
|
/** @todo support zero / negative stride? would be hard to transfer to GL */ |
|
|
_stride{(CORRADE_CONSTEXPR_ASSERT(!(UnsignedInt(stride) & 0xffff8000), |
|
|
"Trade::MeshAttributeData: expected stride to be positive and at most 32k, got" << stride), |
|
|
Short(stride)) |
|
|
}, _name{(CORRADE_CONSTEXPR_ASSERT(Implementation::isVertexFormatCompatibleWithAttribute(name, format), |
|
|
"Trade::MeshAttributeData:" << format << "is not a valid format for" << name), name) |
|
|
}, _arraySize{(CORRADE_CONSTEXPR_ASSERT(!arraySize || Implementation::isAttributeArrayAllowed(name), |
|
|
"Trade::MeshAttributeData:" << name << "can't be an array attribute"), arraySize) |
|
|
}, _isOffsetOnly{(CORRADE_CONSTEXPR_ASSERT(!arraySize || !isVertexFormatImplementationSpecific(format), |
|
|
"Trade::MeshAttributeData: array attributes can't have an implementation-specific format"), true)} {} |
|
|
|
|
|
template<class T> constexpr MeshAttributeData::MeshAttributeData(MeshAttribute name, const Containers::StridedArrayView1D<T>& data) noexcept: MeshAttributeData{name, Implementation::vertexFormatFor<typename std::remove_const<T>::type>(), 0, data, nullptr} {} |
|
|
|
|
|
template<class T> constexpr MeshAttributeData::MeshAttributeData(MeshAttribute name, const Containers::StridedArrayView2D<T>& data) noexcept: MeshAttributeData{name, Implementation::vertexFormatFor<typename std::remove_const<T>::type>(), UnsignedShort(data.size()[1]), Containers::StridedArrayView1D<const void>{{data.data(), ~std::size_t{}}, data.size()[0], data.stride()[0]}, (CORRADE_CONSTEXPR_ASSERT(data.stride()[1] == sizeof(T), "Trade::MeshAttributeData: second view dimension is not contiguous"), nullptr)} {} |
|
|
|
|
|
template<class T> Containers::ArrayView<const T> MeshData::indices() const { |
|
|
Containers::StridedArrayView2D<const char> data = indices(); |
|
|
#ifdef CORRADE_GRACEFUL_ASSERT /* Sigh. Brittle. Better idea? */ |
|
|
if(!data.stride()[1]) return {}; |
|
|
#endif |
|
|
CORRADE_ASSERT(Implementation::meshIndexTypeFor<T>() == _indexType, |
|
|
"Trade::MeshData::indices(): improper type requested for" << _indexType, nullptr); |
|
|
return Containers::arrayCast<1, const T>(data).asContiguous(); |
|
|
} |
|
|
|
|
|
template<class T> Containers::ArrayView<T> MeshData::mutableIndices() { |
|
|
Containers::StridedArrayView2D<char> data = mutableIndices(); |
|
|
#ifdef CORRADE_GRACEFUL_ASSERT /* Sigh. Brittle. Better idea? */ |
|
|
if(!data.stride()[1]) return {}; |
|
|
#endif |
|
|
CORRADE_ASSERT(Implementation::meshIndexTypeFor<T>() == _indexType, |
|
|
"Trade::MeshData::mutableIndices(): improper type requested for" << _indexType, nullptr); |
|
|
return Containers::arrayCast<1, T>(data).asContiguous(); |
|
|
} |
|
|
|
|
|
#ifndef CORRADE_NO_ASSERT |
|
|
template<class T> bool MeshData::checkAttributeTypeCompatibility(const MeshAttributeData& attribute, const char* const prefix) const { |
|
|
CORRADE_ASSERT(!isVertexFormatImplementationSpecific(attribute._format), |
|
|
prefix << "can't cast data from an implementation-specific vertex format" << reinterpret_cast<void*>(vertexFormatUnwrap(attribute._format)), false); |
|
|
CORRADE_ASSERT(Implementation::isVertexFormatCompatible<typename std::remove_extent<T>::type>(attribute._format), |
|
|
prefix << "improper type requested for" << attribute._name << "of format" << attribute._format, false); |
|
|
CORRADE_ASSERT(std::is_array<T>::value == !!attribute._arraySize, |
|
|
prefix << "use T[] to access an array attribute", false); |
|
|
return true; |
|
|
} |
|
|
#endif |
|
|
|
|
|
template<class T, class> Containers::StridedArrayView1D<const T> MeshData::attribute(const UnsignedInt id) const { |
|
|
Containers::StridedArrayView2D<const char> data = attribute(id); |
|
|
#ifdef CORRADE_GRACEFUL_ASSERT /* Sigh. Brittle. Better idea? */ |
|
|
if(!data.stride()[1]) return {}; |
|
|
#endif |
|
|
#ifndef CORRADE_NO_ASSERT |
|
|
if(!checkAttributeTypeCompatibility<T>(_attributes[id], "Trade::MeshData::attribute():")) return {}; |
|
|
#endif |
|
|
return Containers::arrayCast<1, const T>(data); |
|
|
} |
|
|
|
|
|
template<class T, class> Containers::StridedArrayView2D<const typename std::remove_extent<T>::type> MeshData::attribute(const UnsignedInt id) const { |
|
|
Containers::StridedArrayView2D<const char> data = attribute(id); |
|
|
#ifdef CORRADE_GRACEFUL_ASSERT /* Sigh. Brittle. Better idea? */ |
|
|
if(!data.stride()[1]) return {}; |
|
|
#endif |
|
|
const MeshAttributeData& attribute = _attributes[id]; |
|
|
#ifndef CORRADE_NO_ASSERT |
|
|
if(!checkAttributeTypeCompatibility<T>(attribute, "Trade::MeshData::attribute():")) return {}; |
|
|
#endif |
|
|
return Containers::arrayCast<2, const typename std::remove_extent<T>::type>(data); |
|
|
} |
|
|
|
|
|
template<class T, class> Containers::StridedArrayView1D<T> MeshData::mutableAttribute(const UnsignedInt id) { |
|
|
Containers::StridedArrayView2D<char> data = mutableAttribute(id); |
|
|
#ifdef CORRADE_GRACEFUL_ASSERT /* Sigh. Brittle. Better idea? */ |
|
|
if(!data.stride()[1]) return {}; |
|
|
#endif |
|
|
#ifndef CORRADE_NO_ASSERT |
|
|
if(!checkAttributeTypeCompatibility<T>(_attributes[id], "Trade::MeshData::mutableAttribute():")) return {}; |
|
|
#endif |
|
|
return Containers::arrayCast<1, T>(data); |
|
|
} |
|
|
|
|
|
template<class T, class> Containers::StridedArrayView2D<typename std::remove_extent<T>::type> MeshData::mutableAttribute(const UnsignedInt id) { |
|
|
Containers::StridedArrayView2D<char> data = mutableAttribute(id); |
|
|
#ifdef CORRADE_GRACEFUL_ASSERT /* Sigh. Brittle. Better idea? */ |
|
|
if(!data.stride()[1]) return {}; |
|
|
#endif |
|
|
const MeshAttributeData& attribute = _attributes[id]; |
|
|
#ifndef CORRADE_NO_ASSERT |
|
|
if(!checkAttributeTypeCompatibility<T>(attribute, "Trade::MeshData::mutableAttribute():")) return {}; |
|
|
#endif |
|
|
return Containers::arrayCast<2, typename std::remove_extent<T>::type>(data); |
|
|
} |
|
|
|
|
|
template<class T, class> Containers::StridedArrayView1D<const T> MeshData::attribute(MeshAttribute name, UnsignedInt id) const { |
|
|
Containers::StridedArrayView2D<const char> data = attribute(name, id); |
|
|
#ifdef CORRADE_GRACEFUL_ASSERT /* Sigh. Brittle. Better idea? */ |
|
|
if(!data.stride()[1]) return {}; |
|
|
#endif |
|
|
#ifndef CORRADE_NO_ASSERT |
|
|
if(!checkAttributeTypeCompatibility<T>(_attributes[attributeFor(name, id)], "Trade::MeshData::attribute():")) return {}; |
|
|
#endif |
|
|
return Containers::arrayCast<1, const T>(data); |
|
|
} |
|
|
|
|
|
template<class T, class> Containers::StridedArrayView2D<const typename std::remove_extent<T>::type> MeshData::attribute(MeshAttribute name, UnsignedInt id) const { |
|
|
Containers::StridedArrayView2D<const char> data = attribute(name, id); |
|
|
#ifdef CORRADE_GRACEFUL_ASSERT /* Sigh. Brittle. Better idea? */ |
|
|
if(!data.stride()[1]) return {}; |
|
|
#endif |
|
|
const MeshAttributeData& attribute = _attributes[attributeFor(name, id)]; |
|
|
#ifndef CORRADE_NO_ASSERT |
|
|
if(!checkAttributeTypeCompatibility<T>(attribute, "Trade::MeshData::attribute():")) return {}; |
|
|
#endif |
|
|
return Containers::arrayCast<2, const typename std::remove_extent<T>::type>(data); |
|
|
} |
|
|
|
|
|
template<class T, class> Containers::StridedArrayView1D<T> MeshData::mutableAttribute(MeshAttribute name, UnsignedInt id) { |
|
|
Containers::StridedArrayView2D<char> data = mutableAttribute(name, id); |
|
|
#ifdef CORRADE_GRACEFUL_ASSERT /* Sigh. Brittle. Better idea? */ |
|
|
if(!data.stride()[1]) return {}; |
|
|
#endif |
|
|
#ifndef CORRADE_NO_ASSERT |
|
|
if(!checkAttributeTypeCompatibility<T>(_attributes[attributeFor(name, id)], "Trade::MeshData::mutableAttribute():")) return {}; |
|
|
#endif |
|
|
return Containers::arrayCast<1, T>(data); |
|
|
} |
|
|
|
|
|
template<class T, class> Containers::StridedArrayView2D<typename std::remove_extent<T>::type> MeshData::mutableAttribute(MeshAttribute name, UnsignedInt id) { |
|
|
Containers::StridedArrayView2D<char> data = mutableAttribute(name, id); |
|
|
#ifdef CORRADE_GRACEFUL_ASSERT /* Sigh. Brittle. Better idea? */ |
|
|
if(!data.stride()[1]) return {}; |
|
|
#endif |
|
|
const MeshAttributeData& attribute = _attributes[attributeFor(name, id)]; |
|
|
#ifndef CORRADE_NO_ASSERT |
|
|
if(!checkAttributeTypeCompatibility<T>(attribute, "Trade::MeshData::mutableAttribute():")) return {}; |
|
|
#endif |
|
|
return Containers::arrayCast<2, typename std::remove_extent<T>::type>(data); |
|
|
} |
|
|
|
|
|
}} |
|
|
|
|
|
#endif
|
|
|
|