mirror of https://github.com/mosra/magnum.git
Browse Source
Next step is reworking PhongMaterialData on top of this one and deprecating AbstractMaterialData.pull/459/head
8 changed files with 1880 additions and 10 deletions
@ -0,0 +1,49 @@
|
||||
/*
|
||||
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. |
||||
*/ |
||||
|
||||
/* See Magnum/Trade/MaterialData.cpp and Magnum/Trade/Test/MaterialDataTest.cpp */ |
||||
#ifdef _c |
||||
_c(AlphaMask,Float) |
||||
_ct(AlphaBlend,Bool,bool) |
||||
_ct(DoubleSided,Bool,bool) |
||||
_c(AmbientColor,Vector4) |
||||
_c(AmbientTexture,UnsignedInt) |
||||
_c(AmbientTextureMatrix,Matrix3x3) |
||||
_c(AmbientTextureCoordinates,UnsignedInt) |
||||
_c(DiffuseColor,Vector4) |
||||
_c(DiffuseTexture,UnsignedInt) |
||||
_c(DiffuseTextureMatrix,Matrix3x3) |
||||
_c(DiffuseTextureCoordinates,UnsignedInt) |
||||
_c(SpecularColor,Vector4) |
||||
_c(SpecularTexture,UnsignedInt) |
||||
_c(SpecularTextureMatrix,Matrix3x3) |
||||
_c(SpecularTextureCoordinates,UnsignedInt) |
||||
_c(Shininess,Float) |
||||
_c(NormalTexture,UnsignedInt) |
||||
_c(NormalTextureMatrix,Matrix3x3) |
||||
_c(NormalTextureCoordinates,UnsignedInt) |
||||
_c(TextureMatrix,Matrix3x3) |
||||
_c(TextureCoordinates,UnsignedInt) |
||||
#endif |
||||
@ -0,0 +1,314 @@
|
||||
/*
|
||||
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. |
||||
*/ |
||||
|
||||
#include "MaterialData.h" |
||||
|
||||
#include <cstring> |
||||
#include <algorithm> |
||||
|
||||
#include "Magnum/Math/Vector4.h" |
||||
#include "Magnum/Math/Matrix.h" |
||||
#include "Magnum/Trade/Implementation/arrayUtilities.h" |
||||
|
||||
namespace Magnum { namespace Trade { |
||||
|
||||
namespace { |
||||
|
||||
using namespace Containers::Literals; |
||||
|
||||
#ifndef DOXYGEN_GENERATING_OUTPUT /* It gets *really* confused */ |
||||
constexpr struct { |
||||
Containers::StringView name; |
||||
MaterialAttributeType type; |
||||
UnsignedByte size; |
||||
} AttributeMap[]{ |
||||
#define _c(name, type) {#name ## _s, MaterialAttributeType::type, sizeof(type)}, |
||||
#define _ct(name, typeName, type) {#name ## _s, MaterialAttributeType::typeName, sizeof(type)}, |
||||
#include "Magnum/Trade/Implementation/materialAttributeProperties.hpp" |
||||
#undef _c |
||||
#undef _ct |
||||
}; |
||||
#endif |
||||
|
||||
} |
||||
|
||||
std::size_t materialAttributeTypeSize(const MaterialAttributeType type) { |
||||
switch(type) { |
||||
case MaterialAttributeType::Bool: |
||||
return 1; |
||||
|
||||
case MaterialAttributeType::Float: |
||||
case MaterialAttributeType::Deg: |
||||
case MaterialAttributeType::Rad: |
||||
case MaterialAttributeType::UnsignedInt: |
||||
case MaterialAttributeType::Int: |
||||
return 4; |
||||
|
||||
case MaterialAttributeType::Vector2: |
||||
case MaterialAttributeType::Vector2ui: |
||||
case MaterialAttributeType::Vector2i: |
||||
return 8; |
||||
|
||||
case MaterialAttributeType::Vector3: |
||||
case MaterialAttributeType::Vector3ui: |
||||
case MaterialAttributeType::Vector3i: |
||||
return 12; |
||||
|
||||
case MaterialAttributeType::Vector4: |
||||
case MaterialAttributeType::Vector4ui: |
||||
case MaterialAttributeType::Vector4i: |
||||
case MaterialAttributeType::Matrix2x2: |
||||
return 16; |
||||
|
||||
case MaterialAttributeType::Matrix2x3: |
||||
case MaterialAttributeType::Matrix3x2: |
||||
return 24; |
||||
|
||||
case MaterialAttributeType::Matrix2x4: |
||||
case MaterialAttributeType::Matrix4x2: |
||||
return 32; |
||||
|
||||
case MaterialAttributeType::Matrix3x3: |
||||
return 36; |
||||
|
||||
case MaterialAttributeType::Matrix3x4: |
||||
case MaterialAttributeType::Matrix4x3: |
||||
return 48; |
||||
} |
||||
|
||||
CORRADE_ASSERT_UNREACHABLE("Trade::materialAttributeTypeSize(): invalid type" << type, {}); |
||||
} |
||||
|
||||
MaterialAttributeData::MaterialAttributeData(const Containers::StringView name, const MaterialAttributeType type, const std::size_t size, const void* const value) noexcept: _data{} /* zero-initialized */ { |
||||
/* The 2 extra bytes are for a null byte after the name and a type */ |
||||
CORRADE_ASSERT(name.size() + size + 2 <= Implementation::MaterialAttributeDataSize, |
||||
"Trade::MaterialAttributeData: name" << name << "too long, expected at most" << Implementation::MaterialAttributeDataSize - size - 2 << "bytes for" << type << "but got" << name.size(), ); |
||||
_data.type = type; |
||||
std::memcpy(_data.data + 1, name.data(), name.size()); |
||||
std::memcpy(_data.data + Implementation::MaterialAttributeDataSize - size, value, size); |
||||
} |
||||
|
||||
MaterialAttributeData::MaterialAttributeData(const Containers::StringView name, const MaterialAttributeType type, const void* const value) noexcept: MaterialAttributeData{name, type, materialAttributeTypeSize(type), value} {} |
||||
|
||||
MaterialAttributeData::MaterialAttributeData(const MaterialAttribute name, const MaterialAttributeType type, const void* const value) noexcept { |
||||
CORRADE_ASSERT(UnsignedInt(name) - 1 < Containers::arraySize(AttributeMap), |
||||
"Trade::MaterialAttributeData: invalid name" << name, ); |
||||
CORRADE_ASSERT(AttributeMap[UnsignedInt(name) - 1].type == type, |
||||
"Trade::MaterialAttributeData: expected" << AttributeMap[UnsignedInt(name) - 1].type << "for" << name << "but got" << type, ); |
||||
_data.type = type; |
||||
std::memcpy(_data.data + 1, AttributeMap[UnsignedInt(name) - 1].name.data(), AttributeMap[UnsignedInt(name) - 1].name.size()); |
||||
std::memcpy(_data.data + Implementation::MaterialAttributeDataSize - AttributeMap[UnsignedInt(name) - 1].size, value, AttributeMap[UnsignedInt(name) - 1].size); |
||||
} |
||||
|
||||
const void* MaterialAttributeData::value() const { |
||||
return _data.data + Implementation::MaterialAttributeDataSize - materialAttributeTypeSize(_data.type); |
||||
} |
||||
|
||||
MaterialData::MaterialData(Containers::Array<MaterialAttributeData>&& data, const void* const importerState) noexcept: _data{std::move(data)}, _importerState{importerState} { |
||||
#ifndef CORRADE_NO_ASSERT |
||||
/* Not checking what's already done in MaterialAttributeData constructor.
|
||||
Done before sorting so the index refers to the actual input index. */ |
||||
for(std::size_t i = 0; i != _data.size(); ++i) |
||||
CORRADE_ASSERT(_data[i]._data.type != MaterialAttributeType{}, |
||||
"Trade::MaterialData: attribute" << i << "doesn't specify anything", ); |
||||
#endif |
||||
|
||||
/* Check if attributes are sorted already. If not, sort them. Can't just
|
||||
call std::sort() unconditionally because if the data would be immutable |
||||
(for example when acquiring release()d immutable data from another |
||||
instance) it could cause crashes. (I expected not, but apparently ASan |
||||
blows up on that.) */ |
||||
if(_data.size() > 1) for(std::size_t i = 1; i != _data.size(); ++i) { |
||||
if(_data[i - 1].name() < _data[i].name()) continue; |
||||
|
||||
std::sort(_data.begin(), _data.end(), [](const MaterialAttributeData& a, const MaterialAttributeData& b) { |
||||
/* Need to check here (instead of in the outer for loop) as we
|
||||
exit the loop right after the sort and thus duplicates that |
||||
occur after the first non-sorted pair wouldn't get detected. */ |
||||
CORRADE_ASSERT(a.name() != b.name(), |
||||
"Trade::MaterialData: duplicate attribute" << a.name(), false); |
||||
return a.name() < b.name(); |
||||
}); |
||||
break; |
||||
} |
||||
} |
||||
|
||||
MaterialData::MaterialData(const std::initializer_list<MaterialAttributeData> data, const void* const importerState): MaterialData{Implementation::initializerListToArrayWithDefaultDeleter(data), importerState} {} |
||||
|
||||
MaterialData::MaterialData(DataFlags, const Containers::ArrayView<const MaterialAttributeData> data, const void* const importerState) noexcept: _data{Containers::Array<MaterialAttributeData>{const_cast<MaterialAttributeData*>(data.data()), data.size(), [](MaterialAttributeData*, std::size_t){}}}, _importerState{importerState} { |
||||
#ifndef CORRADE_NO_ASSERT |
||||
/* Not checking what's already done in MaterialAttributeData constructor */ |
||||
for(std::size_t i = 0; i != _data.size(); ++i) |
||||
CORRADE_ASSERT(_data[i]._data.type != MaterialAttributeType{}, |
||||
"Trade::MaterialData: attribute" << i << "doesn't specify anything", ); |
||||
|
||||
if(_data.size() > 1) for(std::size_t i = 1; i != _data.size(); ++i) { |
||||
CORRADE_ASSERT(_data[i - 1].name() != _data[i].name(), |
||||
"Trade::MaterialData: duplicate attribute" << _data[i].name(), ); |
||||
CORRADE_ASSERT(_data[i - 1].name() < _data[i].name(), |
||||
"Trade::MaterialData:" << _data[i].name() << "has to be sorted before" << _data[i - 1].name() << "if passing non-owned data", ); |
||||
} |
||||
#endif |
||||
} |
||||
|
||||
MaterialData::MaterialData(MaterialData&&) noexcept = default; |
||||
|
||||
MaterialData::~MaterialData() = default; |
||||
|
||||
MaterialData& MaterialData::operator=(MaterialData&&) noexcept = default; |
||||
|
||||
Containers::StringView MaterialData::attributeString(const MaterialAttribute name) { |
||||
#ifndef CORRADE_NO_ASSERT |
||||
if(UnsignedInt(name) - 1 >= Containers::arraySize(AttributeMap)) |
||||
return nullptr; |
||||
#endif |
||||
return AttributeMap[UnsignedInt(name) - 1].name; |
||||
} |
||||
|
||||
UnsignedInt MaterialData::attributeFor(const Containers::StringView name) const { |
||||
const MaterialAttributeData* const found = std::lower_bound(_data.begin(), _data.end(), name, [](const MaterialAttributeData& a, const Containers::StringView& b) { |
||||
return a.name() < b; |
||||
}); |
||||
if(found == _data.end() || found->name() != name) return ~UnsignedInt{}; |
||||
return found - _data.begin(); |
||||
} |
||||
|
||||
bool MaterialData::hasAttribute(const Containers::StringView name) const { |
||||
return attributeFor(name) != ~UnsignedInt{}; |
||||
} |
||||
|
||||
bool MaterialData::hasAttribute(const MaterialAttribute name) const { |
||||
const Containers::StringView string = attributeString(name); |
||||
CORRADE_ASSERT(string.data(), "Trade::MaterialData::hasAttribute(): invalid name" << name, {}); |
||||
return hasAttribute(string); |
||||
} |
||||
|
||||
UnsignedInt MaterialData::attributeId(const Containers::StringView name) const { |
||||
const UnsignedInt id = attributeFor(name); |
||||
CORRADE_ASSERT(id != ~UnsignedInt{}, |
||||
"Trade::MaterialData::attributeId(): attribute" << name << "not found", {}); |
||||
return id; |
||||
} |
||||
|
||||
UnsignedInt MaterialData::attributeId(const MaterialAttribute name) const { |
||||
const Containers::StringView string = attributeString(name); |
||||
CORRADE_ASSERT(string.data(), "Trade::MaterialData::attributeId(): invalid name" << name, {}); |
||||
return attributeId(string); |
||||
} |
||||
|
||||
Containers::StringView MaterialData::attributeName(const UnsignedInt id) const { |
||||
CORRADE_ASSERT(id < _data.size(), |
||||
"Trade::MaterialData::attributeName(): index" << id << "out of range for" << _data.size() << "attributes", {}); |
||||
return _data[id]._data.data + 1; |
||||
} |
||||
|
||||
MaterialAttributeType MaterialData::attributeType(const UnsignedInt id) const { |
||||
CORRADE_ASSERT(id < _data.size(), |
||||
"Trade::MaterialData::attributeType(): index" << id << "out of range for" << _data.size() << "attributes", {}); |
||||
return _data[id]._data.type; |
||||
} |
||||
|
||||
MaterialAttributeType MaterialData::attributeType(const Containers::StringView name) const { |
||||
const UnsignedInt id = attributeFor(name); |
||||
CORRADE_ASSERT(id != ~UnsignedInt{}, |
||||
"Trade::MaterialData::attributeType(): attribute" << name << "not found", {}); |
||||
return _data[id]._data.type; |
||||
} |
||||
|
||||
MaterialAttributeType MaterialData::attributeType(const MaterialAttribute name) const { |
||||
const Containers::StringView string = attributeString(name); |
||||
CORRADE_ASSERT(string.data(), "Trade::MaterialData::attributeType(): invalid name" << name, {}); |
||||
return attributeType(string); |
||||
} |
||||
|
||||
const void* MaterialData::attribute(const UnsignedInt id) const { |
||||
CORRADE_ASSERT(id < _data.size(), |
||||
"Trade::MaterialData::attribute(): index" << id << "out of range for" << _data.size() << "attributes", {}); |
||||
return _data[id].value(); |
||||
} |
||||
|
||||
const void* MaterialData::attribute(const Containers::StringView name) const { |
||||
const UnsignedInt id = attributeFor(name); |
||||
CORRADE_ASSERT(id != ~UnsignedInt{}, |
||||
"Trade::MaterialData::attribute(): attribute" << name << "not found", {}); |
||||
return _data[id].value(); |
||||
} |
||||
|
||||
const void* MaterialData::attribute(const MaterialAttribute name) const { |
||||
const Containers::StringView string = attributeString(name); |
||||
CORRADE_ASSERT(string.data(), "Trade::MaterialData::attribute(): invalid name" << name, {}); |
||||
return attribute(string); |
||||
} |
||||
|
||||
Containers::Array<MaterialAttributeData> MaterialData::release() { |
||||
return std::move(_data); |
||||
} |
||||
|
||||
Debug& operator<<(Debug& debug, const MaterialAttribute value) { |
||||
debug << "Trade::MaterialAttribute" << Debug::nospace; |
||||
|
||||
if(UnsignedInt(value) - 1 >= Containers::arraySize(AttributeMap)) |
||||
return debug << "(" << Debug::nospace << reinterpret_cast<void*>(UnsignedInt(value)) << Debug::nospace << ")"; |
||||
|
||||
return debug << "::" << Debug::nospace << AttributeMap[UnsignedInt(value) - 1].name; |
||||
} |
||||
|
||||
Debug& operator<<(Debug& debug, const MaterialAttributeType value) { |
||||
debug << "Trade::MaterialAttributeType" << Debug::nospace; |
||||
|
||||
switch(value) { |
||||
/* LCOV_EXCL_START */ |
||||
#define _c(value) case MaterialAttributeType::value: return debug << "::" #value; |
||||
_c(Bool) |
||||
_c(Float) |
||||
_c(Deg) |
||||
_c(Rad) |
||||
_c(UnsignedInt) |
||||
_c(Int) |
||||
_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 |
||||
/* LCOV_EXCL_STOP */ |
||||
} |
||||
|
||||
return debug << "(" << Debug::nospace << reinterpret_cast<void*>(UnsignedShort(value)) << Debug::nospace << ")"; |
||||
} |
||||
|
||||
}} |
||||
@ -0,0 +1,779 @@
|
||||
#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/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 the same form and |
||||
capitalization --- so for example @ref MaterialAttribute::DoubleSided is an |
||||
alias for @cpp "DoubleSided" @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 */ |
||||
|
||||
/**
|
||||
* 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. |
||||
*/ |
||||
AlphaMask = 1, |
||||
|
||||
/**
|
||||
* 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. |
||||
*/ |
||||
AlphaBlend, |
||||
|
||||
/**
|
||||
* Double sided, @ref MaterialAttributeType::Bool. |
||||
* |
||||
* If not present, the default value is @cpp false @ce. |
||||
*/ |
||||
DoubleSided, |
||||
|
||||
/**
|
||||
* Ambient color for Phong materials, @ref MaterialAttributeType::Vector4. |
||||
* |
||||
* If @ref MaterialAttribute::AmbientTexture is present as well, these two |
||||
* are multiplied together. |
||||
*/ |
||||
AmbientColor, |
||||
|
||||
/**
|
||||
* Ambient texture index for Phong materials, |
||||
* @ref MaterialAttributeType::UnsignedInt. |
||||
* |
||||
* If @ref MaterialAttribute::AmbientColor is present as well, these two |
||||
* are multiplied together. |
||||
*/ |
||||
AmbientTexture, |
||||
|
||||
/**
|
||||
* Ambient texture transformation matrix for Phong materials, |
||||
* @ref MaterialAttributeType::Matrix3x3. |
||||
* |
||||
* Has a precedence over @ref MaterialAttribute::TextureMatrix if both are |
||||
* present. |
||||
*/ |
||||
AmbientTextureMatrix, |
||||
|
||||
/**
|
||||
* Ambient texture coordinate set index for Phong materials, |
||||
* @ref MaterialAttributeType::UnsignedInt. |
||||
* |
||||
* Has a precedence over @ref MaterialAttribute::TextureCoordinates if both |
||||
* are present. |
||||
*/ |
||||
AmbientTextureCoordinates, |
||||
|
||||
/**
|
||||
* Diffuse color for Phong materials, @ref MaterialAttributeType::Vector4. |
||||
* |
||||
* If @ref MaterialAttribute::DiffuseTexture is present as well, these two |
||||
* are multiplied together. |
||||
*/ |
||||
DiffuseColor, |
||||
|
||||
/**
|
||||
* Diffuse texture index for Phong materials, |
||||
* @ref MaterialAttributeType::UnsignedInt. |
||||
* |
||||
* If @ref MaterialAttribute::DiffuseColor is present as well, these two |
||||
* are multiplied together. |
||||
*/ |
||||
DiffuseTexture, |
||||
|
||||
/**
|
||||
* Diffuse texture transformation matrix for Phong materials, |
||||
* @ref MaterialAttributeType::Matrix3x3. |
||||
* |
||||
* Has a precedence over @ref MaterialAttribute::TextureMatrix if both are |
||||
* present. |
||||
*/ |
||||
DiffuseTextureMatrix, |
||||
|
||||
/**
|
||||
* Diffuse texture coordinate set index for Phong materials, |
||||
* @ref MaterialAttributeType::UnsignedInt. |
||||
* |
||||
* Has a precedence over @ref MaterialAttribute::TextureCoordinates if both |
||||
* are present. |
||||
*/ |
||||
DiffuseTextureCoordinates, |
||||
|
||||
/**
|
||||
* Specular color for Phong materials, @ref MaterialAttributeType::Vector4. |
||||
* |
||||
* If @ref MaterialAttribute::SpecularTexture is present as well, these two |
||||
* are multiplied together. |
||||
*/ |
||||
SpecularColor, |
||||
|
||||
/**
|
||||
* Specular texture index for Phong materials, |
||||
* @ref MaterialAttributeType::UnsignedInt. |
||||
* |
||||
* If @ref MaterialAttribute::SpecularColor is present as well, these two |
||||
* are multiplied together. |
||||
*/ |
||||
SpecularTexture, |
||||
|
||||
/**
|
||||
* Specular texture transformation matrix for Phong materials, |
||||
* @ref MaterialAttributeType::Matrix3x3. |
||||
* |
||||
* Has a precedence over @ref MaterialAttribute::TextureMatrix if both are |
||||
* present. |
||||
*/ |
||||
SpecularTextureMatrix, |
||||
|
||||
/**
|
||||
* Specular texture coordinate set index for Phong materials, |
||||
* @ref MaterialAttributeType::UnsignedInt. |
||||
* |
||||
* Has a precedence over @ref MaterialAttribute::TextureCoordinates if both |
||||
* are present. |
||||
*/ |
||||
SpecularTextureCoordinates, |
||||
|
||||
/**
|
||||
* Shininess value for Phong materials, @ref MaterialAttributeType::Float. |
||||
*/ |
||||
Shininess, |
||||
|
||||
/**
|
||||
* Tangent-space normal map texture index, |
||||
* @ref MaterialAttributeType::UnsignedInt. |
||||
*/ |
||||
NormalTexture, |
||||
|
||||
/**
|
||||
* Normal texture transformation matrix, |
||||
* @ref MaterialAttributeType::Matrix3x3. |
||||
* |
||||
* Has a precedence over @ref MaterialAttribute::TextureMatrix if both are |
||||
* present. |
||||
*/ |
||||
NormalTextureMatrix, |
||||
|
||||
/**
|
||||
* Normal texture coordinate set index, |
||||
* @ref MaterialAttributeType::UnsignedInt. |
||||
* |
||||
* Has a precedence over @ref MaterialAttribute::TextureCoordinates if both |
||||
* are present. |
||||
*/ |
||||
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. |
||||
*/ |
||||
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. |
||||
*/ |
||||
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" */ |
||||
|
||||
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 */ |
||||
}; |
||||
|
||||
/**
|
||||
@brief Byte size of a material attribute type |
||||
@m_since_latest |
||||
*/ |
||||
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> constexpr /*implicit*/ MaterialAttributeData(Containers::StringView name, const T& value) noexcept; |
||||
#ifndef DOXYGEN_GENERATING_OUTPUT |
||||
/* "Sure can't be constexpr" overload to avoid going through the
|
||||
*insane* overload puzzle when not needed */ |
||||
template<class T> /*implicit*/ MaterialAttributeData(const char* name, const T& value) noexcept: MaterialAttributeData{name, Implementation::MaterialAttributeTypeFor<T>::type(), sizeof(T), &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> /*implicit*/ MaterialAttributeData(MaterialAttribute name, const T& value) noexcept: MaterialAttributeData{name, Implementation::MaterialAttributeTypeFor<T>::type(), &value} {} |
||||
|
||||
/**
|
||||
* @brief Construct from a type-erased value |
||||
* @param name Attribute name |
||||
* @param type Attribute type |
||||
* @param value Type-erased value |
||||
* |
||||
* 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. |
||||
*/ |
||||
/*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(). |
||||
*/ |
||||
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. */ |
||||
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; |
||||
}; |
||||
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{} {} |
||||
|
||||
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, 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, 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) == 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]; |
||||
Data<bool> _1; |
||||
Data<ErasedScalar> _4; |
||||
Data<ErasedVector<2>> _8; |
||||
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 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: |
||||
/**
|
||||
* @brief Construct |
||||
* @param data Attribute data |
||||
* @param importerState Importer-specific state |
||||
* |
||||
* The @p data gets sorted by name internally, expecting no duplicates. |
||||
*/ |
||||
explicit MaterialData(Containers::Array<MaterialAttributeData>&& data, const void* importerState = nullptr) noexcept; |
||||
|
||||
/** @overload */ |
||||
/* Not noexcept because allocation happens inside */ |
||||
explicit MaterialData(std::initializer_list<MaterialAttributeData> data, const void* importerState = nullptr); |
||||
|
||||
/**
|
||||
* @brief Construct a non-owned material data |
||||
* @param dataFlags Ignored. Used only for a safer distinction |
||||
* from the owning constructor. |
||||
* @param data Attribute data |
||||
* @param importerState Importer-specific state |
||||
* |
||||
* The @p data is expected to be already sorted by name, without |
||||
* duplicates. |
||||
*/ |
||||
explicit MaterialData(DataFlags dataFlags, Containers::ArrayView<const MaterialAttributeData> data, 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 Raw attribute data |
||||
* |
||||
* Returns @cpp nullptr @ce if the material has no attributes. |
||||
* @see @ref release() |
||||
*/ |
||||
Containers::ArrayView<const MaterialAttributeData> data() const { return _data; } |
||||
|
||||
/** @brief Attribute count */ |
||||
UnsignedInt attributeCount() const { return _data.size(); } |
||||
|
||||
/** @brief Whether the material has given attribute */ |
||||
bool hasAttribute(Containers::StringView name) const; |
||||
bool hasAttribute(MaterialAttribute name) const; /**< @overload */ |
||||
|
||||
/**
|
||||
* @brief ID of a named attribute |
||||
* |
||||
* The @p name is expected to exist. |
||||
*/ |
||||
UnsignedInt attributeId(Containers::StringView name) const; |
||||
UnsignedInt attributeId(MaterialAttribute name) const; /**< @overload */ |
||||
|
||||
/**
|
||||
* @brief Attribute name |
||||
* |
||||
* The @p id is expected to be smaller than @ref attributeCount() const. |
||||
* The returned view always has |
||||
* @ref Corrade::Containers::StringViewFlag::NullTerminated set. |
||||
* @see @ref attributeType() |
||||
*/ |
||||
Containers::StringView attributeName(UnsignedInt id) const; |
||||
|
||||
/**
|
||||
* @brief Attribute type |
||||
* |
||||
* The @p id is expected to be smaller than @ref attributeCount() const. |
||||
* @see @ref attributeName() |
||||
*/ |
||||
MaterialAttributeType attributeType(UnsignedInt id) const; |
||||
|
||||
/**
|
||||
* @brief Type of a named attribute |
||||
* |
||||
* The @p name is expected to exist. |
||||
* @see @ref hasAttribute() |
||||
*/ |
||||
MaterialAttributeType attributeType(Containers::StringView name) const; |
||||
/** @overload */ |
||||
MaterialAttributeType attributeType(MaterialAttribute name) const; |
||||
|
||||
/**
|
||||
* @brief Type-erased attribute value |
||||
* |
||||
* The @p id is expected to be smaller than @ref attributeCount() const. |
||||
* Cast the pointer to a concrete type based on @ref type(). |
||||
*/ |
||||
const void* attribute(UnsignedInt id) const; |
||||
|
||||
/**
|
||||
* @brief Type-erased value of a named attribute |
||||
* |
||||
* The @p name is expected to exist. Cast the pointer to a concrete |
||||
* type based on @ref attributeType(). |
||||
* @see @ref hasAttribute() |
||||
*/ |
||||
const void* attribute(Containers::StringView name) const; |
||||
const void* attribute(MaterialAttribute name) const; /**< @overload */ |
||||
|
||||
/**
|
||||
* @brief Attribute value |
||||
* |
||||
* The @p id is expected to be smaller than @ref attributeCount() const. |
||||
* Expects that @p T corresponds to @ref attributeType(UnsignedInt) const |
||||
* for given @p id. |
||||
*/ |
||||
template<class T> T attribute(UnsignedInt id) const; |
||||
|
||||
/**
|
||||
* @brief Value of a named attribute |
||||
* |
||||
* The @p name is expected to exist. Expects that @p T corresponds to |
||||
* @ref attributeType(Containers::StringView) const for given @p name. |
||||
* @see @ref hasAttribute() |
||||
*/ |
||||
template<class T> T attribute(Containers::StringView name) const; |
||||
template<class T> T attribute(MaterialAttribute name) const; /**< @overload */ |
||||
|
||||
/**
|
||||
* @brief Release data storage |
||||
* |
||||
* Releases the ownership of the attribute array and resets internal |
||||
* 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. |
||||
* @see @ref data() |
||||
*/ |
||||
Containers::Array<MaterialAttributeData> release(); |
||||
|
||||
/**
|
||||
* @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 helper that doesn't assert, unlike attributeId() */ |
||||
UnsignedInt attributeFor(Containers::StringView name) const; |
||||
|
||||
Containers::Array<MaterialAttributeData> _data; |
||||
const void* _importerState; |
||||
}; |
||||
|
||||
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; |
||||
} |
||||
}; |
||||
#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(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> 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} {} |
||||
|
||||
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()); |
||||
} |
||||
|
||||
template<class T> T MaterialData::attribute(UnsignedInt id) const { |
||||
const void* const value = attribute(id); |
||||
#ifdef CORRADE_GRACEFUL_ASSERT |
||||
if(!value) return {}; |
||||
#endif |
||||
CORRADE_ASSERT(Implementation::MaterialAttributeTypeFor<T>::type() == _data[id]._data.type, |
||||
"Trade::MaterialData::attribute(): improper type requested for" << (_data[id]._data.data + 1) << "of" << _data[id]._data.type, {}); |
||||
return *reinterpret_cast<const T*>(value); |
||||
} |
||||
|
||||
template<class T> T MaterialData::attribute(Containers::StringView name) const { |
||||
const UnsignedInt id = attributeFor(name); |
||||
CORRADE_ASSERT(id != ~UnsignedInt{}, |
||||
"Trade::MaterialData::attribute(): attribute" << name << "not found", {}); |
||||
return attribute<T>(id); |
||||
} |
||||
|
||||
template<class T> T MaterialData::attribute(MaterialAttribute name) const { |
||||
const Containers::StringView string = attributeString(name); |
||||
CORRADE_ASSERT(string.data(), "Trade::MaterialData::attribute(): invalid name" << name, {}); |
||||
return attribute<T>(string); |
||||
} |
||||
|
||||
}} |
||||
|
||||
#endif |
||||
Loading…
Reference in new issue