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.
 
 
 
 
 

1734 lines
77 KiB

#ifndef Magnum_Trade_MaterialData_h
#define Magnum_Trade_MaterialData_h
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
2020 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::MaterialData, @ref Magnum::Trade::MaterialAttributeData, enum @ref Magnum::Trade::MaterialAttribute, @ref Magnum::Trade::MaterialAttributeType
* @m_since_latest
*/
#include <Corrade/Containers/Array.h>
#include <Corrade/Containers/EnumSet.h>
#include <Corrade/Containers/Optional.h>
#include <Corrade/Containers/StringView.h>
#include "Magnum/Magnum.h"
#include "Magnum/Math/RectangularMatrix.h"
#include "Magnum/Trade/Trade.h"
#include "Magnum/Trade/visibility.h"
namespace Magnum { namespace Trade {
/**
@brief Material attribute name
@m_since_latest
Convenience aliases to actual attribute name strings. In most cases the alias
is in the same form and capitalization --- so for example
@ref MaterialAttribute::DoubleSided is an alias for @cpp "DoubleSided" @ce, the
only exception is @ref MaterialAttribute::LayerName which is @cpp "$LayerName" @ce.
When this enum si used in
@ref MaterialAttributeData constructors, the data are additionally checked for
type compatibility. Other than that, there is no difference to the string
variants.
@see @ref MaterialAttributeData, @ref MaterialData
*/
enum class MaterialAttribute: UnsignedInt {
/* Zero used for an invalid value */
/**
* Layer name, @ref MaterialAttributeType::String.
*
* Unlike other attributes where string name matches the enum name, in this
* case the corresponding string is @cpp "$LayerName" @ce, done in order to
* have the layer name attribute appear first in each layer and thus
* simplify layer implementation.
* @see @ref MaterialData::layerName()
*/
LayerName = 1,
/**
* Alpha mask, @ref MaterialAttributeType::Float.
*
* If set together with @ref MaterialAttribute::AlphaBlend, blending is
* preferred, however renderers can fall back to alpha-masked rendering.
* Alpha values below this value are meant to be rendered as fully
* transparent and alpha values above this value as fully opaque.
* @see @ref MaterialAlphaMode, @ref MaterialData::alphaMode(),
* @ref MaterialData::alphaMask()
*/
AlphaMask,
/**
* Alpha blending, @ref MaterialAttributeType::Bool.
*
* If @cpp true @ce, the material is expected to be rendered with blending
* enabled and in correct depth order. If @cpp false @ce or not present,
* the material should be treated as opaque. If set together with
* @ref MaterialAttribute::AlphaMask, blending is preferred, however
* renderers can fall back to alpha-masked rendering.
* @see @ref MaterialAlphaMode, @ref MaterialData::alphaMode()
*/
AlphaBlend,
/**
* Double sided, @ref MaterialAttributeType::Bool.
*
* If not present, the default value is @cpp false @ce.
* @see @ref MaterialData::isDoubleSided()
*/
DoubleSided,
/**
* Ambient color for Phong materials, @ref MaterialAttributeType::Vector4.
*
* If @ref MaterialAttribute::AmbientTexture is present as well, these two
* are multiplied together.
* @see @ref PhongMaterialData::ambientColor()
*/
AmbientColor,
/**
* Ambient texture index for Phong materials,
* @ref MaterialAttributeType::UnsignedInt.
*
* If @ref MaterialAttribute::AmbientColor is present as well, these two
* are multiplied together.
* @see @ref PhongMaterialData::ambientTexture()
*/
AmbientTexture,
/**
* Ambient texture transformation matrix for Phong materials,
* @ref MaterialAttributeType::Matrix3x3.
*
* Has a precedence over @ref MaterialAttribute::TextureMatrix if both are
* present.
* @see @ref PhongMaterialData::ambientTextureMatrix()
*/
AmbientTextureMatrix,
/**
* Ambient texture coordinate set index for Phong materials,
* @ref MaterialAttributeType::UnsignedInt.
*
* Has a precedence over @ref MaterialAttribute::TextureCoordinates if both
* are present.
* @see @ref PhongMaterialData::ambientTextureCoordinates()
*/
AmbientTextureCoordinates,
/**
* Diffuse color for Phong materials, @ref MaterialAttributeType::Vector4.
*
* If @ref MaterialAttribute::DiffuseTexture is present as well, these two
* are multiplied together.
* @see @ref PhongMaterialData::diffuseColor()
*/
DiffuseColor,
/**
* Diffuse texture index for Phong materials,
* @ref MaterialAttributeType::UnsignedInt.
*
* If @ref MaterialAttribute::DiffuseColor is present as well, these two
* are multiplied together.
* @see @ref PhongMaterialData::diffuseTexture()
*/
DiffuseTexture,
/**
* Diffuse texture transformation matrix for Phong materials,
* @ref MaterialAttributeType::Matrix3x3.
*
* Has a precedence over @ref MaterialAttribute::TextureMatrix if both are
* present.
* @see @ref PhongMaterialData::diffuseTextureMatrix()
*/
DiffuseTextureMatrix,
/**
* Diffuse texture coordinate set index for Phong materials,
* @ref MaterialAttributeType::UnsignedInt.
*
* Has a precedence over @ref MaterialAttribute::TextureCoordinates if both
* are present.
* @see @ref PhongMaterialData::diffuseTextureCoordinates()
*/
DiffuseTextureCoordinates,
/**
* Specular color for Phong materials, @ref MaterialAttributeType::Vector4.
*
* If @ref MaterialAttribute::SpecularTexture is present as well, these two
* are multiplied together.
* @see @ref PhongMaterialData::specularColor()
*/
SpecularColor,
/**
* Specular texture index for Phong materials,
* @ref MaterialAttributeType::UnsignedInt.
*
* If @ref MaterialAttribute::SpecularColor is present as well, these two
* are multiplied together.
* @see @ref PhongMaterialData::specularTexture()
*/
SpecularTexture,
/**
* Specular texture transformation matrix for Phong materials,
* @ref MaterialAttributeType::Matrix3x3.
*
* Has a precedence over @ref MaterialAttribute::TextureMatrix if both are
* present.
* @see @ref PhongMaterialData::specularTextureMatrix()
*/
SpecularTextureMatrix,
/**
* Specular texture coordinate set index for Phong materials,
* @ref MaterialAttributeType::UnsignedInt.
*
* Has a precedence over @ref MaterialAttribute::TextureCoordinates if both
* are present.
* @see @ref PhongMaterialData::specularTextureCoordinates()
*/
SpecularTextureCoordinates,
/**
* Shininess value for Phong materials, @ref MaterialAttributeType::Float.
*
* @see @ref PhongMaterialData::shininess()
*/
Shininess,
/**
* Tangent-space normal map texture index,
* @ref MaterialAttributeType::UnsignedInt.
* @see @ref PhongMaterialData::normalTexture()
*/
NormalTexture,
/**
* Normal texture transformation matrix,
* @ref MaterialAttributeType::Matrix3x3.
*
* Has a precedence over @ref MaterialAttribute::TextureMatrix if both are
* present.
* @see @ref PhongMaterialData::normalTextureMatrix()
*/
NormalTextureMatrix,
/**
* Normal texture coordinate set index,
* @ref MaterialAttributeType::UnsignedInt.
*
* Has a precedence over @ref MaterialAttribute::TextureCoordinates if both
* are present.
* @see @ref PhongMaterialData::normalTextureCoordinates()
*/
NormalTextureCoordinates,
/**
* Common texture transformation matrix for all textures,
* @ref MaterialAttributeType::Matrix3x3.
*
* @ref MaterialAttribute::AmbientTextureMatrix /
* @ref MaterialAttribute::DiffuseTextureMatrix /
* @ref MaterialAttribute::SpecularTextureMatrix /
* @ref MaterialAttribute::NormalTextureMatrix have a precedence over this
* attribute for given texture, if present.
* @see @ref PhongMaterialData::textureMatrix()
*/
TextureMatrix,
/**
* Common texture coordinate set index for all textures,
* @ref MaterialAttributeType::UnsignedInt.
*
* @ref MaterialAttribute::AmbientTextureCoordinates /
* @ref MaterialAttribute::DiffuseTextureCoordinates /
* @ref MaterialAttribute::SpecularTextureCoordinates /
* @ref MaterialAttribute::NormalTextureCoordinates have a precedence
* over this attribute for given texture, if present.
* @see @ref PhongMaterialData::textureCoordinates()
*/
TextureCoordinates,
};
/**
@debugoperatorenum{MaterialAttribute}
@m_since_latest
*/
MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, MaterialAttribute value);
/**
@brief Material attribute type
@m_since_latest
See @ref MaterialData for more information.
@see @ref MaterialAttribute, @ref MaterialAttributeData,
@ref materialAttributeTypeSize()
*/
enum class MaterialAttributeType: UnsignedByte {
/* Zero used for an invalid value */
Bool = 1, /**< @cpp bool @ce <b></b> */
Float, /**< @ref Magnum::Float "Float" */
Deg, /**< @ref Magnum::Deg "Deg" */
Rad, /**< @ref Magnum::Rad "Rad" */
UnsignedInt, /**< @ref Magnum::UnsignedInt "UnsignedInt" */
Int, /**< @ref Magnum::Int "Int" */
UnsignedLong, /**< @ref Magnum::UnsignedLong "UnsignedLong" */
Long, /**< @ref Magnum::Long "Long" */
Vector2, /**< @ref Magnum::Vector2 "Vector2" */
Vector2ui, /**< @ref Magnum::Vector2ui "Vector2ui" */
Vector2i, /**< @ref Magnum::Vector2i "Vector2i" */
Vector3, /**< @ref Magnum::Vector3 "Vector3" */
Vector3ui, /**< @ref Magnum::Vector3ui "Vector3ui" */
Vector3i, /**< @ref Magnum::Vector3i "Vector3i" */
Vector4, /**< @ref Magnum::Vector4 "Vector4" */
Vector4ui, /**< @ref Magnum::Vector4ui "Vector4ui" */
Vector4i, /**< @ref Magnum::Vector4i "Vector4i" */
Matrix2x2, /**< @ref Magnum::Matrix2x2 "Matrix2x2" */
Matrix2x3, /**< @ref Magnum::Matrix2x3 "Matrix2x3" */
Matrix2x4, /**< @ref Magnum::Matrix2x4 "Matrix2x4" */
Matrix3x2, /**< @ref Magnum::Matrix3x2 "Matrix3x2" */
Matrix3x3, /**< @ref Magnum::Matrix3x3 "Matrix3x3" */
Matrix3x4, /**< @ref Magnum::Matrix3x4 "Matrix3x4" */
Matrix4x2, /**< @ref Magnum::Matrix4x2 "Matrix4x2" */
Matrix4x3, /**< @ref Magnum::Matrix4x3 "Matrix4x3" */
/* Matrix4x4 not present */
/**
* @cpp const void* @ce, type is not preserved. For convenience it's
* possible to retrieve the value by calling @cpp attribute<const T>() @ce
* with an arbitrary `T` but the user has to ensure the type is correct.
*/
Pointer,
/**
* @cpp void* @ce, type is not preserved. For convenience it's possible to
* retrieve the value by calling @cpp attribute<T>() @ce with an arbitrary
* `T` but the user has to ensure the type is correct.
*/
MutablePointer,
/**
* Null-terminated string. Can be stored using any type convertible to
* @ref Corrade::Containers::StringView, retrieval has to be done using
* @ref Corrade::Containers::StringView.
*/
String
};
/**
@brief Byte size of a material attribute type
@m_since_latest
Can't be used with @ref MaterialAttributeType::String, as the size varies
depending on the value.
*/
MAGNUM_TRADE_EXPORT std::size_t materialAttributeTypeSize(MaterialAttributeType type);
/**
@debugoperatorenum{MaterialAttributeType}
@m_since_latest
*/
MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, MaterialAttributeType value);
namespace Implementation {
template<class> struct MaterialAttributeTypeFor;
enum: std::size_t { MaterialAttributeDataSize = 64 };
}
/**
@brief Material attribute data
@m_since_latest
See @ref MaterialData for more information.
*/
class MAGNUM_TRADE_EXPORT MaterialAttributeData {
public:
/**
* @brief Default constructor
*
* Leaves contents at unspecified values. Provided as a convenience for
* initialization of the attribute array for @ref MaterialData,
* expected to be replaced with concrete values later.
*/
constexpr explicit MaterialAttributeData() noexcept: _data{} {}
/**
* @brief Construct with a string name
* @param name Attribute name
* @param value Attribute value
*
* The @p name together with @p value is expected to fit into 62 bytes.
* @ref MaterialAttributeType is inferred from the type passed.
*
* This function is useful in @cpp constexpr @ce contexts and for
* creating custom material attributes. For known attributes prefer to
* use @ref MaterialAttributeData(MaterialAttribute, const T&) if you
* don't need @cpp constexpr @ce, as it additionally checks that given
* attribute has the expected type.
*/
template<class T
#ifndef DOXYGEN_GENERATING_OUTPUT
, class = typename std::enable_if<!std::is_convertible<const T&, Containers::StringView>::value>::type
#endif
> constexpr /*implicit*/ MaterialAttributeData(Containers::StringView name, const T& value) noexcept;
/**
* @brief Construct with a string name and string value
* @param name Attribute name
* @param value Attribute value
*
* The combined length of @p name and @p value is expected to fit into
* 61 bytes. Type is set to @ref MaterialAttributeType::String.
*
* This function is useful in @cpp constexpr @ce contexts and for
* creating custom material attributes. For known attributes prefer to
* use @ref MaterialAttributeData(MaterialAttribute, const T&) if you
* don't need @cpp constexpr @ce, as it additionally checks that given
* attribute has the expected type.
*/
constexpr /*implicit*/ MaterialAttributeData(Containers::StringView name, Containers::StringView value) noexcept;
#ifndef DOXYGEN_GENERATING_OUTPUT
/* "Sure can't be constexpr" overloads to avoid going through the
*insane* overload puzzle when not needed */
template<class T, class = typename std::enable_if<!std::is_convertible<const T&, Containers::StringView>::value>::type> /*implicit*/ MaterialAttributeData(const char* name, const T& value) noexcept: MaterialAttributeData{name, Implementation::MaterialAttributeTypeFor<T>::type(), sizeof(T), &value} {}
/*implicit*/ MaterialAttributeData(const char* name, Containers::StringView value) noexcept: MaterialAttributeData{name, MaterialAttributeType::String, 0, &value} {}
#endif
/**
* @brief Construct with a predefined name
* @param name Attribute name
* @param value Attribute value
*
* Compared to @ref MaterialAttributeData(Containers::StringView, const T&)
* checks that the attribute is in expected type. The
* @ref MaterialAttribute gets converted to a corresponding string
* name. Apart from the type check, the following two instances are
* equivalent:
*
* @snippet MagnumTrade.cpp MaterialAttributeData-name
*/
template<class T
#ifndef DOXYGEN_GENERATING_OUTPUT
, class = typename std::enable_if<!std::is_convertible<const T&, Containers::StringView>::value>::type
#endif
> /*implicit*/ MaterialAttributeData(MaterialAttribute name, const T& value) noexcept: MaterialAttributeData{name, Implementation::MaterialAttributeTypeFor<T>::type(), &value} {}
/**
* @brief Construct with a predefined name and string value
* @param name Attribute name
* @param value Attribute value
*
* Compared to @ref MaterialAttributeData(Containers::StringView, Containers::StringView)
* checks that the attribute is in expected type. The
* @ref MaterialAttribute gets converted to a corresponding string
* name. Apart from the type check, the following two instances are
* equivalent:
*
* @snippet MagnumTrade.cpp MaterialAttributeData-name
*/
/*implicit*/ MaterialAttributeData(MaterialAttribute name, Containers::StringView value) noexcept: MaterialAttributeData{name, MaterialAttributeType::String, &value} {}
/**
* @brief Construct from a type-erased value
* @param name Attribute name
* @param type Attribute type
* @param value Type-erased value
*
* In case @p type is not @ref MaterialAttributeType::String, copies a
* number of bytes according to @ref materialAttributeTypeSize() from
* @p value. The @p name together with @p value is expected to fit into
* 62 bytes.
*
* In case @p type is @ref MaterialAttributeType::String, @p value is
* expected to point to a @ref Containers::StringView. The combined
* length of @p name and @p value strings is expected to fit into 61
* bytes.
*/
/*implicit*/ MaterialAttributeData(Containers::StringView name, MaterialAttributeType type, const void* value) noexcept;
/**
* @brief Construct with a predefined name
* @param name Attribute name
* @param type Attribute type
* @param value Attribute value
*
* Compared to @ref MaterialAttributeData(Containers::StringView, MaterialAttributeType, const void*)
* checks that the attribute is in expected type. The
* @ref MaterialAttribute gets converted to a corresponding string
* name.
*/
/*implicit*/ MaterialAttributeData(MaterialAttribute name, MaterialAttributeType type, const void* value) noexcept;
/** @brief Attribute type */
MaterialAttributeType type() const { return _data.type; }
/**
* @brief Attribute name
*
* The returned view always has
* @ref Corrade::Containers::StringViewFlag::NullTerminated set.
*/
Containers::StringView name() const { return _data.data + 1; }
/**
* @brief Type-erased attribute value
*
* Cast the pointer to a concrete type based on @ref type(). Note that
* in case of a @ref MaterialAttributeType::Pointer or a
* @ref MaterialAttributeType::MutablePointer, returns a
* *pointer to a pointer*, not the pointer value itself.
*
* In case of a @ref MaterialAttributeType::String, returns a
* null-terminated @cpp const char* @ce (not a pointer to
* @ref Containers::StringView). This doesn't preserve the actual
* string size in case the string data contain zero bytes, thus prefer
* to use typed access in that case.
*/
const void* value() const;
/**
* @brief Attribute value
*
* Expects that @p T corresponds to @ref type().
*/
template<class T> T value() const;
private:
friend MaterialData;
explicit MaterialAttributeData(Containers::StringView name, const MaterialAttributeType type, std::size_t typeSize, const void* value) noexcept;
/* Most of this is needed only for the constexpr constructor (yay C++),
the actual data layout is
|------------- x B ------------|
+------+------- .. -----+------+
| type | name .. \0 | data |
| 1 B | (x - n - 2) B | n B |
+------+------- .. -----+------+
where
- `x` is Implementation::MaterialAttributeDataSize,
- `type` is an 8-bit MaterialAttributeType,
- `data` is of size matching `type`, at the offset of
`(x - materialAttributeTypeSize(type))` B,
- `name` is a null-terminated string filling the rest.
This way the name is always at the same offset to make binary search
lookup fast and efficient, and data being at the end (instead of
right after the null-terminated string) makes them accessible in O(1)
as well. */
struct StringData {
template<std::size_t ...sequence> constexpr explicit StringData(MaterialAttributeType type, Containers::StringView name, Containers::StringView value, Math::Implementation::Sequence<sequence...>): type{type}, nameValue{(sequence < name.size() ? name[sequence] : (sequence - (Implementation::MaterialAttributeDataSize - value.size() - 3) < value.size() ? value[sequence - (Implementation::MaterialAttributeDataSize - value.size() - 3)] : '\0'))...}, size{UnsignedByte(value.size())} {}
constexpr explicit StringData(MaterialAttributeType type, Containers::StringView name, Containers::StringView value): StringData{type, name, value, typename Math::Implementation::GenerateSequence<Implementation::MaterialAttributeDataSize - 2>::Type{}} {}
MaterialAttributeType type;
char nameValue[Implementation::MaterialAttributeDataSize - 2];
UnsignedByte size;
};
union ErasedScalar {
constexpr explicit ErasedScalar(Float value): f{value} {}
constexpr explicit ErasedScalar(Deg value): f{Float(value)} {}
constexpr explicit ErasedScalar(Rad value): f{Float(value)} {}
constexpr explicit ErasedScalar(UnsignedInt value): u{value} {}
constexpr explicit ErasedScalar(Int value): i{value} {}
Float f;
UnsignedInt u;
Int i;
};
union ErasedLongScalar {
constexpr explicit ErasedLongScalar(UnsignedLong value): u{value} {}
constexpr explicit ErasedLongScalar(Long value): i{value} {}
UnsignedLong u;
Long i;
};
template<std::size_t size> union ErasedVector {
constexpr explicit ErasedVector(const Math::Vector<size, Float>& value): f{value} {}
constexpr explicit ErasedVector(const Math::Vector<size, UnsignedInt>& value): u{value} {}
constexpr explicit ErasedVector(const Math::Vector<size, Int>& value): i{value} {}
Math::Vector<size, Float> f;
Math::Vector<size, UnsignedInt> u;
Math::Vector<size, Int> i;
};
template<std::size_t cols, std::size_t rows> union ErasedMatrix {
constexpr explicit ErasedMatrix(const Math::RectangularMatrix<cols, rows, Float>& value): a{value} {}
constexpr explicit ErasedMatrix(const Math::RectangularMatrix<rows, cols, Float>& value): b{value} {}
Math::RectangularMatrix<cols, rows, Float> a;
Math::RectangularMatrix<rows, cols, Float> b;
};
template<class T> struct Data {
template<class U, std::size_t ...sequence> constexpr explicit Data(MaterialAttributeType type, Containers::StringView name, const U& value, Math::Implementation::Sequence<sequence...>): type{type}, name{(sequence < name.size() ? name[sequence] : '\0')...}, value{value} {}
template<class U> constexpr explicit Data(MaterialAttributeType type, Containers::StringView name, const U& value): Data{type, name, value, typename Math::Implementation::GenerateSequence<63 - sizeof(T)>::Type{}} {}
MaterialAttributeType type;
char name[Implementation::MaterialAttributeDataSize - sizeof(MaterialAttributeType) - sizeof(T)];
T value;
};
union CORRADE_ALIGNAS(8) Storage {
constexpr explicit Storage() noexcept: data{} {}
constexpr explicit Storage(Containers::StringView name, Containers::StringView value) noexcept: s{MaterialAttributeType::String, name, value} {}
template<class T> constexpr explicit Storage(typename std::enable_if<sizeof(T) == 1, MaterialAttributeType>::type type, Containers::StringView name, const T& value) noexcept: _1{type, name, value} {}
template<class T> constexpr explicit Storage(typename std::enable_if<sizeof(T) == 4 && !std::is_pointer<T>::value, MaterialAttributeType>::type type, Containers::StringView name, const T& value) noexcept: _4{type, name, value} {}
template<class T> constexpr explicit Storage(typename std::enable_if<sizeof(T) == 8 && !Math::IsVector<T>::value && !std::is_pointer<T>::value, MaterialAttributeType>::type type, Containers::StringView name, const T& value) noexcept: _8{type, name, value} {}
template<class T> constexpr explicit Storage(typename std::enable_if<sizeof(T) == 8 && Math::IsVector<T>::value && !std::is_pointer<T>::value, MaterialAttributeType>::type type, Containers::StringView name, const T& value) noexcept: _8v{type, name, value} {}
constexpr explicit Storage(MaterialAttributeType type, Containers::StringView name, const void* value) noexcept: p{type, name, value} {}
template<class T> constexpr explicit Storage(typename std::enable_if<sizeof(T) == 12, MaterialAttributeType>::type type, Containers::StringView name, const T& value) noexcept: _12{type, name, value} {}
template<class T> constexpr explicit Storage(typename std::enable_if<sizeof(T) == 16 && Math::IsVector<T>::value, MaterialAttributeType>::type type, Containers::StringView name, const T& value) noexcept: _16{type, name, value} {}
template<class T> constexpr explicit Storage(typename std::enable_if<sizeof(T) == 16 && !Math::IsVector<T>::value, MaterialAttributeType>::type type, Containers::StringView name, const T& value) noexcept: _16m{type, name, value} {}
template<class T> constexpr explicit Storage(typename std::enable_if<sizeof(T) == 24, MaterialAttributeType>::type type, Containers::StringView name, const T& value) noexcept: _24{type, name, value} {}
template<class T> constexpr explicit Storage(typename std::enable_if<sizeof(T) == 32, MaterialAttributeType>::type type, Containers::StringView name, const T& value) noexcept: _32{type, name, value} {}
template<class T> constexpr explicit Storage(typename std::enable_if<sizeof(T) == 36, MaterialAttributeType>::type type, Containers::StringView name, const T& value) noexcept: _36{type, name, value} {}
template<class T> constexpr explicit Storage(typename std::enable_if<sizeof(T) == 48, MaterialAttributeType>::type type, Containers::StringView name, const T& value) noexcept: _48{type, name, value} {}
MaterialAttributeType type;
char data[Implementation::MaterialAttributeDataSize];
StringData s;
Data<bool> _1;
Data<const void*> p;
Data<ErasedScalar> _4;
Data<ErasedLongScalar> _8;
Data<ErasedVector<2>> _8v;
Data<ErasedVector<3>> _12;
Data<ErasedVector<4>> _16;
Data<Math::RectangularMatrix<2, 2, Float>> _16m;
Data<ErasedMatrix<2, 3>> _24;
Data<ErasedMatrix<2, 4>> _32;
Data<Math::RectangularMatrix<3, 3, Float>> _36;
Data<ErasedMatrix<3, 4>> _48;
} _data;
static_assert(sizeof(Storage) == Implementation::MaterialAttributeDataSize, "something is off, huh");
};
/**
@brief Material type
@see @ref MaterialTypes, @ref MaterialData::types()
*/
enum class MaterialType: UnsignedInt {
/**
* Phong. Use @ref PhongMaterialData for convenience attribute access.
*/
Phong = 1 << 0
};
/** @debugoperatorenum{MaterialType} */
MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, MaterialType value);
/**
@brief Material types
@m_since_latest
@see @ref MaterialData::types()
*/
typedef Containers::EnumSet<MaterialType> MaterialTypes;
CORRADE_ENUMSET_OPERATORS(MaterialTypes)
/** @debugoperatorenum{MaterialTypes} */
MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, MaterialTypes value);
/**
@brief Material alpha mode
Convenience access to @ref MaterialAttribute::AlphaBlend and
@ref MaterialAttribute::AlphaMask attributes.
@see @ref MaterialData::alphaMode(), @ref MaterialData::alphaMask()
*/
enum class MaterialAlphaMode: UnsignedByte {
/** Alpha value is ignored and the rendered output is fully opaque. */
Opaque,
/**
* The rendered output is either fully transparent or fully opaque,
* depending on the alpha value and specified
* @ref MaterialData::alphaMask() value.
*/
Mask,
/**
* The alpha value is used to combine source and destination colors using
* additive blending.
*/
Blend
};
/** @debugoperatorenum{MaterialAlphaMode} */
MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, MaterialAlphaMode value);
/**
@brief Material data
@m_since_latest
Key-value store for material attributes in one of the types defined by
@ref MaterialAttributeType.
@section Trade-MaterialData-representation Internal representation
The attributes are stored sorted by the key in a contiguous array, with each
@ref MaterialAttributeData item occupying 64 bytes. The item contains a 1-byte
type identifier, the actual value and the rest is occupied with null-terminated
name. This means the name length can vary from 14 bytes for
@ref Magnum::Matrix3x4 "Matrix3x4" / @ref Magnum::Matrix4x3 "Matrix4x3" to 61
bytes for @cpp bool @ce (excluding null terminator). As each item has a fixed
size anyway, there's no value in supporting space-efficient 8-, 16- or
half-float types. Conversely, @ref Magnum::Double "Double" types are currently
not supported either as there isn't currently seen any need for extended
precision.
@m_class{m-block m-warning}
@par Max representable data size
With the current design, @ref MaterialAttributeData is 64 bytes and in
order to fit a type identifier and a string attribute name of a reasonable
length, the maximum data size is capped to 48 bytes. This means
@ref Magnum::Matrix4x4 "Matrix4x4" isn't listed among supported types, but
it shouldn't be a problem in practice --- ever an arbitrary color
correction matrix is just 3x4 values with the bottom row being always
@f$ \begin{pmatrix} 0 & 0 & 0 & 1 \end{pmatrix} @f$. This restriction might
get lifted eventually.
*/
class MAGNUM_TRADE_EXPORT MaterialData {
public:
#ifdef MAGNUM_BUILD_DEPRECATED
/**
* @brief Material flag
* @m_deprecated_since_latest The flags are no longer stored directly
* but generated on-the-fly from attribute data, which makes them
* less efficient than calling @ref hasAttribute(),
* @ref isDoubleSided() etc.
*
* This enum is further extended in subclasses.
* @see @ref Flags, @ref flags()
*/
enum class CORRADE_DEPRECATED_ENUM("use hasAttribute() etc. instead") Flag: UnsignedInt {
/**
* The material is double-sided. Back faces should not be culled
* away but rendered as well, with normals flipped for correct
* lighting.
*/
DoubleSided = 1 << 0
};
/**
* @brief Material flags
* @m_deprecated_since_latest The flags are no longer stored directly
* but generated on-the-fly from attribute data, which makes them
* less efficient than calling @ref hasAttribute(),
* @ref isDoubleSided() etc.
*
* This enum is extended in subclasses.
* @see @ref flags()
*/
CORRADE_IGNORE_DEPRECATED_PUSH /* GCC warns about Flag, ugh */
typedef CORRADE_DEPRECATED("use hasAttribute() etc. instead") Containers::EnumSet<Flag> Flags;
CORRADE_IGNORE_DEPRECATED_POP
#endif
/**
* @brief Construct
* @param types Which material types are described by this
* data. Can be an empty set.
* @param attributeData Attribute data
* @param importerState Importer-specific state
*
* The @p attributeData gets sorted by name internally, expecting no
* duplicates.
*/
explicit MaterialData(MaterialTypes types, Containers::Array<MaterialAttributeData>&& attributeData, const void* importerState = nullptr) noexcept: MaterialData{types, std::move(attributeData), nullptr, importerState} {}
/** @overload */
/* Not noexcept because allocation happens inside */
explicit MaterialData(MaterialTypes types, std::initializer_list<MaterialAttributeData> attributeData, const void* importerState = nullptr): MaterialData{types, attributeData, {}, importerState} {}
/**
* @brief Construct a non-owned material data
* @param types Which material types are described by
* this data. Can be an empty set.
* @param attributeDataFlags Ignored. Used only for a safer
* distinction from the owning constructor.
* @param attributeData Attribute data
* @param importerState Importer-specific state
*
* The @p attributeData is expected to be already sorted by name,
* without duplicates.
*/
explicit MaterialData(MaterialTypes types, DataFlags attributeDataFlags, Containers::ArrayView<const MaterialAttributeData> attributeData, const void* importerState = nullptr) noexcept: MaterialData{types, attributeDataFlags, attributeData, {}, nullptr, importerState} {}
/**
* @brief Construct with layers
* @param types Which material types are described by this
* data. Can be an empty set.
* @param attributeData Attribute data
* @param layerData Layer offset data
* @param importerState Importer-specific state
*
* The @p attributeData gets sorted by name internally, expecting no
* duplicates inside each layer. The @p layerData is expected to be
* either empty or a monotonically non-decreasing sequence of offsets
* not larger than @p attributeData size, with *i*-th item specifying
* end offset of *i*-th layer.
*/
explicit MaterialData(MaterialTypes types, Containers::Array<MaterialAttributeData>&& attributeData, Containers::Array<UnsignedInt>&& layerData, const void* importerState = nullptr) noexcept;
/** @overload */
/* Not noexcept because allocation happens inside */
explicit MaterialData(MaterialTypes types, std::initializer_list<MaterialAttributeData> attributeData, std::initializer_list<UnsignedInt> layerData, const void* importerState = nullptr);
/**
* @brief Construct a non-owned material data with layers
* @param types Which material types are described by
* this data. Can be an empty set.
* @param attributeDataFlags Ignored. Used only for a safer
* distinction from the owning constructor.
* @param attributeData Attribute data
* @param layerDataFlags Ignored. Used only for a safer
* distinction from the owning constructor.
* @param layerData Layer offset data
* @param importerState Importer-specific state
*
* The @p data is expected to be already sorted by name, without
* duplicates inside each layer. The @p layerData is expected to be
* either empty or a monotonically non-decreasing sequence of offsets
* not larger than @p attributeData size, with *i*-th item specifying
* end offset of *i*-th layer.
*/
/* The second (ignored) DataFlags is present in order to make it ready
for a possible extension where only one of the data is non-owned.
But so far I didn't see a need. */
explicit MaterialData(MaterialTypes types, DataFlags attributeDataFlags, Containers::ArrayView<const MaterialAttributeData> attributeData, DataFlags layerDataFlags, Containers::ArrayView<const UnsignedInt> layerData, const void* importerState = nullptr) noexcept;
~MaterialData();
/** @brief Copying is not allowed */
MaterialData(const MaterialData&) = delete;
/** @brief Move constructor */
MaterialData(MaterialData&&) noexcept;
/** @brief Copying is not allowed */
MaterialData& operator=(const MaterialData&) = delete;
/** @brief Move assignment */
MaterialData& operator=(MaterialData&&) noexcept;
/**
* @brief Material types
*
* Each type indicates that the material data can be interpreted as
* given type. For custom materials the set can also be empty.
*/
MaterialTypes types() const { return _types; }
#ifdef MAGNUM_BUILD_DEPRECATED
/**
* @brief Material type
* @m_deprecated_since_latest Use @ref types() instead.
*/
CORRADE_DEPRECATED("use types() instead") MaterialType type() const {
return MaterialType(UnsignedInt(_types & MaterialType::Phong));
}
#endif
/**
* @brief Raw layer offset data
*
* May return @cpp nullptr @ce if the material doesn't have any extra
* layers.
* @see @ref releaseLayerData()
*/
Containers::ArrayView<const UnsignedInt> layerData() const { return _layerOffsets; }
/**
* @brief Raw attribute data
*
* Returns @cpp nullptr @ce if the material has no attributes.
* @see @ref releaseAttributeData()
*/
Containers::ArrayView<const MaterialAttributeData> attributeData() const { return _data; }
/**
* @brief Layer count
*
* There's always at least the base material, so this function returns
* at least @cpp 1 @ce.
*/
UnsignedInt layerCount() const {
return _layerOffsets.empty() ? 1 : _layerOffsets.size();
}
/**
* @brief Whether a material has given named layer
*
* Layers with no name assigned are skipped. The base material (layer
* @cpp 0 @ce is skipped as well) to avoid confusing base material with
* a layer. If you want to create a material consisting of just a
* layer, use @cpp 0 @ce for the first layer offset in the constructor.
* @see @ref hasAttribute()
*/
bool hasLayer(Containers::StringView layer) const;
/**
* @brief ID of a named layer
*
* The @p layer is expected to exist.
* @see @ref hasLayer()
*/
UnsignedInt layerId(Containers::StringView layer) const;
/**
* @brief Layer name
*
* Retrieves a @ref MaterialAttribute::LayerName attribute from given
* layer, if present. Returns a @cpp nullptr @ce view if the layer
* has no name, and an empty non-null view if the layer name is empty.
* The @p layer is expected to be smaller than @ref layerCount() const.
*
* The name, if present, is ignored for the base material (layer
* @cpp 0 @ce) to avoid confsing base material with a layer. If you
* want to create a material consisting of just a layer, use @cpp 0 @ce
* for the first layer offset in the constructor.
*/
Containers::StringView layerName(UnsignedInt layer) const;
/**
* @brief Attribute count in given layer
*
* The @p layer is expected to be smaller than @ref layerCount() const.
*/
UnsignedInt attributeCount(UnsignedInt layer) const;
/**
* @brief Attribute count in a named layer
*
* The @p layer is expected to exist.
* @see @ref hasLayer()
*/
UnsignedInt attributeCount(Containers::StringView layer) const;
/**
* @brief Attribute count in the base material
*
* Equivalent to calling @ref attributeCount(UnsignedInt) const with
* @p layer set to @cpp 0 @ce.
*/
UnsignedInt attributeCount() const { return attributeCount(0); }
/**
* @brief Whether a material layer has given attribute
*
* The @p layer is expected to be smaller than @ref layerCount() const.
* @see @ref tryAttribute(), @ref attributeOr(), @ref hasLayer()
*/
bool hasAttribute(UnsignedInt layer, Containers::StringView name) const;
bool hasAttribute(UnsignedInt layer, MaterialAttribute name) const; /**< @overload */
/**
* @brief Whether a named material layer has given attribute
*
* The @p layer is expected to exist.
* @see @ref tryAttribute(), @ref attributeOr(), @ref hasLayer()
*/
bool hasAttribute(Containers::StringView layer, Containers::StringView name) const;
bool hasAttribute(Containers::StringView layer, MaterialAttribute name) const; /**< @overload */
/**
* @brief Whether the base material has given attribute
*
* Equivalent to calling @ref hasAttribute(UnsignedInt, Containers::StringView) const
* with @p layer set to @cpp 0 @ce.
* @see @ref tryAttribute(), @ref attributeOr()
*/
bool hasAttribute(Containers::StringView name) const {
return hasAttribute(0, name);
}
bool hasAttribute(MaterialAttribute name) const {
return hasAttribute(0, name);
} /**< @overload */
/**
* @brief ID of a named attribute in given material layer
*
* The @p layer is expected to be smaller than @ref layerCount() const
* and @p name is expected to exist in that layer.
* @see @ref hasAttribute()
*/
UnsignedInt attributeId(UnsignedInt layer, Containers::StringView name) const;
UnsignedInt attributeId(UnsignedInt layer, MaterialAttribute name) const; /**< @overload */
/**
* @brief ID of a named attribute in a named material layer
*
* The @p layer is expected to exist and @p name is expected to exist
* in that layer.
* @see @ref hasLayer(), @ref hasAttribute()
*/
UnsignedInt attributeId(Containers::StringView layer, Containers::StringView name) const;
UnsignedInt attributeId(Containers::StringView layer, MaterialAttribute name) const; /**< @overload */
/**
* @brief ID of a named attribute in the base material
*
* Equivalent to calling @ref attributeId(UnsignedInt, Containers::StringView) const
* with @p layer set to @cpp 0 @ce.
*/
UnsignedInt attributeId(Containers::StringView name) const {
return attributeId(0, name);
}
UnsignedInt attributeId(MaterialAttribute name) const {
return attributeId(0, name);
} /**< @overload */
/**
* @brief Name of an attribute in given material layer
*
* The @p layer is expected to be smaller than @ref layerCount() const
* and the @p id is expected to be smaller than @ref attributeCount(UnsignedInt) const
* in that layer. The returned view always has
* @ref Corrade::Containers::StringViewFlag::NullTerminated set.
* @see @ref attributeType()
*/
Containers::StringView attributeName(UnsignedInt layer, UnsignedInt id) const;
/**
* @brief Name of an attribute in a named material layer
*
* The @p layer is expected to exist and the @p id is expected to be smaller than @ref attributeCount(UnsignedInt) const
* in that layer.
* @see @ref hasLayer()
*/
Containers::StringView attributeName(Containers::StringView layer, UnsignedInt id) const;
/**
* @brief Name of an attribute in the base material
*
* Equivalent to calling @ref attributeName(UnsignedInt, UnsignedInt) const
* with @p layer set to @cpp 0 @ce.
*/
Containers::StringView attributeName(UnsignedInt id) const {
return attributeName(0, id);
}
/**
* @brief Type of an attribute in given material layer
*
* The @p layer is expected to be smaller than @ref layerCount() const
* and @p id is expected to be smaller than @ref attributeCount(UnsignedInt) const
* in that layer.
* @see @ref attributeName()
*/
MaterialAttributeType attributeType(UnsignedInt layer, UnsignedInt id) const;
/**
* @brief Type of a named attribute in given material layer
*
* The @p layer is expected to be smaller than @ref layerCount() const
* and @p name is expected to exist in that layer.
* @see @ref hasAttribute()
*/
MaterialAttributeType attributeType(UnsignedInt layer, Containers::StringView name) const;
/** @overload */
MaterialAttributeType attributeType(UnsignedInt layer, MaterialAttribute name) const;
/**
* @brief Type of an attribute in a named material layer
*
* The @p layer is expected to exist and the @p id is expected to be smaller than @ref attributeCount(UnsignedInt) const
* in that layer.
* @see @ref hasLayer()
*/
MaterialAttributeType attributeType(Containers::StringView layer, UnsignedInt id) const;
/**
* @brief Type of a named attribute in a named material layer
*
* The @p layer is expected to exist and @p name is expected to exist
* in that layer.
* @see @ref hasLayer(), @ref hasAttribute()
*/
MaterialAttributeType attributeType(Containers::StringView layer, Containers::StringView name) const;
MaterialAttributeType attributeType(Containers::StringView layer, MaterialAttribute name) const; /**< @overload */
/**
* @brief Type of an attribute in the base material
*
* Equivalent to calling @ref attributeType(UnsignedInt, UnsignedInt) const
* with @p layer set to @cpp 0 @ce.
*/
MaterialAttributeType attributeType(UnsignedInt id) const {
return attributeType(0, id);
}
/**
* @brief Type of a named attribute in the base material
*
* Equivalent to calling @ref attributeType(UnsignedInt, Containers::StringView) const
* with @p layer set to @cpp 0 @ce.
*/
MaterialAttributeType attributeType(Containers::StringView name) const {
return attributeType(0, name);
}
MaterialAttributeType attributeType(MaterialAttribute name) const {
return attributeType(0, name);
} /**< @overload */
/**
* @brief Type-erased value of an attribute in given material layer
*
* The @p layer is expected to be smaller than @ref layerCount() const
* and @p id is expected to be smaller than @ref attributeCount(UnsignedInt) const
* in that layer. Cast the pointer to a concrete type based on
* @ref type(). Note that in case of a
* @ref MaterialAttributeType::Pointer or a
* @ref MaterialAttributeType::MutablePointer, returns a
* *pointer to a pointer*, not the pointer value itself.
*
* In case of a @ref MaterialAttributeType::String returns a
* null-terminated @cpp const char* @ce (not a pointer to
* @ref Containers::StringView). This doesn't preserve the actual
* string size in case the string data contain zero bytes, thus prefer
* to use typed access in that case.
*/
const void* attribute(UnsignedInt layer, UnsignedInt id) const;
/**
* @brief Type-erased value of a named attribute in given material layer
*
* The @p layer is expected to be smaller than @ref layerCount() const
* and @p name is expected to exist in that layer. Cast the pointer to
* a concrete type based on @ref attributeType(). Note that
* in case of a @ref MaterialAttributeType::Pointer or a
* @ref MaterialAttributeType::MutablePointer, returns a
* *pointer to a pointer*, not the pointer value itself.
*
* In case of a @ref MaterialAttributeType::String returns a
* null-terminated @cpp const char* @ce (not a pointer to
* @ref Containers::StringView). This doesn't preserve the actual
* string size in case the string data contain zero bytes, thus prefer
* to use typed access in that case.
* @see @ref hasAttribute(), @ref tryAttribute(), @ref attributeOr()
*/
const void* attribute(UnsignedInt layer, Containers::StringView name) const;
const void* attribute(UnsignedInt layer, MaterialAttribute name) const; /**< @overload */
/**
* @brief Type-erased value of an attribute in a named material layer
*
* The @p layer is expected to exist and the @p id is expected to be smaller than @ref attributeCount(UnsignedInt) const
* in that layer. Cast the pointer to a concrete type based on
* @ref attributeType(). Note that in case of a
* @ref MaterialAttributeType::Pointer or a
* @ref MaterialAttributeType::MutablePointer, returns a
* *pointer to a pointer*, not the pointer value itself.
*
* In case of a @ref MaterialAttributeType::String returns a
* null-terminated @cpp const char* @ce (not a pointer to
* @ref Containers::StringView). This doesn't preserve the actual
* string size in case the string data contain zero bytes, thus prefer
* to use typed access in that case.
* @see @ref hasLayer()
*/
const void* attribute(Containers::StringView layer, UnsignedInt id) const;
/**
* @brief Type-erased value of a named attribute in a named material layer
*
* The @p layer is expected to exist and @p name is expected to exist
* in that layer. Cast the pointer to a concrete type based on
* @ref attributeType(). Note that in case of a
* @ref MaterialAttributeType::Pointer or a
* @ref MaterialAttributeType::MutablePointer, returns a
* *pointer to a pointer*, not the pointer value itself.
*
* In case of a @ref MaterialAttributeType::String returns a
* null-terminated @cpp const char* @ce (not a pointer to
* @ref Containers::StringView). This doesn't preserve the actual
* string size in case the string data contain zero bytes, thus prefer
* to use typed access in that case.
* @see @ref hasLayer(), @ref hasAttribute()
*/
const void* attribute(Containers::StringView layer, Containers::StringView name) const;
const void* attribute(Containers::StringView layer, MaterialAttribute name) const; /**< @overload */
/**
* @brief Type-erased value of an attribute in the base material
*
* Equivalent to calling @ref attribute(UnsignedInt, UnsignedInt) const
* with @p layer set to @cpp 0 @ce.
*/
const void* attribute(UnsignedInt id) const {
return attribute(0, id);
}
/**
* @brief Type-erased value of a named attribute in the base material
*
* Equivalent to calling @ref attribute(UnsignedInt, Containers::StringView) const
* with @p layer set to @cpp 0 @ce.
*/
const void* attribute(Containers::StringView name) const {
return attribute(0, name);
}
const void* attribute(MaterialAttribute name) const {
return attribute(0, name);
} /**< @overload */
/**
* @brief Value of an attribute in given material layer
*
* The @p layer is expected to be smaller than @ref layerCount() const
* and @p id is expected to be smaller than
* @ref attributeCount(UnsignedInt) const in that layer. Expects that
* @p T corresponds to @ref attributeType(UnsignedInt, UnsignedInt) const
* for given @p layer and @p id. In case of a string, the returned view
* always has @ref Corrade::Containers::StringViewFlag::NullTerminated
* set.
*/
template<class T> T attribute(UnsignedInt layer, UnsignedInt id) const;
/**
* @brief Value of a named attribute in given material layer
*
* The @p layer is expected to be smaller than @ref layerCount() const
* and @p name is expected to exist in that layer. Expects that @p T
* corresponds to @ref attributeType(UnsignedInt, Containers::StringView) const
* for given @p layer and @p name. In case of a string, the returned
* view always has
* @ref Corrade::Containers::StringViewFlag::NullTerminated set.
* @see @ref hasLayer(), @ref hasAttribute()
*/
template<class T> T attribute(UnsignedInt layer, Containers::StringView name) const;
template<class T> T attribute(UnsignedInt layer, MaterialAttribute name) const; /**< @overload */
/**
* @brief Value of an attribute in a named material layer
*
* The @p layer is expected to exist and @p id is expected to be
* smaller than @ref attributeCount(UnsignedInt) const in that layer.
* Expects that @p T corresponds to
* @ref attributeType(Containers::StringView, UnsignedInt) const
* for given @p layer and @p id. In case of a string, the returned view
* always has @ref Corrade::Containers::StringViewFlag::NullTerminated
* set.
* @see @ref hasLayer()
*/
template<class T> T attribute(Containers::StringView layer, UnsignedInt id) const;
/**
* @brief Value of a named attribute in a named material layer
*
* The @p layer is expected to exist and @p name is expected to exist
* in that layer. Expects that @p T corresponds to
* @ref attributeType(Containers::StringView, Containers::StringView) const
* for given @p layer and @p name. In case of a string, the returned
* view always has
* @ref Corrade::Containers::StringViewFlag::NullTerminated set.
* @see @ref hasLayer(), @ref hasAttribute()
*/
template<class T> T attribute(Containers::StringView layer, Containers::StringView name) const;
template<class T> T attribute(Containers::StringView layer, MaterialAttribute name) const; /**< @overload */
/**
* @brief Value of an attribute in the base material
*
* Equivalent to calling @ref attribute(UnsignedInt, UnsignedInt) const
* with @p layer set to @cpp 0 @ce.
*/
template<class T> T attribute(UnsignedInt id) const {
return attribute<T>(0, id);
}
/**
* @brief Value of a named attribute in the base material
*
* Equivalent to calling @ref attribute(UnsignedInt, Containers::StringView) const
* with @p layer set to @cpp 0 @ce.
*/
template<class T> T attribute(Containers::StringView name) const {
return attribute<T>(0, name);
}
template<class T> T attribute(MaterialAttribute name) const {
return attribute<T>(0, name);
} /**< @overload */
/**
* @brief Type-erased attribute value in given material layer, if exists
*
* Compared to @ref attribute(UnsignedInt, Containers::StringView name) const,
* if @p name doesn't exist, returns @cpp nullptr @ce instead of
* asserting. Expects that @p layer is smaller than @ref layerCount() const.
* Cast the pointer to a concrete type based on @ref attributeType().
* @see @ref hasAttribute(), @ref attributeOr()
*/
const void* tryAttribute(UnsignedInt layer, Containers::StringView name) const;
const void* tryAttribute(UnsignedInt layer, MaterialAttribute name) const; /**< @overload */
/**
* @brief Type-erased attribute value in a named material layer, if exists
*
* Compared to @ref attribute(Containers::StringView, Containers::StringView name) const,
* if @p name doesn't exist, returns @cpp nullptr @ce instead of
* asserting. Expects that @p layer exists. Cast the pointer to a
* concrete type based on @ref attributeType().
* @see @ref hasLayer(), @ref hasAttribute(), @ref attributeOr()
*/
const void* tryAttribute(Containers::StringView layer, Containers::StringView name) const;
const void* tryAttribute(Containers::StringView layer, MaterialAttribute name) const; /**< @overload */
/**
* @brief Value of a named attribute in given material layer, if exists
*
* Compared to @ref attribute(UnsignedInt, Containers::StringView name) const,
* if @p name doesn't exist, returns @ref Corrade::Containers::NullOpt
* instead of asserting. Expects that @p layer is smaller than
* @ref layerCount() const and that @p T corresponds to
* @ref attributeType(UnsignedInt, Containers::StringView) const for
* given @p layer and @p name.
*/
template<class T> Containers::Optional<T> tryAttribute(UnsignedInt layer, Containers::StringView name) const;
template<class T> Containers::Optional<T> tryAttribute(UnsignedInt layer, MaterialAttribute name) const; /**< @overload */
/**
* @brief Value of a named attribute in a named material layer, if exists
*
* Compared to @ref attribute(Containers::StringView, Containers::StringView name) const,
* if @p name doesn't exist, returns @ref Corrade::Containers::NullOpt
* instead of asserting. Expects that @p layer exists and that @p T
* corresponds to @ref attributeType(Containers::StringView, Containers::StringView) const
* for given @p layer and @p name.
* @see @ref hasLayer()
*/
template<class T> Containers::Optional<T> tryAttribute(Containers::StringView layer, Containers::StringView name) const;
template<class T> Containers::Optional<T> tryAttribute(Containers::StringView layer, MaterialAttribute name) const; /**< @overload */
/**
* @brief Type-erased attribute value in the base material, if exists
*
* Equivalent to calling @ref tryAttribute(UnsignedInt, Containers::StringView) const
* with @p layer set to @cpp 0 @ce.
*/
const void* tryAttribute(Containers::StringView name) const {
return tryAttribute(0, name);
}
const void* tryAttribute(MaterialAttribute name) const {
return tryAttribute(0, name);
} /**< @overload */
/**
* @brief Value of a named attribute in the base material, if exists
*
* Equivalent to calling @ref tryAttribute(UnsignedInt, Containers::StringView) const
* with @p layer set to @cpp 0 @ce.
*/
template<class T> Containers::Optional<T> tryAttribute(Containers::StringView name) const {
return tryAttribute<T>(0, name);
}
template<class T> Containers::Optional<T> tryAttribute(MaterialAttribute name) const {
return tryAttribute<T>(0, name);
} /**< @overload */
/**
* @brief Value of a named attribute in given layer or a default
*
* Compared to @ref attribute(UnsignedInt, Containers::StringView name) const,
* if @p name doesn't exist, returns @p defaultValue instead of
* asserting. Expects that @p layer is smaller than @ref layerCount()
* const
* and that @p T corresponds to @ref attributeType(UnsignedInt, Containers::StringView) const
* for given @p layer and @p name.
*/
template<class T> T attributeOr(UnsignedInt layer, Containers::StringView name, const T& defaultValue) const;
template<class T> T attributeOr(UnsignedInt layer, MaterialAttribute name, const T& defaultValue) const; /**< @overload */
/**
* @brief Value of a named attribute in a named layer or a default
*
* Compared to @ref attribute(Containers::StringView, Containers::StringView name) const,
* if @p name doesn't exist, returns @p defaultValue instead of
* asserting. Expects that @p layer exists and that @p T corresponds to
* @ref attributeType(Containers::StringView, Containers::StringView) const
* for given @p layer and @p name.
* @see @ref hasLayer()
*/
template<class T> T attributeOr(Containers::StringView layer, Containers::StringView name, const T& defaultValue) const;
template<class T> T attributeOr(Containers::StringView layer, MaterialAttribute name, const T& defaultValue) const; /**< @overload */
/**
* @brief Value of a named attribute in the base material or a default
*
* Equivalent to calling @ref attributeOr(UnsignedInt, Containers::StringView, const T&) const
* with @p layer set to @cpp 0 @ce.
*/
template<class T> T attributeOr(Containers::StringView name, const T& defaultValue) const {
return attributeOr<T>(0, name, defaultValue);
}
template<class T> T attributeOr(MaterialAttribute name, const T& defaultValue) const {
return attributeOr<T>(0, name, defaultValue);
}/**< @overload */
/**
* @brief Whether a material is double-sided
*
* Convenience access to the @ref MaterialAttribute::DoubleSided
* attribute. If not present, the default is @cpp false @ce.
*/
bool isDoubleSided() const;
#ifdef MAGNUM_BUILD_DEPRECATED
/**
* @brief Material flags
* @m_deprecated_since_latest The flags are no longer stored directly
* but generated on-the-fly from attribute data, which makes them
* less efficient than calling @ref hasAttribute(),
* @ref isDoubleSided() etc.
*
* Not all bits returned might be defined by @ref Flag, subclasses may
* define extra values.
*/
CORRADE_IGNORE_DEPRECATED_PUSH /* GCC warns about Flags, ugh */
CORRADE_DEPRECATED("use hasAttribute() instead") Flags flags() const;
CORRADE_IGNORE_DEPRECATED_POP
#endif
/**
* @brief Alpha mode
*
* Convenience access to @ref MaterialAttribute::AlphaBlend and
* @ref MaterialAttribute::AlphaMask attributes. If neither is present,
* the default is @ref MaterialAlphaMode::Opaque.
*/
MaterialAlphaMode alphaMode() const;
/**
* @brief Alpha mask
*
* Convenience access to the @ref MaterialAttribute::AlphaMask
* attribute. If not present, the default is @cpp 0.5f @ce.
*/
Float alphaMask() const;
/**
* @brief Release layer data storage
*
* Releases the ownership of the layer offset array and resets internal
* layer-related state to default. The material then behaves like if it
* has no layers. 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.
*
* @attention Querying attributes after calling @ref releaseLayerData()
* has undefined behavior and might lead to crashes. This is done
* intentionally in order to simplify the interaction between this
* function and @ref releaseAttributeData().
* @see @ref layerData()
*/
Containers::Array<UnsignedInt> releaseLayerData();
/**
* @brief Release attribute data storage
*
* Releases the ownership of the attribute array and resets internal
* attribute-related state to default. The material then behaves like
* if it has no attributes. 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.
*
* @attention Querying layers after calling @ref releaseAttributeData()
* has undefined behavior and might lead to crashes. This is done
* intentionally in order to simplify the interaction between this
* function and @ref releaseLayerData().
* @see @ref attributeData()
*/
Containers::Array<MaterialAttributeData> releaseAttributeData();
/**
* @brief Importer-specific state
*
* See @ref AbstractImporter::importerState() for more information.
*/
const void* importerState() const { return _importerState; }
private:
static Containers::StringView attributeString(MaterialAttribute name);
/* Internal helpers that don't assert, unlike layerId() / attributeId() */
UnsignedInt layerFor(Containers::StringView layer) const;
UnsignedInt layerOffset(UnsignedInt layer) const {
return layer && _layerOffsets ? _layerOffsets[layer - 1] : 0;
}
UnsignedInt attributeFor(UnsignedInt layer, Containers::StringView name) const;
Containers::Array<MaterialAttributeData> _data;
Containers::Array<UnsignedInt> _layerOffsets;
MaterialTypes _types;
const void* _importerState;
};
#ifdef MAGNUM_BUILD_DEPRECATED
CORRADE_IGNORE_DEPRECATED_PUSH
CORRADE_ENUMSET_OPERATORS(MaterialData::Flags)
/**
@debugoperatorclassenum{MaterialData,MaterialData::Flag}
@m_deprecated_since_latest The flags are no longer stored directly but
generated on-the-fly from attribute data, which makes them less efficient
than calling @ref MaterialData::hasAttribute(),
@ref MaterialData::isDoubleSided() etc.
*/
/* Not marked with CORRADE_DEPRECATED() as there's enough warnings already */
MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, MaterialData::Flag value);
/**
@debugoperatorclassenum{MaterialData,MaterialData::Flags}
@m_deprecated_since_latest The flags are no longer stored directly but
generated on-the-fly from attribute data, which makes them less efficient
than calling @ref MaterialData::hasAttribute(),
@ref MaterialData::isDoubleSided() etc.
*/
/* Not marked with CORRADE_DEPRECATED() as there's enough warnings already */
MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, MaterialData::Flags value);
CORRADE_IGNORE_DEPRECATED_POP
#endif
namespace Implementation {
/* LCOV_EXCL_START */
template<class T> struct MaterialAttributeTypeFor {
/* C++ why there isn't an obvious way to do such a thing?! */
static_assert(sizeof(T) == 0, "unsupported attribute type");
};
template<> struct MaterialAttributeTypeFor<bool> {
constexpr static MaterialAttributeType type() {
return MaterialAttributeType::Bool;
}
};
template<class T> struct MaterialAttributeTypeFor<const T*> {
constexpr static MaterialAttributeType type() {
return MaterialAttributeType::Pointer;
}
};
template<class T> struct MaterialAttributeTypeFor<T*> {
constexpr static MaterialAttributeType type() {
return MaterialAttributeType::MutablePointer;
}
};
/* No specialization for StringView as this type trait should not be used
in that case */
#ifndef DOXYGEN_GENERATING_OUTPUT
#define _c(type_) template<> struct MaterialAttributeTypeFor<type_> { \
constexpr static MaterialAttributeType type() { \
return MaterialAttributeType::type_; \
} \
};
_c(Float)
_c(Deg)
_c(Rad)
_c(UnsignedInt)
_c(Int)
_c(UnsignedLong)
_c(Long)
_c(Vector2)
_c(Vector2ui)
_c(Vector2i)
_c(Vector3)
_c(Vector3ui)
_c(Vector3i)
_c(Vector4)
_c(Vector4ui)
_c(Vector4i)
_c(Matrix2x2)
_c(Matrix2x3)
_c(Matrix2x4)
_c(Matrix3x2)
_c(Matrix3x3)
_c(Matrix3x4)
_c(Matrix4x2)
_c(Matrix4x3)
#undef _c
#endif
template<> struct MaterialAttributeTypeFor<Color3>: MaterialAttributeTypeFor<Vector3> {};
template<> struct MaterialAttributeTypeFor<Color4>: MaterialAttributeTypeFor<Vector4> {};
template<> struct MaterialAttributeTypeFor<Matrix3>: MaterialAttributeTypeFor<Matrix3x3> {};
/* LCOV_EXCL_STOP */
}
/* The 2 extra bytes are for a null byte after the name and a type */
template<class T
#ifndef DOXYGEN_GENERATING_OUTPUT
, class
#endif
> constexpr MaterialAttributeData::MaterialAttributeData(const Containers::StringView name, const T& value) noexcept: _data{Implementation::MaterialAttributeTypeFor<T>::type(), (CORRADE_CONSTEXPR_ASSERT(name.size() + sizeof(T) + 2 <= Implementation::MaterialAttributeDataSize, "Trade::MaterialAttributeData: name" << name << "too long, expected at most" << Implementation::MaterialAttributeDataSize - sizeof(T) - 2 << "bytes for" << Implementation::MaterialAttributeTypeFor<T>::type() << "but got" << name.size()), name), value} {}
/* The 4 extra bytes are for a null byte after both the name and value, a type
and a string size */
constexpr MaterialAttributeData::MaterialAttributeData(const Containers::StringView name, const Containers::StringView value) noexcept: _data{(CORRADE_CONSTEXPR_ASSERT(name.size() + value.size() + 4 <= Implementation::MaterialAttributeDataSize, "Trade::MaterialAttributeData: name" << name << "and value" << value << "too long, expected at most" << Implementation::MaterialAttributeDataSize - 4 << "bytes in total but got" << name.size() + value.size()), name), value} {}
template<class T> T MaterialAttributeData::value() const {
CORRADE_ASSERT(Implementation::MaterialAttributeTypeFor<T>::type() == _data.type,
"Trade::MaterialAttributeData::value(): improper type requested for" << (_data.data + 1) << "of" << _data.type, {});
return *reinterpret_cast<const T*>(value());
}
#ifndef DOXYGEN_GENERATING_OUTPUT
template<> Containers::StringView MaterialAttributeData::value<Containers::StringView>() const;
#endif
template<class T> T MaterialData::attribute(const UnsignedInt layer, const UnsignedInt id) const {
const void* const value = attribute(layer, id);
#ifdef CORRADE_GRACEFUL_ASSERT
if(!value) return {};
#endif
const Trade::MaterialAttributeData& data = _data[layerOffset(layer) + id];
CORRADE_ASSERT(Implementation::MaterialAttributeTypeFor<T>::type() == data._data.type,
"Trade::MaterialData::attribute(): improper type requested for" << (data._data.data + 1) << "of" << data._data.type, {});
return *reinterpret_cast<const T*>(value);
}
#ifndef DOXYGEN_GENERATING_OUTPUT
template<> Containers::StringView MaterialData::attribute<Containers::StringView>(UnsignedInt, UnsignedInt) const;
#endif
template<class T> T MaterialData::attribute(const UnsignedInt layer, const Containers::StringView name) const {
CORRADE_ASSERT(layer < layerCount(),
"Trade::MaterialData::attribute(): index" << layer << "out of range for" << layerCount() << "layers", {});
const UnsignedInt id = attributeFor(layer, name);
CORRADE_ASSERT(id != ~UnsignedInt{},
"Trade::MaterialData::attribute(): attribute" << name << "not found in layer" << layer, {});
return attribute<T>(layer, id);
}
template<class T> T MaterialData::attribute(const UnsignedInt layer, const MaterialAttribute name) const {
const Containers::StringView string = attributeString(name);
CORRADE_ASSERT(string.data(), "Trade::MaterialData::attribute(): invalid name" << name, {});
return attribute<T>(layer, string);
}
template<class T> T MaterialData::attribute(const Containers::StringView layer, const UnsignedInt id) const {
const UnsignedInt layerId = layerFor(layer);
CORRADE_ASSERT(layerId != ~UnsignedInt{},
"Trade::MaterialData::attribute(): layer" << layer << "not found", {});
CORRADE_ASSERT(id < attributeCount(layer),
"Trade::MaterialData::attribute(): index" << id << "out of range for" << attributeCount(layer) << "attributes in layer" << layer, {});
return attribute<T>(layerId, id);
}
template<class T> T MaterialData::attribute(const Containers::StringView layer, const Containers::StringView name) const {
const UnsignedInt layerId = layerFor(layer);
CORRADE_ASSERT(layerId != ~UnsignedInt{},
"Trade::MaterialData::attribute(): layer" << layer << "not found", {});
const UnsignedInt id = attributeFor(layerId, name);
CORRADE_ASSERT(id != ~UnsignedInt{},
"Trade::MaterialData::attribute(): attribute" << name << "not found in layer" << layer, {});
return attribute<T>(layerId, id);
}
template<class T> T MaterialData::attribute(const Containers::StringView layer, const MaterialAttribute name) const {
const Containers::StringView string = attributeString(name);
CORRADE_ASSERT(string.data(), "Trade::MaterialData::attribute(): invalid name" << name, {});
return attribute<T>(layer, string);
}
template<class T> Containers::Optional<T> MaterialData::tryAttribute(const UnsignedInt layer, const Containers::StringView name) const {
CORRADE_ASSERT(layer < layerCount(),
"Trade::MaterialData::tryAttribute(): index" << layer << "out of range for" << layerCount() << "layers", {});
const UnsignedInt id = attributeFor(layer, name);
if(id == ~UnsignedInt{}) return {};
return attribute<T>(layer, id);
}
template<class T> Containers::Optional<T> MaterialData::tryAttribute(const UnsignedInt layer, const MaterialAttribute name) const {
const Containers::StringView string = attributeString(name);
CORRADE_ASSERT(string.data(), "Trade::MaterialData::tryAttribute(): invalid name" << name, {});
return tryAttribute<T>(layer, string);
}
template<class T> Containers::Optional<T> MaterialData::tryAttribute(const Containers::StringView layer, const Containers::StringView name) const {
const UnsignedInt layerId = layerFor(layer);
CORRADE_ASSERT(layerId != ~UnsignedInt{},
"Trade::MaterialData::tryAttribute(): layer" << layer << "not found", {});
return tryAttribute<T>(layerId, name);
}
template<class T> Containers::Optional<T> MaterialData::tryAttribute(const Containers::StringView layer, const MaterialAttribute name) const {
const Containers::StringView string = attributeString(name);
CORRADE_ASSERT(string.data(), "Trade::MaterialData::tryAttribute(): invalid name" << name, {});
return tryAttribute<T>(layer, string);
}
template<class T> T MaterialData::attributeOr(const UnsignedInt layer, const Containers::StringView name, const T& defaultValue) const {
CORRADE_ASSERT(layer < layerCount(),
"Trade::MaterialData::attributeOr(): index" << layer << "out of range for" << layerCount() << "layers", {});
const UnsignedInt id = attributeFor(layer, name);
if(id == ~UnsignedInt{}) return defaultValue;
return attribute<T>(layer, id);
}
template<class T> T MaterialData::attributeOr(const UnsignedInt layer, const MaterialAttribute name, const T& defaultValue) const {
const Containers::StringView string = attributeString(name);
CORRADE_ASSERT(string.data(), "Trade::MaterialData::attributeOr(): invalid name" << name, {});
return attributeOr<T>(layer, string, defaultValue);
}
template<class T> T MaterialData::attributeOr(const Containers::StringView layer, const Containers::StringView name, const T& defaultValue) const {
const UnsignedInt layerId = layerFor(layer);
CORRADE_ASSERT(layerId != ~UnsignedInt{},
"Trade::MaterialData::attributeOr(): layer" << layer << "not found", {});
return attributeOr<T>(layerId, name, defaultValue);
}
template<class T> T MaterialData::attributeOr(const Containers::StringView layer, const MaterialAttribute name, const T& defaultValue) const {
const Containers::StringView string = attributeString(name);
CORRADE_ASSERT(string.data(), "Trade::MaterialData::attributeOr(): invalid name" << name, {});
return attributeOr<T>(layer, string, defaultValue);
}
}}
#endif