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