You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

2399 lines
118 KiB

#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,
2020, 2021 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{2020,06}
*/
#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{2020,06}
@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::GenericGL::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::GenericGL::Tangent or
* @ref Shaders::GenericGL::Tangent4.
* @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. Corresponds to
* @ref Shaders::GenericGL::Bitangent.
* @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::GenericGL::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::GenericGL::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::GenericGL::Color3 or @ref Shaders::GenericGL::Color4.
* @see @ref MeshData::colorsAsArray()
*/
Color,
/**
* (Instanced) object ID for editor selection or scene annotation. Type is
* usually @ref VertexFormat::UnsignedInt, but can be also
* @ref VertexFormat::UnsignedShort or @ref VertexFormat::UnsignedByte.
* Corresponds to @ref Shaders::GenericGL::ObjectId.
* @see @ref MeshData::objectIdsAsArray()
*/
ObjectId,
/**
* 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(),
* @ref meshAttributeCustom(MeshAttribute),
* @ref meshAttributeCustom(UnsignedShort)
*/
Custom = 32768
};
/**
@debugoperatorenum{MeshAttribute}
@m_since{2020,06}
*/
MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, MeshAttribute value);
/**
@brief Whether a mesh attribute is custom
@m_since{2020,06}
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{2020,06}
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{2020,06}
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{2020,06}
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 @relativeref{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 a contiguous index array with a runtime-specified index type
* @param type Index type
* @param data Index data
*
* This overload is picked over @ref MeshIndexData(MeshIndexType, Containers::StridedArrayView1D<const void>)
* if @p data is convertible to a contiguous view. The @p data size is
* then 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::StridedArrayView1D<const UnsignedByte>),
* @ref MeshIndexData(Containers::StridedArrayView1D<const UnsignedShort>) or
* @ref MeshIndexData(Containers::StridedArrayView1D<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.
*/
#ifndef DOXYGEN_GENERATING_OUTPUT
template<class T, class = typename std::enable_if<std::is_convertible<T, Containers::ArrayView<const void>>::value>::type> explicit MeshIndexData(MeshIndexType type, T&& data) noexcept;
#else
explicit MeshIndexData(MeshIndexType type, Containers::ArrayView<const void> data) noexcept;
#endif
/**
* @brief Construct a strided index array with a runtime-specified index type
* @param type Index type
* @param data Index data
* @m_since_latest
*
* If you know the @p type at compile time, you can use one of the
* @ref MeshIndexData(Containers::StridedArrayView1D<const UnsignedByte>),
* @ref MeshIndexData(Containers::StridedArrayView1D<const UnsignedShort>) or
* @ref MeshIndexData(Containers::StridedArrayView1D<const UnsignedInt>)
* constructors, which infer the index type automatically. The view
* doesn't need to be contiguous and the stride can be even zero or
* negative, but note that such data layout is not commonly supported
* by GPU APIs.
*
* 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.
*/
constexpr explicit MeshIndexData(MeshIndexType type, Containers::StridedArrayView1D<const void> data) noexcept;
/**
* @brief Construct with unsigned byte indices
*
* The view doesn't need to be contiguous and the stride can be even
* zero or negative, but note that such data layout is not commonly
* supported by GPU APIs.
*/
constexpr explicit MeshIndexData(Containers::StridedArrayView1D<const UnsignedByte> data) noexcept: MeshIndexData{MeshIndexType::UnsignedByte, data} {}
/**
* @brief Construct with unsigned short indices
*
* The view doesn't need to be contiguous and the stride can be even
* zero or negative, but note that such data layout is not commonly
* supported by GPU APIs.
*/
constexpr explicit MeshIndexData(Containers::StridedArrayView1D<const UnsignedShort> data) noexcept: MeshIndexData{MeshIndexType::UnsignedShort, data} {}
/**
* @brief Construct with unsigned int indices
*
* The view doesn't need to be contiguous and the stride can be even
* zero or negative, but note that such data layout is not commonly
* supported by GPU APIs.
*/
constexpr explicit MeshIndexData(Containers::StridedArrayView1D<const UnsignedInt> data) noexcept: MeshIndexData{MeshIndexType::UnsignedInt, data} {}
/**
* @brief Constructor
*
* Expects that the second dimension of @p data is contiguous and its
* size is either 1, 2 or 4, corresponding to one of the
* @ref MeshIndexType values. The first dimension doesn't need to be
* contiguous and its stride can be even zero or negative, but note
* that such data layout is not commonly supported by GPU APIs. As a
* special case, if second dimension size is 0, the constructor is
* equivalent to @ref MeshIndexData(std::nullptr_t).
*/
explicit MeshIndexData(const Containers::StridedArrayView2D<const char>& data) noexcept;
/** @brief Index type */
constexpr MeshIndexType type() const { return _type; }
/**
* @brief Type-erased index data
*
* In rare cases the stride may be different from the index type size
* and even be zero or negative, such data layouts are however not
* commonly supported by GPU APIs.
*/
constexpr Containers::StridedArrayView1D<const void> data() const { return _data; }
private:
friend MeshData;
MeshIndexType _type;
/* Void so the constructors can be constexpr */
Containers::StridedArrayView1D<const void> _data;
};
/**
@brief Mesh attribute data
@m_since{2020,06}
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>, InterleaveFlags).
@section Trade-MeshAttributeData-usage Usage
The most straightforward usage is constructing an instance from a pair of a
@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 or a 2D view and
supply @ref VertexFormat explicitly.
@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 mainly to avoid pointer patching during data serialization, but also 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: _format{}, _name{}, _isOffsetOnly{false}, _vertexCount{}, _stride{}, _arraySize{}, _data{} {}
/**
* @brief Type-erased constructor
* @param name Attribute name
* @param format Vertex format
* @param data Attribute data
* @param arraySize Array size. Use @cpp 0 @ce for non-array
* attributes.
*
* Expects that @p data stride fits into a signed 16-bit value,
* @p format corresponds to @p name and @p arraySize is zero for
* builtin attributes. The stride can be zero or negative, but note
* that such data layouts are not commonly supported by GPU APIs.
*/
explicit MeshAttributeData(MeshAttribute name, VertexFormat format, const Containers::StridedArrayView1D<const void>& data, UnsignedShort arraySize = 0) noexcept;
/**
* @brief Constructor
* @param name Attribute name
* @param format Vertex format
* @param data Attribute data
* @param arraySize Array size. Use @cpp 0 @ce for non-array
* attributes.
*
* Expects that the second dimension of @p data is contiguous and its
* size matches @p format and @p arraySize, that @p format corresponds
* to @p name and @p arraySize is zero for builtin attributes. The
* stride is expected to fit into a signed 16-bit value. It can be zero
* or negative, but note that such data layouts are not commonly
* supported by GPU APIs.
*/
explicit MeshAttributeData(MeshAttribute name, VertexFormat format, const Containers::StridedArrayView2D<const char>& data, UnsignedShort arraySize = 0) noexcept;
/** @overload */
explicit MeshAttributeData(MeshAttribute name, VertexFormat format, std::nullptr_t, UnsignedShort arraySize = 0) noexcept: MeshAttributeData{nullptr, name, format, nullptr, arraySize} {}
/**
* @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>&, UnsignedShort).
* 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).
* @todo Pick a type based on the combination of T and name? E.g., for
* a Tangent it would pick Vector3sNormalized instead of Vector3s
*/
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, const Containers::StridedArrayView1D<const void>&, UnsignedShort)
* 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. Note that instances created this way
* can't be used in most @ref MeshTools algorithms.
*
* Expects that @p arraySize is zero for builtin attributes and
* @p stride fits into a signed 16-bit value. The stride can be zero or
* negative, but note that such data layouts are not commonly supported
* by GPU APIs.
*
* Additionally, for even more flexibility, the @p vertexCount can be
* overridden 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): _format{}, _name{}, _isOffsetOnly{false}, _vertexCount{0}, _stride{
(CORRADE_CONSTEXPR_ASSERT(padding >= -32768 && padding <= 32767,
"Trade::MeshAttributeData: expected padding to fit into 16 bits but got" << padding), Short(padding))
}, _arraySize{}, _data{nullptr} {}
/**
* @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
*
* In rare cases the stride may be zero or negative, such data layouts
* are however not commonly supported by GPU APIs.
* @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.
* In rare cases the stride of the returned view may be zero or
* negative, such data layouts are however not commonly supported by
* GPU APIs.
* @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 offset-only, 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. In rare cases the stride of the returned view may be zero
* or negative, such data layouts are however not commonly supported by
* GPU APIs.
* @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:
friend MeshData;
/* Delegated to by all ArrayView constructors, which additionally check
either stride or second dimension size. Nullptr first, to avoid
accidental matches as much as possible. */
constexpr explicit MeshAttributeData(std::nullptr_t, MeshAttribute name, VertexFormat format, const Containers::StridedArrayView1D<const void>& data, UnsignedShort arraySize) noexcept;
VertexFormat _format;
MeshAttribute _name;
bool _isOffsetOnly;
/* 1 byte free for more stuff on 64b (23, aligned to 24) and on 32b
(19, aligned to 20) */
/* Vertex count in MeshData is currently 32-bit, so this doesn't need
to be 64-bit either */
UnsignedInt _vertexCount;
/* 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;
UnsignedShort _arraySize;
/* Data pointer last. Its size varies between 32 and 64 bit and having
it last reduces the amount of pain when serializing. */
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;
};
/** @relatesalso MeshAttributeData
@brief Create a non-owning array of @ref MeshAttributeData items
@m_since{2020,06}
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{2020,06}
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-compile Quick usage with MeshTools::compile()
If all you want is to create a @ref GL::Mesh that can be rendered by builtin
shaders, a simple yet efficient way is to use @ref MeshTools::compile():
@snippet MagnumTrade.cpp MeshData-usage-compile
This one-liner uploads the data and configures the mesh for all attributes
known by Magnum that are present in it. It's however rather opaque and doesn't
give you any opportunity to do anything with the mesh data before they get sent
to the GPU. It also won't be able to deal with any custom attributes that the
mesh contains. Continue below to see how to achieve a similar effect with
lower-level APIs.
@m_class{m-note m-success}
@par
A generic mesh setup using the high-level utility is used in the
@ref examples-primitives and @ref examples-viewer examples.
@section Trade-MeshData-usage Basic usage
The second simplest usage is accessing attributes through the convenience
functions @ref positions2DAsArray(), @ref positions3DAsArray(),
@ref tangentsAsArray(), @ref bitangentsAsArray(), @ref normalsAsArray(),
@ref tangentsAsArray(), @ref textureCoordinates2DAsArray(),
@ref colorsAsArray() and @ref objectIdsAsArray(). You're expected to check for
attribute presence first with either @ref hasAttribute() (or
@ref attributeCount(MeshAttribute) const, as there can be multiple sets of
texture coordinates, for example). If you are creating a @ref GL::Mesh, the
usual path forward is then to @ref MeshTools::interleave() attributes of
interest, upload them to a @ref GL::Buffer and configure attribute binding for
the mesh.
The mesh can be also indexed, in which case the index buffer is exposed through
@ref indicesAsArray().
@snippet MagnumTrade.cpp MeshData-usage
@section Trade-MeshData-usage-advanced Advanced usage
The @ref positions2DAsArray(), ... functions shown above always return a
newly-allocated @relativeref{Corrade,Containers::Array} instance in a
well-defined canonical type. While that's convenient and fine at a smaller
scale, it can take significant amount of time for large models. Or maybe the
imported data is 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. There's a lot of possible types of each
attribute (floats, packed integers, ...), so @ref GL::DynamicAttribute accepts
also a pair of @ref GL::Attribute defined by the shader and the actual
@ref VertexFormat, figuring out the GL-specific properties such as component
count or element data type for you:
@snippet MagnumTrade.cpp MeshData-usage-advanced
This approach is especially useful when dealing with custom attributes. See
also @ref MeshTools::compile(const Trade::MeshData&, GL::Buffer&, GL::Buffer&)
for a combined way that gives you both the flexibility needed for custom
attributes as well as the convenience for builtin attributes.
@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, and if not then you may want to make a mutable copy first using
@ref MeshTools::owned(). The following snippet applies a transformation to the
mesh positions:
@snippet MagnumTrade.cpp MeshData-usage-mutable
If the transformation includes a rotation or non-uniform scaling, you may want
to do a similar operation with normals and tangents as well.
@section Trade-MeshData-populating Populating an instance
A @ref MeshData instance by default takes over the ownership of an
@relativeref{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 @relativeref{Corrade,Containers::Array}
you pass @ref DataFlags describing if the data is mutable or not together with
an @relativeref{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 @relativeref{Magnum,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 are 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;
/** @brief Copying is not allowed */
MeshData(const MeshData&) = delete;
/** @brief Move constructor */
MeshData(MeshData&&) noexcept;
~MeshData();
/** @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(),
* @ref MeshTools::primitiveCount(MeshPrimitive, UnsignedInt)
*/
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 Index stride
* @m_since_latest
*
* Stride between consecutive elements in the @ref indexData() array.
* In rare cases the stride may be different from the index type size
* and even be zero or negative, such data layouts are however not
* commonly supported by GPU APIs.
* @see @ref MeshTools::isInterleaved()
*/
Short indexStride() const;
/**
* @brief Mesh indices
*
* For an indexed mesh, the second dimension of the view is guaranteed
* to be contiguous and its size is equal to type size, even in case
* there's zero indices. For a non-indexed mesh, the returned view has
* a zero size in both dimensions. In rare cases the first dimension
* stride may be different from the index type size and even be zero or
* negative, such data layouts are however not commonly supported by
* GPU APIs.
*
* Use the templated overload below to get the indices in a concrete
* type.
* @see @relativeref{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(). In rare cases the first dimension stride may be
* different from the index type size and even be zero or negative,
* such data layouts are however not commonly supported by GPU APIs.
* You can also use the non-templated @ref indicesAsArray() accessor to
* get indices converted to a contiguous 32-bit array, but note that
* such operation involves extra allocation and data conversion.
* @see @ref isIndexed(), @ref attribute(), @ref mutableIndices(),
* @relativeref{Corrade,Containers::StridedArrayView::isContiguous()}
*/
template<class T> Containers::StridedArrayView1D<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::StridedArrayView1D<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.
* @see @ref MeshTools::primitiveCount(MeshPrimitive, UnsignedInt)
*/
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 UnsignedInt(_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. This is also the reason why
* there's no overload taking a @ref MeshAttribute, unlike the other
* 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. In rare cases the stride may be zero or
* negative, such data layouts are however not commonly supported by
* GPU APIs. 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()
*/
Short 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 Find an absolute ID of a named attribute
* @m_since_latest
*
* If @p name isn't present or @p id is not smaller than
* @ref attributeCount(MeshAttribute) const, returns
* @ref Containers::NullOpt. The lookup is done in an
* @f$ \mathcal{O}(n) @f$ complexity with @f$ n @f$ being the attribute
* count.
* @see @ref hasAttribute(), @ref attributeId()
*/
Containers::Optional<UnsignedInt> findAttributeId(MeshAttribute name, UnsignedInt id = 0) const;
/**
* @brief Absolute ID of a named attribute
*
* Like @ref findAttributeId(), but 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. In rare cases the stride may be zero or
* negative, such data layouts are however not commonly supported by
* GPU APIs. The @p id is expected to be smaller than
* @ref attributeCount(MeshAttribute) const.
* @see @ref attributeStride(UnsignedInt) const
*/
Short 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, possibly
* multiplied by array size, and to *absolute* attribute stride for
* implementation-specific values) and is guaranteed to be contiguous.
* In rare cases the first dimension stride may be zero or negative,
* such data layouts are however not commonly supported by GPU APIs.
*
* Use the templated overload below to get the attribute in a concrete
* type.
* @see @relativeref{Corrade,Containers::StridedArrayView::isContiguous()},
* @ref vertexFormatSize(),
* @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. In rare cases the stride of the returned view
* may be zero or negative, such data layouts are however not commonly
* supported by GPU APIs. You can also use the non-templated
* @ref positions2DAsArray(), @ref positions3DAsArray(),
* @ref tangentsAsArray(), @ref bitangentSignsAsArray(),
* @ref bitangentsAsArray(), @ref normalsAsArray(),
* @ref textureCoordinates2DAsArray(), @ref colorsAsArray() and
* @ref objectIdsAsArray() accessors to get common attributes converted
* to usual types in contiguous arrays, but note that these operations
* involve extra allocation and data conversion.
* @see @ref attribute(MeshAttribute, UnsignedInt) const,
* @ref mutableAttribute(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.
* In rare cases the first dimension stride may be zero or negative,
* such data layouts are however not commonly supported by GPU APIs.
*
* Use the templated overload below to get the attribute in a concrete
* type.
* @see @ref attribute(UnsignedInt) const,
* @ref mutableAttribute(MeshAttribute, UnsignedInt),
* @relativeref{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. 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. In rare cases the stride of the returned view may be
* zero or negative, such data layouts are however not commonly
* supported by GPU APIs. You can also use the non-templated
* @ref positions2DAsArray(), @ref positions3DAsArray(),
* @ref tangentsAsArray(), @ref bitangentSignsAsArray(),
* @ref bitangentsAsArray(), @ref normalsAsArray(),
* @ref textureCoordinates2DAsArray(), @ref colorsAsArray() and
* @ref objectIdsAsArray() accessors to get common attributes converted
* to usual types in contiguous arrays, 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(const 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(const 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(const 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. Use @ref bitangentSignsInto() to
* extract the fourth component wit bitangent direction, if present.
* @see @ref vertexCount()
*/
void tangentsInto(const 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(const 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(const 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(const 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(const 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(const Containers::StridedArrayView1D<Color4>& destination, UnsignedInt id = 0) const;
/**
* @brief Object IDs as 32-bit integers
*
* Convenience alternative to @ref attribute(MeshAttribute, UnsignedInt) const
* with @ref MeshAttribute::ObjectId as the first argument. Converts
* the object ID 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 objectIdsInto(), @ref attributeFormat(),
* @ref isVertexFormatImplementationSpecific()
*/
Containers::Array<UnsignedInt> objectIdsAsArray(UnsignedInt id = 0) const;
/**
* @brief Object IDs as 32-bit integers into a pre-allocated view
*
* Like @ref objectIdsAsArray(), 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 objectIdsInto(const Containers::StridedArrayView1D<UnsignedInt>& 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;
friend AbstractSceneConverter;
/* Internal helper without the extra overhead from Optional, returns
~UnsignedInt{} on failure */
UnsignedInt findAttributeIdInternal(MeshAttribute name, UnsignedInt id) const;
/* Like attribute(), but returning just a 1D view */
MAGNUM_TRADE_LOCAL Containers::StridedArrayView1D<const void> attributeDataViewInternal(const MeshAttributeData& attribute) const;
#ifndef CORRADE_NO_ASSERT
template<class T> bool checkVertexFormatCompatibility(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 _indexCount, _vertexCount;
MeshPrimitive _primitive;
MeshIndexType _indexType;
/* 3 byte padding, reserved for 4-byte MeshIndexType */
Short _indexStride;
DataFlags _indexDataFlags, _vertexDataFlags;
/* 4 byte padding on 64bit */
const void* _importerState;
const char* _indices;
Containers::Array<MeshAttributeData> _attributes;
Containers::Array<char> _indexData, _vertexData;
};
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; }
}
#ifndef DOXYGEN_GENERATING_OUTPUT
template<class T, class> MeshIndexData::MeshIndexData(const MeshIndexType type, T&& data) noexcept: _type{type} {
const Containers::ArrayView<const void> erased = data;
const std::size_t typeSize = meshIndexTypeSize(type);
CORRADE_ASSERT(erased.size()%typeSize == 0,
"Trade::MeshIndexData: view size" << erased.size() << "does not correspond to" << type, );
_data = Containers::StridedArrayView1D<const void>{erased, erased.size()/typeSize, std::ptrdiff_t(typeSize)};
}
#endif
constexpr MeshIndexData::MeshIndexData(const MeshIndexType type, const Containers::StridedArrayView1D<const void> data) noexcept:
_type{type},
/* The actual limitation is in MeshData, but better to have it caught here
already */
_data{(CORRADE_CONSTEXPR_ASSERT(data.stride() >= -32768 && data.stride() <= 32767,
"Trade::MeshIndexData: expected stride to fit into 16 bits but got" << data.stride()), data)}
{}
namespace Implementation {
/* 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 vertex format");
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; }
template<> constexpr VertexFormat vertexFormatFor<Matrix3>() { return VertexFormat::Matrix3x3; }
template<> constexpr VertexFormat vertexFormatFor<Matrix3d>() { return VertexFormat::Matrix3x3d; }
template<> constexpr VertexFormat vertexFormatFor<Matrix4>() { return VertexFormat::Matrix4x4; }
template<> constexpr VertexFormat vertexFormatFor<Matrix4d>() { return VertexFormat::Matrix4x4d; }
#ifndef CORRADE_NO_ASSERT
/* 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)) ||
(name == MeshAttribute::ObjectId &&
(format == VertexFormat::UnsignedInt ||
format == VertexFormat::UnsignedShort ||
format == VertexFormat::UnsignedByte)) ||
/* Custom attributes can be anything */
isMeshAttributeCustom(name);
}
constexpr bool isAttributeArrayAllowed(MeshAttribute name) {
return isMeshAttributeCustom(name);
}
#endif
}
constexpr MeshAttributeData::MeshAttributeData(std::nullptr_t, const MeshAttribute name, const VertexFormat format, const Containers::StridedArrayView1D<const void>& data, const UnsignedShort arraySize) noexcept:
_format{(CORRADE_CONSTEXPR_ASSERT(!arraySize || !isVertexFormatImplementationSpecific(format),
"Trade::MeshAttributeData: array attributes can't have an implementation-specific format"), format)},
_name{(CORRADE_CONSTEXPR_ASSERT(Implementation::isVertexFormatCompatibleWithAttribute(name, format),
"Trade::MeshAttributeData:" << format << "is not a valid format for" << name), name)},
_isOffsetOnly{false}, _vertexCount{UnsignedInt(data.size())},
_stride{(CORRADE_CONSTEXPR_ASSERT(data.stride() >= -32768 && data.stride() <= 32767,
"Trade::MeshAttributeData: expected stride to fit into 16 bits but got" << data.stride()),
Short(data.stride()))},
_arraySize{(CORRADE_CONSTEXPR_ASSERT(!arraySize || Implementation::isAttributeArrayAllowed(name),
"Trade::MeshAttributeData:" << name << "can't be an array attribute"), arraySize)},
_data{data.data()} {}
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:
_format{(CORRADE_CONSTEXPR_ASSERT(!arraySize || !isVertexFormatImplementationSpecific(format),
"Trade::MeshAttributeData: array attributes can't have an implementation-specific format"), format)},
_name{(CORRADE_CONSTEXPR_ASSERT(Implementation::isVertexFormatCompatibleWithAttribute(name, format),
"Trade::MeshAttributeData:" << format << "is not a valid format for" << name), name)},
_isOffsetOnly{true}, _vertexCount{vertexCount},
_stride{(CORRADE_CONSTEXPR_ASSERT(stride >= -32768 && stride <= 32767,
"Trade::MeshAttributeData: expected stride to fit into 16 bits but got" << stride),
Short(stride))},
_arraySize{(CORRADE_CONSTEXPR_ASSERT(!arraySize || Implementation::isAttributeArrayAllowed(name),
"Trade::MeshAttributeData:" << name << "can't be an array attribute"), arraySize)},
_data{offset} {}
template<class T> constexpr MeshAttributeData::MeshAttributeData(MeshAttribute name, const Containers::StridedArrayView1D<T>& data) noexcept: MeshAttributeData{nullptr, name, Implementation::vertexFormatFor<typename std::remove_const<T>::type>(), data, 0} {}
template<class T> constexpr MeshAttributeData::MeshAttributeData(MeshAttribute name, const Containers::StridedArrayView2D<T>& data) noexcept: MeshAttributeData{
/* Not using isContiguous<1>() as that's not constexpr */
(CORRADE_CONSTEXPR_ASSERT(data.stride()[1] == sizeof(T), "Trade::MeshAttributeData: second view dimension is not contiguous"), nullptr),
name,
Implementation::vertexFormatFor<typename std::remove_const<T>::type>(),
Containers::StridedArrayView1D<const void>{{data.data(), ~std::size_t{}}, data.size()[0], data.stride()[0]},
UnsignedShort(data.size()[1])
} {}
template<class T> Containers::StridedArrayView1D<const T> MeshData::indices() const {
CORRADE_ASSERT(isIndexed(),
"Trade::MeshData::indices(): the mesh is not indexed", {});
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(): indices are" << _indexType << "but requested" << Implementation::meshIndexTypeFor<T>(), {});
return Containers::arrayCast<1, const T>(data);
}
template<class T> Containers::StridedArrayView1D<T> MeshData::mutableIndices() {
CORRADE_ASSERT(isIndexed(),
"Trade::MeshData::mutableIndices(): the mesh is not indexed", {});
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(): indices are" << _indexType << "but requested" << Implementation::meshIndexTypeFor<T>(), {});
return Containers::arrayCast<1, T>(data);
}
#ifndef CORRADE_NO_ASSERT
template<class T> bool MeshData::checkVertexFormatCompatibility(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 << attribute._name << "is" << attribute._format << "but requested a type equivalent to" << Implementation::vertexFormatFor<typename std::remove_extent<T>::type>(), false);
if(attribute._arraySize) CORRADE_ASSERT(std::is_array<T>::value,
prefix << attribute._name << "is an array attribute, use T[] to access it", false);
else CORRADE_ASSERT(!std::is_array<T>::value,
prefix << attribute._name << "is not an array attribute, can't use T[] to access it", 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(!checkVertexFormatCompatibility<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
#ifndef CORRADE_NO_ASSERT
if(!checkVertexFormatCompatibility<T>(_attributes[id], "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(!checkVertexFormatCompatibility<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
#ifndef CORRADE_NO_ASSERT
if(!checkVertexFormatCompatibility<T>(_attributes[id], "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(const MeshAttribute name, const 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(!checkVertexFormatCompatibility<T>(_attributes[findAttributeIdInternal(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(const MeshAttribute name, const 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(!checkVertexFormatCompatibility<T>(_attributes[findAttributeIdInternal(name, id)], "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 MeshAttribute name, const 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(!checkVertexFormatCompatibility<T>(_attributes[findAttributeIdInternal(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(const MeshAttribute name, const 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(!checkVertexFormatCompatibility<T>(_attributes[findAttributeIdInternal(name, id)], "Trade::MeshData::mutableAttribute():")) return {};
#endif
return Containers::arrayCast<2, typename std::remove_extent<T>::type>(data);
}
}}
#endif