From b7199c07108a37fadf33fbafc1a13574d64e07ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 22 Jul 2020 16:39:19 +0200 Subject: [PATCH 01/36] Trade: remove deprecated mutable accessors in PhongMaterialData. Those would be unfortunately very hard to preserve when switching to the new MaterialData. These accessors mattered mostly only when populating the instance (i.e., in importer plugins) so such breakage shouldn't be too much of a problem for regular users. --- doc/changelog.dox | 9 ++ src/Magnum/Trade/PhongMaterialData.cpp | 18 ---- src/Magnum/Trade/PhongMaterialData.h | 57 ----------- src/Magnum/Trade/Test/MaterialDataTest.cpp | 108 ++++++++++----------- 4 files changed, 59 insertions(+), 133 deletions(-) diff --git a/doc/changelog.dox b/doc/changelog.dox index 3705cb8226..f8d91247c9 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -162,6 +162,11 @@ See also: afterwards. This can cause compilation breakages in case the type constructor has the parent parameter non-optional, pass the parent explicitly in that case. +- Mutable access to @ref Trade::PhongMaterialData color and texture + information, deprecated in 2020.06, is now removed, as it's impossible to + implement through the redesigned @ref Trade::MaterialData APIs. However + these APIs were mainly used to populate the contents in asset importers, so + this shouldn't cause any breakages in user code. @section changelog-2020-06 2020.06 @@ -821,6 +826,10 @@ Released 2020-06-27, tagged as deprecated on the 3D variant, use separate @ref Shaders::MeshVisualizer3D::setTransformationMatrix() and @ref Shaders::MeshVisualizer3D::setProjectionMatrix() instead +- Mutable access to @ref Trade::PhongMaterialData color and texture + information is deprecated. This was mainly used to populate the contents + in asset importers, but the class was redesigned and this is no longer + needed. - The unprefixed and alien-looking @cpp FLOAT_EQUALITY_PRECISION @ce, @cpp DOUBLE_EQUALITY_PRECISION @ce and @cpp LONG_DOUBLE_EQUALITY_PRECISION @ce macros are no longer used by any code and thus deprecated in favor of using diff --git a/src/Magnum/Trade/PhongMaterialData.cpp b/src/Magnum/Trade/PhongMaterialData.cpp index c0e667dead..85435a2d52 100644 --- a/src/Magnum/Trade/PhongMaterialData.cpp +++ b/src/Magnum/Trade/PhongMaterialData.cpp @@ -75,12 +75,6 @@ UnsignedInt PhongMaterialData::ambientTexture() const { return _ambientTexture; } -#ifdef MAGNUM_BUILD_DEPRECATED -UnsignedInt& PhongMaterialData::ambientTexture() { - CORRADE_ASSERT(flags() & Flag::AmbientTexture, "Trade::PhongMaterialData::ambientTexture(): the material doesn't have an ambient texture", _ambientTexture); - return _ambientTexture; -} -#endif UnsignedInt PhongMaterialData::ambientCoordinateSet() const { CORRADE_ASSERT(flags() & Flag::AmbientTexture, "Trade::PhongMaterialData::ambientCoordinateSet(): the material doesn't have an ambient texture", {}); @@ -92,12 +86,6 @@ UnsignedInt PhongMaterialData::diffuseTexture() const { return _diffuseTexture; } -#ifdef MAGNUM_BUILD_DEPRECATED -UnsignedInt& PhongMaterialData::diffuseTexture() { - CORRADE_ASSERT(flags() & Flag::DiffuseTexture, "Trade::PhongMaterialData::diffuseTexture(): the material doesn't have a diffuse texture", _diffuseTexture); - return _diffuseTexture; -} -#endif UnsignedInt PhongMaterialData::diffuseCoordinateSet() const { CORRADE_ASSERT(flags() & Flag::DiffuseTexture, "Trade::PhongMaterialData::diffuseCoordinateSet(): the material doesn't have a diffuse texture", {}); @@ -109,12 +97,6 @@ UnsignedInt PhongMaterialData::specularTexture() const { return _specularTexture; } -#ifdef MAGNUM_BUILD_DEPRECATED -UnsignedInt& PhongMaterialData::specularTexture() { - CORRADE_ASSERT(flags() & Flag::SpecularTexture, "Trade::PhongMaterialData::specularTexture(): the material doesn't have a specular texture", _specularTexture); - return _specularTexture; -} -#endif UnsignedInt PhongMaterialData::specularCoordinateSet() const { CORRADE_ASSERT(flags() & Flag::SpecularTexture, "Trade::PhongMaterialData::specularCoordinateSet(): the material doesn't have a specular texture", {}); diff --git a/src/Magnum/Trade/PhongMaterialData.h b/src/Magnum/Trade/PhongMaterialData.h index 3565b8892d..835522cc34 100644 --- a/src/Magnum/Trade/PhongMaterialData.h +++ b/src/Magnum/Trade/PhongMaterialData.h @@ -212,18 +212,6 @@ class MAGNUM_TRADE_EXPORT PhongMaterialData: public AbstractMaterialData { */ Color4 ambientColor() const { return _ambientColor; } - #ifdef MAGNUM_BUILD_DEPRECATED - /** - * @m_deprecated_since{2020,06} Use the constructor to populate all - * values instead. - */ - /* Not marked with CORRADE_DEPRECATED() because the compiler picks this - overload over the const one for a non-const instance and there's no - way to selectively enable deprecation warnings only if the code - attempts to modify the value. */ - Color4& ambientColor() { return _ambientColor; } - #endif - /** * @brief Ambient texture ID * @@ -233,15 +221,6 @@ class MAGNUM_TRADE_EXPORT PhongMaterialData: public AbstractMaterialData { */ UnsignedInt ambientTexture() const; - #ifdef MAGNUM_BUILD_DEPRECATED - /** - * @m_deprecated_since{2020,06} Use the constructor to populate all - * values instead of modifying the instance afterwards. - */ - /* Not using CORRADE_DEPRECATED(), see why above */ - UnsignedInt& ambientTexture(); - #endif - /** * @brief Ambient texture coordinate set * @m_since{2020,06} @@ -260,15 +239,6 @@ class MAGNUM_TRADE_EXPORT PhongMaterialData: public AbstractMaterialData { */ Color4 diffuseColor() const { return _diffuseColor; } - #ifdef MAGNUM_BUILD_DEPRECATED - /** - * @m_deprecated_since{2020,06} Use the constructor to populate all - * values instead of modifying the instance afterwards. - */ - /* Not using CORRADE_DEPRECATED(), see why above */ - Color4& diffuseColor() { return _diffuseColor; } - #endif - /** * @brief Diffuse texture ID * @@ -278,15 +248,6 @@ class MAGNUM_TRADE_EXPORT PhongMaterialData: public AbstractMaterialData { */ UnsignedInt diffuseTexture() const; - #ifdef MAGNUM_BUILD_DEPRECATED - /** - * @m_deprecated_since{2020,06} Use the constructor to populate all - * values instead of modifying the instance afterwards. - */ - /* Not using CORRADE_DEPRECATED(), see why above */ - UnsignedInt& diffuseTexture(); - #endif - /** * @brief Diffuse texture coordinate set * @m_since{2020,06} @@ -305,15 +266,6 @@ class MAGNUM_TRADE_EXPORT PhongMaterialData: public AbstractMaterialData { */ Color4 specularColor() const { return _specularColor; } - #ifdef MAGNUM_BUILD_DEPRECATED - /** - * @m_deprecated_since{2020,06} Use the constructor to populate all - * values instead of modifying the instance afterwards. - */ - /* Not using CORRADE_DEPRECATED(), see why above */ - Color4& specularColor() { return _specularColor; } - #endif - /** * @brief Specular texture ID * @@ -323,15 +275,6 @@ class MAGNUM_TRADE_EXPORT PhongMaterialData: public AbstractMaterialData { */ UnsignedInt specularTexture() const; - #ifdef MAGNUM_BUILD_DEPRECATED - /** - * @m_deprecated_since{2020,06} Use the constructor to populate all - * values instead of modifying the instance afterwards. - */ - /* Not using CORRADE_DEPRECATED(), see why above */ - UnsignedInt& specularTexture(); - #endif - /** * @brief Specular texture coordinate set * @m_since{2020,06} diff --git a/src/Magnum/Trade/Test/MaterialDataTest.cpp b/src/Magnum/Trade/Test/MaterialDataTest.cpp index 26ecfd31c6..d683b24e34 100644 --- a/src/Magnum/Trade/Test/MaterialDataTest.cpp +++ b/src/Magnum/Trade/Test/MaterialDataTest.cpp @@ -88,18 +88,16 @@ void MaterialDataTest::constructPhong() { 0xacabad_rgbf, {}, {}, {}, MaterialAlphaMode::Mask, 0.3f, 80.0f, &a}; - /** @todo use data directly once deprecated mutable access is gone */ - const PhongMaterialData& cdata = data; - CORRADE_COMPARE(cdata.type(), MaterialType::Phong); - CORRADE_COMPARE(cdata.flags(), PhongMaterialData::Flags{}); - CORRADE_COMPARE(cdata.ambientColor(), 0xccffbb_rgbf); - CORRADE_COMPARE(cdata.diffuseColor(), 0xebefbf_rgbf); - CORRADE_COMPARE(cdata.specularColor(), 0xacabad_rgbf); - CORRADE_COMPARE(cdata.textureMatrix(), Matrix3{}); - CORRADE_COMPARE(cdata.alphaMode(), MaterialAlphaMode::Mask); - CORRADE_COMPARE(cdata.alphaMask(), 0.3f); - CORRADE_COMPARE(cdata.shininess(), 80.0f); - CORRADE_COMPARE(cdata.importerState(), &a); + CORRADE_COMPARE(data.type(), MaterialType::Phong); + CORRADE_COMPARE(data.flags(), PhongMaterialData::Flags{}); + CORRADE_COMPARE(data.ambientColor(), 0xccffbb_rgbf); + CORRADE_COMPARE(data.diffuseColor(), 0xebefbf_rgbf); + CORRADE_COMPARE(data.specularColor(), 0xacabad_rgbf); + CORRADE_COMPARE(data.textureMatrix(), Matrix3{}); + CORRADE_COMPARE(data.alphaMode(), MaterialAlphaMode::Mask); + CORRADE_COMPARE(data.alphaMask(), 0.3f); + CORRADE_COMPARE(data.shininess(), 80.0f); + CORRADE_COMPARE(data.importerState(), &a); } void MaterialDataTest::constructPhongTextured() { @@ -113,22 +111,20 @@ void MaterialDataTest::constructPhongTextured() { 0xacabad_rgbf, 17, {}, {}, MaterialAlphaMode::Blend, 0.37f, 96.0f, &a}; - /** @todo use data directly once deprecated mutable access is gone */ - const PhongMaterialData& cdata = data; - CORRADE_COMPARE(cdata.type(), MaterialType::Phong); - CORRADE_COMPARE(cdata.flags(), PhongMaterialData::Flag::AmbientTexture|PhongMaterialData::Flag::SpecularTexture); - CORRADE_COMPARE(cdata.ambientColor(), 0x111111_rgbf); - CORRADE_COMPARE(cdata.ambientTexture(), 42); - CORRADE_COMPARE(cdata.ambientCoordinateSet(), 0); - CORRADE_COMPARE(cdata.diffuseColor(), 0xeebbff_rgbf); - CORRADE_COMPARE(cdata.specularColor(), 0xacabad_rgbf); - CORRADE_COMPARE(cdata.specularTexture(), 17); - CORRADE_COMPARE(cdata.specularCoordinateSet(), 0); - CORRADE_COMPARE(cdata.textureMatrix(), Matrix3{}); - CORRADE_COMPARE(cdata.alphaMode(), MaterialAlphaMode::Blend); - CORRADE_COMPARE(cdata.alphaMask(), 0.37f); - CORRADE_COMPARE(cdata.shininess(), 96.0f); - CORRADE_COMPARE(cdata.importerState(), &a); + CORRADE_COMPARE(data.type(), MaterialType::Phong); + CORRADE_COMPARE(data.flags(), PhongMaterialData::Flag::AmbientTexture|PhongMaterialData::Flag::SpecularTexture); + CORRADE_COMPARE(data.ambientColor(), 0x111111_rgbf); + CORRADE_COMPARE(data.ambientTexture(), 42); + CORRADE_COMPARE(data.ambientCoordinateSet(), 0); + CORRADE_COMPARE(data.diffuseColor(), 0xeebbff_rgbf); + CORRADE_COMPARE(data.specularColor(), 0xacabad_rgbf); + CORRADE_COMPARE(data.specularTexture(), 17); + CORRADE_COMPARE(data.specularCoordinateSet(), 0); + CORRADE_COMPARE(data.textureMatrix(), Matrix3{}); + CORRADE_COMPARE(data.alphaMode(), MaterialAlphaMode::Blend); + CORRADE_COMPARE(data.alphaMask(), 0.37f); + CORRADE_COMPARE(data.shininess(), 96.0f); + CORRADE_COMPARE(data.importerState(), &a); } void MaterialDataTest::constructPhongTexturedTextureTransform() { @@ -143,20 +139,18 @@ void MaterialDataTest::constructPhongTexturedTextureTransform() { Matrix3::rotation(90.0_degf), MaterialAlphaMode::Opaque, 0.5f, 96.0f, &a}; - /** @todo use data directly once deprecated mutable access is gone */ - const PhongMaterialData& cdata = data; - CORRADE_COMPARE(cdata.type(), MaterialType::Phong); - CORRADE_COMPARE(cdata.flags(), PhongMaterialData::Flag::DiffuseTexture|PhongMaterialData::Flag::NormalTexture|PhongMaterialData::Flag::TextureTransformation); - CORRADE_COMPARE(cdata.ambientColor(), 0x111111_rgbf); - CORRADE_COMPARE(cdata.diffuseColor(), 0xeebbff_rgbf); - CORRADE_COMPARE(cdata.diffuseTexture(), 42); - CORRADE_COMPARE(cdata.specularColor(), 0xacabad_rgbf); - CORRADE_COMPARE(cdata.normalTexture(), 17); - CORRADE_COMPARE(cdata.textureMatrix(), Matrix3::rotation(90.0_degf)); - CORRADE_COMPARE(cdata.alphaMode(), MaterialAlphaMode::Opaque); - CORRADE_COMPARE(cdata.alphaMask(), 0.5f); - CORRADE_COMPARE(cdata.shininess(), 96.0f); - CORRADE_COMPARE(cdata.importerState(), &a); + CORRADE_COMPARE(data.type(), MaterialType::Phong); + CORRADE_COMPARE(data.flags(), PhongMaterialData::Flag::DiffuseTexture|PhongMaterialData::Flag::NormalTexture|PhongMaterialData::Flag::TextureTransformation); + CORRADE_COMPARE(data.ambientColor(), 0x111111_rgbf); + CORRADE_COMPARE(data.diffuseColor(), 0xeebbff_rgbf); + CORRADE_COMPARE(data.diffuseTexture(), 42); + CORRADE_COMPARE(data.specularColor(), 0xacabad_rgbf); + CORRADE_COMPARE(data.normalTexture(), 17); + CORRADE_COMPARE(data.textureMatrix(), Matrix3::rotation(90.0_degf)); + CORRADE_COMPARE(data.alphaMode(), MaterialAlphaMode::Opaque); + CORRADE_COMPARE(data.alphaMask(), 0.5f); + CORRADE_COMPARE(data.shininess(), 96.0f); + CORRADE_COMPARE(data.importerState(), &a); } void MaterialDataTest::constructPhongTexturedCoordinateSets() { @@ -171,22 +165,20 @@ void MaterialDataTest::constructPhongTexturedCoordinateSets() { {}, 0, {}, MaterialAlphaMode::Blend, 0.37f, 96.0f, &a}; - /** @todo use data directly once deprecated mutable access is gone */ - const PhongMaterialData& cdata = data; - CORRADE_COMPARE(cdata.type(), MaterialType::Phong); - CORRADE_COMPARE(cdata.flags(), PhongMaterialData::Flag::AmbientTexture|PhongMaterialData::Flag::SpecularTexture|PhongMaterialData::Flag::TextureCoordinateSets); - CORRADE_COMPARE(cdata.ambientColor(), 0x111111_rgbf); - CORRADE_COMPARE(cdata.ambientTexture(), 42); - CORRADE_COMPARE(cdata.ambientCoordinateSet(), 3); - CORRADE_COMPARE(cdata.diffuseColor(), 0xeebbff_rgbf); - CORRADE_COMPARE(cdata.specularColor(), 0xacabad_rgbf); - CORRADE_COMPARE(cdata.specularTexture(), 17); - CORRADE_COMPARE(cdata.specularCoordinateSet(), 1); - CORRADE_COMPARE(cdata.textureMatrix(), Matrix3{}); - CORRADE_COMPARE(cdata.alphaMode(), MaterialAlphaMode::Blend); - CORRADE_COMPARE(cdata.alphaMask(), 0.37f); - CORRADE_COMPARE(cdata.shininess(), 96.0f); - CORRADE_COMPARE(cdata.importerState(), &a); + CORRADE_COMPARE(data.type(), MaterialType::Phong); + CORRADE_COMPARE(data.flags(), PhongMaterialData::Flag::AmbientTexture|PhongMaterialData::Flag::SpecularTexture|PhongMaterialData::Flag::TextureCoordinateSets); + CORRADE_COMPARE(data.ambientColor(), 0x111111_rgbf); + CORRADE_COMPARE(data.ambientTexture(), 42); + CORRADE_COMPARE(data.ambientCoordinateSet(), 3); + CORRADE_COMPARE(data.diffuseColor(), 0xeebbff_rgbf); + CORRADE_COMPARE(data.specularColor(), 0xacabad_rgbf); + CORRADE_COMPARE(data.specularTexture(), 17); + CORRADE_COMPARE(data.specularCoordinateSet(), 1); + CORRADE_COMPARE(data.textureMatrix(), Matrix3{}); + CORRADE_COMPARE(data.alphaMode(), MaterialAlphaMode::Blend); + CORRADE_COMPARE(data.alphaMask(), 0.37f); + CORRADE_COMPARE(data.shininess(), 96.0f); + CORRADE_COMPARE(data.importerState(), &a); } void MaterialDataTest::constructPhongTextureTransformNoTextures() { From 6811198532c49ddf0476f26c56aafceb40914540 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 4 Aug 2020 17:12:58 +0200 Subject: [PATCH 02/36] Trade: rename MaterialData::*CoordinateSet() to *TextureCoordinates(). Better since it has the same prefix as other texture-related attributes, such as *TextureMatrix(). Not using *TextureCoordinateSet() because that's overly long, *TextureSet() is OTOH confusing (and especially so if we'd introduce *TextureLayer()). --- doc/changelog.dox | 10 ++ src/Magnum/Trade/PhongMaterialData.cpp | 42 ++++----- src/Magnum/Trade/PhongMaterialData.h | 103 ++++++++++++++++----- src/Magnum/Trade/Test/MaterialDataTest.cpp | 64 ++++++------- 4 files changed, 141 insertions(+), 78 deletions(-) diff --git a/doc/changelog.dox b/doc/changelog.dox index f8d91247c9..e4fb6ed8fd 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -111,6 +111,16 @@ See also: coordinates), @ref MeshTools::compile() should be using only the first set but it wasn't. +@subsection changelog-latest-deprecated Deprecated APIs + +- @cpp Trade::PhongMaterialData::ambientCoordinateSet() @ce, + @cpp diffuseCoordinateSet() @ce, @cpp specularCoordinateSet() @ce and + @cpp normalCoordinateSet() @ce are deprecated in favor of more consistently + named @ref Trade::PhongMaterialData::ambientTextureCoordinates(), + @ref Trade::PhongMaterialData::diffuseTextureCoordinates() "diffuseTextureCoordinates()", + @ref Trade::PhongMaterialData::specularTextureCoordinates() "specularTextureCoordinates()" + and @ref Trade::PhongMaterialData::normalTextureCoordinates() "normalTextureCoordinates()" + @subsection changelog-latest-compatibility Potential compatibility breakages, removed APIs - Removed remaining APIs deprecated in version 2018.10, in particular: diff --git a/src/Magnum/Trade/PhongMaterialData.cpp b/src/Magnum/Trade/PhongMaterialData.cpp index 85435a2d52..c5c920f782 100644 --- a/src/Magnum/Trade/PhongMaterialData.cpp +++ b/src/Magnum/Trade/PhongMaterialData.cpp @@ -32,29 +32,29 @@ namespace Magnum { namespace Trade { using namespace Math::Literals; -PhongMaterialData::PhongMaterialData(const Flags flags, const Color4& ambientColor, const UnsignedInt ambientTexture, const UnsignedInt ambientCoordinateSet, const Color4& diffuseColor, const UnsignedInt diffuseTexture, const UnsignedInt diffuseCoordinateSet, const Color4& specularColor, const UnsignedInt specularTexture, const UnsignedInt specularCoordinateSet, const UnsignedInt normalTexture, const UnsignedInt normalCoordinateSet, const Matrix3& textureMatrix, const MaterialAlphaMode alphaMode, const Float alphaMask, const Float shininess, const void* const importerState) noexcept: AbstractMaterialData{MaterialType::Phong, AbstractMaterialData::Flag(UnsignedShort(flags)), alphaMode, alphaMask, importerState}, _ambientColor{ambientColor}, _diffuseColor{diffuseColor}, _specularColor{specularColor}, _shininess{shininess} { +PhongMaterialData::PhongMaterialData(const Flags flags, const Color4& ambientColor, const UnsignedInt ambientTexture, const UnsignedInt ambientTextureCoordinates, const Color4& diffuseColor, const UnsignedInt diffuseTexture, const UnsignedInt diffuseTextureCoordinates, const Color4& specularColor, const UnsignedInt specularTexture, const UnsignedInt specularTextureCoordinates, const UnsignedInt normalTexture, const UnsignedInt normalTextureCoordinates, const Matrix3& textureMatrix, const MaterialAlphaMode alphaMode, const Float alphaMask, const Float shininess, const void* const importerState) noexcept: AbstractMaterialData{MaterialType::Phong, AbstractMaterialData::Flag(UnsignedShort(flags)), alphaMode, alphaMask, importerState}, _ambientColor{ambientColor}, _diffuseColor{diffuseColor}, _specularColor{specularColor}, _shininess{shininess} { CORRADE_ASSERT(!(flags & Flag::TextureTransformation) || (flags & (Flag::AmbientTexture|Flag::DiffuseTexture|Flag::SpecularTexture|Flag::NormalTexture)), "Trade::PhongMaterialData: texture transformation enabled but the material has no textures", ); CORRADE_ASSERT((flags & Flag::TextureTransformation) || textureMatrix == Matrix3{}, "PhongMaterialData::PhongMaterialData: non-default texture matrix requires Flag::TextureTransformation to be enabled", ); - CORRADE_ASSERT((flags & Flag::TextureCoordinateSets) || (ambientCoordinateSet == 0 && diffuseCoordinateSet == 0 && specularCoordinateSet == 0 && normalCoordinateSet == 0), - "PhongMaterialData::PhongMaterialData: non-zero texture coordinate sets require Flag::TextureCoordinateSets to be enabled", ); + CORRADE_ASSERT((flags & Flag::TextureCoordinates) || (ambientTextureCoordinates == 0 && diffuseTextureCoordinates == 0 && specularTextureCoordinates == 0 && normalTextureCoordinates == 0), + "PhongMaterialData::PhongMaterialData: non-zero texture coordinate sets require Flag::TextureCoordinates to be enabled", ); if(flags & Flag::AmbientTexture) { _ambientTexture = ambientTexture; - _ambientCoordinateSet = ambientCoordinateSet; + _ambientTextureCoordinates = ambientTextureCoordinates; } if(flags & Flag::DiffuseTexture) { _diffuseTexture = diffuseTexture; - _diffuseCoordinateSet = diffuseCoordinateSet; + _diffuseTextureCoordinates = diffuseTextureCoordinates; } if(flags & Flag::SpecularTexture) { _specularTexture = specularTexture; - _specularCoordinateSet = specularCoordinateSet; + _specularTextureCoordinates = specularTextureCoordinates; } if(flags & Flag::NormalTexture) { _normalTexture = normalTexture; - _normalCoordinateSet = normalCoordinateSet; + _normalTextureCoordinates = normalTextureCoordinates; } if(flags & Flag::TextureTransformation) _textureMatrix = textureMatrix; @@ -76,9 +76,9 @@ UnsignedInt PhongMaterialData::ambientTexture() const { } -UnsignedInt PhongMaterialData::ambientCoordinateSet() const { - CORRADE_ASSERT(flags() & Flag::AmbientTexture, "Trade::PhongMaterialData::ambientCoordinateSet(): the material doesn't have an ambient texture", {}); - return _ambientCoordinateSet; +UnsignedInt PhongMaterialData::ambientTextureCoordinates() const { + CORRADE_ASSERT(flags() & Flag::AmbientTexture, "Trade::PhongMaterialData::ambientTextureCoordinates(): the material doesn't have an ambient texture", {}); + return _ambientTextureCoordinates; } UnsignedInt PhongMaterialData::diffuseTexture() const { @@ -87,9 +87,9 @@ UnsignedInt PhongMaterialData::diffuseTexture() const { } -UnsignedInt PhongMaterialData::diffuseCoordinateSet() const { - CORRADE_ASSERT(flags() & Flag::DiffuseTexture, "Trade::PhongMaterialData::diffuseCoordinateSet(): the material doesn't have a diffuse texture", {}); - return _diffuseCoordinateSet; +UnsignedInt PhongMaterialData::diffuseTextureCoordinates() const { + CORRADE_ASSERT(flags() & Flag::DiffuseTexture, "Trade::PhongMaterialData::diffuseTextureCoordinates(): the material doesn't have a diffuse texture", {}); + return _diffuseTextureCoordinates; } UnsignedInt PhongMaterialData::specularTexture() const { @@ -98,9 +98,9 @@ UnsignedInt PhongMaterialData::specularTexture() const { } -UnsignedInt PhongMaterialData::specularCoordinateSet() const { - CORRADE_ASSERT(flags() & Flag::SpecularTexture, "Trade::PhongMaterialData::specularCoordinateSet(): the material doesn't have a specular texture", {}); - return _specularCoordinateSet; +UnsignedInt PhongMaterialData::specularTextureCoordinates() const { + CORRADE_ASSERT(flags() & Flag::SpecularTexture, "Trade::PhongMaterialData::specularTextureCoordinates(): the material doesn't have a specular texture", {}); + return _specularTextureCoordinates; } UnsignedInt PhongMaterialData::normalTexture() const { @@ -108,9 +108,9 @@ UnsignedInt PhongMaterialData::normalTexture() const { return _normalTexture; } -UnsignedInt PhongMaterialData::normalCoordinateSet() const { - CORRADE_ASSERT(flags() & Flag::NormalTexture, "Trade::PhongMaterialData::normalCoordinateSet(): the material doesn't have a normal texture", {}); - return _normalCoordinateSet; +UnsignedInt PhongMaterialData::normalTextureCoordinates() const { + CORRADE_ASSERT(flags() & Flag::NormalTexture, "Trade::PhongMaterialData::normalTextureCoordinates(): the material doesn't have a normal texture", {}); + return _normalTextureCoordinates; } Debug& operator<<(Debug& debug, const PhongMaterialData::Flag value) { @@ -125,7 +125,7 @@ Debug& operator<<(Debug& debug, const PhongMaterialData::Flag value) { _c(SpecularTexture) _c(NormalTexture) _c(TextureTransformation) - _c(TextureCoordinateSets) + _c(TextureCoordinates) #undef _c /* LCOV_EXCL_STOP */ } @@ -141,7 +141,7 @@ Debug& operator<<(Debug& debug, const PhongMaterialData::Flags value) { PhongMaterialData::Flag::SpecularTexture, PhongMaterialData::Flag::NormalTexture, PhongMaterialData::Flag::TextureTransformation, - PhongMaterialData::Flag::TextureCoordinateSets}); + PhongMaterialData::Flag::TextureCoordinates}); } }} diff --git a/src/Magnum/Trade/PhongMaterialData.h b/src/Magnum/Trade/PhongMaterialData.h index 835522cc34..afa54b64dc 100644 --- a/src/Magnum/Trade/PhongMaterialData.h +++ b/src/Magnum/Trade/PhongMaterialData.h @@ -77,9 +77,18 @@ class MAGNUM_TRADE_EXPORT PhongMaterialData: public AbstractMaterialData { /** * The material uses non-default texture coordinate sets - * @m_since{2020,06} + * @m_since_latest + */ + TextureCoordinates = 1 << 6, + + #ifdef MAGNUM_BUILD_DEPRECATED + /** + * The material uses non-default texture coordinate sets + * @m_deprecated_since_latest Use @ref Flag::TextureCoordinates + * instead. */ - TextureCoordinateSets = 1 << 6 + TextureCoordinateSets CORRADE_DEPRECATED_ENUM("use Flag::TextureCoordinates instead") = TextureCoordinates + #endif }; /** @@ -121,7 +130,7 @@ class MAGNUM_TRADE_EXPORT PhongMaterialData: public AbstractMaterialData { * @param importerState Importer-specific state * @m_since{2020,06} * - * All `*CoordinateSet` accessors are implicitly zero with this + * All `*TextureCoordinates()` accessors are implicitly zero with this * constructor. If @p textureMatrix is not default-constructed, expects * @ref Flag::TextureTransformation to be enabled as well. */ @@ -136,26 +145,26 @@ class MAGNUM_TRADE_EXPORT PhongMaterialData: public AbstractMaterialData { * default value for a textured material. * @param ambientTexture Ambient texture ID. Ignored if @p flags * doesn't have @ref Flag::AmbientTexture. - * @param ambientCoordinateSet Ambient texture coordinate set. Ignored - * if @p flags doesn't have @ref Flag::AmbientTexture + * @param ambientTextureCoordinates Ambient texture coordinate set. + * Ignored if @p flags doesn't have @ref Flag::AmbientTexture * @param diffuseColor Diffuse color. Use * @cpp 0xffffffff_rgbaf @ce for a default value for both a * non-textured and a textured material. * @param diffuseTexture Diffuse texture ID. Ignored if @p flags * doesn't have @ref Flag::DiffuseTexture. - * @param diffuseCoordinateSet Diffuse texture coordinate set. Ignored - * if @p flags doesn't have @ref Flag::DiffuseTexture + * @param diffuseTextureCoordinates Diffuse texture coordinate set. + * Ignored if @p flags doesn't have @ref Flag::DiffuseTexture * @param specularColor Specular color. Use * @cpp 0xffffffff_rgbaf @ce for a default value for both a * non-textured and a textured material. * @param specularTexture Specular texture ID. Ignored if * @p flags doesn't have @ref Flag::SpecularTexture. - * @param specularCoordinateSet Specular texture coordinate set. + * @param specularTextureCoordinates Specular texture coordinate set. * Ignored if @p flags doesn't have @ref Flag::SpecularTexture. * @param normalTexture Normal texture ID. Ignored if @p flags * doesn't have @ref Flag::NormalTexture. - * @param normalCoordinateSet Normal texture coordinate set. Ignored - * if @p flags doesn't have @ref Flag::NormalTexture. + * @param normalTextureCoordinates Normal texture coordinate set. + * Ignored if @p flags doesn't have @ref Flag::NormalTexture. * @param textureMatrix Texture coordinate transformation * @param alphaMode Alpha mode. Use * @ref MaterialAlphaMode::Opaque for a default value. @@ -168,10 +177,10 @@ class MAGNUM_TRADE_EXPORT PhongMaterialData: public AbstractMaterialData { * * If @p textureMatrix is not default-constructed, expects * @ref Flag::TextureTransformation to be enabled as well. If any - * `*CoordinateSet` is non-zero, expects @ref Flag::TextureCoordinateSets - * to be enabled as well. + * `*Coordinates` parameter is non-zero, expects + * @ref Flag::TextureCoordinates to be enabled as well. */ - explicit PhongMaterialData(Flags flags, const Color4& ambientColor, UnsignedInt ambientTexture, UnsignedInt ambientCoordinateSet, const Color4& diffuseColor, UnsignedInt diffuseTexture, UnsignedInt diffuseCoordinateSet, const Color4& specularColor, UnsignedInt specularTexture, UnsignedInt specularCoordinateSet, UnsignedInt normalTexture, UnsignedInt normalCoordinateSet, const Matrix3& textureMatrix, MaterialAlphaMode alphaMode, Float alphaMask, Float shininess, const void* importerState = nullptr) noexcept; + explicit PhongMaterialData(Flags flags, const Color4& ambientColor, UnsignedInt ambientTexture, UnsignedInt ambientTextureCoordinates, const Color4& diffuseColor, UnsignedInt diffuseTexture, UnsignedInt diffuseTextureCoordinates, const Color4& specularColor, UnsignedInt specularTexture, UnsignedInt specularTextureCoordinates, UnsignedInt normalTexture, UnsignedInt normalTextureCoordinates, const Matrix3& textureMatrix, MaterialAlphaMode alphaMode, Float alphaMask, Float shininess, const void* importerState = nullptr) noexcept; #ifdef MAGNUM_BUILD_DEPRECATED /** @@ -223,12 +232,23 @@ class MAGNUM_TRADE_EXPORT PhongMaterialData: public AbstractMaterialData { /** * @brief Ambient texture coordinate set - * @m_since{2020,06} + * @m_since_latest * * Available only if the material has @ref Flag::AmbientTexture. * @see @ref flags(), @ref AbstractImporter::texture() */ - UnsignedInt ambientCoordinateSet() const; + UnsignedInt ambientTextureCoordinates() const; + + #ifdef MAGNUM_BUILD_DEPRECATED + /** + * @brief @copybrief ambientTextureCoordinates() + * @m_deprecated_since_latest Use @ref ambientTextureCoordinates() + * instead. + */ + CORRADE_DEPRECATED("use ambientTextureCoordinates() instead") UnsignedInt ambientCoordinateSet() const { + return ambientTextureCoordinates(); + } + #endif /** * @brief Diffuse color @@ -250,12 +270,23 @@ class MAGNUM_TRADE_EXPORT PhongMaterialData: public AbstractMaterialData { /** * @brief Diffuse texture coordinate set - * @m_since{2020,06} + * @m_since_latest * * Available only if the material has @ref Flag::DiffuseTexture. * @see @ref flags(), @ref AbstractImporter::texture() */ - UnsignedInt diffuseCoordinateSet() const; + UnsignedInt diffuseTextureCoordinates() const; + + #ifdef MAGNUM_BUILD_DEPRECATED + /** + * @brief @copybrief diffuseTextureCoordinates() + * @m_deprecated_since_latest Use @ref diffuseTextureCoordinates() + * instead. + */ + CORRADE_DEPRECATED("use diffuseTextureCoordinates() instead") UnsignedInt diffuseCoordinateSet() const { + return diffuseTextureCoordinates(); + } + #endif /** * @brief Specular color @@ -277,12 +308,23 @@ class MAGNUM_TRADE_EXPORT PhongMaterialData: public AbstractMaterialData { /** * @brief Specular texture coordinate set - * @m_since{2020,06} + * @m_since_latest * * Available only if the material has @ref Flag::SpecularTexture. * @see @ref flags(), @ref AbstractImporter::texture() */ - UnsignedInt specularCoordinateSet() const; + UnsignedInt specularTextureCoordinates() const; + + #ifdef MAGNUM_BUILD_DEPRECATED + /** + * @brief @copybrief specularTextureCoordinates() + * @m_deprecated_since_latest Use @ref specularTextureCoordinates() + * instead. + */ + CORRADE_DEPRECATED("use specularTextureCoordinates() instead") UnsignedInt specularCoordinateSet() const { + return specularTextureCoordinates(); + } + #endif /** * @brief Normal texture ID @@ -295,12 +337,23 @@ class MAGNUM_TRADE_EXPORT PhongMaterialData: public AbstractMaterialData { /** * @brief Normal texture coordinate set - * @m_since{2020,06} + * @m_since_latest * * Available only if the material has @ref Flag::NormalTexture. * @see @ref flags(), @ref AbstractImporter::texture() */ - UnsignedInt normalCoordinateSet() const; + UnsignedInt normalTextureCoordinates() const; + + #ifdef MAGNUM_BUILD_DEPRECATED + /** + * @brief @copybrief normalTextureCoordinates() + * @m_deprecated_since_latest Use @ref normalTextureCoordinates() + * instead. + */ + CORRADE_DEPRECATED("use normalTextureCoordinates() instead") UnsignedInt normalCoordinateSet() const { + return normalTextureCoordinates(); + } + #endif /** * @brief Texture coordinate transformation matrix @@ -320,15 +373,15 @@ class MAGNUM_TRADE_EXPORT PhongMaterialData: public AbstractMaterialData { and thus better noticeable */ Color4 _ambientColor; UnsignedInt _ambientTexture{~UnsignedInt{}}; - UnsignedInt _ambientCoordinateSet; + UnsignedInt _ambientTextureCoordinates; Color4 _diffuseColor; UnsignedInt _diffuseTexture{~UnsignedInt{}}; - UnsignedInt _diffuseCoordinateSet; + UnsignedInt _diffuseTextureCoordinates; Color4 _specularColor; UnsignedInt _specularTexture{~UnsignedInt{}}; - UnsignedInt _specularCoordinateSet; + UnsignedInt _specularTextureCoordinates; UnsignedInt _normalTexture{~UnsignedInt{}}; - UnsignedInt _normalCoordinateSet; + UnsignedInt _normalTextureCoordinates; Matrix3 _textureMatrix; Float _shininess; }; diff --git a/src/Magnum/Trade/Test/MaterialDataTest.cpp b/src/Magnum/Trade/Test/MaterialDataTest.cpp index d683b24e34..acbc60fa37 100644 --- a/src/Magnum/Trade/Test/MaterialDataTest.cpp +++ b/src/Magnum/Trade/Test/MaterialDataTest.cpp @@ -38,10 +38,10 @@ class MaterialDataTest: public TestSuite::Tester { void constructPhong(); void constructPhongTextured(); void constructPhongTexturedTextureTransform(); - void constructPhongTexturedCoordinateSets(); + void constructPhongTexturedCoordinates(); void constructPhongTextureTransformNoTextures(); void constructPhongNoTextureTransformationFlag(); - void constructPhongNoTextureCoordinateSetsFlag(); + void constructPhongNoTextureCoordinatesFlag(); void constructCopy(); void constructMovePhong(); @@ -60,10 +60,10 @@ MaterialDataTest::MaterialDataTest() { addTests({&MaterialDataTest::constructPhong, &MaterialDataTest::constructPhongTextured, &MaterialDataTest::constructPhongTexturedTextureTransform, - &MaterialDataTest::constructPhongTexturedCoordinateSets, + &MaterialDataTest::constructPhongTexturedCoordinates, &MaterialDataTest::constructPhongTextureTransformNoTextures, &MaterialDataTest::constructPhongNoTextureTransformationFlag, - &MaterialDataTest::constructPhongNoTextureCoordinateSetsFlag, + &MaterialDataTest::constructPhongNoTextureCoordinatesFlag, &MaterialDataTest::constructCopy, &MaterialDataTest::constructMovePhong, @@ -115,11 +115,11 @@ void MaterialDataTest::constructPhongTextured() { CORRADE_COMPARE(data.flags(), PhongMaterialData::Flag::AmbientTexture|PhongMaterialData::Flag::SpecularTexture); CORRADE_COMPARE(data.ambientColor(), 0x111111_rgbf); CORRADE_COMPARE(data.ambientTexture(), 42); - CORRADE_COMPARE(data.ambientCoordinateSet(), 0); + CORRADE_COMPARE(data.ambientTextureCoordinates(), 0); CORRADE_COMPARE(data.diffuseColor(), 0xeebbff_rgbf); CORRADE_COMPARE(data.specularColor(), 0xacabad_rgbf); CORRADE_COMPARE(data.specularTexture(), 17); - CORRADE_COMPARE(data.specularCoordinateSet(), 0); + CORRADE_COMPARE(data.specularTextureCoordinates(), 0); CORRADE_COMPARE(data.textureMatrix(), Matrix3{}); CORRADE_COMPARE(data.alphaMode(), MaterialAlphaMode::Blend); CORRADE_COMPARE(data.alphaMask(), 0.37f); @@ -153,12 +153,12 @@ void MaterialDataTest::constructPhongTexturedTextureTransform() { CORRADE_COMPARE(data.importerState(), &a); } -void MaterialDataTest::constructPhongTexturedCoordinateSets() { +void MaterialDataTest::constructPhongTexturedCoordinates() { using namespace Math::Literals; const int a{}; PhongMaterialData data{ - PhongMaterialData::Flag::AmbientTexture|PhongMaterialData::Flag::SpecularTexture|PhongMaterialData::Flag::TextureCoordinateSets, + PhongMaterialData::Flag::AmbientTexture|PhongMaterialData::Flag::SpecularTexture|PhongMaterialData::Flag::TextureCoordinates, 0x111111_rgbf, 42, 3, 0xeebbff_rgbf, {}, 0, 0xacabad_rgbf, 17, 1, @@ -166,14 +166,14 @@ void MaterialDataTest::constructPhongTexturedCoordinateSets() { MaterialAlphaMode::Blend, 0.37f, 96.0f, &a}; CORRADE_COMPARE(data.type(), MaterialType::Phong); - CORRADE_COMPARE(data.flags(), PhongMaterialData::Flag::AmbientTexture|PhongMaterialData::Flag::SpecularTexture|PhongMaterialData::Flag::TextureCoordinateSets); + CORRADE_COMPARE(data.flags(), PhongMaterialData::Flag::AmbientTexture|PhongMaterialData::Flag::SpecularTexture|PhongMaterialData::Flag::TextureCoordinates); CORRADE_COMPARE(data.ambientColor(), 0x111111_rgbf); CORRADE_COMPARE(data.ambientTexture(), 42); - CORRADE_COMPARE(data.ambientCoordinateSet(), 3); + CORRADE_COMPARE(data.ambientTextureCoordinates(), 3); CORRADE_COMPARE(data.diffuseColor(), 0xeebbff_rgbf); CORRADE_COMPARE(data.specularColor(), 0xacabad_rgbf); CORRADE_COMPARE(data.specularTexture(), 17); - CORRADE_COMPARE(data.specularCoordinateSet(), 1); + CORRADE_COMPARE(data.specularTextureCoordinates(), 1); CORRADE_COMPARE(data.textureMatrix(), Matrix3{}); CORRADE_COMPARE(data.alphaMode(), MaterialAlphaMode::Blend); CORRADE_COMPARE(data.alphaMask(), 0.37f); @@ -215,7 +215,7 @@ void MaterialDataTest::constructPhongNoTextureTransformationFlag() { "PhongMaterialData::PhongMaterialData: non-default texture matrix requires Flag::TextureTransformation to be enabled\n"); } -void MaterialDataTest::constructPhongNoTextureCoordinateSetsFlag() { +void MaterialDataTest::constructPhongNoTextureCoordinatesFlag() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif @@ -228,7 +228,7 @@ void MaterialDataTest::constructPhongNoTextureCoordinateSetsFlag() { {}, {}, 3, {}, 4, {}, {}, 0.5f, 80.0f}; CORRADE_COMPARE(out.str(), - "PhongMaterialData::PhongMaterialData: non-zero texture coordinate sets require Flag::TextureCoordinateSets to be enabled\n"); + "PhongMaterialData::PhongMaterialData: non-zero texture coordinate sets require Flag::TextureCoordinates to be enabled\n"); } void MaterialDataTest::constructCopy() { @@ -243,7 +243,7 @@ void MaterialDataTest::constructMovePhong() { const int a{}; PhongMaterialData data{ - PhongMaterialData::Flag::AmbientTexture|PhongMaterialData::Flag::DiffuseTexture|PhongMaterialData::Flag::SpecularTexture|PhongMaterialData::Flag::NormalTexture|PhongMaterialData::Flag::TextureTransformation|PhongMaterialData::Flag::TextureCoordinateSets, + PhongMaterialData::Flag::AmbientTexture|PhongMaterialData::Flag::DiffuseTexture|PhongMaterialData::Flag::SpecularTexture|PhongMaterialData::Flag::NormalTexture|PhongMaterialData::Flag::TextureTransformation|PhongMaterialData::Flag::TextureCoordinates, 0x111111_rgbf, 1, 0, 0xeebbff_rgbf, 42, 1, 0xacabad_rgbf, 24, 2, 17, 3, @@ -252,18 +252,18 @@ void MaterialDataTest::constructMovePhong() { PhongMaterialData b{std::move(data)}; CORRADE_COMPARE(b.type(), MaterialType::Phong); - CORRADE_COMPARE(b.flags(), PhongMaterialData::Flag::AmbientTexture|PhongMaterialData::Flag::DiffuseTexture|PhongMaterialData::Flag::SpecularTexture|PhongMaterialData::Flag::NormalTexture|PhongMaterialData::Flag::TextureTransformation|PhongMaterialData::Flag::TextureCoordinateSets); + CORRADE_COMPARE(b.flags(), PhongMaterialData::Flag::AmbientTexture|PhongMaterialData::Flag::DiffuseTexture|PhongMaterialData::Flag::SpecularTexture|PhongMaterialData::Flag::NormalTexture|PhongMaterialData::Flag::TextureTransformation|PhongMaterialData::Flag::TextureCoordinates); CORRADE_COMPARE(b.ambientColor(), 0x111111_rgbf); CORRADE_COMPARE(b.ambientTexture(), 1); - CORRADE_COMPARE(b.ambientCoordinateSet(), 0); + CORRADE_COMPARE(b.ambientTextureCoordinates(), 0); CORRADE_COMPARE(b.diffuseColor(), 0xeebbff_rgbf); CORRADE_COMPARE(b.diffuseTexture(), 42); - CORRADE_COMPARE(b.diffuseCoordinateSet(), 1); + CORRADE_COMPARE(b.diffuseTextureCoordinates(), 1); CORRADE_COMPARE(b.specularColor(), 0xacabad_rgbf); CORRADE_COMPARE(b.specularTexture(), 24); - CORRADE_COMPARE(b.specularCoordinateSet(), 2); + CORRADE_COMPARE(b.specularTextureCoordinates(), 2); CORRADE_COMPARE(b.normalTexture(), 17); - CORRADE_COMPARE(b.normalCoordinateSet(), 3); + CORRADE_COMPARE(b.normalTextureCoordinates(), 3); CORRADE_COMPARE(b.textureMatrix(), Matrix3::rotation(90.0_degf)); CORRADE_COMPARE(b.alphaMode(), MaterialAlphaMode::Blend); CORRADE_COMPARE(b.alphaMask(), 0.55f); @@ -279,18 +279,18 @@ void MaterialDataTest::constructMovePhong() { d = std::move(b); CORRADE_COMPARE(d.type(), MaterialType::Phong); - CORRADE_COMPARE(d.flags(), PhongMaterialData::Flag::AmbientTexture|PhongMaterialData::Flag::DiffuseTexture|PhongMaterialData::Flag::SpecularTexture|PhongMaterialData::Flag::NormalTexture|PhongMaterialData::Flag::TextureTransformation|PhongMaterialData::Flag::TextureCoordinateSets); + CORRADE_COMPARE(d.flags(), PhongMaterialData::Flag::AmbientTexture|PhongMaterialData::Flag::DiffuseTexture|PhongMaterialData::Flag::SpecularTexture|PhongMaterialData::Flag::NormalTexture|PhongMaterialData::Flag::TextureTransformation|PhongMaterialData::Flag::TextureCoordinates); CORRADE_COMPARE(d.ambientColor(), 0x111111_rgbf); CORRADE_COMPARE(d.ambientTexture(), 1); - CORRADE_COMPARE(d.ambientCoordinateSet(), 0); + CORRADE_COMPARE(d.ambientTextureCoordinates(), 0); CORRADE_COMPARE(d.diffuseColor(), 0xeebbff_rgbf); CORRADE_COMPARE(d.diffuseTexture(), 42); - CORRADE_COMPARE(d.diffuseCoordinateSet(), 1); + CORRADE_COMPARE(d.diffuseTextureCoordinates(), 1); CORRADE_COMPARE(d.specularColor(), 0xacabad_rgbf); CORRADE_COMPARE(d.specularTexture(), 24); - CORRADE_COMPARE(d.specularCoordinateSet(), 2); + CORRADE_COMPARE(d.specularTextureCoordinates(), 2); CORRADE_COMPARE(d.normalTexture(), 17); - CORRADE_COMPARE(d.normalCoordinateSet(), 3); + CORRADE_COMPARE(d.normalTextureCoordinates(), 3); CORRADE_COMPARE(d.textureMatrix(), Matrix3::rotation(90.0_degf)); CORRADE_COMPARE(d.alphaMode(), MaterialAlphaMode::Blend); CORRADE_COMPARE(d.alphaMask(), 0.55f); @@ -315,22 +315,22 @@ void MaterialDataTest::accessInvalidTextures() { std::ostringstream out; Error redirectError{&out}; a.ambientTexture(); - a.ambientCoordinateSet(); + a.ambientTextureCoordinates(); a.diffuseTexture(); - a.diffuseCoordinateSet(); + a.diffuseTextureCoordinates(); a.specularTexture(); - a.specularCoordinateSet(); + a.specularTextureCoordinates(); a.normalTexture(); - a.normalCoordinateSet(); + a.normalTextureCoordinates(); CORRADE_COMPARE(out.str(), "Trade::PhongMaterialData::ambientTexture(): the material doesn't have an ambient texture\n" - "Trade::PhongMaterialData::ambientCoordinateSet(): the material doesn't have an ambient texture\n" + "Trade::PhongMaterialData::ambientTextureCoordinates(): the material doesn't have an ambient texture\n" "Trade::PhongMaterialData::diffuseTexture(): the material doesn't have a diffuse texture\n" - "Trade::PhongMaterialData::diffuseCoordinateSet(): the material doesn't have a diffuse texture\n" + "Trade::PhongMaterialData::diffuseTextureCoordinates(): the material doesn't have a diffuse texture\n" "Trade::PhongMaterialData::specularTexture(): the material doesn't have a specular texture\n" - "Trade::PhongMaterialData::specularCoordinateSet(): the material doesn't have a specular texture\n" + "Trade::PhongMaterialData::specularTextureCoordinates(): the material doesn't have a specular texture\n" "Trade::PhongMaterialData::normalTexture(): the material doesn't have a normal texture\n" - "Trade::PhongMaterialData::normalCoordinateSet(): the material doesn't have a normal texture\n"); + "Trade::PhongMaterialData::normalTextureCoordinates(): the material doesn't have a normal texture\n"); } void MaterialDataTest::debugType() { From 9557de053441600b20743e7684f6ee5a98b58ead Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 10 Aug 2020 14:48:05 +0200 Subject: [PATCH 03/36] Shaders,Trade: clarify use{less,ful}ness of specular alpha. --- src/Magnum/Shaders/Phong.cpp | 2 +- src/Magnum/Shaders/Phong.frag | 2 +- src/Magnum/Shaders/Phong.h | 9 +++++++-- src/Magnum/Trade/PhongMaterialData.h | 4 ++-- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/Magnum/Shaders/Phong.cpp b/src/Magnum/Shaders/Phong.cpp index 03709088e4..dfdd6f915c 100644 --- a/src/Magnum/Shaders/Phong.cpp +++ b/src/Magnum/Shaders/Phong.cpp @@ -208,7 +208,7 @@ Phong::Phong(const Flags flags, const UnsignedInt lightCount): _flags{flags}, _l setProjectionMatrix({}); if(lightCount) { setDiffuseColor(Magnum::Color4{1.0f}); - setSpecularColor(Magnum::Color4{1.0f}); + setSpecularColor(Magnum::Color4{1.0f, 0.0f}); setShininess(80.0f); setLightColors(Containers::Array{Containers::DirectInit, lightCount, Magnum::Color4{1.0f}}); /* Light position is zero by default */ diff --git a/src/Magnum/Shaders/Phong.frag b/src/Magnum/Shaders/Phong.frag index 6e2edf8237..43a05e5712 100644 --- a/src/Magnum/Shaders/Phong.frag +++ b/src/Magnum/Shaders/Phong.frag @@ -93,7 +93,7 @@ layout(location = 6) #endif uniform lowp vec4 specularColor #ifndef GL_ES - = vec4(1.0) + = vec4(1.0, 1.0, 1.0, 0.0) #endif ; diff --git a/src/Magnum/Shaders/Phong.h b/src/Magnum/Shaders/Phong.h index ab895989e0..fcd5b55573 100644 --- a/src/Magnum/Shaders/Phong.h +++ b/src/Magnum/Shaders/Phong.h @@ -80,6 +80,11 @@ working on the framebuffer, you need to enable @ref GL::Renderer::Feature::Blending and set up the blending function. See @ref GL::Renderer::setBlendFunction() for details. +To avoid specular highlights on transparent areas, specular alpha should be +always set to @cpp 0.0f @ce. On the other hand, non-zero specular alpha can be +for example used to render transparent materials which are still expected to +have specular highlights such as glass or soap bubbles. + An alternative is to enable @ref Flag::AlphaMask and tune @ref setAlphaMask() for simple binary alpha-masked drawing that doesn't require depth sorting or blending enabled. Note that this feature is implemented using the GLSL @@ -525,10 +530,10 @@ class MAGNUM_SHADERS_EXPORT Phong: public GL::AbstractShaderProgram { * @brief Set specular color * @return Reference to self (for method chaining) * - * Initial value is @cpp 0xffffffff_rgbaf @ce. Color will be multiplied + * Initial value is @cpp 0xffffff00_rgbaf @ce. Color will be multiplied * with specular texture if @ref Flag::SpecularTexture is set. If you * want to have a fully diffuse material, set specular color to - * @cpp 0x000000ff_rgbaf @ce. If @ref lightCount() is zero, this + * @cpp 0x00000000_rgbaf @ce. If @ref lightCount() is zero, this * function is a no-op, as specular color doesn't contribute to the * output in that case. * @see @ref bindSpecularTexture() diff --git a/src/Magnum/Trade/PhongMaterialData.h b/src/Magnum/Trade/PhongMaterialData.h index afa54b64dc..abeccc3cbb 100644 --- a/src/Magnum/Trade/PhongMaterialData.h +++ b/src/Magnum/Trade/PhongMaterialData.h @@ -114,7 +114,7 @@ class MAGNUM_TRADE_EXPORT PhongMaterialData: public AbstractMaterialData { * @param diffuseTexture Diffuse texture ID. Ignored if @p flags * doesn't have @ref Flag::DiffuseTexture * @param specularColor Specular color. Use - * @cpp 0xffffffff_rgbaf @ce for a default value for both a + * @cpp 0xffffff00_rgbaf @ce for a default value for both a * non-textured and a textured material. * @param specularTexture Specular texture ID. Ignored if @p flags * doesn't have @ref Flag::SpecularTexture. @@ -155,7 +155,7 @@ class MAGNUM_TRADE_EXPORT PhongMaterialData: public AbstractMaterialData { * @param diffuseTextureCoordinates Diffuse texture coordinate set. * Ignored if @p flags doesn't have @ref Flag::DiffuseTexture * @param specularColor Specular color. Use - * @cpp 0xffffffff_rgbaf @ce for a default value for both a + * @cpp 0xffffff00_rgbaf @ce for a default value for both a * non-textured and a textured material. * @param specularTexture Specular texture ID. Ignored if * @p flags doesn't have @ref Flag::SpecularTexture. From 14d260a3ea7658af1145d836de6d508f0a2833c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 17 Jul 2020 01:42:01 +0200 Subject: [PATCH 04/36] Trade: a redesigned MaterialData class. Next step is reworking PhongMaterialData on top of this one and deprecating AbstractMaterialData. --- doc/changelog.dox | 6 + src/Magnum/Trade/CMakeLists.txt | 5 +- .../materialAttributeProperties.hpp | 49 ++ src/Magnum/Trade/MaterialData.cpp | 314 +++++++ src/Magnum/Trade/MaterialData.h | 779 ++++++++++++++++++ src/Magnum/Trade/Test/CMakeLists.txt | 1 + src/Magnum/Trade/Test/MaterialDataTest.cpp | 732 +++++++++++++++- src/Magnum/Trade/Trade.h | 4 + 8 files changed, 1880 insertions(+), 10 deletions(-) create mode 100644 src/Magnum/Trade/Implementation/materialAttributeProperties.hpp create mode 100644 src/Magnum/Trade/MaterialData.cpp create mode 100644 src/Magnum/Trade/MaterialData.h diff --git a/doc/changelog.dox b/doc/changelog.dox index e4fb6ed8fd..7d830da931 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -72,6 +72,12 @@ See also: - Added @ref SceneGraph::Object::move() +@subsubsection changelog-latest-new-trade Trade library + +- A new, redesigned @ref Trade::MaterialData class allowing to store custom + material attributes as well as more material types together in a single + instance. See [mosra/magnum#459](https://github.com/mosra/magnum/pull/459). + @subsection changelog-latest-changes Changes and improvements @subsubsection changelog-latest-changes-gl GL library diff --git a/src/Magnum/Trade/CMakeLists.txt b/src/Magnum/Trade/CMakeLists.txt index f27b368201..0ce26fe344 100644 --- a/src/Magnum/Trade/CMakeLists.txt +++ b/src/Magnum/Trade/CMakeLists.txt @@ -42,6 +42,7 @@ set(MagnumTrade_GracefulAssert_SRCS AnimationData.cpp CameraData.cpp ImageData.cpp + MaterialData.cpp MeshData.cpp ObjectData2D.cpp ObjectData3D.cpp @@ -58,6 +59,7 @@ set(MagnumTrade_HEADERS Data.h ImageData.h LightData.h + MaterialData.h MeshData.h MeshObjectData2D.h MeshObjectData3D.h @@ -72,7 +74,8 @@ set(MagnumTrade_HEADERS set(MagnumTrade_PRIVATE_HEADERS Implementation/arrayUtilities.h - Implementation/converterUtilities.h) + Implementation/converterUtilities.h + Implementation/materialAttributeProperties.hpp) if(MAGNUM_BUILD_DEPRECATED) list(APPEND MagnumTrade_GracefulAssert_SRCS diff --git a/src/Magnum/Trade/Implementation/materialAttributeProperties.hpp b/src/Magnum/Trade/Implementation/materialAttributeProperties.hpp new file mode 100644 index 0000000000..037219da0b --- /dev/null +++ b/src/Magnum/Trade/Implementation/materialAttributeProperties.hpp @@ -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š + + 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 diff --git a/src/Magnum/Trade/MaterialData.cpp b/src/Magnum/Trade/MaterialData.cpp new file mode 100644 index 0000000000..b2dc17f9a5 --- /dev/null +++ b/src/Magnum/Trade/MaterialData.cpp @@ -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š + + 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 +#include + +#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&& 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 data, const void* const importerState): MaterialData{Implementation::initializerListToArrayWithDefaultDeleter(data), importerState} {} + +MaterialData::MaterialData(DataFlags, const Containers::ArrayView data, const void* const importerState) noexcept: _data{Containers::Array{const_cast(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 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(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(UnsignedShort(value)) << Debug::nospace << ")"; +} + +}} diff --git a/src/Magnum/Trade/MaterialData.h b/src/Magnum/Trade/MaterialData.h new file mode 100644 index 0000000000..92fcfc5544 --- /dev/null +++ b/src/Magnum/Trade/MaterialData.h @@ -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š + + 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 +#include + +#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 */ + + 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 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 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 /*implicit*/ MaterialAttributeData(const char* name, const T& value) noexcept: MaterialAttributeData{name, Implementation::MaterialAttributeTypeFor::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 /*implicit*/ MaterialAttributeData(MaterialAttribute name, const T& value) noexcept: MaterialAttributeData{name, Implementation::MaterialAttributeTypeFor::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 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 union ErasedVector { + constexpr explicit ErasedVector(const Math::Vector& value): f{value} {} + constexpr explicit ErasedVector(const Math::Vector& value): u{value} {} + constexpr explicit ErasedVector(const Math::Vector& value): i{value} {} + + Math::Vector f; + Math::Vector u; + Math::Vector i; + }; + template union ErasedMatrix { + constexpr explicit ErasedMatrix(const Math::RectangularMatrix& value): a{value} {} + constexpr explicit ErasedMatrix(const Math::RectangularMatrix& value): b{value} {} + + Math::RectangularMatrix a; + Math::RectangularMatrix b; + }; + template struct Data { + template constexpr explicit Data(MaterialAttributeType type, Containers::StringView name, const U& value, Math::Implementation::Sequence): type{type}, name{(sequence < name.size() ? name[sequence] : '\0')...}, value{value} {} + template 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 constexpr explicit Storage(typename std::enable_if::type type, Containers::StringView name, const T& value) noexcept: _1{type, name, value} {} + template constexpr explicit Storage(typename std::enable_if::type type, Containers::StringView name, const T& value) noexcept: _4{type, name, value} {} + template constexpr explicit Storage(typename std::enable_if::type type, Containers::StringView name, const T& value) noexcept: _8{type, name, value} {} + template constexpr explicit Storage(typename std::enable_if::type type, Containers::StringView name, const T& value) noexcept: _12{type, name, value} {} + template constexpr explicit Storage(typename std::enable_if::value, MaterialAttributeType>::type type, Containers::StringView name, const T& value) noexcept: _16{type, name, value} {} + template constexpr explicit Storage(typename std::enable_if::value, MaterialAttributeType>::type type, Containers::StringView name, const T& value) noexcept: _16m{type, name, value} {} + template constexpr explicit Storage(typename std::enable_if::type type, Containers::StringView name, const T& value) noexcept: _24{type, name, value} {} + template constexpr explicit Storage(typename std::enable_if::type type, Containers::StringView name, const T& value) noexcept: _32{type, name, value} {} + template constexpr explicit Storage(typename std::enable_if::type type, Containers::StringView name, const T& value) noexcept: _36{type, name, value} {} + template constexpr explicit Storage(typename std::enable_if::type type, Containers::StringView name, const T& value) noexcept: _48{type, name, value} {} + + MaterialAttributeType type; + char data[Implementation::MaterialAttributeDataSize]; + Data _1; + Data _4; + Data> _8; + Data> _12; + Data> _16; + Data> _16m; + Data> _24; + Data> _32; + Data> _36; + Data> _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&& data, const void* importerState = nullptr) noexcept; + + /** @overload */ + /* Not noexcept because allocation happens inside */ + explicit MaterialData(std::initializer_list 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 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 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 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 T attribute(Containers::StringView name) const; + template 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 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 _data; + const void* _importerState; +}; + +namespace Implementation { + /* LCOV_EXCL_START */ + template 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 { + constexpr static MaterialAttributeType type() { + return MaterialAttributeType::Bool; + } + }; + #ifndef DOXYGEN_GENERATING_OUTPUT + #define _c(type_) template<> struct MaterialAttributeTypeFor { \ + 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: MaterialAttributeTypeFor {}; + template<> struct MaterialAttributeTypeFor: MaterialAttributeTypeFor {}; + template<> struct MaterialAttributeTypeFor: MaterialAttributeTypeFor {}; + /* LCOV_EXCL_STOP */ +} + +/* The 2 extra bytes are for a null byte after the name and a type */ +template constexpr MaterialAttributeData::MaterialAttributeData(const Containers::StringView name, const T& value) noexcept: _data{Implementation::MaterialAttributeTypeFor::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::type() << "but got" << name.size()), name), value} {} + +template T MaterialAttributeData::value() const { + CORRADE_ASSERT(Implementation::MaterialAttributeTypeFor::type() == _data.type, + "Trade::MaterialAttributeData::value(): improper type requested for" << (_data.data + 1) << "of" << _data.type, {}); + return *reinterpret_cast(value()); +} + +template T MaterialData::attribute(UnsignedInt id) const { + const void* const value = attribute(id); + #ifdef CORRADE_GRACEFUL_ASSERT + if(!value) return {}; + #endif + CORRADE_ASSERT(Implementation::MaterialAttributeTypeFor::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(value); +} + +template 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(id); +} + +template T MaterialData::attribute(MaterialAttribute name) const { + const Containers::StringView string = attributeString(name); + CORRADE_ASSERT(string.data(), "Trade::MaterialData::attribute(): invalid name" << name, {}); + return attribute(string); +} + +}} + +#endif diff --git a/src/Magnum/Trade/Test/CMakeLists.txt b/src/Magnum/Trade/Test/CMakeLists.txt index 38b91579f2..d04cd597b4 100644 --- a/src/Magnum/Trade/Test/CMakeLists.txt +++ b/src/Magnum/Trade/Test/CMakeLists.txt @@ -60,6 +60,7 @@ corrade_add_test(TradeTextureDataTest TextureDataTest.cpp LIBRARIES MagnumTrade) set_property(TARGET TradeAnimationDataTest + TradeMaterialDataTest TradeMeshDataTest APPEND PROPERTY COMPILE_DEFINITIONS "CORRADE_GRACEFUL_ASSERT") diff --git a/src/Magnum/Trade/Test/MaterialDataTest.cpp b/src/Magnum/Trade/Test/MaterialDataTest.cpp index acbc60fa37..ea9461868c 100644 --- a/src/Magnum/Trade/Test/MaterialDataTest.cpp +++ b/src/Magnum/Trade/Test/MaterialDataTest.cpp @@ -23,10 +23,16 @@ DEALINGS IN THE SOFTWARE. */ +#include #include +#include #include +#include #include +#include +#include "Magnum/Math/Matrix3.h" +#include "Magnum/Trade/MaterialData.h" #include "Magnum/Trade/PhongMaterialData.h" namespace Magnum { namespace Trade { namespace Test { namespace { @@ -35,6 +41,45 @@ class MaterialDataTest: public TestSuite::Tester { public: explicit MaterialDataTest(); + void attributeTypeSize(); + void attributeTypeSizeInvalid(); + + void attributeMap(); + + void constructAttributeDefault(); + void constructAttributeString(); + void constructAttributeName(); + void constructAttributeTypeErasedString(); + void constructAttributeTypeErasedName(); + + template void constructAttributeStringConstexpr(); + + void constructAttributeInvalidName(); + void constructAttributeWrongTypeForName(); + void constructAttributeInvalidType(); + void constructAttributeTooLarge(); + void constructAttributeWrongAccessType(); + + void construct(); + void constructEmptyAttribute(); + void constructDuplicateAttribute(); + void constructFromImmutableSortedArray(); + + void constructNonOwned(); + void constructNonOwnedEmptyAttribute(); + void constructNonOwnedNotSorted(); + void constructNonOwnedDuplicateAttribute(); + + void constructCopy(); + void constructMove(); + + void accessOutOfBounds(); + void accessInvalidAttributeName(); + void accessNotFound(); + void accessWrongType(); + + void release(); + void constructPhong(); void constructPhongTextured(); void constructPhongTexturedTextureTransform(); @@ -42,11 +87,14 @@ class MaterialDataTest: public TestSuite::Tester { void constructPhongTextureTransformNoTextures(); void constructPhongNoTextureTransformationFlag(); void constructPhongNoTextureCoordinatesFlag(); - void constructCopy(); - void constructMovePhong(); + void constructPhongCopy(); + void constructPhongMove(); void accessInvalidTextures(); + void debugAttribute(); + void debugAttributeType(); + void debugType(); void debugFlag(); void debugFlags(); @@ -57,18 +105,84 @@ class MaterialDataTest: public TestSuite::Tester { }; MaterialDataTest::MaterialDataTest() { - addTests({&MaterialDataTest::constructPhong, + addTests({&MaterialDataTest::attributeTypeSize, + &MaterialDataTest::attributeTypeSizeInvalid, + &MaterialDataTest::attributeMap, + + &MaterialDataTest::constructAttributeDefault, + &MaterialDataTest::constructAttributeString, + &MaterialDataTest::constructAttributeName, + &MaterialDataTest::constructAttributeTypeErasedString, + &MaterialDataTest::constructAttributeTypeErasedName, + + &MaterialDataTest::constructAttributeStringConstexpr, + &MaterialDataTest::constructAttributeStringConstexpr, + &MaterialDataTest::constructAttributeStringConstexpr, + &MaterialDataTest::constructAttributeStringConstexpr, + &MaterialDataTest::constructAttributeStringConstexpr, + &MaterialDataTest::constructAttributeStringConstexpr, + &MaterialDataTest::constructAttributeStringConstexpr, + &MaterialDataTest::constructAttributeStringConstexpr, + &MaterialDataTest::constructAttributeStringConstexpr, + &MaterialDataTest::constructAttributeStringConstexpr, + &MaterialDataTest::constructAttributeStringConstexpr, + &MaterialDataTest::constructAttributeStringConstexpr, + &MaterialDataTest::constructAttributeStringConstexpr, + &MaterialDataTest::constructAttributeStringConstexpr, + &MaterialDataTest::constructAttributeStringConstexpr, + &MaterialDataTest::constructAttributeStringConstexpr, + &MaterialDataTest::constructAttributeStringConstexpr, + &MaterialDataTest::constructAttributeStringConstexpr, + &MaterialDataTest::constructAttributeStringConstexpr, + &MaterialDataTest::constructAttributeStringConstexpr, + &MaterialDataTest::constructAttributeStringConstexpr, + &MaterialDataTest::constructAttributeStringConstexpr, + &MaterialDataTest::constructAttributeStringConstexpr, + + &MaterialDataTest::constructAttributeInvalidName, + &MaterialDataTest::constructAttributeWrongTypeForName, + &MaterialDataTest::constructAttributeInvalidType, + &MaterialDataTest::constructAttributeTooLarge, + &MaterialDataTest::constructAttributeWrongAccessType, + + &MaterialDataTest::construct, + &MaterialDataTest::constructEmptyAttribute}); + + addRepeatedTests({&MaterialDataTest::constructDuplicateAttribute}, + 5*4*3*2); + + addTests({&MaterialDataTest::constructFromImmutableSortedArray, + + &MaterialDataTest::constructNonOwned, + &MaterialDataTest::constructNonOwnedEmptyAttribute, + &MaterialDataTest::constructNonOwnedNotSorted, + &MaterialDataTest::constructNonOwnedDuplicateAttribute, + + &MaterialDataTest::constructCopy, + &MaterialDataTest::constructMove, + + &MaterialDataTest::accessOutOfBounds, + &MaterialDataTest::accessInvalidAttributeName, + &MaterialDataTest::accessNotFound, + &MaterialDataTest::accessWrongType, + + &MaterialDataTest::release, + + &MaterialDataTest::constructPhong, &MaterialDataTest::constructPhongTextured, &MaterialDataTest::constructPhongTexturedTextureTransform, &MaterialDataTest::constructPhongTexturedCoordinates, &MaterialDataTest::constructPhongTextureTransformNoTextures, &MaterialDataTest::constructPhongNoTextureTransformationFlag, &MaterialDataTest::constructPhongNoTextureCoordinatesFlag, - &MaterialDataTest::constructCopy, - &MaterialDataTest::constructMovePhong, + &MaterialDataTest::constructPhongCopy, + &MaterialDataTest::constructPhongMove, &MaterialDataTest::accessInvalidTextures, + &MaterialDataTest::debugAttribute, + &MaterialDataTest::debugAttributeType, + &MaterialDataTest::debugType, &MaterialDataTest::debugFlag, &MaterialDataTest::debugFlags, @@ -78,6 +192,594 @@ MaterialDataTest::MaterialDataTest() { &MaterialDataTest::debugPhongFlags}); } +using namespace Containers::Literals; +using namespace Math::Literals; + +void MaterialDataTest::attributeTypeSize() { + CORRADE_COMPARE(materialAttributeTypeSize(MaterialAttributeType::Bool), 1); + CORRADE_COMPARE(materialAttributeTypeSize(MaterialAttributeType::Deg), 4); + CORRADE_COMPARE(materialAttributeTypeSize(MaterialAttributeType::Vector2i), 8); + CORRADE_COMPARE(materialAttributeTypeSize(MaterialAttributeType::Vector3), 12); + CORRADE_COMPARE(materialAttributeTypeSize(MaterialAttributeType::Vector4ui), 16); + CORRADE_COMPARE(materialAttributeTypeSize(MaterialAttributeType::Matrix2x3), 24); + CORRADE_COMPARE(materialAttributeTypeSize(MaterialAttributeType::Matrix4x2), 32); + CORRADE_COMPARE(materialAttributeTypeSize(MaterialAttributeType::Matrix3x3), 36); + CORRADE_COMPARE(materialAttributeTypeSize(MaterialAttributeType::Matrix3x4), 48); +} + +void MaterialDataTest::attributeTypeSizeInvalid() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + std::ostringstream out; + Error redirectError{&out}; + materialAttributeTypeSize(MaterialAttributeType(0x0)); + materialAttributeTypeSize(MaterialAttributeType(0xfe)); + CORRADE_COMPARE(out.str(), + "Trade::materialAttributeTypeSize(): invalid type Trade::MaterialAttributeType(0x0)\n" + "Trade::materialAttributeTypeSize(): invalid type Trade::MaterialAttributeType(0xfe)\n"); +} + +void MaterialDataTest::attributeMap() { + /* Ensure all attribute names are: + - present in the map, + - that their translated string name corresponds to the enum value name, + - that the calculated type size corresponds to the actual type, + - and that the name together with the type fits. + This goes through the first 16 bits, which should be enough. Going + through 32 bits takes 8 seconds, too much. */ + for(UnsignedInt i = 1; i <= 0xffff; ++i) { + /* Attribute 0 reserved for an invalid value */ + + const auto attribute = MaterialAttribute(i); + #ifdef __GNUC__ + #pragma GCC diagnostic push + #pragma GCC diagnostic error "-Wswitch" + #endif + switch(attribute) { + #define _c(name_, type) \ + case MaterialAttribute::name_: \ + CORRADE_COMPARE((MaterialAttributeData{MaterialAttribute::name_, type{}}.name()), #name_); \ + CORRADE_COMPARE(materialAttributeTypeSize(MaterialAttributeType::type), sizeof(type)); \ + CORRADE_COMPARE_AS(sizeof(type) + Containers::arraySize(#name_) + sizeof(MaterialAttributeType), sizeof(MaterialAttributeData), TestSuite::Compare::LessOrEqual); \ + break; + #define _ct(name_, typeName, type) \ + case MaterialAttribute::name_: \ + CORRADE_COMPARE((MaterialAttributeData{MaterialAttribute::name_, type{}}.name()), #name_); \ + CORRADE_COMPARE(materialAttributeTypeSize(MaterialAttributeType::typeName), sizeof(type)); \ + CORRADE_COMPARE_AS(sizeof(type) + Containers::arraySize(#name_) + sizeof(MaterialAttributeType), sizeof(MaterialAttributeData), TestSuite::Compare::LessOrEqual); \ + break; + #include "Magnum/Trade/Implementation/materialAttributeProperties.hpp" + #undef _c + #undef _ct + } + #ifdef __GNUC__ + #pragma GCC diagnostic pop + #endif + } +} + +void MaterialDataTest::constructAttributeDefault() { + MaterialAttributeData attribute; + CORRADE_COMPARE(attribute.name(), ""); + CORRADE_COMPARE(attribute.type(), MaterialAttributeType{}); + + constexpr MaterialAttributeData cattribute; + CORRADE_COMPARE(cattribute.name(), ""); + CORRADE_COMPARE(cattribute.type(), MaterialAttributeType{}); +} + +void MaterialDataTest::constructAttributeString() { + MaterialAttributeData attribute{"colorTransform", Matrix3::scaling({2.0f, 0.3f})}; + CORRADE_COMPARE(attribute.name(), "colorTransform"); + CORRADE_COMPARE(attribute.name().flags(), Containers::StringViewFlag::NullTerminated); + CORRADE_COMPARE(attribute.name()[attribute.name().size()], '\0'); + CORRADE_COMPARE(attribute.type(), MaterialAttributeType::Matrix3x3); + CORRADE_COMPARE(attribute.value(), Matrix3::scaling({2.0f, 0.3f})); + CORRADE_COMPARE(*reinterpret_cast(attribute.value()), Matrix3::scaling({2.0f, 0.3f})); +} + +void MaterialDataTest::constructAttributeName() { + MaterialAttributeData attribute{MaterialAttribute::DiffuseColor, 0xff3366aa_rgbaf}; + CORRADE_COMPARE(attribute.name(), "DiffuseColor"_s); + CORRADE_COMPARE(attribute.name().flags(), Containers::StringViewFlag::NullTerminated); + CORRADE_COMPARE(attribute.name()[attribute.name().size()], '\0'); + CORRADE_COMPARE(attribute.type(), MaterialAttributeType::Vector4); + CORRADE_COMPARE(attribute.value(), 0xff3366aa_rgbaf); + CORRADE_COMPARE(*reinterpret_cast(attribute.value()), 0xff3366aa_rgbaf); +} + +void MaterialDataTest::constructAttributeTypeErasedString() { + const Vector2i data{37, -458}; + MaterialAttributeData attribute{"millibitsOfInformation", MaterialAttributeType::Vector2i, &data}; + CORRADE_COMPARE(attribute.name(), "millibitsOfInformation"); + CORRADE_COMPARE(attribute.name().flags(), Containers::StringViewFlag::NullTerminated); + CORRADE_COMPARE(attribute.name()[attribute.name().size()], '\0'); + CORRADE_COMPARE(attribute.type(), MaterialAttributeType::Vector2i); + CORRADE_COMPARE(attribute.value(), (Vector2i{37, -458})); +} + +void MaterialDataTest::constructAttributeTypeErasedName() { + const Float data = 85.1f; + MaterialAttributeData attribute{MaterialAttribute::Shininess, MaterialAttributeType::Float, &data}; + CORRADE_COMPARE(attribute.name(), "Shininess"); + CORRADE_COMPARE(attribute.name().flags(), Containers::StringViewFlag::NullTerminated); + CORRADE_COMPARE(attribute.name()[attribute.name().size()], '\0'); + CORRADE_COMPARE(attribute.type(), MaterialAttributeType::Float); + CORRADE_COMPARE(attribute.value(), 85.1f); +} + +template struct TypeName { + static const char* name() { return Math::TypeTraits::name(); } +}; +template<> struct TypeName { + static const char* name() { return "bool"; } +}; +#define _c(type) template<> struct TypeName { \ + static const char* name() { return #type; } \ +}; +_c(Deg) +_c(Rad) +_c(Vector2) +_c(Vector2i) +_c(Vector2ui) +_c(Vector3) +_c(Vector3i) +_c(Vector3ui) +_c(Vector4) +_c(Vector4i) +_c(Vector4ui) +_c(Matrix2x2) +_c(Matrix2x3) +_c(Matrix2x4) +_c(Matrix3x2) +_c(Matrix3x3) +_c(Matrix3x4) +_c(Matrix4x2) +_c(Matrix4x3) +#undef _c + +template void MaterialDataTest::constructAttributeStringConstexpr() { + setTestCaseTemplateName(TypeName::name()); + + /* "templateAttrib" is 14 chars, which is the maximum for 48-bit types */ + constexpr MaterialAttributeData attribute{"templateAttrib"_s, T(15)}; + CORRADE_COMPARE(attribute.name(), "templateAttrib"); + CORRADE_COMPARE(attribute.name().flags(), Containers::StringViewFlag::NullTerminated); + CORRADE_COMPARE(attribute.name()[attribute.name().size()], '\0'); + CORRADE_COMPARE(attribute.value(), T(15)); +} + +void MaterialDataTest::constructAttributeInvalidName() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + std::ostringstream out; + Error redirectError{&out}; + MaterialAttributeData{MaterialAttribute(0x0), 5}; + MaterialAttributeData{MaterialAttribute(0xfefe), 5}; + CORRADE_COMPARE(out.str(), + "Trade::MaterialAttributeData: invalid name Trade::MaterialAttribute(0x0)\n" + "Trade::MaterialAttributeData: invalid name Trade::MaterialAttribute(0xfefe)\n"); +} + +void MaterialDataTest::constructAttributeWrongTypeForName() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + std::ostringstream out; + Error redirectError{&out}; + MaterialAttributeData{MaterialAttribute::DiffuseColor, Vector3ui{255, 16, 24}}; + CORRADE_COMPARE(out.str(), + "Trade::MaterialAttributeData: expected Trade::MaterialAttributeType::Vector4 for Trade::MaterialAttribute::DiffuseColor but got Trade::MaterialAttributeType::Vector3ui\n"); +} + +void MaterialDataTest::constructAttributeInvalidType() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + std::ostringstream out; + Error redirectError{&out}; + MaterialAttributeData{"bla", MaterialAttributeType(0x0), nullptr}; + MaterialAttributeData{"bla", MaterialAttributeType(0xfe), nullptr}; + CORRADE_COMPARE(out.str(), + "Trade::materialAttributeTypeSize(): invalid type Trade::MaterialAttributeType(0x0)\n" + "Trade::materialAttributeTypeSize(): invalid type Trade::MaterialAttributeType(0xfe)\n"); +} + +void MaterialDataTest::constructAttributeTooLarge() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + std::ostringstream out; + Error redirectError{&out}; + MaterialAttributeData{"attributeIsLong", Matrix3x4{}}; + /* Constexpr variant has the same assert, but in the header. It should have + the same output. */ + /*constexpr*/ MaterialAttributeData{"attributeIsLong"_s, Matrix3x4{}}; + CORRADE_COMPARE(out.str(), + "Trade::MaterialAttributeData: name attributeIsLong too long, expected at most 14 bytes for Trade::MaterialAttributeType::Matrix3x4 but got 15\n" + "Trade::MaterialAttributeData: name attributeIsLong too long, expected at most 14 bytes for Trade::MaterialAttributeType::Matrix3x4 but got 15\n"); +} + +void MaterialDataTest::constructAttributeWrongAccessType() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + std::ostringstream out; + Error redirectError{&out}; + MaterialAttributeData{"thing3", Matrix4x3{}}.value(); + CORRADE_COMPARE(out.str(), "Trade::MaterialAttributeData::value(): improper type requested for thing3 of Trade::MaterialAttributeType::Matrix4x3\n"); +} + +void MaterialDataTest::construct() { + int state; + MaterialData data{{ + {MaterialAttribute::DoubleSided, true}, + {MaterialAttribute::DiffuseTextureCoordinates, 5u}, + {"highlightColor", 0x335566ff_rgbaf}, + {MaterialAttribute::AmbientTextureMatrix, Matrix3::scaling({0.5f, 1.0f})} + }, &state}; + + CORRADE_COMPARE(data.attributeCount(), 4); + CORRADE_COMPARE(data.data().size(), 4); + CORRADE_COMPARE(data.importerState(), &state); + + /* Verify sorting */ + CORRADE_COMPARE(data.attributeName(0), "AmbientTextureMatrix"); + CORRADE_COMPARE(data.attributeName(1), "DiffuseTextureCoordinates"); + CORRADE_COMPARE(data.attributeName(2), "DoubleSided"); + CORRADE_COMPARE(data.attributeName(3), "highlightColor"); + + /* Access by ID */ + CORRADE_COMPARE(data.attributeType(0), MaterialAttributeType::Matrix3x3); + CORRADE_COMPARE(data.attributeType(1), MaterialAttributeType::UnsignedInt); + CORRADE_COMPARE(data.attributeType(2), MaterialAttributeType::Bool); + CORRADE_COMPARE(data.attributeType(3), MaterialAttributeType::Vector4); + + CORRADE_COMPARE(data.attribute(0), Matrix3::scaling({0.5f, 1.0f})); + CORRADE_COMPARE(data.attribute(1), 5); + CORRADE_COMPARE(data.attribute(2), true); + CORRADE_COMPARE(data.attribute(3), 0x335566ff_rgbaf); + + CORRADE_COMPARE(*static_cast(data.attribute(0)), Matrix3::scaling({0.5f, 1.0f})); + CORRADE_COMPARE(*static_cast(data.attribute(1)), 5); + CORRADE_COMPARE(*static_cast(data.attribute(2)), true); + CORRADE_COMPARE(*static_cast(data.attribute(3)), 0x335566ff_rgbaf); + + /* Access by name */ + CORRADE_VERIFY(data.hasAttribute(MaterialAttribute::DoubleSided)); + CORRADE_VERIFY(data.hasAttribute(MaterialAttribute::AmbientTextureMatrix)); + CORRADE_VERIFY(!data.hasAttribute(MaterialAttribute::TextureMatrix)); + + CORRADE_COMPARE(data.attributeId(MaterialAttribute::DoubleSided), 2); + CORRADE_COMPARE(data.attributeId(MaterialAttribute::AmbientTextureMatrix), 0); + CORRADE_COMPARE(data.attributeId(MaterialAttribute::DiffuseTextureCoordinates), 1); + + CORRADE_COMPARE(data.attributeType(MaterialAttribute::AmbientTextureMatrix), MaterialAttributeType::Matrix3x3); + CORRADE_COMPARE(data.attributeType(MaterialAttribute::DiffuseTextureCoordinates), MaterialAttributeType::UnsignedInt); + CORRADE_COMPARE(data.attributeType(MaterialAttribute::DoubleSided), MaterialAttributeType::Bool); + + CORRADE_COMPARE(data.attribute(MaterialAttribute::AmbientTextureMatrix), Matrix3::scaling({0.5f, 1.0f})); + CORRADE_COMPARE(data.attribute(MaterialAttribute::DiffuseTextureCoordinates), 5); + CORRADE_COMPARE(data.attribute(MaterialAttribute::DoubleSided), true); + + CORRADE_COMPARE(*static_cast(data.attribute(MaterialAttribute::AmbientTextureMatrix)), Matrix3::scaling({0.5f, 1.0f})); + CORRADE_COMPARE(*static_cast(data.attribute(MaterialAttribute::DiffuseTextureCoordinates)), 5); + CORRADE_COMPARE(*static_cast(data.attribute(MaterialAttribute::DoubleSided)), true); + + /* Access by string */ + CORRADE_VERIFY(data.hasAttribute("DoubleSided")); + CORRADE_VERIFY(data.hasAttribute("highlightColor")); + CORRADE_VERIFY(!data.hasAttribute("TextureMatrix")); + + CORRADE_COMPARE(data.attributeId("DoubleSided"), 2); + CORRADE_COMPARE(data.attributeId("AmbientTextureMatrix"), 0); + CORRADE_COMPARE(data.attributeId("DiffuseTextureCoordinates"), 1); + CORRADE_COMPARE(data.attributeId("highlightColor"), 3); + + CORRADE_COMPARE(data.attributeType("AmbientTextureMatrix"), MaterialAttributeType::Matrix3x3); + CORRADE_COMPARE(data.attributeType("DiffuseTextureCoordinates"), MaterialAttributeType::UnsignedInt); + CORRADE_COMPARE(data.attributeType("DoubleSided"), MaterialAttributeType::Bool); + CORRADE_COMPARE(data.attributeType("highlightColor"), MaterialAttributeType::Vector4); + + CORRADE_COMPARE(data.attribute("AmbientTextureMatrix"), Matrix3::scaling({0.5f, 1.0f})); + CORRADE_COMPARE(data.attribute("DiffuseTextureCoordinates"), 5); + CORRADE_COMPARE(data.attribute("DoubleSided"), true); + CORRADE_COMPARE(data.attribute("highlightColor"), 0x335566ff_rgbaf); + + CORRADE_COMPARE(*static_cast(data.attribute("AmbientTextureMatrix")), Matrix3::scaling({0.5f, 1.0f})); + CORRADE_COMPARE(*static_cast(data.attribute("DiffuseTextureCoordinates")), 5); + CORRADE_COMPARE(*static_cast(data.attribute("DoubleSided")), true); + CORRADE_COMPARE(*static_cast(data.attribute("highlightColor")), 0x335566ff_rgbaf); +} + +void MaterialDataTest::constructEmptyAttribute() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + std::ostringstream out; + Error redirectError{&out}; + MaterialData{{ + {"DiffuseTexture"_s, 12u}, + MaterialAttributeData{} + }}; + CORRADE_COMPARE(out.str(), "Trade::MaterialData: attribute 1 doesn't specify anything\n"); +} + +void MaterialDataTest::constructDuplicateAttribute() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + auto attributes = Containers::Array{Containers::InPlaceInit, { + {MaterialAttribute::DoubleSided, true}, + {MaterialAttribute::DiffuseTextureCoordinates, 5u}, + {"highlightColor", 0x335566ff_rgbaf}, + {MaterialAttribute::AmbientTextureMatrix, Matrix3::scaling({0.5f, 1.0f})}, + {MaterialAttribute::DiffuseTextureCoordinates, 5u} + }}; + + /* Testing that it asserts in all input permutations */ + for(std::size_t i = 0; i != testCaseRepeatId(); ++i) + std::next_permutation(attributes.begin(), attributes.end(), [](const MaterialAttributeData& a, const MaterialAttributeData& b) { + return a.name() < b.name(); + }); + + std::ostringstream out; + Error redirectError{&out}; + MaterialData data{std::move(attributes)}; + /* Because with graceful asserts it doesn't exit on error, the assertion + might get printed multiple times */ + CORRADE_COMPARE(Utility::String::partition(out.str(), '\n')[0], "Trade::MaterialData: duplicate attribute DiffuseTextureCoordinates"); +} + +void MaterialDataTest::constructFromImmutableSortedArray() { + constexpr MaterialAttributeData attributes[]{ + {"hello this is first"_s, 1}, + {"yay this is last"_s, Vector4{0.2f, 0.6f, 0.4f, 1.0f}} + }; + + MaterialData data{Containers::Array{const_cast(attributes), Containers::arraySize(attributes), [](MaterialAttributeData*, std::size_t) {}}}; + + CORRADE_COMPARE(data.attributeCount(), 2); + CORRADE_COMPARE(data.attributeName(0), "hello this is first"); + CORRADE_COMPARE(data.attributeName(1), "yay this is last"); +} + +void MaterialDataTest::constructNonOwned() { + constexpr MaterialAttributeData attributes[]{ + {"AmbientTextureMatrix"_s, Matrix3{{0.5f, 0.0f, 0.0f}, + {0.0f, 1.0f, 0.0f}, + {0.0f, 0.0f, 1.0f}}}, + {"DiffuseTextureCoordinates"_s, 5u}, + {"DoubleSided"_s, true}, + {"highlightColor"_s, Vector4{0.2f, 0.6f, 0.4f, 1.0f}} + }; + + int state; + MaterialData data{{}, attributes, &state}; + + /* Expecting the same output as in construct() */ + CORRADE_COMPARE(data.attributeCount(), 4); + CORRADE_COMPARE(data.data().size(), 4); + CORRADE_COMPARE(data.data().data(), attributes); + CORRADE_COMPARE(data.importerState(), &state); + + /* We sorted the input already */ + CORRADE_COMPARE(data.attributeName(0), "AmbientTextureMatrix"); + CORRADE_COMPARE(data.attributeName(1), "DiffuseTextureCoordinates"); + CORRADE_COMPARE(data.attributeName(2), "DoubleSided"); + CORRADE_COMPARE(data.attributeName(3), "highlightColor"); + + /* No need to verify the contents as there's no difference in access in + owned vs non-owned */ +} + +void MaterialDataTest::constructNonOwnedEmptyAttribute() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + MaterialAttributeData attributes[]{ + {"DiffuseTexture"_s, 12u}, + MaterialAttributeData{} + }; + + std::ostringstream out; + Error redirectError{&out}; + /* nullptr to avoid attributes interpreted as importerState */ + MaterialData{{}, attributes, nullptr}; + CORRADE_COMPARE(out.str(), "Trade::MaterialData: attribute 1 doesn't specify anything\n"); +} + +void MaterialDataTest::constructNonOwnedNotSorted() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + MaterialAttributeData attributes[]{ + {"DiffuseTextureCoordinates"_s, 5u}, + {"DiffuseTexture"_s, 12u} + }; + + std::ostringstream out; + Error redirectError{&out}; + /* nullptr to avoid attributes interpreted as importerState */ + MaterialData{{}, attributes, nullptr}; + CORRADE_COMPARE(out.str(), "Trade::MaterialData: DiffuseTexture has to be sorted before DiffuseTextureCoordinates if passing non-owned data\n"); +} + +void MaterialDataTest::constructNonOwnedDuplicateAttribute() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + MaterialAttributeData attributes[]{ + {"DiffuseTexture"_s, 35u}, + {"DiffuseTextureCoordinates"_s, 5u}, + {"DiffuseTextureCoordinates"_s, 12u} + }; + + std::ostringstream out; + Error redirectError{&out}; + /* nullptr to avoid attributes interpreted as importerState */ + MaterialData{{}, attributes, nullptr}; + CORRADE_COMPARE(out.str(), "Trade::MaterialData: duplicate attribute DiffuseTextureCoordinates\n"); +} + +void MaterialDataTest::constructCopy() { + CORRADE_VERIFY(!(std::is_constructible{})); + CORRADE_VERIFY(!(std::is_assignable{})); +} + +void MaterialDataTest::constructMove() { + int state; + MaterialData a{{ + {MaterialAttribute::DoubleSided, true}, + {"boredomFactor", 5} + }, &state}; + + MaterialData b{std::move(a)}; + CORRADE_COMPARE(a.attributeCount(), 0); + CORRADE_COMPARE(b.attributeCount(), 2); + CORRADE_COMPARE(b.attributeName(0), "DoubleSided"); + CORRADE_COMPARE(b.importerState(), &state); + + MaterialData c{{ + {MaterialAttribute::AlphaMask, 0.5f} + }}; + c = std::move(b); + CORRADE_COMPARE(b.attributeCount(), 1); + CORRADE_COMPARE(c.attributeCount(), 2); + CORRADE_COMPARE(c.attributeName(0), "DoubleSided"); + CORRADE_COMPARE(c.importerState(), &state); + + CORRADE_VERIFY(std::is_nothrow_move_constructible::value); + CORRADE_VERIFY(std::is_nothrow_move_assignable::value); +} + +void MaterialDataTest::accessOutOfBounds() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + MaterialData data{{ + {MaterialAttribute::AlphaMask, 0.5f}, + {MaterialAttribute::SpecularTexture, 3u} + }}; + + std::ostringstream out; + Error redirectError{&out}; + data.attributeName(2); + data.attributeType(2); + data.attribute(2); + data.attribute(2); + CORRADE_COMPARE(out.str(), + "Trade::MaterialData::attributeName(): index 2 out of range for 2 attributes\n" + "Trade::MaterialData::attributeType(): index 2 out of range for 2 attributes\n" + "Trade::MaterialData::attribute(): index 2 out of range for 2 attributes\n" + "Trade::MaterialData::attribute(): index 2 out of range for 2 attributes\n"); +} + +void MaterialDataTest::accessInvalidAttributeName() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + MaterialData data{}; + + std::ostringstream out; + Error redirectError{&out}; + data.hasAttribute(MaterialAttribute(0x0)); + data.hasAttribute(MaterialAttribute(0xfefe)); + data.attributeId(MaterialAttribute(0x0)); + data.attributeId(MaterialAttribute(0xfefe)); + data.attributeType(MaterialAttribute(0x0)); + data.attributeType(MaterialAttribute(0xfefe)); + data.attribute(MaterialAttribute(0x0)); + data.attribute(MaterialAttribute(0xfefe)); + data.attribute(MaterialAttribute(0x0)); + data.attribute(MaterialAttribute(0xfefe)); + CORRADE_COMPARE(out.str(), + "Trade::MaterialData::hasAttribute(): invalid name Trade::MaterialAttribute(0x0)\n" + "Trade::MaterialData::hasAttribute(): invalid name Trade::MaterialAttribute(0xfefe)\n" + "Trade::MaterialData::attributeId(): invalid name Trade::MaterialAttribute(0x0)\n" + "Trade::MaterialData::attributeId(): invalid name Trade::MaterialAttribute(0xfefe)\n" + "Trade::MaterialData::attributeType(): invalid name Trade::MaterialAttribute(0x0)\n" + "Trade::MaterialData::attributeType(): invalid name Trade::MaterialAttribute(0xfefe)\n" + "Trade::MaterialData::attribute(): invalid name Trade::MaterialAttribute(0x0)\n" + "Trade::MaterialData::attribute(): invalid name Trade::MaterialAttribute(0xfefe)\n" + "Trade::MaterialData::attribute(): invalid name Trade::MaterialAttribute(0x0)\n" + "Trade::MaterialData::attribute(): invalid name Trade::MaterialAttribute(0xfefe)\n"); +} + +void MaterialDataTest::accessNotFound() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + MaterialData data{{ + {"DiffuseColor", 0xff3366aa_rgbaf} + }}; + + CORRADE_VERIFY(!data.hasAttribute("DiffuseColour")); + + std::ostringstream out; + Error redirectError{&out}; + data.attributeId("DiffuseColour"); + data.attributeType("DiffuseColour"); + data.attribute("DiffuseColour"); + data.attribute("DiffuseColour"); + CORRADE_COMPARE(out.str(), + "Trade::MaterialData::attributeId(): attribute DiffuseColour not found\n" + "Trade::MaterialData::attributeType(): attribute DiffuseColour not found\n" + "Trade::MaterialData::attribute(): attribute DiffuseColour not found\n" + "Trade::MaterialData::attribute(): attribute DiffuseColour not found\n"); +} + +void MaterialDataTest::accessWrongType() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + MaterialData data{{ + {"DiffuseColor", 0xff3366aa_rgbaf} + }}; + + std::ostringstream out; + Error redirectError{&out}; + data.attribute(0); + data.attribute(MaterialAttribute::DiffuseColor); + data.attribute("DiffuseColor"); + CORRADE_COMPARE(out.str(), + "Trade::MaterialData::attribute(): improper type requested for DiffuseColor of Trade::MaterialAttributeType::Vector4\n" + "Trade::MaterialData::attribute(): improper type requested for DiffuseColor of Trade::MaterialAttributeType::Vector4\n" + "Trade::MaterialData::attribute(): improper type requested for DiffuseColor of Trade::MaterialAttributeType::Vector4\n"); +} + +void MaterialDataTest::release() { + MaterialData data{{ + {"DiffuseColor", 0xff3366aa_rgbaf}, + {MaterialAttribute::NormalTexture, 0u} + }}; + + const void* pointer = data.data().data(); + + Containers::Array released = data.release(); + CORRADE_COMPARE(released.data(), pointer); + CORRADE_COMPARE(released.size(), 2); + CORRADE_VERIFY(!data.data()); + CORRADE_COMPARE(data.attributeCount(), 0); +} + void MaterialDataTest::constructPhong() { using namespace Math::Literals; @@ -198,8 +900,6 @@ void MaterialDataTest::constructPhongTextureTransformNoTextures() { } void MaterialDataTest::constructPhongNoTextureTransformationFlag() { - using namespace Math::Literals; - #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif @@ -231,14 +931,14 @@ void MaterialDataTest::constructPhongNoTextureCoordinatesFlag() { "PhongMaterialData::PhongMaterialData: non-zero texture coordinate sets require Flag::TextureCoordinates to be enabled\n"); } -void MaterialDataTest::constructCopy() { +void MaterialDataTest::constructPhongCopy() { CORRADE_VERIFY(!(std::is_constructible{})); CORRADE_VERIFY(!(std::is_constructible{})); CORRADE_VERIFY(!(std::is_assignable{})); CORRADE_VERIFY(!(std::is_assignable{})); } -void MaterialDataTest::constructMovePhong() { +void MaterialDataTest::constructPhongMove() { using namespace Math::Literals; const int a{}; @@ -333,6 +1033,20 @@ void MaterialDataTest::accessInvalidTextures() { "Trade::PhongMaterialData::normalTextureCoordinates(): the material doesn't have a normal texture\n"); } +void MaterialDataTest::debugAttribute() { + std::ostringstream out; + + Debug{&out} << MaterialAttribute::DiffuseTextureCoordinates << MaterialAttribute(0xfefe) << MaterialAttribute{}; + CORRADE_COMPARE(out.str(), "Trade::MaterialAttribute::DiffuseTextureCoordinates Trade::MaterialAttribute(0xfefe) Trade::MaterialAttribute(0x0)\n"); +} + +void MaterialDataTest::debugAttributeType() { + std::ostringstream out; + + Debug{&out} << MaterialAttributeType::Matrix3x2 << MaterialAttributeType(0xfe); + CORRADE_COMPARE(out.str(), "Trade::MaterialAttributeType::Matrix3x2 Trade::MaterialAttributeType(0xfe)\n"); +} + void MaterialDataTest::debugType() { std::ostringstream out; diff --git a/src/Magnum/Trade/Trade.h b/src/Magnum/Trade/Trade.h index 93c9f8c818..164ae036a4 100644 --- a/src/Magnum/Trade/Trade.h +++ b/src/Magnum/Trade/Trade.h @@ -48,9 +48,13 @@ class AbstractSceneConverter; typedef CORRADE_DEPRECATED("use InputFileCallbackPolicy instead") InputFileCallbackPolicy ImporterFileCallbackPolicy; #endif +enum class MaterialAttribute: UnsignedInt; +enum class MaterialAttributeType: UnsignedByte; enum class MaterialType: UnsignedByte; enum class MaterialAlphaMode: UnsignedByte; class AbstractMaterialData; +class MaterialAttributeData; +class MaterialData; enum class AnimationTrackTargetType: UnsignedByte; enum class AnimationTrackType: UnsignedByte; From 9992614c3d8d075e855163fac6f21c58294a3bc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 20 Jul 2020 23:16:53 +0200 Subject: [PATCH 05/36] Trade: "optional" access to MaterialData attributes. --- src/Magnum/Trade/MaterialData.cpp | 12 ++++ src/Magnum/Trade/MaterialData.h | 67 +++++++++++++++++++++- src/Magnum/Trade/Test/MaterialDataTest.cpp | 51 +++++++++++++++- 3 files changed, 127 insertions(+), 3 deletions(-) diff --git a/src/Magnum/Trade/MaterialData.cpp b/src/Magnum/Trade/MaterialData.cpp index b2dc17f9a5..b33c4b3949 100644 --- a/src/Magnum/Trade/MaterialData.cpp +++ b/src/Magnum/Trade/MaterialData.cpp @@ -262,6 +262,18 @@ const void* MaterialData::attribute(const MaterialAttribute name) const { return attribute(string); } +const void* MaterialData::tryAttribute(const Containers::StringView name) const { + const UnsignedInt id = attributeFor(name); + if(id == ~UnsignedInt{}) return nullptr; + return _data[id].value(); +} + +const void* MaterialData::tryAttribute(const MaterialAttribute name) const { + const Containers::StringView string = attributeString(name); + CORRADE_ASSERT(string.data(), "Trade::MaterialData::tryAttribute(): invalid name" << name, {}); + return tryAttribute(string); +} + Containers::Array MaterialData::release() { return std::move(_data); } diff --git a/src/Magnum/Trade/MaterialData.h b/src/Magnum/Trade/MaterialData.h index 92fcfc5544..d5ada04777 100644 --- a/src/Magnum/Trade/MaterialData.h +++ b/src/Magnum/Trade/MaterialData.h @@ -31,6 +31,7 @@ */ #include +#include #include #include "Magnum/Magnum.h" @@ -590,7 +591,11 @@ class MAGNUM_TRADE_EXPORT MaterialData { /** @brief Attribute count */ UnsignedInt attributeCount() const { return _data.size(); } - /** @brief Whether the material has given attribute */ + /** + * @brief Whether the material has given attribute + * + * @see @ref tryAttribute(), @ref attributeOr() + */ bool hasAttribute(Containers::StringView name) const; bool hasAttribute(MaterialAttribute name) const; /**< @overload */ @@ -643,7 +648,7 @@ class MAGNUM_TRADE_EXPORT MaterialData { * * The @p name is expected to exist. Cast the pointer to a concrete * type based on @ref attributeType(). - * @see @ref hasAttribute() + * @see @ref hasAttribute(), @ref tryAttribute(), @ref attributeOr() */ const void* attribute(Containers::StringView name) const; const void* attribute(MaterialAttribute name) const; /**< @overload */ @@ -667,6 +672,40 @@ class MAGNUM_TRADE_EXPORT MaterialData { template T attribute(Containers::StringView name) const; template T attribute(MaterialAttribute name) const; /**< @overload */ + /** + * @brief Type-erased attribute value, if exists + * + * Compared to @ref attribute(Containers::StringView name) const, if + * @p name doesn't exist, returns @cpp nullptr @ce instead of + * asserting. Cast the pointer to a concrete type based on + * @ref attributeType(). + * @see @ref hasAttribute(), @ref attributeOr() + */ + const void* tryAttribute(Containers::StringView name) const; + const void* tryAttribute(MaterialAttribute name) const; /**< @overload */ + + /** + * @brief Value of a named attribute, if exists + * + * Compared to @ref attribute(Containers::StringView name) const, if + * @p name doesn't exist, returns @ref Corrade::Containers::NullOpt + * instead of asserting. Expects that @p T corresponds to + * @ref attributeType(Containers::StringView) const for given @p name. + */ + template Containers::Optional tryAttribute(Containers::StringView name) const; + template Containers::Optional tryAttribute(MaterialAttribute name) const; /**< @overload */ + + /** + * @brief Value of a named attribute or a default + * + * Compared to @ref attribute(Containers::StringView name) const, if + * @p name doesn't exist, returns @p defaultValue instead of asserting. + * Expects that @p T corresponds to + * @ref attributeType(Containers::StringView) const for given @p name. + */ + template T attributeOr(Containers::StringView name, const T& defaultValue) const; + template T attributeOr(MaterialAttribute name, const T& defaultValue) const; /**< @overload */ + /** * @brief Release data storage * @@ -774,6 +813,30 @@ template T MaterialData::attribute(MaterialAttribute name) const { return attribute(string); } +template Containers::Optional MaterialData::tryAttribute(Containers::StringView name) const { + const UnsignedInt id = attributeFor(name); + if(id == ~UnsignedInt{}) return {}; + return attribute(id); +} + +template Containers::Optional MaterialData::tryAttribute(MaterialAttribute name) const { + const Containers::StringView string = attributeString(name); + CORRADE_ASSERT(string.data(), "Trade::MaterialData::tryAttribute(): invalid name" << name, {}); + return tryAttribute(string); +} + +template T MaterialData::attributeOr(Containers::StringView name, const T& defaultValue) const { + const UnsignedInt id = attributeFor(name); + if(id == ~UnsignedInt{}) return defaultValue; + return attribute(id); +} + +template T MaterialData::attributeOr(MaterialAttribute name, const T& defaultValue) const { + const Containers::StringView string = attributeString(name); + CORRADE_ASSERT(string.data(), "Trade::MaterialData::attributeOr(): invalid name" << name, {}); + return attributeOr(string, defaultValue); +} + }} #endif diff --git a/src/Magnum/Trade/Test/MaterialDataTest.cpp b/src/Magnum/Trade/Test/MaterialDataTest.cpp index ea9461868c..14260820ac 100644 --- a/src/Magnum/Trade/Test/MaterialDataTest.cpp +++ b/src/Magnum/Trade/Test/MaterialDataTest.cpp @@ -73,6 +73,7 @@ class MaterialDataTest: public TestSuite::Tester { void constructCopy(); void constructMove(); + void accessOptional(); void accessOutOfBounds(); void accessInvalidAttributeName(); void accessNotFound(); @@ -161,6 +162,7 @@ MaterialDataTest::MaterialDataTest() { &MaterialDataTest::constructCopy, &MaterialDataTest::constructMove, + &MaterialDataTest::accessOptional, &MaterialDataTest::accessOutOfBounds, &MaterialDataTest::accessInvalidAttributeName, &MaterialDataTest::accessNotFound, @@ -666,6 +668,31 @@ void MaterialDataTest::constructMove() { CORRADE_VERIFY(std::is_nothrow_move_assignable::value); } +void MaterialDataTest::accessOptional() { + MaterialData data{{ + {MaterialAttribute::AlphaMask, 0.5f}, + {MaterialAttribute::SpecularTexture, 3u} + }}; + + /* This exists */ + CORRADE_VERIFY(data.tryAttribute("SpecularTexture")); + CORRADE_VERIFY(data.tryAttribute(MaterialAttribute::SpecularTexture)); + CORRADE_COMPARE(*static_cast(data.tryAttribute("SpecularTexture")), 3); + CORRADE_COMPARE(*static_cast(data.tryAttribute(MaterialAttribute::SpecularTexture)), 3); + CORRADE_COMPARE(data.tryAttribute("SpecularTexture"), 3); + CORRADE_COMPARE(data.tryAttribute(MaterialAttribute::SpecularTexture), 3); + CORRADE_COMPARE(data.attributeOr("SpecularTexture", 5u), 3); + CORRADE_COMPARE(data.attributeOr(MaterialAttribute::SpecularTexture, 5u), 3); + + /* This doesn't */ + CORRADE_VERIFY(!data.tryAttribute("DiffuseTexture")); + CORRADE_VERIFY(!data.tryAttribute(MaterialAttribute::DiffuseTexture)); + CORRADE_VERIFY(!data.tryAttribute("DiffuseTexture")); + CORRADE_VERIFY(!data.tryAttribute(MaterialAttribute::DiffuseTexture)); + CORRADE_COMPARE(data.attributeOr("DiffuseTexture", 5u), 5); + CORRADE_COMPARE(data.attributeOr(MaterialAttribute::DiffuseTexture, 5u), 5); +} + void MaterialDataTest::accessOutOfBounds() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); @@ -708,6 +735,12 @@ void MaterialDataTest::accessInvalidAttributeName() { data.attribute(MaterialAttribute(0xfefe)); data.attribute(MaterialAttribute(0x0)); data.attribute(MaterialAttribute(0xfefe)); + data.tryAttribute(MaterialAttribute(0x0)); + data.tryAttribute(MaterialAttribute(0xfefe)); + data.tryAttribute(MaterialAttribute(0x0)); + data.tryAttribute(MaterialAttribute(0xfefe)); + data.attributeOr(MaterialAttribute(0x0), 42); + data.attributeOr(MaterialAttribute(0xfefe), 42); CORRADE_COMPARE(out.str(), "Trade::MaterialData::hasAttribute(): invalid name Trade::MaterialAttribute(0x0)\n" "Trade::MaterialData::hasAttribute(): invalid name Trade::MaterialAttribute(0xfefe)\n" @@ -718,7 +751,13 @@ void MaterialDataTest::accessInvalidAttributeName() { "Trade::MaterialData::attribute(): invalid name Trade::MaterialAttribute(0x0)\n" "Trade::MaterialData::attribute(): invalid name Trade::MaterialAttribute(0xfefe)\n" "Trade::MaterialData::attribute(): invalid name Trade::MaterialAttribute(0x0)\n" - "Trade::MaterialData::attribute(): invalid name Trade::MaterialAttribute(0xfefe)\n"); + "Trade::MaterialData::attribute(): invalid name Trade::MaterialAttribute(0xfefe)\n" + "Trade::MaterialData::tryAttribute(): invalid name Trade::MaterialAttribute(0x0)\n" + "Trade::MaterialData::tryAttribute(): invalid name Trade::MaterialAttribute(0xfefe)\n" + "Trade::MaterialData::tryAttribute(): invalid name Trade::MaterialAttribute(0x0)\n" + "Trade::MaterialData::tryAttribute(): invalid name Trade::MaterialAttribute(0xfefe)\n" + "Trade::MaterialData::attributeOr(): invalid name Trade::MaterialAttribute(0x0)\n" + "Trade::MaterialData::attributeOr(): invalid name Trade::MaterialAttribute(0xfefe)\n"); } void MaterialDataTest::accessNotFound() { @@ -759,7 +798,17 @@ void MaterialDataTest::accessWrongType() { data.attribute(0); data.attribute(MaterialAttribute::DiffuseColor); data.attribute("DiffuseColor"); + data.tryAttribute(MaterialAttribute::DiffuseColor); + data.tryAttribute("DiffuseColor"); + data.attributeOr(MaterialAttribute::DiffuseColor, Color3{1.0f}); + data.attributeOr("DiffuseColor", Color3{1.0f}); CORRADE_COMPARE(out.str(), + "Trade::MaterialData::attribute(): improper type requested for DiffuseColor of Trade::MaterialAttributeType::Vector4\n" + "Trade::MaterialData::attribute(): improper type requested for DiffuseColor of Trade::MaterialAttributeType::Vector4\n" + "Trade::MaterialData::attribute(): improper type requested for DiffuseColor of Trade::MaterialAttributeType::Vector4\n" + /* tryAttribute() and attributeOr() delegate to attribute() so the + assert is the same */ + "Trade::MaterialData::attribute(): improper type requested for DiffuseColor of Trade::MaterialAttributeType::Vector4\n" "Trade::MaterialData::attribute(): improper type requested for DiffuseColor of Trade::MaterialAttributeType::Vector4\n" "Trade::MaterialData::attribute(): improper type requested for DiffuseColor of Trade::MaterialAttributeType::Vector4\n" "Trade::MaterialData::attribute(): improper type requested for DiffuseColor of Trade::MaterialAttributeType::Vector4\n"); From f250e7a4c97a4f9e65fcf2592444b65dbba4a798 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 4 Aug 2020 18:10:56 +0200 Subject: [PATCH 06/36] Trade: add a type enum *set* to MaterialData. Compared to previous AbstractMaterialData, which was always just a single type, the new data can describe several different materials at once. This is the case for example with glTF, where a material can be metallic/roughness but also have an alternative description using specular/glossiness. Currently the usage is undocumented, but when everything is in place, if a material advertises given type, it can be then cast to one of its convenience subclasses. --- src/Magnum/Trade/AbstractMaterialData.cpp | 14 ------- src/Magnum/Trade/AbstractMaterialData.h | 17 +------- src/Magnum/Trade/MaterialData.cpp | 27 +++++++++++-- src/Magnum/Trade/MaterialData.h | 45 ++++++++++++++++++++-- src/Magnum/Trade/Test/MaterialDataTest.cpp | 45 ++++++++++++++-------- src/Magnum/Trade/Trade.h | 2 +- 6 files changed, 97 insertions(+), 53 deletions(-) diff --git a/src/Magnum/Trade/AbstractMaterialData.cpp b/src/Magnum/Trade/AbstractMaterialData.cpp index aeec370421..0a7a1e3b8c 100644 --- a/src/Magnum/Trade/AbstractMaterialData.cpp +++ b/src/Magnum/Trade/AbstractMaterialData.cpp @@ -38,20 +38,6 @@ AbstractMaterialData::AbstractMaterialData(const MaterialType type, const Flags AbstractMaterialData::~AbstractMaterialData() = default; -Debug& operator<<(Debug& debug, const MaterialType value) { - debug << "Trade::MaterialType" << Debug::nospace; - - switch(value) { - /* LCOV_EXCL_START */ - #define _c(value) case MaterialType::value: return debug << "::" #value; - _c(Phong) - #undef _c - /* LCOV_EXCL_STOP */ - } - - return debug << "(" << Debug::nospace << reinterpret_cast(UnsignedByte(value)) << Debug::nospace << ")"; -} - Debug& operator<<(Debug& debug, const AbstractMaterialData::Flag value) { debug << "Trade::AbstractMaterialData::Flag" << Debug::nospace; diff --git a/src/Magnum/Trade/AbstractMaterialData.h b/src/Magnum/Trade/AbstractMaterialData.h index 37bc0ae280..b833bf7402 100644 --- a/src/Magnum/Trade/AbstractMaterialData.h +++ b/src/Magnum/Trade/AbstractMaterialData.h @@ -29,22 +29,10 @@ * @brief Class @ref Magnum::Trade::AbstractMaterialData, enum @ref Magnum::Trade::MaterialType */ -#include - -#include "Magnum/Magnum.h" -#include "Magnum/Trade/visibility.h" +#include "Magnum/Trade/MaterialData.h" namespace Magnum { namespace Trade { -/** -@brief Material type - -@see @ref AbstractMaterialData::type() -*/ -enum class MaterialType: UnsignedByte { - Phong /**< Phong shading (see @ref PhongMaterialData) */ -}; - /** @brief Material alpha mode @@ -165,9 +153,6 @@ class MAGNUM_TRADE_EXPORT AbstractMaterialData { const void* _importerState; }; -/** @debugoperatorenum{MaterialType} */ -MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, MaterialType value); - /** @debugoperatorenum{MaterialAlphaMode} */ MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, MaterialAlphaMode value); diff --git a/src/Magnum/Trade/MaterialData.cpp b/src/Magnum/Trade/MaterialData.cpp index b33c4b3949..727a5a4a57 100644 --- a/src/Magnum/Trade/MaterialData.cpp +++ b/src/Magnum/Trade/MaterialData.cpp @@ -27,6 +27,7 @@ #include #include +#include #include "Magnum/Math/Vector4.h" #include "Magnum/Math/Matrix.h" @@ -126,7 +127,7 @@ const void* MaterialAttributeData::value() const { return _data.data + Implementation::MaterialAttributeDataSize - materialAttributeTypeSize(_data.type); } -MaterialData::MaterialData(Containers::Array&& data, const void* const importerState) noexcept: _data{std::move(data)}, _importerState{importerState} { +MaterialData::MaterialData(const MaterialTypes types, Containers::Array&& data, const void* const importerState) noexcept: _data{std::move(data)}, _types{types}, _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. */ @@ -155,9 +156,9 @@ MaterialData::MaterialData(Containers::Array&& data, cons } } -MaterialData::MaterialData(const std::initializer_list data, const void* const importerState): MaterialData{Implementation::initializerListToArrayWithDefaultDeleter(data), importerState} {} +MaterialData::MaterialData(const MaterialTypes types, const std::initializer_list data, const void* const importerState): MaterialData{types, Implementation::initializerListToArrayWithDefaultDeleter(data), importerState} {} -MaterialData::MaterialData(DataFlags, const Containers::ArrayView data, const void* const importerState) noexcept: _data{Containers::Array{const_cast(data.data()), data.size(), [](MaterialAttributeData*, std::size_t){}}}, _importerState{importerState} { +MaterialData::MaterialData(const MaterialTypes types, DataFlags, const Containers::ArrayView data, const void* const importerState) noexcept: _data{Containers::Array{const_cast(data.data()), data.size(), [](MaterialAttributeData*, std::size_t){}}}, _types{types}, _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) @@ -323,4 +324,24 @@ Debug& operator<<(Debug& debug, const MaterialAttributeType value) { return debug << "(" << Debug::nospace << reinterpret_cast(UnsignedShort(value)) << Debug::nospace << ")"; } +Debug& operator<<(Debug& debug, const MaterialType value) { + debug << "Trade::MaterialType" << Debug::nospace; + + switch(value) { + /* LCOV_EXCL_START */ + #define _c(value) case MaterialType::value: return debug << "::" #value; + _c(Phong) + #undef _c + /* LCOV_EXCL_STOP */ + } + + return debug << "(" << Debug::nospace << reinterpret_cast(UnsignedByte(value)) << Debug::nospace << ")"; +} + +Debug& operator<<(Debug& debug, const MaterialTypes value) { + return Containers::enumSetDebugOutput(debug, value, "Trade::MaterialTypes{}", { + MaterialType::Phong + }); +} + }} diff --git a/src/Magnum/Trade/MaterialData.h b/src/Magnum/Trade/MaterialData.h index d5ada04777..be54b499f9 100644 --- a/src/Magnum/Trade/MaterialData.h +++ b/src/Magnum/Trade/MaterialData.h @@ -507,6 +507,32 @@ class MAGNUM_TRADE_EXPORT MaterialAttributeData { static_assert(sizeof(Storage) == Implementation::MaterialAttributeDataSize, "something is off, huh"); }; +/** +@brief Material type + +@see @ref MaterialTypes, @ref MaterialData::types(), + @ref AbstractMaterialData::type() +*/ +enum class MaterialType: UnsignedInt { + Phong = 1 << 0 /**< Phong shading */ +}; + +/** @debugoperatorenum{MaterialType} */ +MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, MaterialType value); + +/** +@brief Material types +@m_since_latest + +@see @ref MaterialData::types() +*/ +typedef Containers::EnumSet MaterialTypes; + +CORRADE_ENUMSET_OPERATORS(MaterialTypes) + +/** @debugoperatorenum{MaterialTypes} */ +MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, MaterialTypes value); + /** @brief Material data @m_since_latest @@ -543,19 +569,23 @@ class MAGNUM_TRADE_EXPORT MaterialData { public: /** * @brief Construct + * @param types Which material types are described by this + * data. Can be an empty set. * @param data Attribute data * @param importerState Importer-specific state * * The @p data gets sorted by name internally, expecting no duplicates. */ - explicit MaterialData(Containers::Array&& data, const void* importerState = nullptr) noexcept; + explicit MaterialData(MaterialTypes types, Containers::Array&& data, const void* importerState = nullptr) noexcept; /** @overload */ /* Not noexcept because allocation happens inside */ - explicit MaterialData(std::initializer_list data, const void* importerState = nullptr); + explicit MaterialData(MaterialTypes types, std::initializer_list data, const void* importerState = nullptr); /** * @brief Construct a non-owned material data + * @param types Which material types are described by this + * data. Can be an empty set. * @param dataFlags Ignored. Used only for a safer distinction * from the owning constructor. * @param data Attribute data @@ -564,7 +594,7 @@ class MAGNUM_TRADE_EXPORT MaterialData { * The @p data is expected to be already sorted by name, without * duplicates. */ - explicit MaterialData(DataFlags dataFlags, Containers::ArrayView data, const void* importerState = nullptr) noexcept; + explicit MaterialData(MaterialTypes types, DataFlags dataFlags, Containers::ArrayView data, const void* importerState = nullptr) noexcept; ~MaterialData(); @@ -580,6 +610,14 @@ class MAGNUM_TRADE_EXPORT MaterialData { /** @brief Move assignment */ MaterialData& operator=(MaterialData&&) noexcept; + /** + * @brief Material types + * + * Each type indicates that the material data can be interpreted as + * given type. For custom materials the set can also be empty. + */ + MaterialTypes types() const { return _types; } + /** * @brief Raw attribute data * @@ -731,6 +769,7 @@ class MAGNUM_TRADE_EXPORT MaterialData { UnsignedInt attributeFor(Containers::StringView name) const; Containers::Array _data; + MaterialTypes _types; const void* _importerState; }; diff --git a/src/Magnum/Trade/Test/MaterialDataTest.cpp b/src/Magnum/Trade/Test/MaterialDataTest.cpp index 14260820ac..913dcc176c 100644 --- a/src/Magnum/Trade/Test/MaterialDataTest.cpp +++ b/src/Magnum/Trade/Test/MaterialDataTest.cpp @@ -97,6 +97,7 @@ class MaterialDataTest: public TestSuite::Tester { void debugAttributeType(); void debugType(); + void debugTypes(); void debugFlag(); void debugFlags(); void debugAlphaMode(); @@ -186,6 +187,7 @@ MaterialDataTest::MaterialDataTest() { &MaterialDataTest::debugAttributeType, &MaterialDataTest::debugType, + &MaterialDataTest::debugTypes, &MaterialDataTest::debugFlag, &MaterialDataTest::debugFlags, &MaterialDataTest::debugAlphaMode, @@ -422,13 +424,14 @@ void MaterialDataTest::constructAttributeWrongAccessType() { void MaterialDataTest::construct() { int state; - MaterialData data{{ + MaterialData data{MaterialType::Phong, { {MaterialAttribute::DoubleSided, true}, {MaterialAttribute::DiffuseTextureCoordinates, 5u}, {"highlightColor", 0x335566ff_rgbaf}, {MaterialAttribute::AmbientTextureMatrix, Matrix3::scaling({0.5f, 1.0f})} }, &state}; + CORRADE_COMPARE(data.types(), MaterialType::Phong); CORRADE_COMPARE(data.attributeCount(), 4); CORRADE_COMPARE(data.data().size(), 4); CORRADE_COMPARE(data.importerState(), &state); @@ -509,7 +512,7 @@ void MaterialDataTest::constructEmptyAttribute() { std::ostringstream out; Error redirectError{&out}; - MaterialData{{ + MaterialData{{}, { {"DiffuseTexture"_s, 12u}, MaterialAttributeData{} }}; @@ -537,7 +540,7 @@ void MaterialDataTest::constructDuplicateAttribute() { std::ostringstream out; Error redirectError{&out}; - MaterialData data{std::move(attributes)}; + MaterialData data{{}, std::move(attributes)}; /* Because with graceful asserts it doesn't exit on error, the assertion might get printed multiple times */ CORRADE_COMPARE(Utility::String::partition(out.str(), '\n')[0], "Trade::MaterialData: duplicate attribute DiffuseTextureCoordinates"); @@ -549,7 +552,7 @@ void MaterialDataTest::constructFromImmutableSortedArray() { {"yay this is last"_s, Vector4{0.2f, 0.6f, 0.4f, 1.0f}} }; - MaterialData data{Containers::Array{const_cast(attributes), Containers::arraySize(attributes), [](MaterialAttributeData*, std::size_t) {}}}; + MaterialData data{{}, Containers::Array{const_cast(attributes), Containers::arraySize(attributes), [](MaterialAttributeData*, std::size_t) {}}}; CORRADE_COMPARE(data.attributeCount(), 2); CORRADE_COMPARE(data.attributeName(0), "hello this is first"); @@ -567,9 +570,10 @@ void MaterialDataTest::constructNonOwned() { }; int state; - MaterialData data{{}, attributes, &state}; + MaterialData data{MaterialType::Phong, {}, attributes, &state}; /* Expecting the same output as in construct() */ + CORRADE_COMPARE(data.types(), MaterialType::Phong); CORRADE_COMPARE(data.attributeCount(), 4); CORRADE_COMPARE(data.data().size(), 4); CORRADE_COMPARE(data.data().data(), attributes); @@ -598,7 +602,7 @@ void MaterialDataTest::constructNonOwnedEmptyAttribute() { std::ostringstream out; Error redirectError{&out}; /* nullptr to avoid attributes interpreted as importerState */ - MaterialData{{}, attributes, nullptr}; + MaterialData{{}, {}, attributes, nullptr}; CORRADE_COMPARE(out.str(), "Trade::MaterialData: attribute 1 doesn't specify anything\n"); } @@ -615,7 +619,7 @@ void MaterialDataTest::constructNonOwnedNotSorted() { std::ostringstream out; Error redirectError{&out}; /* nullptr to avoid attributes interpreted as importerState */ - MaterialData{{}, attributes, nullptr}; + MaterialData{{}, {}, attributes, nullptr}; CORRADE_COMPARE(out.str(), "Trade::MaterialData: DiffuseTexture has to be sorted before DiffuseTextureCoordinates if passing non-owned data\n"); } @@ -633,7 +637,7 @@ void MaterialDataTest::constructNonOwnedDuplicateAttribute() { std::ostringstream out; Error redirectError{&out}; /* nullptr to avoid attributes interpreted as importerState */ - MaterialData{{}, attributes, nullptr}; + MaterialData{{}, {}, attributes, nullptr}; CORRADE_COMPARE(out.str(), "Trade::MaterialData: duplicate attribute DiffuseTextureCoordinates\n"); } @@ -644,22 +648,24 @@ void MaterialDataTest::constructCopy() { void MaterialDataTest::constructMove() { int state; - MaterialData a{{ + MaterialData a{MaterialType::Phong, { {MaterialAttribute::DoubleSided, true}, {"boredomFactor", 5} }, &state}; MaterialData b{std::move(a)}; CORRADE_COMPARE(a.attributeCount(), 0); + CORRADE_COMPARE(b.types(), MaterialType::Phong); CORRADE_COMPARE(b.attributeCount(), 2); CORRADE_COMPARE(b.attributeName(0), "DoubleSided"); CORRADE_COMPARE(b.importerState(), &state); - MaterialData c{{ + MaterialData c{MaterialTypes{}, { {MaterialAttribute::AlphaMask, 0.5f} }}; c = std::move(b); CORRADE_COMPARE(b.attributeCount(), 1); + CORRADE_COMPARE(c.types(), MaterialType::Phong); CORRADE_COMPARE(c.attributeCount(), 2); CORRADE_COMPARE(c.attributeName(0), "DoubleSided"); CORRADE_COMPARE(c.importerState(), &state); @@ -669,7 +675,7 @@ void MaterialDataTest::constructMove() { } void MaterialDataTest::accessOptional() { - MaterialData data{{ + MaterialData data{{}, { {MaterialAttribute::AlphaMask, 0.5f}, {MaterialAttribute::SpecularTexture, 3u} }}; @@ -698,7 +704,7 @@ void MaterialDataTest::accessOutOfBounds() { CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif - MaterialData data{{ + MaterialData data{{}, { {MaterialAttribute::AlphaMask, 0.5f}, {MaterialAttribute::SpecularTexture, 3u} }}; @@ -721,7 +727,7 @@ void MaterialDataTest::accessInvalidAttributeName() { CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif - MaterialData data{}; + MaterialData data{{}, {}}; std::ostringstream out; Error redirectError{&out}; @@ -765,7 +771,7 @@ void MaterialDataTest::accessNotFound() { CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif - MaterialData data{{ + MaterialData data{{}, { {"DiffuseColor", 0xff3366aa_rgbaf} }}; @@ -789,7 +795,7 @@ void MaterialDataTest::accessWrongType() { CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif - MaterialData data{{ + MaterialData data{{}, { {"DiffuseColor", 0xff3366aa_rgbaf} }}; @@ -815,7 +821,7 @@ void MaterialDataTest::accessWrongType() { } void MaterialDataTest::release() { - MaterialData data{{ + MaterialData data{{}, { {"DiffuseColor", 0xff3366aa_rgbaf}, {MaterialAttribute::NormalTexture, 0u} }}; @@ -1103,6 +1109,13 @@ void MaterialDataTest::debugType() { CORRADE_COMPARE(out.str(), "Trade::MaterialType::Phong Trade::MaterialType(0xbe)\n"); } +void MaterialDataTest::debugTypes() { + std::ostringstream out; + + Debug{&out} << (MaterialType::Phong|MaterialType(0xe0)) << MaterialTypes{}; + CORRADE_COMPARE(out.str(), "Trade::MaterialType::Phong|Trade::MaterialType(0xe0) Trade::MaterialTypes{}\n"); +} + void MaterialDataTest::debugFlag() { std::ostringstream out; diff --git a/src/Magnum/Trade/Trade.h b/src/Magnum/Trade/Trade.h index 164ae036a4..154dd4762b 100644 --- a/src/Magnum/Trade/Trade.h +++ b/src/Magnum/Trade/Trade.h @@ -50,7 +50,7 @@ typedef CORRADE_DEPRECATED("use InputFileCallbackPolicy instead") InputFileCallb enum class MaterialAttribute: UnsignedInt; enum class MaterialAttributeType: UnsignedByte; -enum class MaterialType: UnsignedByte; +enum class MaterialType: UnsignedInt; enum class MaterialAlphaMode: UnsignedByte; class AbstractMaterialData; class MaterialAttributeData; From 8456537fef2fbe7c28754e13c8dd24ee8860bcfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 22 Jul 2020 13:40:45 +0200 Subject: [PATCH 07/36] Trade: deprecate AbstractMaterialData, rewrite PhongMaterialData. AbstractMaterialData is now just a typedef to MaterialData, with all existing public APIs moved to (and marked as deprecated, if they don't make sense anymore). The new class doesn't have a virtual destructor as that's not the desired use anymore -- and AbstractImporter::material() APIs will be returning an Optional instead of a Pointer, which means any potential subclasses will be sliced away. PhongMaterialData is reimplemented using the new key/value store, with no own members anymore -- thus having the same size as MaterialData, and safe to be casted from it to access the helper APIs. --- doc/changelog.dox | 51 ++- doc/snippets/MagnumTrade.cpp | 1 + src/Magnum/Trade/AbstractMaterialData.cpp | 77 ---- src/Magnum/Trade/AbstractMaterialData.h | 143 +------ src/Magnum/Trade/CMakeLists.txt | 4 +- src/Magnum/Trade/MaterialData.cpp | 68 ++++ src/Magnum/Trade/MaterialData.h | 176 ++++++++- src/Magnum/Trade/PhongMaterialData.cpp | 204 ++++++++-- src/Magnum/Trade/PhongMaterialData.h | 350 +++++++++++++----- src/Magnum/Trade/Test/MaterialDataTest.cpp | 410 ++++++++++++++------- src/Magnum/Trade/Trade.h | 4 +- 11 files changed, 1012 insertions(+), 476 deletions(-) delete mode 100644 src/Magnum/Trade/AbstractMaterialData.cpp diff --git a/doc/changelog.dox b/doc/changelog.dox index 7d830da931..2004f6f152 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -94,6 +94,15 @@ See also: @subsubsection changelog-latest-changes-trade Trade library - Recognizing TIFF file header magic in @ref Trade::AnyImageImporter "AnyImageImporter" +- Added @ref Trade::PhongMaterialData::ambientTextureMatrix(), + @ref Trade::PhongMaterialData::diffuseTextureMatrix(), + @ref Trade::PhongMaterialData::specularTextureMatrix() and + @ref Trade::PhongMaterialData::normalTextureMatrix() exposing also + per-texture coordinate transformation, similarly to per-texture coordinate + sets added in 2020.06 +- Added @ref Trade::PhongMaterialData::textureCoordinates() exposing a common + texture coordinate set as a complement to a per-texture property added in + 2020.06 @subsection changelog-latest-buildsystem Build system @@ -119,6 +128,9 @@ See also: @subsection changelog-latest-deprecated Deprecated APIs +- @cpp Trade::AbstractMaterialData @ce as well as its containing header + `Magnum/Trade/AbstractMaterialData.h` is now a deprecated alias to the new + and more flexible @ref Trade::MaterialData. - @cpp Trade::PhongMaterialData::ambientCoordinateSet() @ce, @cpp diffuseCoordinateSet() @ce, @cpp specularCoordinateSet() @ce and @cpp normalCoordinateSet() @ce are deprecated in favor of more consistently @@ -126,6 +138,18 @@ See also: @ref Trade::PhongMaterialData::diffuseTextureCoordinates() "diffuseTextureCoordinates()", @ref Trade::PhongMaterialData::specularTextureCoordinates() "specularTextureCoordinates()" and @ref Trade::PhongMaterialData::normalTextureCoordinates() "normalTextureCoordinates()" +- @ref Trade::PhongMaterialData constructor is deprecated as the designated + way is to populate the @ref Trade::MaterialData class directly instead. +- @cpp Trade::MaterialData::type() @ce (coming from the original + `AbstractMaterialData` class) is deprecated in favor of + @ref Trade::MaterialData::types(), as a material data can now contain + attributes for multiple different material types at once +- @cpp Trade::MaterialData::flags() @ce (coming from the original + `AbstractMaterialData` class), @cpp Trade::PhongMaterialData::flags() @ce + and related enum (sets) are deprecated. The flags are no longer stored + directly but rather generated on-the-fly from attribute data, which makes + them less efficient than calling @ref Trade::MaterialData::hasAttribute() + etc. @subsection changelog-latest-compatibility Potential compatibility breakages, removed APIs @@ -160,11 +184,12 @@ See also: - @ref Trade::CameraData constructor not taking an explicit type enum, use @ref Trade::CameraData::CameraData(CameraType, Rad, Float, Float, Float, const void*) instead - - @ref Trade::AbstractMaterialData constructor taking just two parameters - and @ref Trade::PhongMaterialData constructor taking three parameters, - use @ref Trade::AbstractMaterialData::AbstractMaterialData(MaterialType, Flags, MaterialAlphaMode, Float, const void*) - and @ref Trade::PhongMaterialData::PhongMaterialData(Flags, MaterialAlphaMode, Float, Float, const void*) - instead + - @cpp Trade::AbstractMaterialData @ce constructor taking just two + parameters and @ref Trade::PhongMaterialData constructor taking three + parameters, use either the (also deprecated) + @cpp Trade::AbstractMaterialData::AbstractMaterialData(MaterialType, Flags, MaterialAlphaMode, Float, const void*) @ce + and @cpp Trade::PhongMaterialData::PhongMaterialData(Flags, MaterialAlphaMode, Float, Float, const void*) @ce + constructors or directly the new @ref Trade::MaterialData class - All includes of @ref Corrade/Containers/PointerStl.h and @ref Corrade/Containers/ArrayViewStl.h that were added in 2019.01 for preserving backwards compatibility after the move from @ref std::unique_ptr @@ -183,6 +208,16 @@ See also: implement through the redesigned @ref Trade::MaterialData APIs. However these APIs were mainly used to populate the contents in asset importers, so this shouldn't cause any breakages in user code. +- @ref Trade::MaterialData::flags() now calculates the output on-the-fly + based on what attributes are present. This means that arbitrary extra bits + passed to the constructor from @cpp AbstractMaterialData @ce subclasses are + now discarded, and thus subclasses have to override the + @ref Trade::MaterialData::flags() "flags()" function to keep the same + behavior. +- @ref Trade::MaterialData, which the deprecated + @cpp Trade::AbstractMaterialData @ce aliases to, doesn't have a + @cpp virtual @ce destructor as subclasses with extra data members aren't a + desired use case anymore. @section changelog-2020-06 2020.06 @@ -2486,10 +2521,10 @@ Released 2018-10-23, tagged as - `Shaders::VertexColor::Color` is deprecated, use the direct @ref Shaders::VertexColor::Color3 or @ref Shaders::VertexColor::Color4 alternatives instead -- @ref Trade::AbstractMaterialData constructor taking just two parameters +- @cpp Trade::AbstractMaterialData @ce constructor taking just two parameters and @ref Trade::PhongMaterialData constructor taking three parameters are - deprecated, use @ref Trade::AbstractMaterialData::AbstractMaterialData(MaterialType, Flags, MaterialAlphaMode, Float, const void*) - and @ref Trade::PhongMaterialData::PhongMaterialData(Flags, MaterialAlphaMode, Float, Float, const void*) + deprecated, use @cpp Trade::AbstractMaterialData::AbstractMaterialData(MaterialType, Flags, MaterialAlphaMode, Float, const void*) @ce + and @cpp Trade::PhongMaterialData::PhongMaterialData(Flags, MaterialAlphaMode, Float, Float, const void*) @ce instead - @ref Trade::CameraData constructor not taking an explicit type enum is deprecated, use @ref Trade::CameraData::CameraData(CameraType, Rad, Float, Float, Float, const void*) diff --git a/doc/snippets/MagnumTrade.cpp b/doc/snippets/MagnumTrade.cpp index 57dcb68e17..d94679d9f1 100644 --- a/doc/snippets/MagnumTrade.cpp +++ b/doc/snippets/MagnumTrade.cpp @@ -33,6 +33,7 @@ #include "Magnum/Mesh.h" #include "Magnum/PixelFormat.h" #include "Magnum/Animation/Player.h" +#include "Magnum/Math/Color.h" #include "Magnum/Math/Swizzle.h" #include "Magnum/MeshTools/Interleave.h" #include "Magnum/MeshTools/Transform.h" diff --git a/src/Magnum/Trade/AbstractMaterialData.cpp b/src/Magnum/Trade/AbstractMaterialData.cpp deleted file mode 100644 index 0a7a1e3b8c..0000000000 --- a/src/Magnum/Trade/AbstractMaterialData.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/* - This file is part of Magnum. - - Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, - 2020 Vladimír Vondruš - - 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 "AbstractMaterialData.h" - -#include -#include - -namespace Magnum { namespace Trade { - -AbstractMaterialData::AbstractMaterialData(AbstractMaterialData&&) noexcept = default; - -AbstractMaterialData& AbstractMaterialData::operator=(AbstractMaterialData&&) noexcept = default; - -AbstractMaterialData::AbstractMaterialData(const MaterialType type, const Flags flags, const MaterialAlphaMode alphaMode, const Float alphaMask, const void* const importerState) noexcept: _type{type}, _alphaMode{alphaMode}, _flags{flags}, _alphaMask{alphaMask}, _importerState{importerState} {} - -AbstractMaterialData::~AbstractMaterialData() = default; - -Debug& operator<<(Debug& debug, const AbstractMaterialData::Flag value) { - debug << "Trade::AbstractMaterialData::Flag" << Debug::nospace; - - switch(value) { - /* LCOV_EXCL_START */ - #define _c(value) case AbstractMaterialData::Flag::value: return debug << "::" #value; - _c(DoubleSided) - #undef _c - /* LCOV_EXCL_STOP */ - } - - return debug << "(" << Debug::nospace << reinterpret_cast(UnsignedByte(value)) << Debug::nospace << ")"; -} - -Debug& operator<<(Debug& debug, const AbstractMaterialData::Flags value) { - return Containers::enumSetDebugOutput(debug, value, "Trade::AbstractMaterialData::Flags{}", { - AbstractMaterialData::Flag::DoubleSided - }); -} - -Debug& operator<<(Debug& debug, const MaterialAlphaMode value) { - debug << "Trade::MaterialAlphaMode" << Debug::nospace; - - switch(value) { - /* LCOV_EXCL_START */ - #define _c(value) case MaterialAlphaMode::value: return debug << "::" #value; - _c(Opaque) - _c(Mask) - _c(Blend) - #undef _c - /* LCOV_EXCL_STOP */ - } - - return debug << "(" << Debug::nospace << reinterpret_cast(UnsignedByte(value)) << Debug::nospace << ")"; -} - -}} diff --git a/src/Magnum/Trade/AbstractMaterialData.h b/src/Magnum/Trade/AbstractMaterialData.h index b833bf7402..d0dedb11ea 100644 --- a/src/Magnum/Trade/AbstractMaterialData.h +++ b/src/Magnum/Trade/AbstractMaterialData.h @@ -25,143 +25,34 @@ DEALINGS IN THE SOFTWARE. */ +#ifdef MAGNUM_BUILD_DEPRECATED /** @file * @brief Class @ref Magnum::Trade::AbstractMaterialData, enum @ref Magnum::Trade::MaterialType + * @m_deprecated_since_latest Use @ref Magnum/Trade/MaterialData.h and the + * @ref Magnum::Trade::MaterialData "MaterialData" class instead. */ +#endif -#include "Magnum/Trade/MaterialData.h" - -namespace Magnum { namespace Trade { - -/** -@brief Material alpha mode +#include "Magnum/configure.h" -@see @ref AbstractMaterialData::alphaMode(), - @ref AbstractMaterialData::alphaMask() -*/ -enum class MaterialAlphaMode: UnsignedByte { - /** Alpha value is ignored and the rendered output is fully opaque. */ - Opaque, +#ifdef MAGNUM_BUILD_DEPRECATED +#include "Magnum/Trade/MaterialData.h" - /** - * The rendered output is either fully transparent or fully opaque, - * depending on the alpha value and specified - * @ref AbstractMaterialData::alphaMask() value. - */ - Mask, +#ifndef _MAGNUM_NO_DEPRECATED_MESHDATA +CORRADE_DEPRECATED_FILE("use Magnum/Trade/MaterialData.h and the MaterialData class instead") +#endif - /** - * The alpha value is used to combine source and destination colors using - * additive blending. - */ - Blend -}; +namespace Magnum { namespace Trade { /** -@brief Base for material data - -Subclasses provide access to parameters for given material type. +@brief @copybrief MaterialData +@m_deprecated_since_latest Use @ref MaterialData instead. */ -class MAGNUM_TRADE_EXPORT AbstractMaterialData { - public: - /** - * @brief Material flag - * - * This enum is extended in subclasses. - * @see @ref Flags, @ref flags() - */ - enum class Flag: UnsignedShort { - /** - * The material is double-sided. Back faces should not be culled - * away but rendered as well, with normals flipped for correct - * lighting. - */ - DoubleSided = 1 << 0 - }; - - /** - * @brief Material flags - * - * This enum is extended in subclasses. - * @see @ref flags() - */ - typedef Containers::EnumSet Flags; - - virtual ~AbstractMaterialData(); - - /** @brief Copying is not allowed */ - AbstractMaterialData(const AbstractMaterialData&) = delete; - - /** @brief Move constructor */ - AbstractMaterialData(AbstractMaterialData&&) noexcept; - - /** @brief Copying is not allowed */ - AbstractMaterialData& operator=(const AbstractMaterialData&) = delete; - - /** @brief Move assignment */ - AbstractMaterialData& operator=(AbstractMaterialData&&) noexcept; - - /** @brief Material type */ - MaterialType type() const { return _type; } - - /** - * @brief Material flags - * - * Not all bits returned might be defiend by @ref Flag, subclasses - * define extra values. - */ - Flags flags() const { return _flags; } - - /** @brief Alpha mode */ - MaterialAlphaMode alphaMode() const { return _alphaMode; } - - /** - * @brief Alpha mask - * - * If @ref alphaMode() is @ref MaterialAlphaMode::Mask, alpha values - * below this value are rendered as fully transparent and alpha values - * above this value as fully opaque. If @ref alphaMode() is not - * @ref MaterialAlphaMode::Mask, this value is ignored. - */ - Float alphaMask() const { return _alphaMask; } - - /** - * @brief Importer-specific state - * - * See @ref AbstractImporter::importerState() for more information. - */ - const void* importerState() const { return _importerState; } - - protected: - /** - * @brief Constructor - * @param type Material type - * @param flags Untyped material flags - * @param alphaMode Alpha mode. Use - * @ref MaterialAlphaMode::Opaque for a default value. - * @param alphaMask Alpha mask value. Use @cpp 0.5f @ce for a - * default value. - * @param importerState Importer-specific state - */ - explicit AbstractMaterialData(MaterialType type, Flags flags, MaterialAlphaMode alphaMode, Float alphaMask, const void* importerState = nullptr) noexcept; - - private: - MaterialType _type; - MaterialAlphaMode _alphaMode; - Flags _flags; - Float _alphaMask; - const void* _importerState; -}; - -/** @debugoperatorenum{MaterialAlphaMode} */ -MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, MaterialAlphaMode value); - -/** @debugoperatorclassenum{AbstractMaterialData,AbstractMaterialData::Flag} */ -MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, AbstractMaterialData::Flag value); - -/** @debugoperatorclassenum{AbstractMaterialData,AbstractMaterialData::Flags} */ -MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, AbstractMaterialData::Flags value); +typedef CORRADE_DEPRECATED("use MaterialData instead") MaterialData AbstractMaterialData; }} +#else +#error use Magnum/Trade/MaterialData.h and the MaterialData class instead +#endif #endif diff --git a/src/Magnum/Trade/CMakeLists.txt b/src/Magnum/Trade/CMakeLists.txt index 0ce26fe344..e2019cb8fd 100644 --- a/src/Magnum/Trade/CMakeLists.txt +++ b/src/Magnum/Trade/CMakeLists.txt @@ -26,7 +26,6 @@ find_package(Corrade REQUIRED PluginManager) set(MagnumTrade_SRCS - AbstractMaterialData.cpp ArrayAllocator.cpp Data.cpp LightData.cpp @@ -51,7 +50,6 @@ set(MagnumTrade_GracefulAssert_SRCS set(MagnumTrade_HEADERS AbstractImporter.h AbstractImageConverter.h - AbstractMaterialData.h AbstractSceneConverter.h AnimationData.h ArrayAllocator.h @@ -89,6 +87,8 @@ if(MAGNUM_BUILD_DEPRECATED) MeshData2D.cpp MeshData3D.cpp) list(APPEND MagnumTrade_HEADERS + AbstractMaterialData.h + MeshData2D.h MeshData3D.h) endif() diff --git a/src/Magnum/Trade/MaterialData.cpp b/src/Magnum/Trade/MaterialData.cpp index 727a5a4a57..47c65d15a8 100644 --- a/src/Magnum/Trade/MaterialData.cpp +++ b/src/Magnum/Trade/MaterialData.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include "Magnum/Math/Vector4.h" #include "Magnum/Math/Matrix.h" @@ -275,6 +276,33 @@ const void* MaterialData::tryAttribute(const MaterialAttribute name) const { return tryAttribute(string); } +#ifdef MAGNUM_BUILD_DEPRECATED +CORRADE_IGNORE_DEPRECATED_PUSH +MaterialData::Flags MaterialData::flags() const { + Flags flags; + if(isDoubleSided()) + flags |= Flag::DoubleSided; + return flags; +} +CORRADE_IGNORE_DEPRECATED_POP +#endif + +bool MaterialData::isDoubleSided() const { + return attributeOr(MaterialAttribute::DoubleSided, false); +} + +MaterialAlphaMode MaterialData::alphaMode() const { + if(attributeOr(MaterialAttribute::AlphaBlend, false)) + return MaterialAlphaMode::Blend; + if(hasAttribute(MaterialAttribute::AlphaMask)) + return MaterialAlphaMode::Mask; + return MaterialAlphaMode::Opaque; +} + +Float MaterialData::alphaMask() const { + return attributeOr(MaterialAttribute::AlphaMask, 0.5f); +} + Containers::Array MaterialData::release() { return std::move(_data); } @@ -344,4 +372,44 @@ Debug& operator<<(Debug& debug, const MaterialTypes value) { }); } +#ifdef MAGNUM_BUILD_DEPRECATED +CORRADE_IGNORE_DEPRECATED_PUSH +Debug& operator<<(Debug& debug, const MaterialData::Flag value) { + debug << "Trade::MaterialData::Flag" << Debug::nospace; + + switch(value) { + /* LCOV_EXCL_START */ + #define _c(value) case MaterialData::Flag::value: return debug << "::" #value; + _c(DoubleSided) + #undef _c + /* LCOV_EXCL_STOP */ + } + + return debug << "(" << Debug::nospace << reinterpret_cast(UnsignedByte(value)) << Debug::nospace << ")"; +} + +Debug& operator<<(Debug& debug, const MaterialData::Flags value) { + return Containers::enumSetDebugOutput(debug, value, "Trade::MaterialData::Flags{}", { + MaterialData::Flag::DoubleSided + }); +} +CORRADE_IGNORE_DEPRECATED_POP +#endif + +Debug& operator<<(Debug& debug, const MaterialAlphaMode value) { + debug << "Trade::MaterialAlphaMode" << Debug::nospace; + + switch(value) { + /* LCOV_EXCL_START */ + #define _c(value) case MaterialAlphaMode::value: return debug << "::" #value; + _c(Opaque) + _c(Mask) + _c(Blend) + #undef _c + /* LCOV_EXCL_STOP */ + } + + return debug << "(" << Debug::nospace << reinterpret_cast(UnsignedByte(value)) << Debug::nospace << ")"; +} + }} diff --git a/src/Magnum/Trade/MaterialData.h b/src/Magnum/Trade/MaterialData.h index be54b499f9..9ca17cc583 100644 --- a/src/Magnum/Trade/MaterialData.h +++ b/src/Magnum/Trade/MaterialData.h @@ -31,6 +31,7 @@ */ #include +#include #include #include @@ -63,6 +64,8 @@ enum class MaterialAttribute: UnsignedInt { * preferred, however renderers can fall back to alpha-masked rendering. * Alpha values below this value are meant to be rendered as fully * transparent and alpha values above this value as fully opaque. + * @see @ref MaterialAlphaMode, @ref MaterialData::alphaMode(), + * @ref MaterialData::alphaMask() */ AlphaMask = 1, @@ -74,6 +77,7 @@ enum class MaterialAttribute: UnsignedInt { * the material should be treated as opaque. If set together with * @ref MaterialAttribute::AlphaMask, blending is preferred, however * renderers can fall back to alpha-masked rendering. + * @see @ref MaterialAlphaMode, @ref MaterialData::alphaMode() */ AlphaBlend, @@ -81,6 +85,7 @@ enum class MaterialAttribute: UnsignedInt { * Double sided, @ref MaterialAttributeType::Bool. * * If not present, the default value is @cpp false @ce. + * @see @ref MaterialData::isDoubleSided() */ DoubleSided, @@ -89,6 +94,7 @@ enum class MaterialAttribute: UnsignedInt { * * If @ref MaterialAttribute::AmbientTexture is present as well, these two * are multiplied together. + * @see @ref PhongMaterialData::ambientColor() */ AmbientColor, @@ -98,6 +104,7 @@ enum class MaterialAttribute: UnsignedInt { * * If @ref MaterialAttribute::AmbientColor is present as well, these two * are multiplied together. + * @see @ref PhongMaterialData::ambientTexture() */ AmbientTexture, @@ -107,6 +114,7 @@ enum class MaterialAttribute: UnsignedInt { * * Has a precedence over @ref MaterialAttribute::TextureMatrix if both are * present. + * @see @ref PhongMaterialData::ambientTextureMatrix() */ AmbientTextureMatrix, @@ -116,6 +124,7 @@ enum class MaterialAttribute: UnsignedInt { * * Has a precedence over @ref MaterialAttribute::TextureCoordinates if both * are present. + * @see @ref PhongMaterialData::ambientTextureCoordinates() */ AmbientTextureCoordinates, @@ -124,6 +133,7 @@ enum class MaterialAttribute: UnsignedInt { * * If @ref MaterialAttribute::DiffuseTexture is present as well, these two * are multiplied together. + * @see @ref PhongMaterialData::diffuseColor() */ DiffuseColor, @@ -133,6 +143,7 @@ enum class MaterialAttribute: UnsignedInt { * * If @ref MaterialAttribute::DiffuseColor is present as well, these two * are multiplied together. + * @see @ref PhongMaterialData::diffuseTexture() */ DiffuseTexture, @@ -142,6 +153,7 @@ enum class MaterialAttribute: UnsignedInt { * * Has a precedence over @ref MaterialAttribute::TextureMatrix if both are * present. + * @see @ref PhongMaterialData::diffuseTextureMatrix() */ DiffuseTextureMatrix, @@ -151,6 +163,7 @@ enum class MaterialAttribute: UnsignedInt { * * Has a precedence over @ref MaterialAttribute::TextureCoordinates if both * are present. + * @see @ref PhongMaterialData::diffuseTextureCoordinates() */ DiffuseTextureCoordinates, @@ -159,6 +172,7 @@ enum class MaterialAttribute: UnsignedInt { * * If @ref MaterialAttribute::SpecularTexture is present as well, these two * are multiplied together. + * @see @ref PhongMaterialData::specularColor() */ SpecularColor, @@ -168,6 +182,7 @@ enum class MaterialAttribute: UnsignedInt { * * If @ref MaterialAttribute::SpecularColor is present as well, these two * are multiplied together. + * @see @ref PhongMaterialData::specularTexture() */ SpecularTexture, @@ -177,6 +192,7 @@ enum class MaterialAttribute: UnsignedInt { * * Has a precedence over @ref MaterialAttribute::TextureMatrix if both are * present. + * @see @ref PhongMaterialData::specularTextureMatrix() */ SpecularTextureMatrix, @@ -186,17 +202,21 @@ enum class MaterialAttribute: UnsignedInt { * * Has a precedence over @ref MaterialAttribute::TextureCoordinates if both * are present. + * @see @ref PhongMaterialData::specularTextureCoordinates() */ SpecularTextureCoordinates, /** * Shininess value for Phong materials, @ref MaterialAttributeType::Float. + * + * @see @ref PhongMaterialData::shininess() */ Shininess, /** * Tangent-space normal map texture index, * @ref MaterialAttributeType::UnsignedInt. + * @see @ref PhongMaterialData::normalTexture() */ NormalTexture, @@ -206,6 +226,7 @@ enum class MaterialAttribute: UnsignedInt { * * Has a precedence over @ref MaterialAttribute::TextureMatrix if both are * present. + * @see @ref PhongMaterialData::normalTextureMatrix() */ NormalTextureMatrix, @@ -215,6 +236,7 @@ enum class MaterialAttribute: UnsignedInt { * * Has a precedence over @ref MaterialAttribute::TextureCoordinates if both * are present. + * @see @ref PhongMaterialData::normalTextureCoordinates() */ NormalTextureCoordinates, @@ -227,6 +249,7 @@ enum class MaterialAttribute: UnsignedInt { * @ref MaterialAttribute::SpecularTextureMatrix / * @ref MaterialAttribute::NormalTextureMatrix have a precedence over this * attribute for given texture, if present. + * @see @ref PhongMaterialData::textureMatrix() */ TextureMatrix, @@ -237,8 +260,9 @@ enum class MaterialAttribute: UnsignedInt { * @ref MaterialAttribute::AmbientTextureCoordinates / * @ref MaterialAttribute::DiffuseTextureCoordinates / * @ref MaterialAttribute::SpecularTextureCoordinates / - * @ref MaterialAttribute::NormalTextureCoordinates have a precedence over - * this attribute for given texture, if present. + * @ref MaterialAttribute::NormalTextureCoordinates have a precedence + * over this attribute for given texture, if present. + * @see @ref PhongMaterialData::textureCoordinates() */ TextureCoordinates, }; @@ -510,11 +534,13 @@ class MAGNUM_TRADE_EXPORT MaterialAttributeData { /** @brief Material type -@see @ref MaterialTypes, @ref MaterialData::types(), - @ref AbstractMaterialData::type() +@see @ref MaterialTypes, @ref MaterialData::types() */ enum class MaterialType: UnsignedInt { - Phong = 1 << 0 /**< Phong shading */ + /** + * Phong. Use @ref PhongMaterialData for convenience attribute access. + */ + Phong = 1 << 0 }; /** @debugoperatorenum{MaterialType} */ @@ -533,6 +559,34 @@ CORRADE_ENUMSET_OPERATORS(MaterialTypes) /** @debugoperatorenum{MaterialTypes} */ MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, MaterialTypes value); +/** +@brief Material alpha mode + +Convenience access to @ref MaterialAttribute::AlphaBlend and +@ref MaterialAttribute::AlphaMask attributes. +@see @ref MaterialData::alphaMode(), @ref MaterialData::alphaMask() +*/ +enum class MaterialAlphaMode: UnsignedByte { + /** Alpha value is ignored and the rendered output is fully opaque. */ + Opaque, + + /** + * The rendered output is either fully transparent or fully opaque, + * depending on the alpha value and specified + * @ref MaterialData::alphaMask() value. + */ + Mask, + + /** + * The alpha value is used to combine source and destination colors using + * additive blending. + */ + Blend +}; + +/** @debugoperatorenum{MaterialAlphaMode} */ +MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, MaterialAlphaMode value); + /** @brief Material data @m_since_latest @@ -567,6 +621,41 @@ precision. */ class MAGNUM_TRADE_EXPORT MaterialData { public: + #ifdef MAGNUM_BUILD_DEPRECATED + /** + * @brief Material flag + * @m_deprecated_since_latest The flags are no longer stored directly + * but generated on-the-fly from attribute data, which makes them + * less efficient than calling @ref hasAttribute(), + * @ref isDoubleSided() etc. + * + * This enum is further extended in subclasses. + * @see @ref Flags, @ref flags() + */ + enum class CORRADE_DEPRECATED_ENUM("use hasAttribute() etc. instead") Flag: UnsignedInt { + /** + * The material is double-sided. Back faces should not be culled + * away but rendered as well, with normals flipped for correct + * lighting. + */ + DoubleSided = 1 << 0 + }; + + /** + * @brief Material flags + * @m_deprecated_since_latest The flags are no longer stored directly + * but generated on-the-fly from attribute data, which makes them + * less efficient than calling @ref hasAttribute(), + * @ref isDoubleSided() etc. + * + * This enum is extended in subclasses. + * @see @ref flags() + */ + CORRADE_IGNORE_DEPRECATED_PUSH /* GCC warns about Flag, ugh */ + typedef CORRADE_DEPRECATED("use hasAttribute() etc. instead") Containers::EnumSet Flags; + CORRADE_IGNORE_DEPRECATED_POP + #endif + /** * @brief Construct * @param types Which material types are described by this @@ -618,6 +707,16 @@ class MAGNUM_TRADE_EXPORT MaterialData { */ MaterialTypes types() const { return _types; } + #ifdef MAGNUM_BUILD_DEPRECATED + /** + * @brief Material type + * @m_deprecated_since_latest Use @ref types() instead. + */ + CORRADE_DEPRECATED("use types() instead") MaterialType type() const { + return MaterialType(UnsignedInt(_types & MaterialType::Phong)); + } + #endif + /** * @brief Raw attribute data * @@ -744,6 +843,47 @@ class MAGNUM_TRADE_EXPORT MaterialData { template T attributeOr(Containers::StringView name, const T& defaultValue) const; template T attributeOr(MaterialAttribute name, const T& defaultValue) const; /**< @overload */ + /** + * @brief Whether a material is double-sided + * + * Convenience access to the @ref MaterialAttribute::DoubleSided + * attribute. If not present, the default is @cpp false @ce. + */ + bool isDoubleSided() const; + + #ifdef MAGNUM_BUILD_DEPRECATED + /** + * @brief Material flags + * @m_deprecated_since_latest The flags are no longer stored directly + * but generated on-the-fly from attribute data, which makes them + * less efficient than calling @ref hasAttribute(), + * @ref isDoubleSided() etc. + * + * Not all bits returned might be defined by @ref Flag, subclasses may + * define extra values. + */ + CORRADE_IGNORE_DEPRECATED_PUSH /* GCC warns about Flags, ugh */ + CORRADE_DEPRECATED("use hasAttribute() instead") Flags flags() const; + CORRADE_IGNORE_DEPRECATED_POP + #endif + + /** + * @brief Alpha mode + * + * Convenience access to @ref MaterialAttribute::AlphaBlend and + * @ref MaterialAttribute::AlphaMask attributes. If neither is present, + * the default is @ref MaterialAlphaMode::Opaque. + */ + MaterialAlphaMode alphaMode() const; + + /** + * @brief Alpha mask + * + * Convenience access to the @ref MaterialAttribute::AlphaMask + * attribute. If not present, the default is @cpp 0.5f @ce. + */ + Float alphaMask() const; + /** * @brief Release data storage * @@ -773,6 +913,32 @@ class MAGNUM_TRADE_EXPORT MaterialData { const void* _importerState; }; +#ifdef MAGNUM_BUILD_DEPRECATED +CORRADE_IGNORE_DEPRECATED_PUSH +CORRADE_ENUMSET_OPERATORS(MaterialData::Flags) + +/** +@debugoperatorclassenum{MaterialData,MaterialData::Flag} +@m_deprecated_since_latest The flags are no longer stored directly but + generated on-the-fly from attribute data, which makes them less efficient + than calling @ref MaterialData::hasAttribute(), + @ref MaterialData::isDoubleSided() etc. +*/ +/* Not marked with CORRADE_DEPRECATED() as there's enough warnings already */ +MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, MaterialData::Flag value); + +/** +@debugoperatorclassenum{MaterialData,MaterialData::Flags} +@m_deprecated_since_latest The flags are no longer stored directly but + generated on-the-fly from attribute data, which makes them less efficient + than calling @ref MaterialData::hasAttribute(), + @ref MaterialData::isDoubleSided() etc. +*/ +/* Not marked with CORRADE_DEPRECATED() as there's enough warnings already */ +MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, MaterialData::Flags value); +CORRADE_IGNORE_DEPRECATED_POP +#endif + namespace Implementation { /* LCOV_EXCL_START */ template struct MaterialAttributeTypeFor { diff --git a/src/Magnum/Trade/PhongMaterialData.cpp b/src/Magnum/Trade/PhongMaterialData.cpp index c5c920f782..bf0b48915f 100644 --- a/src/Magnum/Trade/PhongMaterialData.cpp +++ b/src/Magnum/Trade/PhongMaterialData.cpp @@ -27,92 +27,228 @@ #include "PhongMaterialData.h" #include +#ifdef MAGNUM_BUILD_DEPRECATED +#include +#endif + +#include "Magnum/Math/Color.h" +#include "Magnum/Math/Matrix3.h" namespace Magnum { namespace Trade { using namespace Math::Literals; -PhongMaterialData::PhongMaterialData(const Flags flags, const Color4& ambientColor, const UnsignedInt ambientTexture, const UnsignedInt ambientTextureCoordinates, const Color4& diffuseColor, const UnsignedInt diffuseTexture, const UnsignedInt diffuseTextureCoordinates, const Color4& specularColor, const UnsignedInt specularTexture, const UnsignedInt specularTextureCoordinates, const UnsignedInt normalTexture, const UnsignedInt normalTextureCoordinates, const Matrix3& textureMatrix, const MaterialAlphaMode alphaMode, const Float alphaMask, const Float shininess, const void* const importerState) noexcept: AbstractMaterialData{MaterialType::Phong, AbstractMaterialData::Flag(UnsignedShort(flags)), alphaMode, alphaMask, importerState}, _ambientColor{ambientColor}, _diffuseColor{diffuseColor}, _specularColor{specularColor}, _shininess{shininess} { +#ifdef MAGNUM_BUILD_DEPRECATED +CORRADE_IGNORE_DEPRECATED_PUSH +PhongMaterialData::PhongMaterialData(const Flags flags, const Color4& ambientColor, const UnsignedInt ambientTexture, const UnsignedInt ambientTextureCoordinates, const Color4& diffuseColor, const UnsignedInt diffuseTexture, const UnsignedInt diffuseTextureCoordinates, const Color4& specularColor, const UnsignedInt specularTexture, const UnsignedInt specularTextureCoordinates, const UnsignedInt normalTexture, const UnsignedInt normalTextureCoordinates, const Matrix3& textureMatrix, const MaterialAlphaMode alphaMode, const Float alphaMask, const Float shininess, const void* const importerState) noexcept: MaterialData{MaterialType::Phong, [&](){ + Containers::Array data; + + if(flags & Flag::DoubleSided) + arrayAppend(data, Containers::InPlaceInit, MaterialAttribute::DoubleSided, true); + if(alphaMode == MaterialAlphaMode::Blend) + arrayAppend(data, Containers::InPlaceInit, MaterialAttribute::AlphaBlend, true); + /* Include a mask also if it has a non-default value to stay compatible + with existing behavior */ + if(alphaMode == MaterialAlphaMode::Mask || alphaMask != 0.0f) + arrayAppend(data, Containers::InPlaceInit, MaterialAttribute::AlphaMask, alphaMask); + CORRADE_ASSERT(!(flags & Flag::TextureTransformation) || (flags & (Flag::AmbientTexture|Flag::DiffuseTexture|Flag::SpecularTexture|Flag::NormalTexture)), - "Trade::PhongMaterialData: texture transformation enabled but the material has no textures", ); + "Trade::PhongMaterialData: texture transformation enabled but the material has no textures", data); CORRADE_ASSERT((flags & Flag::TextureTransformation) || textureMatrix == Matrix3{}, - "PhongMaterialData::PhongMaterialData: non-default texture matrix requires Flag::TextureTransformation to be enabled", ); - CORRADE_ASSERT((flags & Flag::TextureCoordinates) || (ambientTextureCoordinates == 0 && diffuseTextureCoordinates == 0 && specularTextureCoordinates == 0 && normalTextureCoordinates == 0), - "PhongMaterialData::PhongMaterialData: non-zero texture coordinate sets require Flag::TextureCoordinates to be enabled", ); + "PhongMaterialData::PhongMaterialData: non-default texture matrix requires Flag::TextureTransformation to be enabled", data); + CORRADE_ASSERT((flags & Flag::TextureCoordinateSets) || (ambientTextureCoordinates == 0 && diffuseTextureCoordinates == 0 && specularTextureCoordinates == 0 && normalTextureCoordinates == 0), + "PhongMaterialData::PhongMaterialData: non-zero texture coordinate sets require Flag::TextureCoordinates to be enabled", data); + arrayAppend(data, Containers::InPlaceInit, MaterialAttribute::AmbientColor, ambientColor); if(flags & Flag::AmbientTexture) { - _ambientTexture = ambientTexture; - _ambientTextureCoordinates = ambientTextureCoordinates; + arrayAppend(data, Containers::InPlaceInit, MaterialAttribute::AmbientTexture, ambientTexture); + if(ambientTextureCoordinates) + arrayAppend(data, Containers::InPlaceInit, MaterialAttribute::AmbientTextureCoordinates, ambientTextureCoordinates); } + + arrayAppend(data, Containers::InPlaceInit, MaterialAttribute::DiffuseColor, diffuseColor); if(flags & Flag::DiffuseTexture) { - _diffuseTexture = diffuseTexture; - _diffuseTextureCoordinates = diffuseTextureCoordinates; + arrayAppend(data, Containers::InPlaceInit, MaterialAttribute::DiffuseTexture, diffuseTexture); + if(diffuseTextureCoordinates) + arrayAppend(data, Containers::InPlaceInit, MaterialAttribute::DiffuseTextureCoordinates, diffuseTextureCoordinates); } + + arrayAppend(data, Containers::InPlaceInit, MaterialAttribute::SpecularColor, specularColor); if(flags & Flag::SpecularTexture) { - _specularTexture = specularTexture; - _specularTextureCoordinates = specularTextureCoordinates; + arrayAppend(data, Containers::InPlaceInit, MaterialAttribute::SpecularTexture, specularTexture); + if(specularTextureCoordinates) + arrayAppend(data, Containers::InPlaceInit, MaterialAttribute::SpecularTextureCoordinates, specularTextureCoordinates); } + if(flags & Flag::NormalTexture) { - _normalTexture = normalTexture; - _normalTextureCoordinates = normalTextureCoordinates; + arrayAppend(data, Containers::InPlaceInit, MaterialAttribute::NormalTexture, normalTexture); + if(normalTextureCoordinates) + arrayAppend(data, Containers::InPlaceInit, MaterialAttribute::NormalTextureCoordinates, normalTextureCoordinates); } + if(flags & Flag::TextureTransformation) - _textureMatrix = textureMatrix; + arrayAppend(data, Containers::InPlaceInit, MaterialAttribute::TextureMatrix, textureMatrix); + + arrayAppend(data, Containers::InPlaceInit, MaterialAttribute::Shininess, shininess); + + return data; +}(), importerState} { + /* The data can't be filled here because it won't be sorted correctly */ } PhongMaterialData::PhongMaterialData(const Flags flags, const Color4& ambientColor, const UnsignedInt ambientTexture, const Color4& diffuseColor, const UnsignedInt diffuseTexture, const Color4& specularColor, const UnsignedInt specularTexture, const UnsignedInt normalTexture, const Matrix3& textureMatrix, const MaterialAlphaMode alphaMode, const Float alphaMask, const Float shininess, const void* const importerState) noexcept: PhongMaterialData{flags, ambientColor, ambientTexture, 0, diffuseColor, diffuseTexture, 0, specularColor, specularTexture, 0, normalTexture, 0, textureMatrix, alphaMode, alphaMask, shininess, importerState} {} -#ifdef MAGNUM_BUILD_DEPRECATED PhongMaterialData::PhongMaterialData(const Flags flags, const MaterialAlphaMode alphaMode, const Float alphaMask, const Float shininess, const void* const importerState) noexcept: PhongMaterialData{flags, 0x000000ff_rgbaf, {}, 0xffffffff_rgbaf, {}, 0xffffffff_rgbaf, {}, {}, {}, alphaMode, alphaMask, shininess, importerState} {} +CORRADE_IGNORE_DEPRECATED_POP +#endif + +#ifdef MAGNUM_BUILD_DEPRECATED +CORRADE_IGNORE_DEPRECATED_PUSH +PhongMaterialData::Flags PhongMaterialData::flags() const { + /* "Append" to flags recognized by the base class */ + Flags flags{Flag(UnsignedInt(MaterialData::flags()))}; + + if(hasAttribute(MaterialAttribute::AmbientTexture)) + flags |= Flag::AmbientTexture; + if(hasAttribute(MaterialAttribute::DiffuseTexture)) + flags |= Flag::DiffuseTexture; + if(hasAttribute(MaterialAttribute::SpecularTexture)) + flags |= Flag::SpecularTexture; + if(hasAttribute(MaterialAttribute::NormalTexture)) + flags |= Flag::NormalTexture; + if(hasTextureTransformation()) + flags |= Flag::TextureTransformation; + if(hasTextureCoordinates()) + flags |= Flag::TextureCoordinates; + + return flags; +} +CORRADE_IGNORE_DEPRECATED_POP #endif -PhongMaterialData::PhongMaterialData(PhongMaterialData&& other) noexcept = default; +bool PhongMaterialData::hasTextureTransformation() const { + return hasAttribute(MaterialAttribute::AmbientTextureMatrix) || + hasAttribute(MaterialAttribute::DiffuseTextureMatrix) || + hasAttribute(MaterialAttribute::SpecularTextureMatrix) || + hasAttribute(MaterialAttribute::NormalTextureMatrix) || + hasAttribute(MaterialAttribute::TextureMatrix); +} + +bool PhongMaterialData::hasTextureCoordinates() const { + return attributeOr(MaterialAttribute::AmbientTextureCoordinates, 0u) || + attributeOr(MaterialAttribute::DiffuseTextureCoordinates, 0u) || + attributeOr(MaterialAttribute::SpecularTextureCoordinates, 0u) || + attributeOr(MaterialAttribute::NormalTextureCoordinates, 0u) || + attributeOr(MaterialAttribute::TextureCoordinates, 0u); +} -PhongMaterialData& PhongMaterialData::operator=(PhongMaterialData&& other) noexcept = default; +Color4 PhongMaterialData::ambientColor() const { + return attributeOr(MaterialAttribute::AmbientColor, + hasAttribute(MaterialAttribute::AmbientTexture) ? 0xffffffff_rgbaf : 0x000000ff_rgbaf); +} UnsignedInt PhongMaterialData::ambientTexture() const { - CORRADE_ASSERT(flags() & Flag::AmbientTexture, "Trade::PhongMaterialData::ambientTexture(): the material doesn't have an ambient texture", {}); - return _ambientTexture; + return attribute(MaterialAttribute::AmbientTexture); } +Matrix3 PhongMaterialData::ambientTextureMatrix() const { + CORRADE_ASSERT(hasAttribute(MaterialAttribute::AmbientTexture), + "Trade::PhongMaterialData::ambientTextureMatrix(): the material doesn't have an ambient texture", {}); + if(Containers::Optional set = tryAttribute(MaterialAttribute::AmbientTextureMatrix)) + return *set; + return attributeOr(MaterialAttribute::TextureMatrix, Matrix3{}); +} UnsignedInt PhongMaterialData::ambientTextureCoordinates() const { - CORRADE_ASSERT(flags() & Flag::AmbientTexture, "Trade::PhongMaterialData::ambientTextureCoordinates(): the material doesn't have an ambient texture", {}); - return _ambientTextureCoordinates; + CORRADE_ASSERT(hasAttribute(MaterialAttribute::AmbientTexture), + "Trade::PhongMaterialData::ambientTextureCoordinates(): the material doesn't have an ambient texture", {}); + if(Containers::Optional set = tryAttribute(MaterialAttribute::AmbientTextureCoordinates)) + return *set; + return attributeOr(MaterialAttribute::TextureCoordinates, 0u); +} + +Color4 PhongMaterialData::diffuseColor() const { + return attributeOr(MaterialAttribute::DiffuseColor, 0xffffffff_rgbaf); } UnsignedInt PhongMaterialData::diffuseTexture() const { - CORRADE_ASSERT(flags() & Flag::DiffuseTexture, "Trade::PhongMaterialData::diffuseTexture(): the material doesn't have a diffuse texture", {}); - return _diffuseTexture; + return attribute(MaterialAttribute::DiffuseTexture); } +Matrix3 PhongMaterialData::diffuseTextureMatrix() const { + CORRADE_ASSERT(hasAttribute(MaterialAttribute::DiffuseTexture), + "Trade::PhongMaterialData::diffuseTextureMatrix(): the material doesn't have a diffuse texture", {}); + if(Containers::Optional set = tryAttribute(MaterialAttribute::DiffuseTextureMatrix)) + return *set; + return attributeOr(MaterialAttribute::TextureMatrix, Matrix3{}); +} UnsignedInt PhongMaterialData::diffuseTextureCoordinates() const { - CORRADE_ASSERT(flags() & Flag::DiffuseTexture, "Trade::PhongMaterialData::diffuseTextureCoordinates(): the material doesn't have a diffuse texture", {}); - return _diffuseTextureCoordinates; + CORRADE_ASSERT(hasAttribute(MaterialAttribute::DiffuseTexture), + "Trade::PhongMaterialData::diffuseTextureCoordinates(): the material doesn't have a diffuse texture", {}); + if(Containers::Optional set = tryAttribute(MaterialAttribute::DiffuseTextureCoordinates)) + return *set; + return attributeOr(MaterialAttribute::TextureCoordinates, 0u); +} + +Color4 PhongMaterialData::specularColor() const { + return attributeOr(MaterialAttribute::SpecularColor, 0xffffffff_rgbaf); } UnsignedInt PhongMaterialData::specularTexture() const { - CORRADE_ASSERT(flags() & Flag::SpecularTexture, "Trade::PhongMaterialData::specularTexture(): the material doesn't have a specular texture", {}); - return _specularTexture; + return attribute(MaterialAttribute::SpecularTexture); } +Matrix3 PhongMaterialData::specularTextureMatrix() const { + CORRADE_ASSERT(hasAttribute(MaterialAttribute::SpecularTexture), + "Trade::PhongMaterialData::specularTextureMatrix(): the material doesn't have a specular texture", {}); + if(Containers::Optional set = tryAttribute(MaterialAttribute::SpecularTextureMatrix)) + return *set; + return attributeOr(MaterialAttribute::TextureMatrix, Matrix3{}); +} UnsignedInt PhongMaterialData::specularTextureCoordinates() const { - CORRADE_ASSERT(flags() & Flag::SpecularTexture, "Trade::PhongMaterialData::specularTextureCoordinates(): the material doesn't have a specular texture", {}); - return _specularTextureCoordinates; + CORRADE_ASSERT(hasAttribute(MaterialAttribute::SpecularTexture), + "Trade::PhongMaterialData::specularTextureCoordinates(): the material doesn't have a specular texture", {}); + if(Containers::Optional set = tryAttribute(MaterialAttribute::SpecularTextureCoordinates)) + return *set; + return attributeOr(MaterialAttribute::TextureCoordinates, 0u); } UnsignedInt PhongMaterialData::normalTexture() const { - CORRADE_ASSERT(flags() & Flag::NormalTexture, "Trade::PhongMaterialData::normalTexture(): the material doesn't have a normal texture", {}); - return _normalTexture; + return attribute(MaterialAttribute::NormalTexture); +} + +Matrix3 PhongMaterialData::normalTextureMatrix() const { + CORRADE_ASSERT(hasAttribute(MaterialAttribute::NormalTexture), + "Trade::PhongMaterialData::normalTextureMatrix(): the material doesn't have a normal texture", {}); + if(Containers::Optional set = tryAttribute(MaterialAttribute::NormalTextureMatrix)) + return *set; + return attributeOr(MaterialAttribute::TextureMatrix, Matrix3{}); } UnsignedInt PhongMaterialData::normalTextureCoordinates() const { - CORRADE_ASSERT(flags() & Flag::NormalTexture, "Trade::PhongMaterialData::normalTextureCoordinates(): the material doesn't have a normal texture", {}); - return _normalTextureCoordinates; + CORRADE_ASSERT(hasAttribute(MaterialAttribute::NormalTexture), + "Trade::PhongMaterialData::normalTextureCoordinates(): the material doesn't have a normal texture", {}); + if(Containers::Optional set = tryAttribute(MaterialAttribute::NormalTextureCoordinates)) + return *set; + return attributeOr(MaterialAttribute::TextureCoordinates, 0u); +} + +Matrix3 PhongMaterialData::textureMatrix() const { + return attributeOr(MaterialAttribute::TextureMatrix, Matrix3{}); +} + +UnsignedInt PhongMaterialData::textureCoordinates() const { + return attributeOr(MaterialAttribute::TextureCoordinates, 0u); +} + +Float PhongMaterialData::shininess() const { + return attributeOr(MaterialAttribute::Shininess, 80.0f); } +#ifdef MAGNUM_BUILD_DEPRECATED +CORRADE_IGNORE_DEPRECATED_PUSH Debug& operator<<(Debug& debug, const PhongMaterialData::Flag value) { debug << "Trade::PhongMaterialData::Flag" << Debug::nospace; @@ -143,5 +279,7 @@ Debug& operator<<(Debug& debug, const PhongMaterialData::Flags value) { PhongMaterialData::Flag::TextureTransformation, PhongMaterialData::Flag::TextureCoordinates}); } +CORRADE_IGNORE_DEPRECATED_POP +#endif }} diff --git a/src/Magnum/Trade/PhongMaterialData.h b/src/Magnum/Trade/PhongMaterialData.h index abeccc3cbb..d7d8cce3f9 100644 --- a/src/Magnum/Trade/PhongMaterialData.h +++ b/src/Magnum/Trade/PhongMaterialData.h @@ -30,10 +30,7 @@ * @brief Class @ref Magnum::Trade::PhongMaterialData */ -#include "Magnum/Magnum.h" -#include "Magnum/Math/Color.h" -#include "Magnum/Math/Matrix3.h" -#include "Magnum/Trade/AbstractMaterialData.h" +#include "Magnum/Trade/MaterialData.h" namespace Magnum { namespace Trade { @@ -42,16 +39,22 @@ namespace Magnum { namespace Trade { @see @ref AbstractImporter::material() */ -class MAGNUM_TRADE_EXPORT PhongMaterialData: public AbstractMaterialData { +class MAGNUM_TRADE_EXPORT PhongMaterialData: public MaterialData { public: + #ifdef MAGNUM_BUILD_DEPRECATED /** * @brief Material flag + * @m_deprecated_since_latest The flags are no longer stored directly + * but generated on-the-fly from attribute data, which makes them + * less efficient than calling @ref hasAttribute(), + * @ref isDoubleSided(), @ref hasTextureTransformation(), + * @ref hasTextureCoordinates() etc. * - * A superset of @ref AbstractMaterialData::Flag. + * A superset of @ref MaterialData::Flag. * @see @ref Flags, @ref flags() */ - enum class Flag: UnsignedShort { - /** @copydoc AbstractMaterialData::Flag::DoubleSided */ + enum class CORRADE_DEPRECATED_ENUM("use hasAttribute() etc. instead") Flag: UnsignedInt { + /** @copydoc MaterialData::Flag::DoubleSided */ DoubleSided = 1 << 0, /** The material has an ambient texture */ @@ -93,12 +96,30 @@ class MAGNUM_TRADE_EXPORT PhongMaterialData: public AbstractMaterialData { /** * @brief Material flags + * @m_deprecated_since_latest The flags are no longer stored directly + * but generated on-the-fly from attribute data, which makes them + * less efficient than calling @ref hasAttribute(), + * @ref isDoubleSided(), @ref hasTextureTransformation(), + * @ref hasTextureCoordinates() etc. * - * A superset of @ref AbstractMaterialData::Flags. + * A superset of @ref MaterialData::Flags. * @see @ref flags() */ - typedef Containers::EnumSet Flags; + CORRADE_IGNORE_DEPRECATED_PUSH /* GCC warns about Flags, ugh */ + typedef CORRADE_DEPRECATED("use hasAttribute() etc. instead") Containers::EnumSet Flags; + CORRADE_IGNORE_DEPRECATED_POP + #endif + #ifndef DOXYGEN_GENERATING_OUTPUT + /* Allow constructing subclasses directly. While not used in the + general Importer workflow, it allows users to create instances with + desired convenience APIs easier (and simplifies testing). It's + however hidden from the docs as constructing instances this way + isn't really common and it would add a lot of noise. */ + using MaterialData::MaterialData; + #endif + + #ifdef MAGNUM_BUILD_DEPRECATED /** * @brief Constructor * @param flags Material flags @@ -107,12 +128,12 @@ class MAGNUM_TRADE_EXPORT PhongMaterialData: public AbstractMaterialData { * non-textured material and @cpp 0xffffffff_rgbaf @ce for a * default value for a textured material. * @param ambientTexture Ambient texture ID. Ignored if @p flags - * doesn't have @ref Flag::AmbientTexture + * doesn't have @ref Flag::AmbientTexture. * @param diffuseColor Diffuse color. Use * @cpp 0xffffffff_rgbaf @ce for a default value for both a * non-textured and a textured material. * @param diffuseTexture Diffuse texture ID. Ignored if @p flags - * doesn't have @ref Flag::DiffuseTexture + * doesn't have @ref Flag::DiffuseTexture. * @param specularColor Specular color. Use * @cpp 0xffffff00_rgbaf @ce for a default value for both a * non-textured and a textured material. @@ -128,13 +149,19 @@ class MAGNUM_TRADE_EXPORT PhongMaterialData: public AbstractMaterialData { * @param shininess Shininess. Use @cpp 80.0f @ce for a default * value. * @param importerState Importer-specific state - * @m_since{2020,06} * * All `*TextureCoordinates()` accessors are implicitly zero with this * constructor. If @p textureMatrix is not default-constructed, expects * @ref Flag::TextureTransformation to be enabled as well. + * + * @m_deprecated_since_latest Populate a @ref MaterialData instance + * using @ref MaterialData::MaterialData(MaterialTypes, Containers::Array&&, const void*) + * instead. This class is not meant to be constructed directly + * anymore. */ - explicit PhongMaterialData(Flags flags, const Color4& ambientColor, UnsignedInt ambientTexture, const Color4& diffuseColor, UnsignedInt diffuseTexture, const Color4& specularColor, UnsignedInt specularTexture, UnsignedInt normalTexture, const Matrix3& textureMatrix, MaterialAlphaMode alphaMode, Float alphaMask, Float shininess, const void* importerState = nullptr) noexcept; + CORRADE_IGNORE_DEPRECATED_PUSH /* GCC warns about Flags, ugh */ + explicit CORRADE_DEPRECATED("use MaterialData::MaterialData(MaterialTypes, Containers::Array&&, const void*) instead") PhongMaterialData(Flags flags, const Color4& ambientColor, UnsignedInt ambientTexture, const Color4& diffuseColor, UnsignedInt diffuseTexture, const Color4& specularColor, UnsignedInt specularTexture, UnsignedInt normalTexture, const Matrix3& textureMatrix, MaterialAlphaMode alphaMode, Float alphaMask, Float shininess, const void* importerState = nullptr) noexcept; + CORRADE_IGNORE_DEPRECATED_POP /** * @brief Construct with non-zero texture coordinate sets @@ -173,69 +200,122 @@ class MAGNUM_TRADE_EXPORT PhongMaterialData: public AbstractMaterialData { * @param shininess Shininess. Use @cpp 80.0f @ce for a * default value. * @param importerState Importer-specific state - * @m_since{2020,06} * * If @p textureMatrix is not default-constructed, expects * @ref Flag::TextureTransformation to be enabled as well. If any * `*Coordinates` parameter is non-zero, expects * @ref Flag::TextureCoordinates to be enabled as well. + * + * @m_deprecated_since_latest Populate a @ref MaterialData instance + * using @ref MaterialData::MaterialData(MaterialTypes, Containers::Array&&, const void*) + * instead. This class is not meant to be constructed directly + * anymore. */ - explicit PhongMaterialData(Flags flags, const Color4& ambientColor, UnsignedInt ambientTexture, UnsignedInt ambientTextureCoordinates, const Color4& diffuseColor, UnsignedInt diffuseTexture, UnsignedInt diffuseTextureCoordinates, const Color4& specularColor, UnsignedInt specularTexture, UnsignedInt specularTextureCoordinates, UnsignedInt normalTexture, UnsignedInt normalTextureCoordinates, const Matrix3& textureMatrix, MaterialAlphaMode alphaMode, Float alphaMask, Float shininess, const void* importerState = nullptr) noexcept; + CORRADE_IGNORE_DEPRECATED_PUSH /* GCC warns about Flags, ugh */ + explicit CORRADE_DEPRECATED("use MaterialData::MaterialData(MaterialTypes, Containers::Array&&, const void*) instead") PhongMaterialData(Flags flags, const Color4& ambientColor, UnsignedInt ambientTexture, UnsignedInt ambientTextureCoordinates, const Color4& diffuseColor, UnsignedInt diffuseTexture, UnsignedInt diffuseTextureCoordinates, const Color4& specularColor, UnsignedInt specularTexture, UnsignedInt specularTextureCoordinates, UnsignedInt normalTexture, UnsignedInt normalTextureCoordinates, const Matrix3& textureMatrix, MaterialAlphaMode alphaMode, Float alphaMask, Float shininess, const void* importerState = nullptr) noexcept; + CORRADE_IGNORE_DEPRECATED_POP - #ifdef MAGNUM_BUILD_DEPRECATED /** * @brief Constructor - * @m_deprecated_since{2020,06} Use @ref PhongMaterialData(Flags, const Color4&, UnsignedInt, const Color4&, UnsignedInt, const Color4&, UnsignedInt, UnsignedInt, const Matrix3&, MaterialAlphaMode, Float, Float, const void*) - * instead. + * @m_deprecated_since{2020,06} Populate a @ref MaterialData instance + * using @ref MaterialData::MaterialData(MaterialTypes, Containers::Array&&, const void*) + * instead. This class is not meant to be constructed directly + * anymore. */ - explicit CORRADE_DEPRECATED("use PhongMaterialData(Flags, const Color4&, UnsignedInt, const Color4&, UnsignedInt, const Color4&, UnsignedInt, UnsignedInt, const Matrix3&, MaterialAlphaMode, Float, Float, const void*) instead") PhongMaterialData(Flags flags, MaterialAlphaMode alphaMode, Float alphaMask, Float shininess, const void* importerState = nullptr) noexcept; + CORRADE_IGNORE_DEPRECATED_PUSH /* GCC warns about Flags, ugh */ + explicit CORRADE_DEPRECATED("use MaterialData::MaterialData(MaterialTypes, Containers::Array&&, const void*) instead") PhongMaterialData(Flags flags, MaterialAlphaMode alphaMode, Float alphaMask, Float shininess, const void* importerState = nullptr) noexcept; + CORRADE_IGNORE_DEPRECATED_POP #endif - /** @brief Copying is not allowed */ - PhongMaterialData(const PhongMaterialData&) = delete; - - /** @brief Move constructor */ - PhongMaterialData(PhongMaterialData&& other) noexcept; - - /** @brief Copying is not allowed */ - PhongMaterialData& operator=(const PhongMaterialData&) = delete; + /** + * @brief Whether the material has texture transformation + * @m_since_latest + * + * Returns @cpp true @ce if any of the + * @ref MaterialAttribute::AmbientTextureMatrix, + * @ref MaterialAttribute::DiffuseTextureMatrix, + * @ref MaterialAttribute::SpecularTextureMatrix, + * @ref MaterialAttribute::NormalTextureMatrix or + * @ref MaterialAttribute::TextureMatrix attributes is present, + * @cpp false @ce otherwise. + */ + bool hasTextureTransformation() const; - /** @brief Move assignment */ - PhongMaterialData& operator=(PhongMaterialData&& other) noexcept; + /** + * @brief Whether the material uses extra texture coordinate sets + * @m_since_latest + * + * Returns @cpp true @ce if any of the + * @ref MaterialAttribute::AmbientTextureCoordinates, + * @ref MaterialAttribute::DiffuseTextureCoordinates, + * @ref MaterialAttribute::SpecularTextureCoordinates, + * @ref MaterialAttribute::NormalTextureCoordinates or + * @ref MaterialAttribute::TextureCoordinates attributes is present and + * has a non-zero value, @cpp false @ce otherwise. + */ + bool hasTextureCoordinates() const; + #ifdef MAGNUM_BUILD_DEPRECATED /** * @brief Material flags + * @m_deprecated_since_latest The flags are no longer stored directly + * but generated on-the-fly from attribute data, which makes them + * less efficient than calling @ref hasAttribute(), + * @ref isDoubleSided(), @ref hasTextureTransformation(), + * @ref hasTextureCoordinates() etc. * - * A superset of @ref AbstractMaterialData::flags(). + * A superset of @ref MaterialData::flags(). */ - Flags flags() const { - return Flag(UnsignedShort(AbstractMaterialData::flags())); - } + CORRADE_IGNORE_DEPRECATED_PUSH /* GCC warns about Flags, ugh */ + CORRADE_DEPRECATED("use hasAttribute() instead") Flags flags() const; + CORRADE_IGNORE_DEPRECATED_POP + #endif /** * @brief Ambient color * - * If the material has @ref Flag::AmbientTexture, the color and texture - * is multiplied together. - * @see @ref flags() + * Convenience access to the @ref MaterialAttribute::AmbientColor + * attribute. If not present, the default is either + * @cpp 0xffffffff_rgbaf @ce if there's + * @ref MaterialAttribute::AmbientTexture and @cpp 0x000000ff_rgbaf @ce + * otherwise. + * + * If the material has @ref MaterialAttribute::AmbientTexture, the + * color and texture is meant to be multiplied together. + * @see @ref hasAttribute() */ - Color4 ambientColor() const { return _ambientColor; } + Color4 ambientColor() const; /** * @brief Ambient texture ID * - * Available only if the material has @ref Flag::AmbientTexture. - * Multiplied with @ref ambientColor(). - * @see @ref flags(), @ref AbstractImporter::texture() + * Available only if @ref MaterialAttribute::AmbientTexture is + * present. Meant to be multiplied with @ref ambientColor(). + * @see @ref hasAttribute() */ UnsignedInt ambientTexture() const; + /** + * @brief Ambient texture coordinate transformation matrix + * @m_since_latest + * + * Convenience access to the @ref MaterialAttribute::AmbientTextureMatrix + * / @ref MaterialAttribute::TextureMatrix attributes. If neither is + * present, the default is an identity matrix. Available only if the + * material has @ref MaterialAttribute::AmbientTexture. + * @see @ref hasAttribute() + */ + Matrix3 ambientTextureMatrix() const; + /** * @brief Ambient texture coordinate set * @m_since_latest * - * Available only if the material has @ref Flag::AmbientTexture. - * @see @ref flags(), @ref AbstractImporter::texture() + * Convenience access to the @ref MaterialAttribute::AmbientTextureCoordinates + * / @ref MaterialAttribute::TextureCoordinates attributes. If neither + * is present, the default is @cpp 0 @ce. Available only if the + * material has @ref MaterialAttribute::AmbientTexture. + * @see @ref hasAttribute() */ UnsignedInt ambientTextureCoordinates() const; @@ -253,27 +333,45 @@ class MAGNUM_TRADE_EXPORT PhongMaterialData: public AbstractMaterialData { /** * @brief Diffuse color * - * If the material has @ref Flag::DiffuseTexture, the color and texture - * is multiplied together. - * @see @ref flags() + * Convenience access to the @ref MaterialAttribute::DiffuseColor + * attribute. If not present, the default is @cpp 0xffffffff_rgbaf @ce. + * + * If the material has @ref MaterialAttribute::DiffuseTexture, the + * color and texture is meant to be multiplied together. + * @see @ref hasAttribute() */ - Color4 diffuseColor() const { return _diffuseColor; } + Color4 diffuseColor() const; /** * @brief Diffuse texture ID * - * Available only if the material has @ref Flag::DiffuseTexture. - * Multiplied with @ref diffuseColor(). - * @see @ref flags(), @ref AbstractImporter::texture() + * Available only if @ref MaterialAttribute::DiffuseTexture is + * present. Meant to be multiplied with @ref diffuseColor(). + * @see @ref hasAttribute(), @ref AbstractImporter::texture() */ UnsignedInt diffuseTexture() const; + /** + * @brief Diffuse texture coordinate transformation matrix + * @m_since_latest + * + * Convenience access to the @ref MaterialAttribute::DiffuseTextureMatrix + * / @ref MaterialAttribute::TextureMatrix attributes. If neither is + * present, the default is an identity matrix. Available only if the + * material has @ref MaterialAttribute::DiffuseTexture. + * @see @ref hasAttribute() + */ + Matrix3 diffuseTextureMatrix() const; + /** * @brief Diffuse texture coordinate set * @m_since_latest * - * Available only if the material has @ref Flag::DiffuseTexture. - * @see @ref flags(), @ref AbstractImporter::texture() + * Convenience access to the @ref MaterialAttribute::DiffuseTextureCoordinates + * / @ref MaterialAttribute::TextureCoordinates attributes. If neither + * is present, the default is @cpp 0 @ce. Available only if the + * material has @ref MaterialAttribute::DiffuseTexture. + * @see @ref hasAttribute() */ UnsignedInt diffuseTextureCoordinates() const; @@ -291,27 +389,45 @@ class MAGNUM_TRADE_EXPORT PhongMaterialData: public AbstractMaterialData { /** * @brief Specular color * - * If the material has @ref Flag::SpecularTexture, the color and - * texture is multiplied together. - * @see @ref flags() + * Convenience access to the @ref MaterialAttribute::SpecularColor + * attribute. If not present, the default is @cpp 0xffffffff_rgbaf @ce. + * + * If the material has @ref MaterialAttribute::SpecularTexture, the + * color and texture is meant to be multiplied together. + * @see @ref hasAttribute() */ - Color4 specularColor() const { return _specularColor; } + Color4 specularColor() const; /** * @brief Specular texture ID * - * Available only if the material has @ref Flag::SpecularTexture. - * Multiplied with @ref specularColor(). - * @see @ref flags(), @ref AbstractImporter::texture() + * Available only if @ref MaterialAttribute::SpecularTexture is + * present. Meant to be multiplied with @ref specularColor(). + * @see @ref hasAttribute(), @ref AbstractImporter::texture() */ UnsignedInt specularTexture() const; + /** + * @brief Specular texture coordinate transformation matrix + * @m_since_latest + * + * Convenience access to the @ref MaterialAttribute::SpecularTextureMatrix + * / @ref MaterialAttribute::TextureMatrix attributes. If neither is + * present, the default is an identity matrix. Available only if the + * material has @ref MaterialAttribute::SpecularTexture. + * @see @ref hasAttribute() + */ + Matrix3 specularTextureMatrix() const; + /** * @brief Specular texture coordinate set * @m_since_latest * - * Available only if the material has @ref Flag::SpecularTexture. - * @see @ref flags(), @ref AbstractImporter::texture() + * Convenience access to the @ref MaterialAttribute::SpecularTextureCoordinates + * / @ref MaterialAttribute::TextureCoordinates attributes. If neither + * is present, the default is @cpp 0 @ce. Available only if the + * material has @ref MaterialAttribute::SpecularTexture. + * @see @ref hasAttribute() */ UnsignedInt specularTextureCoordinates() const; @@ -330,17 +446,32 @@ class MAGNUM_TRADE_EXPORT PhongMaterialData: public AbstractMaterialData { * @brief Normal texture ID * @m_since{2020,06} * - * Available only if the material has @ref Flag::NormalTexture. - * @see @ref flags(), @ref AbstractImporter::texture() + * Available only if @ref MaterialAttribute::NormalTexture is present. + * @see @ref hasAttribute(), @ref AbstractImporter::texture() */ UnsignedInt normalTexture() const; + /** + * @brief Normal texture coordinate transformation matrix + * @m_since_latest + * + * Convenience access to the @ref MaterialAttribute::NormalTextureMatrix + * / @ref MaterialAttribute::TextureMatrix attributes. If neither is + * present, the default is an identity matrix. Available only if the + * material has @ref MaterialAttribute::NormalTexture. + * @see @ref hasAttribute() + */ + Matrix3 normalTextureMatrix() const; + /** * @brief Normal texture coordinate set * @m_since_latest * - * Available only if the material has @ref Flag::NormalTexture. - * @see @ref flags(), @ref AbstractImporter::texture() + * Convenience access to the @ref MaterialAttribute::NormalTextureCoordinates + * / @ref MaterialAttribute::TextureCoordinates attributes. If neither + * is present, the default is @cpp 0 @ce. Available only if the + * material has @ref MaterialAttribute::NormalTexture. + * @see @ref hasAttribute() */ UnsignedInt normalTextureCoordinates() const; @@ -356,43 +487,78 @@ class MAGNUM_TRADE_EXPORT PhongMaterialData: public AbstractMaterialData { #endif /** - * @brief Texture coordinate transformation matrix + * @brief Common texture coordinate transformation matrix for all textures * @m_since{2020,06} * - * If the material doesn't have @ref Flag::TextureTransformation, - * returns an identity matrix. - * @see @ref flags() + * Convenience access to the @ref MaterialAttribute::TextureMatrix + * attribute. If not present, the default is an identity matrix. Note + * that the material may also define per-texture transformation using + * the @ref MaterialAttribute::AmbientTextureMatrix, + * @ref MaterialAttribute::DiffuseTextureMatrix, + * @ref MaterialAttribute::SpecularTextureMatrix and + * @ref MaterialAttribute::NormalTextureMatrix attributes, which then + * take precedence over the common one. + * @see @ref hasAttribute(), @ref ambientTextureMatrix(), + * @ref diffuseTextureMatrix(), @ref specularTextureMatrix(), + * @ref normalTextureMatrix() + */ + Matrix3 textureMatrix() const; + + /** + * @brief Common texture coordinate set index for all textures + * @m_since_latest + * + * Convenience access to the @ref MaterialAttribute::TextureCoordinates + * attribute. If not present, the default is @cpp 0 @ce. Note that the + * material may also define per-texture coordinate set using the + * @ref MaterialAttribute::AmbientTextureCoordinates, + * @ref MaterialAttribute::DiffuseTextureCoordinates, + * @ref MaterialAttribute::SpecularTextureCoordinates and + * @ref MaterialAttribute::NormalTextureCoordinates attributes, which + * then take precedence over the common one. + * @see @ref hasAttribute(), @ref ambientTextureCoordinates(), + * @ref diffuseTextureCoordinates(), + * @ref specularTextureCoordinates(), + * @ref normalTextureCoordinates() + */ + UnsignedInt textureCoordinates() const; + + /** + * @brief Shininess + * + * Convenience access to the @ref MaterialAttribute::Shininess + * attribute. If not present, the default is @cpp 80.0f @ce. */ - Matrix3 textureMatrix() const { return _textureMatrix; } - - /** @brief Shininess */ - Float shininess() const { return _shininess; } - - private: - /* Initializing texture IDs to insane values to make accidents worse - and thus better noticeable */ - Color4 _ambientColor; - UnsignedInt _ambientTexture{~UnsignedInt{}}; - UnsignedInt _ambientTextureCoordinates; - Color4 _diffuseColor; - UnsignedInt _diffuseTexture{~UnsignedInt{}}; - UnsignedInt _diffuseTextureCoordinates; - Color4 _specularColor; - UnsignedInt _specularTexture{~UnsignedInt{}}; - UnsignedInt _specularTextureCoordinates; - UnsignedInt _normalTexture{~UnsignedInt{}}; - UnsignedInt _normalTextureCoordinates; - Matrix3 _textureMatrix; - Float _shininess; + Float shininess() const; }; +#ifdef MAGNUM_BUILD_DEPRECATED +CORRADE_IGNORE_DEPRECATED_PUSH CORRADE_ENUMSET_OPERATORS(PhongMaterialData::Flags) -/** @debugoperatorclassenum{PhongMaterialData,PhongMaterialData::Flag} */ +/** +@debugoperatorclassenum{PhongMaterialData,PhongMaterialData::Flag} +@m_deprecated_since_latest The flags are no longer stored directly but + generated on-the-fly from attribute data, which makes them less efficient + than calling @ref MaterialData::hasAttribute(), + @ref MaterialData::isDoubleSided(), + @ref PhongMaterialData::hasTextureTransformation(), + @ref PhongMaterialData::hasTextureCoordinates() etc. +*/ MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, PhongMaterialData::Flag value); -/** @debugoperatorclassenum{PhongMaterialData,PhongMaterialData::Flags} */ +/** +@debugoperatorclassenum{PhongMaterialData,PhongMaterialData::Flags} +@m_deprecated_since_latest The flags are no longer stored directly but + generated on-the-fly from attribute data, which makes them less efficient + than calling @ref MaterialData::hasAttribute(), + @ref MaterialData::isDoubleSided(), + @ref PhongMaterialData::hasTextureTransformation(), + @ref PhongMaterialData::hasTextureCoordinates() etc. +*/ MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, PhongMaterialData::Flags value); +CORRADE_IGNORE_DEPRECATED_POP +#endif }} diff --git a/src/Magnum/Trade/Test/MaterialDataTest.cpp b/src/Magnum/Trade/Test/MaterialDataTest.cpp index 913dcc176c..6ad01acc00 100644 --- a/src/Magnum/Trade/Test/MaterialDataTest.cpp +++ b/src/Magnum/Trade/Test/MaterialDataTest.cpp @@ -31,6 +31,7 @@ #include #include +#include "Magnum/Math/Color.h" #include "Magnum/Math/Matrix3.h" #include "Magnum/Trade/MaterialData.h" #include "Magnum/Trade/PhongMaterialData.h" @@ -73,6 +74,7 @@ class MaterialDataTest: public TestSuite::Tester { void constructCopy(); void constructMove(); + void access(); void accessOptional(); void accessOutOfBounds(); void accessInvalidAttributeName(); @@ -81,29 +83,38 @@ class MaterialDataTest: public TestSuite::Tester { void release(); - void constructPhong(); - void constructPhongTextured(); - void constructPhongTexturedTextureTransform(); - void constructPhongTexturedCoordinates(); - void constructPhongTextureTransformNoTextures(); - void constructPhongNoTextureTransformationFlag(); - void constructPhongNoTextureCoordinatesFlag(); - void constructPhongCopy(); - void constructPhongMove(); + #ifdef MAGNUM_BUILD_DEPRECATED + void constructPhongDeprecated(); + void constructPhongDeprecatedTextured(); + void constructPhongDeprecatedTexturedTextureTransform(); + void constructPhongDeprecatedTexturedCoordinates(); + void constructPhongDeprecatedTextureTransformNoTextures(); + void constructPhongDeprecatedNoTextureTransformationFlag(); + void constructPhongDeprecatedNoTextureCoordinatesFlag(); + #endif - void accessInvalidTextures(); + void phongAccess(); + void phongAccessDefaults(); + void phongAccessTextured(); + void phongAccessTexturedDefaults(); + void phongAccessTexturedSingleMatrixCoordinates(); + void phongAccessInvalidTextures(); void debugAttribute(); void debugAttributeType(); void debugType(); void debugTypes(); + #ifdef MAGNUM_BUILD_DEPRECATED void debugFlag(); void debugFlags(); + #endif void debugAlphaMode(); + #ifdef MAGNUM_BUILD_DEPRECATED void debugPhongFlag(); void debugPhongFlags(); + #endif }; MaterialDataTest::MaterialDataTest() { @@ -163,6 +174,7 @@ MaterialDataTest::MaterialDataTest() { &MaterialDataTest::constructCopy, &MaterialDataTest::constructMove, + &MaterialDataTest::access, &MaterialDataTest::accessOptional, &MaterialDataTest::accessOutOfBounds, &MaterialDataTest::accessInvalidAttributeName, @@ -171,29 +183,39 @@ MaterialDataTest::MaterialDataTest() { &MaterialDataTest::release, - &MaterialDataTest::constructPhong, - &MaterialDataTest::constructPhongTextured, - &MaterialDataTest::constructPhongTexturedTextureTransform, - &MaterialDataTest::constructPhongTexturedCoordinates, - &MaterialDataTest::constructPhongTextureTransformNoTextures, - &MaterialDataTest::constructPhongNoTextureTransformationFlag, - &MaterialDataTest::constructPhongNoTextureCoordinatesFlag, - &MaterialDataTest::constructPhongCopy, - &MaterialDataTest::constructPhongMove, - - &MaterialDataTest::accessInvalidTextures, + #ifdef MAGNUM_BUILD_DEPRECATED + &MaterialDataTest::constructPhongDeprecated, + &MaterialDataTest::constructPhongDeprecatedTextured, + &MaterialDataTest::constructPhongDeprecatedTexturedTextureTransform, + &MaterialDataTest::constructPhongDeprecatedTexturedCoordinates, + &MaterialDataTest::constructPhongDeprecatedTextureTransformNoTextures, + &MaterialDataTest::constructPhongDeprecatedNoTextureTransformationFlag, + &MaterialDataTest::constructPhongDeprecatedNoTextureCoordinatesFlag, + #endif + + &MaterialDataTest::phongAccess, + &MaterialDataTest::phongAccessDefaults, + &MaterialDataTest::phongAccessTextured, + &MaterialDataTest::phongAccessTexturedDefaults, + &MaterialDataTest::phongAccessTexturedSingleMatrixCoordinates, + &MaterialDataTest::phongAccessInvalidTextures, &MaterialDataTest::debugAttribute, &MaterialDataTest::debugAttributeType, &MaterialDataTest::debugType, &MaterialDataTest::debugTypes, + #ifdef MAGNUM_BUILD_DEPRECATED &MaterialDataTest::debugFlag, &MaterialDataTest::debugFlags, + #endif &MaterialDataTest::debugAlphaMode, + #ifdef MAGNUM_BUILD_DEPRECATED &MaterialDataTest::debugPhongFlag, - &MaterialDataTest::debugPhongFlags}); + &MaterialDataTest::debugPhongFlags + #endif + }); } using namespace Containers::Literals; @@ -674,6 +696,32 @@ void MaterialDataTest::constructMove() { CORRADE_VERIFY(std::is_nothrow_move_assignable::value); } +void MaterialDataTest::access() { + MaterialData a{{}, { + {MaterialAttribute::DoubleSided, false}, + {MaterialAttribute::AlphaBlend, true}, + {MaterialAttribute::AlphaMask, 0.9f} + }}; + CORRADE_VERIFY(!a.isDoubleSided()); + CORRADE_COMPARE(a.alphaMode(), MaterialAlphaMode::Blend); + CORRADE_COMPARE(a.alphaMask(), 0.9f); + + MaterialData b{{}, { + {MaterialAttribute::AlphaBlend, false}, + {MaterialAttribute::AlphaMask, 0.3f} + }}; + CORRADE_VERIFY(!b.isDoubleSided()); + CORRADE_COMPARE(b.alphaMode(), MaterialAlphaMode::Mask); + CORRADE_COMPARE(b.alphaMask(), 0.3f); + + MaterialData c{{}, { + {MaterialAttribute::DoubleSided, true}, + }}; + CORRADE_VERIFY(c.isDoubleSided()); + CORRADE_COMPARE(c.alphaMode(), MaterialAlphaMode::Opaque); + CORRADE_COMPARE(c.alphaMask(), 0.5f); +} + void MaterialDataTest::accessOptional() { MaterialData data{{}, { {MaterialAttribute::AlphaMask, 0.5f}, @@ -835,18 +883,22 @@ void MaterialDataTest::release() { CORRADE_COMPARE(data.attributeCount(), 0); } -void MaterialDataTest::constructPhong() { - using namespace Math::Literals; - +#ifdef MAGNUM_BUILD_DEPRECATED +void MaterialDataTest::constructPhongDeprecated() { const int a{}; - PhongMaterialData data{{}, + CORRADE_IGNORE_DEPRECATED_PUSH + PhongMaterialData data{PhongMaterialData::Flag::DoubleSided, 0xccffbb_rgbf, {}, 0xebefbf_rgbf, {}, 0xacabad_rgbf, {}, {}, {}, MaterialAlphaMode::Mask, 0.3f, 80.0f, &a}; + CORRADE_IGNORE_DEPRECATED_POP + CORRADE_COMPARE(data.types(), MaterialType::Phong); + CORRADE_IGNORE_DEPRECATED_PUSH CORRADE_COMPARE(data.type(), MaterialType::Phong); - CORRADE_COMPARE(data.flags(), PhongMaterialData::Flags{}); + CORRADE_COMPARE(data.flags(), PhongMaterialData::Flag::DoubleSided); + CORRADE_IGNORE_DEPRECATED_POP CORRADE_COMPARE(data.ambientColor(), 0xccffbb_rgbf); CORRADE_COMPARE(data.diffuseColor(), 0xebefbf_rgbf); CORRADE_COMPARE(data.specularColor(), 0xacabad_rgbf); @@ -857,19 +909,22 @@ void MaterialDataTest::constructPhong() { CORRADE_COMPARE(data.importerState(), &a); } -void MaterialDataTest::constructPhongTextured() { - using namespace Math::Literals; - +void MaterialDataTest::constructPhongDeprecatedTextured() { const int a{}; + CORRADE_IGNORE_DEPRECATED_PUSH PhongMaterialData data{ PhongMaterialData::Flag::AmbientTexture|PhongMaterialData::Flag::SpecularTexture, 0x111111_rgbf, 42, 0xeebbff_rgbf, {}, 0xacabad_rgbf, 17, {}, {}, MaterialAlphaMode::Blend, 0.37f, 96.0f, &a}; + CORRADE_IGNORE_DEPRECATED_POP + CORRADE_COMPARE(data.types(), MaterialType::Phong); + CORRADE_IGNORE_DEPRECATED_PUSH CORRADE_COMPARE(data.type(), MaterialType::Phong); CORRADE_COMPARE(data.flags(), PhongMaterialData::Flag::AmbientTexture|PhongMaterialData::Flag::SpecularTexture); + CORRADE_IGNORE_DEPRECATED_POP CORRADE_COMPARE(data.ambientColor(), 0x111111_rgbf); CORRADE_COMPARE(data.ambientTexture(), 42); CORRADE_COMPARE(data.ambientTextureCoordinates(), 0); @@ -884,207 +939,290 @@ void MaterialDataTest::constructPhongTextured() { CORRADE_COMPARE(data.importerState(), &a); } -void MaterialDataTest::constructPhongTexturedTextureTransform() { - using namespace Math::Literals; - +void MaterialDataTest::constructPhongDeprecatedTexturedTextureTransform() { const int a{}; + CORRADE_IGNORE_DEPRECATED_PUSH PhongMaterialData data{ PhongMaterialData::Flag::DiffuseTexture|PhongMaterialData::Flag::NormalTexture|PhongMaterialData::Flag::TextureTransformation, 0x111111_rgbf, {}, 0xeebbff_rgbf, 42, 0xacabad_rgbf, {}, 17, Matrix3::rotation(90.0_degf), - MaterialAlphaMode::Opaque, 0.5f, 96.0f, &a}; + MaterialAlphaMode::Mask, 0.5f, 96.0f, &a}; + CORRADE_IGNORE_DEPRECATED_POP + CORRADE_COMPARE(data.types(), MaterialType::Phong); + CORRADE_IGNORE_DEPRECATED_PUSH CORRADE_COMPARE(data.type(), MaterialType::Phong); CORRADE_COMPARE(data.flags(), PhongMaterialData::Flag::DiffuseTexture|PhongMaterialData::Flag::NormalTexture|PhongMaterialData::Flag::TextureTransformation); + CORRADE_IGNORE_DEPRECATED_POP CORRADE_COMPARE(data.ambientColor(), 0x111111_rgbf); CORRADE_COMPARE(data.diffuseColor(), 0xeebbff_rgbf); CORRADE_COMPARE(data.diffuseTexture(), 42); CORRADE_COMPARE(data.specularColor(), 0xacabad_rgbf); CORRADE_COMPARE(data.normalTexture(), 17); CORRADE_COMPARE(data.textureMatrix(), Matrix3::rotation(90.0_degf)); - CORRADE_COMPARE(data.alphaMode(), MaterialAlphaMode::Opaque); + CORRADE_COMPARE(data.alphaMode(), MaterialAlphaMode::Mask); CORRADE_COMPARE(data.alphaMask(), 0.5f); CORRADE_COMPARE(data.shininess(), 96.0f); CORRADE_COMPARE(data.importerState(), &a); } -void MaterialDataTest::constructPhongTexturedCoordinates() { - using namespace Math::Literals; - +void MaterialDataTest::constructPhongDeprecatedTexturedCoordinates() { const int a{}; + CORRADE_IGNORE_DEPRECATED_PUSH PhongMaterialData data{ - PhongMaterialData::Flag::AmbientTexture|PhongMaterialData::Flag::SpecularTexture|PhongMaterialData::Flag::TextureCoordinates, + PhongMaterialData::Flag::AmbientTexture|PhongMaterialData::Flag::DiffuseTexture|PhongMaterialData::Flag::SpecularTexture|PhongMaterialData::Flag::NormalTexture|PhongMaterialData::Flag::TextureCoordinates, 0x111111_rgbf, 42, 3, - 0xeebbff_rgbf, {}, 0, + 0xeebbff_rgbf, {}, 6, 0xacabad_rgbf, 17, 1, - {}, 0, {}, + 0, 8, {}, MaterialAlphaMode::Blend, 0.37f, 96.0f, &a}; + CORRADE_IGNORE_DEPRECATED_POP + CORRADE_COMPARE(data.types(), MaterialType::Phong); + CORRADE_IGNORE_DEPRECATED_PUSH CORRADE_COMPARE(data.type(), MaterialType::Phong); - CORRADE_COMPARE(data.flags(), PhongMaterialData::Flag::AmbientTexture|PhongMaterialData::Flag::SpecularTexture|PhongMaterialData::Flag::TextureCoordinates); + CORRADE_COMPARE(data.flags(), PhongMaterialData::Flag::AmbientTexture|PhongMaterialData::Flag::DiffuseTexture|PhongMaterialData::Flag::SpecularTexture|PhongMaterialData::Flag::NormalTexture|PhongMaterialData::Flag::TextureCoordinates); + CORRADE_IGNORE_DEPRECATED_POP CORRADE_COMPARE(data.ambientColor(), 0x111111_rgbf); CORRADE_COMPARE(data.ambientTexture(), 42); CORRADE_COMPARE(data.ambientTextureCoordinates(), 3); CORRADE_COMPARE(data.diffuseColor(), 0xeebbff_rgbf); + CORRADE_COMPARE(data.diffuseTextureCoordinates(), 6); CORRADE_COMPARE(data.specularColor(), 0xacabad_rgbf); CORRADE_COMPARE(data.specularTexture(), 17); CORRADE_COMPARE(data.specularTextureCoordinates(), 1); + CORRADE_COMPARE(data.normalTexture(), 0); + CORRADE_COMPARE(data.normalTextureCoordinates(), 8); CORRADE_COMPARE(data.textureMatrix(), Matrix3{}); + CORRADE_COMPARE(data.ambientTextureCoordinates(), 3); CORRADE_COMPARE(data.alphaMode(), MaterialAlphaMode::Blend); CORRADE_COMPARE(data.alphaMask(), 0.37f); CORRADE_COMPARE(data.shininess(), 96.0f); CORRADE_COMPARE(data.importerState(), &a); } -void MaterialDataTest::constructPhongTextureTransformNoTextures() { +void MaterialDataTest::constructPhongDeprecatedTextureTransformNoTextures() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif std::ostringstream out; Error redirectError{&out}; + CORRADE_IGNORE_DEPRECATED_PUSH PhongMaterialData a{PhongMaterialData::Flag::TextureTransformation, {}, {}, {}, {}, {}, {}, {}, {}, {}, 0.5f, 80.0f}; + CORRADE_IGNORE_DEPRECATED_POP CORRADE_COMPARE(out.str(), "Trade::PhongMaterialData: texture transformation enabled but the material has no textures\n"); } -void MaterialDataTest::constructPhongNoTextureTransformationFlag() { +void MaterialDataTest::constructPhongDeprecatedNoTextureTransformationFlag() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif std::ostringstream out; Error redirectError{&out}; + CORRADE_IGNORE_DEPRECATED_PUSH PhongMaterialData a{{}, {}, {}, {}, {}, {}, {}, {}, Matrix3::rotation(90.0_degf), {}, 0.5f, 80.0f}; + CORRADE_IGNORE_DEPRECATED_POP CORRADE_COMPARE(out.str(), "PhongMaterialData::PhongMaterialData: non-default texture matrix requires Flag::TextureTransformation to be enabled\n"); } -void MaterialDataTest::constructPhongNoTextureCoordinatesFlag() { +void MaterialDataTest::constructPhongDeprecatedNoTextureCoordinatesFlag() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif std::ostringstream out; Error redirectError{&out}; + CORRADE_IGNORE_DEPRECATED_PUSH PhongMaterialData a{{}, {}, {}, 1, {}, {}, 2, {}, {}, 3, {}, 4, {}, {}, 0.5f, 80.0f}; + CORRADE_IGNORE_DEPRECATED_POP CORRADE_COMPARE(out.str(), "PhongMaterialData::PhongMaterialData: non-zero texture coordinate sets require Flag::TextureCoordinates to be enabled\n"); } +#endif + +void MaterialDataTest::phongAccess() { + MaterialData base{MaterialType::Phong, { + {MaterialAttribute::AmbientColor, 0xccffbbff_rgbaf}, + {MaterialAttribute::DiffuseColor, 0xebefbfff_rgbaf}, + {MaterialAttribute::SpecularColor, 0xacabadff_rgbaf}, + {MaterialAttribute::Shininess, 96.0f} + }}; + + CORRADE_COMPARE(base.types(), MaterialType::Phong); + const PhongMaterialData& data = static_cast(base); -void MaterialDataTest::constructPhongCopy() { - CORRADE_VERIFY(!(std::is_constructible{})); - CORRADE_VERIFY(!(std::is_constructible{})); - CORRADE_VERIFY(!(std::is_assignable{})); - CORRADE_VERIFY(!(std::is_assignable{})); + CORRADE_VERIFY(!data.hasTextureTransformation()); + CORRADE_VERIFY(!data.hasTextureCoordinates()); + CORRADE_COMPARE(data.ambientColor(), 0xccffbb_rgbf); + CORRADE_COMPARE(data.diffuseColor(), 0xebefbf_rgbf); + CORRADE_COMPARE(data.specularColor(), 0xacabad_rgbf); + CORRADE_COMPARE(data.shininess(), 96.0f); } -void MaterialDataTest::constructPhongMove() { - using namespace Math::Literals; +void MaterialDataTest::phongAccessDefaults() { + MaterialData base{{}, {}}; - const int a{}; - PhongMaterialData data{ - PhongMaterialData::Flag::AmbientTexture|PhongMaterialData::Flag::DiffuseTexture|PhongMaterialData::Flag::SpecularTexture|PhongMaterialData::Flag::NormalTexture|PhongMaterialData::Flag::TextureTransformation|PhongMaterialData::Flag::TextureCoordinates, - 0x111111_rgbf, 1, 0, - 0xeebbff_rgbf, 42, 1, - 0xacabad_rgbf, 24, 2, 17, 3, - Matrix3::rotation(90.0_degf), - MaterialAlphaMode::Blend, 0.55f, 13.0f, &a}; - - PhongMaterialData b{std::move(data)}; - CORRADE_COMPARE(b.type(), MaterialType::Phong); - CORRADE_COMPARE(b.flags(), PhongMaterialData::Flag::AmbientTexture|PhongMaterialData::Flag::DiffuseTexture|PhongMaterialData::Flag::SpecularTexture|PhongMaterialData::Flag::NormalTexture|PhongMaterialData::Flag::TextureTransformation|PhongMaterialData::Flag::TextureCoordinates); - CORRADE_COMPARE(b.ambientColor(), 0x111111_rgbf); - CORRADE_COMPARE(b.ambientTexture(), 1); - CORRADE_COMPARE(b.ambientTextureCoordinates(), 0); - CORRADE_COMPARE(b.diffuseColor(), 0xeebbff_rgbf); - CORRADE_COMPARE(b.diffuseTexture(), 42); - CORRADE_COMPARE(b.diffuseTextureCoordinates(), 1); - CORRADE_COMPARE(b.specularColor(), 0xacabad_rgbf); - CORRADE_COMPARE(b.specularTexture(), 24); - CORRADE_COMPARE(b.specularTextureCoordinates(), 2); - CORRADE_COMPARE(b.normalTexture(), 17); - CORRADE_COMPARE(b.normalTextureCoordinates(), 3); - CORRADE_COMPARE(b.textureMatrix(), Matrix3::rotation(90.0_degf)); - CORRADE_COMPARE(b.alphaMode(), MaterialAlphaMode::Blend); - CORRADE_COMPARE(b.alphaMask(), 0.55f); - CORRADE_COMPARE(b.shininess(), 13.0f); - CORRADE_COMPARE(b.importerState(), &a); - - const int c{}; - PhongMaterialData d{{}, - 0xccffbb_rgbf, {}, - 0xebefbf_rgbf, {}, - 0xacabad_rgbf, {}, {}, {}, - MaterialAlphaMode::Mask, 0.3f, 80.0f, &c}; - d = std::move(b); - - CORRADE_COMPARE(d.type(), MaterialType::Phong); - CORRADE_COMPARE(d.flags(), PhongMaterialData::Flag::AmbientTexture|PhongMaterialData::Flag::DiffuseTexture|PhongMaterialData::Flag::SpecularTexture|PhongMaterialData::Flag::NormalTexture|PhongMaterialData::Flag::TextureTransformation|PhongMaterialData::Flag::TextureCoordinates); - CORRADE_COMPARE(d.ambientColor(), 0x111111_rgbf); - CORRADE_COMPARE(d.ambientTexture(), 1); - CORRADE_COMPARE(d.ambientTextureCoordinates(), 0); - CORRADE_COMPARE(d.diffuseColor(), 0xeebbff_rgbf); - CORRADE_COMPARE(d.diffuseTexture(), 42); - CORRADE_COMPARE(d.diffuseTextureCoordinates(), 1); - CORRADE_COMPARE(d.specularColor(), 0xacabad_rgbf); - CORRADE_COMPARE(d.specularTexture(), 24); - CORRADE_COMPARE(d.specularTextureCoordinates(), 2); - CORRADE_COMPARE(d.normalTexture(), 17); - CORRADE_COMPARE(d.normalTextureCoordinates(), 3); - CORRADE_COMPARE(d.textureMatrix(), Matrix3::rotation(90.0_degf)); - CORRADE_COMPARE(d.alphaMode(), MaterialAlphaMode::Blend); - CORRADE_COMPARE(d.alphaMask(), 0.55f); - CORRADE_COMPARE(d.shininess(), 13.0f); - CORRADE_COMPARE(d.importerState(), &a); - - CORRADE_VERIFY(std::is_nothrow_move_constructible::value); - CORRADE_VERIFY(std::is_nothrow_move_assignable::value); + CORRADE_COMPARE(base.types(), MaterialTypes{}); + /* Casting is fine even if the type doesn't include Phong */ + const PhongMaterialData& data = static_cast(base); + + CORRADE_VERIFY(!data.hasTextureTransformation()); + CORRADE_VERIFY(!data.hasTextureCoordinates()); + CORRADE_COMPARE(data.ambientColor(), 0x000000_rgbf); + CORRADE_COMPARE(data.diffuseColor(), 0xffffff_rgbf); + CORRADE_COMPARE(data.specularColor(), 0xffffff_rgbf); + CORRADE_COMPARE(data.textureMatrix(), Matrix3{}); + CORRADE_COMPARE(data.textureCoordinates(), 0); + CORRADE_COMPARE(data.shininess(), 80.0f); +} + +void MaterialDataTest::phongAccessTextured() { + PhongMaterialData data{{}, { + {MaterialAttribute::AmbientColor, 0x111111ff_rgbaf}, + {MaterialAttribute::AmbientTexture, 42u}, + {MaterialAttribute::AmbientTextureMatrix, Matrix3::scaling({0.5f, 1.0f})}, + {MaterialAttribute::AmbientTextureCoordinates, 2u}, + {MaterialAttribute::DiffuseTexture, 33u}, + {MaterialAttribute::DiffuseColor, 0xeebbffff_rgbaf}, + {MaterialAttribute::DiffuseTextureMatrix, Matrix3::scaling({0.5f, 0.5f})}, + {MaterialAttribute::DiffuseTextureCoordinates, 3u}, + {MaterialAttribute::SpecularColor, 0xacabadff_rgbaf}, + {MaterialAttribute::SpecularTexture, 17u}, + {MaterialAttribute::SpecularTextureMatrix, Matrix3::scaling({1.0f, 1.0f})}, + {MaterialAttribute::SpecularTextureCoordinates, 4u}, + {MaterialAttribute::NormalTexture, 0u}, + {MaterialAttribute::NormalTextureMatrix, Matrix3::scaling({1.0f, 0.5f})}, + {MaterialAttribute::NormalTextureCoordinates, 5u} + }}; + + CORRADE_VERIFY(data.hasTextureTransformation()); + CORRADE_VERIFY(data.hasTextureCoordinates()); + CORRADE_COMPARE(data.ambientColor(), 0x111111_rgbf); + CORRADE_COMPARE(data.ambientTexture(), 42); + CORRADE_COMPARE(data.ambientTextureMatrix(), Matrix3::scaling({0.5f, 1.0f})); + CORRADE_COMPARE(data.ambientTextureCoordinates(), 2); + CORRADE_COMPARE(data.diffuseColor(), 0xeebbff_rgbf); + CORRADE_COMPARE(data.diffuseTexture(), 33); + CORRADE_COMPARE(data.diffuseTextureMatrix(), Matrix3::scaling({0.5f, 0.5f})); + CORRADE_COMPARE(data.diffuseTextureCoordinates(), 3); + CORRADE_COMPARE(data.specularColor(), 0xacabad_rgbf); + CORRADE_COMPARE(data.specularTexture(), 17); + CORRADE_COMPARE(data.specularTextureMatrix(), Matrix3::scaling({1.0f, 1.0f})); + CORRADE_COMPARE(data.specularTextureCoordinates(), 4); + CORRADE_COMPARE(data.normalTexture(), 0); + CORRADE_COMPARE(data.normalTextureMatrix(), Matrix3::scaling({1.0f, 0.5f})); + CORRADE_COMPARE(data.normalTextureCoordinates(), 5); + + CORRADE_COMPARE(data.textureMatrix(), Matrix3{}); + CORRADE_COMPARE(data.textureCoordinates(), 0); +} + +void MaterialDataTest::phongAccessTexturedDefaults() { + PhongMaterialData data{{}, { + {MaterialAttribute::AmbientTexture, 42u}, + {MaterialAttribute::DiffuseTexture, 33u}, + {MaterialAttribute::SpecularTexture, 17u}, + {MaterialAttribute::NormalTexture, 1u} + }}; + + CORRADE_VERIFY(!data.hasTextureTransformation()); + CORRADE_VERIFY(!data.hasTextureCoordinates()); + CORRADE_COMPARE(data.ambientColor(), 0xffffffff_rgbaf); + CORRADE_COMPARE(data.ambientTexture(), 42); + CORRADE_COMPARE(data.ambientTextureMatrix(), Matrix3{}); + CORRADE_COMPARE(data.ambientTextureCoordinates(), 0); + CORRADE_COMPARE(data.diffuseColor(), 0xffffffff_rgbaf); + CORRADE_COMPARE(data.diffuseTexture(), 33); + CORRADE_COMPARE(data.diffuseTextureMatrix(), Matrix3{}); + CORRADE_COMPARE(data.diffuseTextureCoordinates(), 0); + CORRADE_COMPARE(data.specularColor(), 0xffffffff_rgbaf); + CORRADE_COMPARE(data.specularTexture(), 17); + CORRADE_COMPARE(data.specularTextureMatrix(), Matrix3{}); + CORRADE_COMPARE(data.specularTextureCoordinates(), 0); + CORRADE_COMPARE(data.normalTexture(), 1); + CORRADE_COMPARE(data.normalTextureMatrix(), Matrix3{}); + CORRADE_COMPARE(data.normalTextureCoordinates(), 0); + + CORRADE_COMPARE(data.textureMatrix(), Matrix3{}); + CORRADE_COMPARE(data.textureCoordinates(), 0); } -void MaterialDataTest::accessInvalidTextures() { +void MaterialDataTest::phongAccessTexturedSingleMatrixCoordinates() { + PhongMaterialData data{{}, { + {MaterialAttribute::AmbientTexture, 42u}, + {MaterialAttribute::DiffuseTexture, 33u}, + {MaterialAttribute::SpecularTexture, 17u}, + {MaterialAttribute::NormalTexture, 0u}, + {MaterialAttribute::TextureMatrix, Matrix3::translation({0.5f, 1.0f})}, + {MaterialAttribute::TextureCoordinates, 2u} + }}; + + CORRADE_VERIFY(data.hasTextureTransformation()); + CORRADE_VERIFY(data.hasTextureCoordinates()); + CORRADE_COMPARE(data.ambientTextureMatrix(), Matrix3::translation({0.5f, 1.0f})); + CORRADE_COMPARE(data.ambientTextureCoordinates(), 2); + CORRADE_COMPARE(data.diffuseTextureMatrix(), Matrix3::translation({0.5f, 1.0f})); + CORRADE_COMPARE(data.diffuseTextureCoordinates(), 2); + CORRADE_COMPARE(data.specularTextureMatrix(), Matrix3::translation({0.5f, 1.0f})); + CORRADE_COMPARE(data.specularTextureCoordinates(), 2); + CORRADE_COMPARE(data.normalTextureMatrix(), Matrix3::translation({0.5f, 1.0f})); + CORRADE_COMPARE(data.normalTextureCoordinates(), 2); + + CORRADE_COMPARE(data.textureMatrix(), Matrix3::translation({0.5f, 1.0f})); + CORRADE_COMPARE(data.textureCoordinates(), 2); +} + +void MaterialDataTest::phongAccessInvalidTextures() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif - PhongMaterialData a{{}, - {}, {}, - {}, {}, - {}, {}, {}, {}, - {}, 0.5f, 80.0f}; + PhongMaterialData data{{}, {}}; std::ostringstream out; Error redirectError{&out}; - a.ambientTexture(); - a.ambientTextureCoordinates(); - a.diffuseTexture(); - a.diffuseTextureCoordinates(); - a.specularTexture(); - a.specularTextureCoordinates(); - a.normalTexture(); - a.normalTextureCoordinates(); + data.ambientTexture(); + data.ambientTextureMatrix(); + data.ambientTextureCoordinates(); + data.diffuseTexture(); + data.diffuseTextureMatrix(); + data.diffuseTextureCoordinates(); + data.specularTexture(); + data.specularTextureMatrix(); + data.specularTextureCoordinates(); + data.normalTexture(); + data.normalTextureMatrix(); + data.normalTextureCoordinates(); CORRADE_COMPARE(out.str(), - "Trade::PhongMaterialData::ambientTexture(): the material doesn't have an ambient texture\n" + "Trade::MaterialData::attribute(): attribute AmbientTexture not found\n" + "Trade::PhongMaterialData::ambientTextureMatrix(): the material doesn't have an ambient texture\n" "Trade::PhongMaterialData::ambientTextureCoordinates(): the material doesn't have an ambient texture\n" - "Trade::PhongMaterialData::diffuseTexture(): the material doesn't have a diffuse texture\n" + "Trade::MaterialData::attribute(): attribute DiffuseTexture not found\n" + "Trade::PhongMaterialData::diffuseTextureMatrix(): the material doesn't have a diffuse texture\n" "Trade::PhongMaterialData::diffuseTextureCoordinates(): the material doesn't have a diffuse texture\n" - "Trade::PhongMaterialData::specularTexture(): the material doesn't have a specular texture\n" + "Trade::MaterialData::attribute(): attribute SpecularTexture not found\n" + "Trade::PhongMaterialData::specularTextureMatrix(): the material doesn't have a specular texture\n" "Trade::PhongMaterialData::specularTextureCoordinates(): the material doesn't have a specular texture\n" - "Trade::PhongMaterialData::normalTexture(): the material doesn't have a normal texture\n" + "Trade::MaterialData::attribute(): attribute NormalTexture not found\n" + "Trade::PhongMaterialData::normalTextureMatrix(): the material doesn't have a normal texture\n" "Trade::PhongMaterialData::normalTextureCoordinates(): the material doesn't have a normal texture\n"); } @@ -1116,19 +1254,23 @@ void MaterialDataTest::debugTypes() { CORRADE_COMPARE(out.str(), "Trade::MaterialType::Phong|Trade::MaterialType(0xe0) Trade::MaterialTypes{}\n"); } +#ifdef MAGNUM_BUILD_DEPRECATED +CORRADE_IGNORE_DEPRECATED_PUSH void MaterialDataTest::debugFlag() { std::ostringstream out; - Debug{&out} << AbstractMaterialData::Flag::DoubleSided << AbstractMaterialData::Flag(0xf0); - CORRADE_COMPARE(out.str(), "Trade::AbstractMaterialData::Flag::DoubleSided Trade::AbstractMaterialData::Flag(0xf0)\n"); + Debug{&out} << MaterialData::Flag::DoubleSided << MaterialData::Flag(0xf0); + CORRADE_COMPARE(out.str(), "Trade::MaterialData::Flag::DoubleSided Trade::MaterialData::Flag(0xf0)\n"); } void MaterialDataTest::debugFlags() { std::ostringstream out; - Debug{&out} << AbstractMaterialData::Flag::DoubleSided << AbstractMaterialData::Flags{}; - CORRADE_COMPARE(out.str(), "Trade::AbstractMaterialData::Flag::DoubleSided Trade::AbstractMaterialData::Flags{}\n"); + Debug{&out} << MaterialData::Flag::DoubleSided << MaterialData::Flags{}; + CORRADE_COMPARE(out.str(), "Trade::MaterialData::Flag::DoubleSided Trade::MaterialData::Flags{}\n"); } +CORRADE_IGNORE_DEPRECATED_POP +#endif void MaterialDataTest::debugAlphaMode() { std::ostringstream out; @@ -1137,6 +1279,8 @@ void MaterialDataTest::debugAlphaMode() { CORRADE_COMPARE(out.str(), "Trade::MaterialAlphaMode::Opaque Trade::MaterialAlphaMode(0xee)\n"); } +#ifdef MAGNUM_BUILD_DEPRECATED +CORRADE_IGNORE_DEPRECATED_PUSH void MaterialDataTest::debugPhongFlag() { std::ostringstream out; @@ -1150,6 +1294,8 @@ void MaterialDataTest::debugPhongFlags() { Debug{&out} << (PhongMaterialData::Flag::DiffuseTexture|PhongMaterialData::Flag::SpecularTexture) << PhongMaterialData::Flags{}; CORRADE_COMPARE(out.str(), "Trade::PhongMaterialData::Flag::DiffuseTexture|Trade::PhongMaterialData::Flag::SpecularTexture Trade::PhongMaterialData::Flags{}\n"); } +CORRADE_IGNORE_DEPRECATED_POP +#endif }}}} diff --git a/src/Magnum/Trade/Trade.h b/src/Magnum/Trade/Trade.h index 154dd4762b..c3541a1838 100644 --- a/src/Magnum/Trade/Trade.h +++ b/src/Magnum/Trade/Trade.h @@ -52,9 +52,11 @@ enum class MaterialAttribute: UnsignedInt; enum class MaterialAttributeType: UnsignedByte; enum class MaterialType: UnsignedInt; enum class MaterialAlphaMode: UnsignedByte; -class AbstractMaterialData; class MaterialAttributeData; class MaterialData; +#ifdef MAGNUM_BUILD_DEPRECATED +typedef CORRADE_DEPRECATED("use MaterialData instead") MaterialData AbstractMaterialData; +#endif enum class AnimationTrackTargetType: UnsignedByte; enum class AnimationTrackType: UnsignedByte; From c59fa42e7b6b9e65c20d351e103c9c1e762c08b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 25 Jul 2020 18:05:49 +0200 Subject: [PATCH 08/36] Trade: allow storing 64bit ints and pointers in MaterialData. Suited mainly for custom app-specific material properties (e.g., actual texture pointers and handles), not really planning on using this in Magnum itself. --- src/Magnum/Trade/MaterialData.cpp | 10 ++ src/Magnum/Trade/MaterialData.h | 60 +++++++++- src/Magnum/Trade/Test/MaterialDataTest.cpp | 122 +++++++++++++++++++++ 3 files changed, 186 insertions(+), 6 deletions(-) diff --git a/src/Magnum/Trade/MaterialData.cpp b/src/Magnum/Trade/MaterialData.cpp index 47c65d15a8..947e8613e5 100644 --- a/src/Magnum/Trade/MaterialData.cpp +++ b/src/Magnum/Trade/MaterialData.cpp @@ -68,6 +68,8 @@ std::size_t materialAttributeTypeSize(const MaterialAttributeType type) { case MaterialAttributeType::Int: return 4; + case MaterialAttributeType::UnsignedLong: + case MaterialAttributeType::Long: case MaterialAttributeType::Vector2: case MaterialAttributeType::Vector2ui: case MaterialAttributeType::Vector2i: @@ -98,6 +100,10 @@ std::size_t materialAttributeTypeSize(const MaterialAttributeType type) { case MaterialAttributeType::Matrix3x4: case MaterialAttributeType::Matrix4x3: return 48; + + case MaterialAttributeType::Pointer: + case MaterialAttributeType::MutablePointer: + return sizeof(void*); } CORRADE_ASSERT_UNREACHABLE("Trade::materialAttributeTypeSize(): invalid type" << type, {}); @@ -328,6 +334,8 @@ Debug& operator<<(Debug& debug, const MaterialAttributeType value) { _c(Rad) _c(UnsignedInt) _c(Int) + _c(UnsignedLong) + _c(Long) _c(Vector2) _c(Vector2ui) _c(Vector2i) @@ -345,6 +353,8 @@ Debug& operator<<(Debug& debug, const MaterialAttributeType value) { _c(Matrix3x4) _c(Matrix4x2) _c(Matrix4x3) + _c(Pointer) + _c(MutablePointer) #undef _c /* LCOV_EXCL_STOP */ } diff --git a/src/Magnum/Trade/MaterialData.h b/src/Magnum/Trade/MaterialData.h index 9ca17cc583..3d172ee802 100644 --- a/src/Magnum/Trade/MaterialData.h +++ b/src/Magnum/Trade/MaterialData.h @@ -291,6 +291,8 @@ enum class MaterialAttributeType: UnsignedByte { Rad, /**< @ref Magnum::Rad "Rad" */ UnsignedInt, /**< @ref Magnum::UnsignedInt "UnsignedInt" */ Int, /**< @ref Magnum::Int "Int" */ + UnsignedLong, /**< @ref Magnum::UnsignedLong "UnsignedLong" */ + Long, /**< @ref Magnum::Long "Long" */ Vector2, /**< @ref Magnum::Vector2 "Vector2" */ Vector2ui, /**< @ref Magnum::Vector2ui "Vector2ui" */ @@ -316,6 +318,20 @@ enum class MaterialAttributeType: UnsignedByte { Matrix4x3, /**< @ref Magnum::Matrix4x3 "Matrix4x3" */ /* Matrix4x4 not present */ + + /** + * @cpp const void* @ce, type is not preserved. For convenience it's + * possible to retrieve the value by calling @cpp attribute() @ce + * with an arbitrary `T` but the user has to ensure the type is correct. + */ + Pointer, + + /** + * @cpp void* @ce, type is not preserved. For convenience it's possible to + * retrieve the value by calling @cpp attribute() @ce with an arbitrary + * `T` but the user has to ensure the type is correct. + */ + MutablePointer, }; /** @@ -427,7 +443,10 @@ class MAGNUM_TRADE_EXPORT MaterialAttributeData { /** * @brief Type-erased attribute value * - * Cast the pointer to a concrete type based on @ref type(). + * Cast the pointer to a concrete type based on @ref type(). Note that + * in case of a @ref MaterialAttributeType::Pointer or a + * @ref MaterialAttributeType::MutablePointer, returns a + * *pointer to a pointer*, not the pointer value itself. */ const void* value() const; @@ -476,6 +495,13 @@ class MAGNUM_TRADE_EXPORT MaterialAttributeData { UnsignedInt u; Int i; }; + union ErasedLongScalar { + constexpr explicit ErasedLongScalar(UnsignedLong value): u{value} {} + constexpr explicit ErasedLongScalar(Long value): i{value} {} + + UnsignedLong u; + Long i; + }; template union ErasedVector { constexpr explicit ErasedVector(const Math::Vector& value): f{value} {} constexpr explicit ErasedVector(const Math::Vector& value): u{value} {} @@ -504,8 +530,10 @@ class MAGNUM_TRADE_EXPORT MaterialAttributeData { constexpr explicit Storage() noexcept: data{} {} template constexpr explicit Storage(typename std::enable_if::type type, Containers::StringView name, const T& value) noexcept: _1{type, name, value} {} - template constexpr explicit Storage(typename std::enable_if::type type, Containers::StringView name, const T& value) noexcept: _4{type, name, value} {} - template constexpr explicit Storage(typename std::enable_if::type type, Containers::StringView name, const T& value) noexcept: _8{type, name, value} {} + template constexpr explicit Storage(typename std::enable_if::value, MaterialAttributeType>::type type, Containers::StringView name, const T& value) noexcept: _4{type, name, value} {} + template constexpr explicit Storage(typename std::enable_if::value && !std::is_pointer::value, MaterialAttributeType>::type type, Containers::StringView name, const T& value) noexcept: _8{type, name, value} {} + template constexpr explicit Storage(typename std::enable_if::value && !std::is_pointer::value, MaterialAttributeType>::type type, Containers::StringView name, const T& value) noexcept: _8v{type, name, value} {} + constexpr explicit Storage(MaterialAttributeType type, Containers::StringView name, const void* value) noexcept: p{type, name, value} {} template constexpr explicit Storage(typename std::enable_if::type type, Containers::StringView name, const T& value) noexcept: _12{type, name, value} {} template constexpr explicit Storage(typename std::enable_if::value, MaterialAttributeType>::type type, Containers::StringView name, const T& value) noexcept: _16{type, name, value} {} template constexpr explicit Storage(typename std::enable_if::value, MaterialAttributeType>::type type, Containers::StringView name, const T& value) noexcept: _16m{type, name, value} {} @@ -517,8 +545,10 @@ class MAGNUM_TRADE_EXPORT MaterialAttributeData { MaterialAttributeType type; char data[Implementation::MaterialAttributeDataSize]; Data _1; + Data p; Data _4; - Data> _8; + Data _8; + Data> _8v; Data> _12; Data> _16; Data> _16m; @@ -776,7 +806,10 @@ class MAGNUM_TRADE_EXPORT MaterialData { * @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(). + * Cast the pointer to a concrete type based on @ref type(). Note that + * in case of a @ref MaterialAttributeType::Pointer or a + * @ref MaterialAttributeType::MutablePointer, returns a + * *pointer to a pointer*, not the pointer value itself. */ const void* attribute(UnsignedInt id) const; @@ -784,7 +817,10 @@ class MAGNUM_TRADE_EXPORT MaterialData { * @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(). + * type based on @ref attributeType(). Note that + * in case of a @ref MaterialAttributeType::Pointer or a + * @ref MaterialAttributeType::MutablePointer, returns a + * *pointer to a pointer*, not the pointer value itself. * @see @ref hasAttribute(), @ref tryAttribute(), @ref attributeOr() */ const void* attribute(Containers::StringView name) const; @@ -950,6 +986,16 @@ namespace Implementation { return MaterialAttributeType::Bool; } }; + template struct MaterialAttributeTypeFor { + constexpr static MaterialAttributeType type() { + return MaterialAttributeType::Pointer; + } + }; + template struct MaterialAttributeTypeFor { + constexpr static MaterialAttributeType type() { + return MaterialAttributeType::MutablePointer; + } + }; #ifndef DOXYGEN_GENERATING_OUTPUT #define _c(type_) template<> struct MaterialAttributeTypeFor { \ constexpr static MaterialAttributeType type() { \ @@ -961,6 +1007,8 @@ namespace Implementation { _c(Rad) _c(UnsignedInt) _c(Int) + _c(UnsignedLong) + _c(Long) _c(Vector2) _c(Vector2ui) _c(Vector2i) diff --git a/src/Magnum/Trade/Test/MaterialDataTest.cpp b/src/Magnum/Trade/Test/MaterialDataTest.cpp index 6ad01acc00..8cfd7c9bfe 100644 --- a/src/Magnum/Trade/Test/MaterialDataTest.cpp +++ b/src/Magnum/Trade/Test/MaterialDataTest.cpp @@ -55,11 +55,15 @@ class MaterialDataTest: public TestSuite::Tester { template void constructAttributeStringConstexpr(); + void constructAttributePointer(); + void constructAttributeMutablePointer(); + void constructAttributeInvalidName(); void constructAttributeWrongTypeForName(); void constructAttributeInvalidType(); void constructAttributeTooLarge(); void constructAttributeWrongAccessType(); + void constructAttributeWrongAccessPointerType(); void construct(); void constructEmptyAttribute(); @@ -75,11 +79,13 @@ class MaterialDataTest: public TestSuite::Tester { void constructMove(); void access(); + void accessPointer(); void accessOptional(); void accessOutOfBounds(); void accessInvalidAttributeName(); void accessNotFound(); void accessWrongType(); + void accessWrongPointerType(); void release(); @@ -134,6 +140,8 @@ MaterialDataTest::MaterialDataTest() { &MaterialDataTest::constructAttributeStringConstexpr, &MaterialDataTest::constructAttributeStringConstexpr, &MaterialDataTest::constructAttributeStringConstexpr, + &MaterialDataTest::constructAttributeStringConstexpr, + &MaterialDataTest::constructAttributeStringConstexpr, &MaterialDataTest::constructAttributeStringConstexpr, &MaterialDataTest::constructAttributeStringConstexpr, &MaterialDataTest::constructAttributeStringConstexpr, @@ -152,11 +160,15 @@ MaterialDataTest::MaterialDataTest() { &MaterialDataTest::constructAttributeStringConstexpr, &MaterialDataTest::constructAttributeStringConstexpr, + &MaterialDataTest::constructAttributePointer, + &MaterialDataTest::constructAttributeMutablePointer, + &MaterialDataTest::constructAttributeInvalidName, &MaterialDataTest::constructAttributeWrongTypeForName, &MaterialDataTest::constructAttributeInvalidType, &MaterialDataTest::constructAttributeTooLarge, &MaterialDataTest::constructAttributeWrongAccessType, + &MaterialDataTest::constructAttributeWrongAccessPointerType, &MaterialDataTest::construct, &MaterialDataTest::constructEmptyAttribute}); @@ -175,11 +187,13 @@ MaterialDataTest::MaterialDataTest() { &MaterialDataTest::constructMove, &MaterialDataTest::access, + &MaterialDataTest::accessPointer, &MaterialDataTest::accessOptional, &MaterialDataTest::accessOutOfBounds, &MaterialDataTest::accessInvalidAttributeName, &MaterialDataTest::accessNotFound, &MaterialDataTest::accessWrongType, + &MaterialDataTest::accessWrongPointerType, &MaterialDataTest::release, @@ -377,6 +391,54 @@ template void MaterialDataTest::constructAttributeStringConstexpr() { CORRADE_COMPARE(attribute.value(), T(15)); } +constexpr Int SomeData = 3; + +void MaterialDataTest::constructAttributePointer() { + MaterialAttributeData attribute{"pointer!", &SomeData}; + CORRADE_COMPARE(attribute.name(), "pointer!"); + CORRADE_COMPARE(attribute.type(), MaterialAttributeType::Pointer); + CORRADE_COMPARE(*static_cast(attribute.value()), &SomeData); + CORRADE_COMPARE(attribute.value(), &SomeData); + /* Any type works */ + CORRADE_COMPARE(attribute.value(), &SomeData); + + constexpr MaterialAttributeData cattribute{"pointer!"_s, &SomeData}; + CORRADE_COMPARE(cattribute.name(), "pointer!"); + CORRADE_COMPARE(cattribute.type(), MaterialAttributeType::Pointer); + CORRADE_COMPARE(*static_cast(cattribute.value()), &SomeData); + CORRADE_COMPARE(cattribute.value(), &SomeData); + + /* Type-erased variant */ + const Int* pointer = &SomeData; + MaterialAttributeData typeErased{"pointer!", MaterialAttributeType::Pointer, &pointer}; + CORRADE_COMPARE(typeErased.name(), "pointer!"); + CORRADE_COMPARE(typeErased.type(), MaterialAttributeType::Pointer); + CORRADE_COMPARE(typeErased.value(), &SomeData); + /* Any type works */ + CORRADE_COMPARE(typeErased.value(), &SomeData); +} + +void MaterialDataTest::constructAttributeMutablePointer() { + Float data = 85.1f; + + MaterialAttributeData attribute{"pointer!", &data}; + CORRADE_COMPARE(attribute.name(), "pointer!"); + CORRADE_COMPARE(attribute.type(), MaterialAttributeType::MutablePointer); + CORRADE_COMPARE(*static_cast(attribute.value()), &data); + CORRADE_COMPARE(attribute.value(), &data); + /* Any type works */ + CORRADE_COMPARE(attribute.value(), &data); + + /* Type-erased variant */ + Float* pointer = &data; + MaterialAttributeData typeErased{"pointer!", MaterialAttributeType::MutablePointer, &pointer}; + CORRADE_COMPARE(typeErased.name(), "pointer!"); + CORRADE_COMPARE(typeErased.type(), MaterialAttributeType::MutablePointer); + CORRADE_COMPARE(typeErased.value(), &data); + /* Any type works */ + CORRADE_COMPARE(typeErased.value(), &data); +} + void MaterialDataTest::constructAttributeInvalidName() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); @@ -444,6 +506,23 @@ void MaterialDataTest::constructAttributeWrongAccessType() { CORRADE_COMPARE(out.str(), "Trade::MaterialAttributeData::value(): improper type requested for thing3 of Trade::MaterialAttributeType::Matrix4x3\n"); } +void MaterialDataTest::constructAttributeWrongAccessPointerType() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + Int a = 3; + const Float b = 57.0f; + + std::ostringstream out; + Error redirectError{&out}; + MaterialAttributeData{"thing3", &a}.value(); + MaterialAttributeData{"boom", &b}.value(); + CORRADE_COMPARE(out.str(), + "Trade::MaterialAttributeData::value(): improper type requested for thing3 of Trade::MaterialAttributeType::MutablePointer\n" + "Trade::MaterialAttributeData::value(): improper type requested for boom of Trade::MaterialAttributeType::Pointer\n"); +} + void MaterialDataTest::construct() { int state; MaterialData data{MaterialType::Phong, { @@ -722,6 +801,23 @@ void MaterialDataTest::access() { CORRADE_COMPARE(c.alphaMask(), 0.5f); } +void MaterialDataTest::accessPointer() { + const Float a = 3.0f; + Long b = -4; + + MaterialData data{{}, { + {"pointer", &a}, + {"mutable", &b} + }}; + CORRADE_COMPARE(data.attributeType("pointer"), MaterialAttributeType::Pointer); + CORRADE_COMPARE(data.attributeType("mutable"), MaterialAttributeType::MutablePointer); + + CORRADE_COMPARE(*static_cast(data.attribute("pointer")), &a); + CORRADE_COMPARE(*static_cast(data.attribute("mutable")), &b); + CORRADE_COMPARE(data.attribute("pointer"), &a); + CORRADE_COMPARE(data.attribute("mutable"), &b); +} + void MaterialDataTest::accessOptional() { MaterialData data{{}, { {MaterialAttribute::AlphaMask, 0.5f}, @@ -868,6 +964,32 @@ void MaterialDataTest::accessWrongType() { "Trade::MaterialData::attribute(): improper type requested for DiffuseColor of Trade::MaterialAttributeType::Vector4\n"); } +void MaterialDataTest::accessWrongPointerType() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + Int a = 3; + const Double b = 57.0; + + MaterialData data{{}, { + {"mutablePointer", &a}, + {"pointer", &b} + }}; + + /* These are fine (type is not checked) */ + data.attribute("mutablePointer"); + data.attribute("pointer"); + + std::ostringstream out; + Error redirectError{&out}; + data.attribute("mutablePointer"); + data.attribute("pointer"); + CORRADE_COMPARE(out.str(), + "Trade::MaterialData::attribute(): improper type requested for mutablePointer of Trade::MaterialAttributeType::MutablePointer\n" + "Trade::MaterialData::attribute(): improper type requested for pointer of Trade::MaterialAttributeType::Pointer\n"); +} + void MaterialDataTest::release() { MaterialData data{{}, { {"DiffuseColor", 0xff3366aa_rgbaf}, From b424b9dc4b4cbedd2c39217e981d3e26f39e0de4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 25 Jul 2020 22:08:31 +0200 Subject: [PATCH 09/36] Trade: allow storing strings in MaterialData. Because We Can. No, actually, this will be used for upcoming material layers, it's not a bloat. However, this means you can do things like Trade::MaterialData weDoCssHere{{}, { {"color", "navy"}, {"highlight", "rgba(35, 255, 78, 0.75)"}, {"dropShadow", "var(--shadow-color)"} }}; --- src/Magnum/Trade/MaterialData.cpp | 50 ++++++++- src/Magnum/Trade/MaterialData.h | 110 +++++++++++++++++-- src/Magnum/Trade/Test/MaterialDataTest.cpp | 121 ++++++++++++++++++++- 3 files changed, 270 insertions(+), 11 deletions(-) diff --git a/src/Magnum/Trade/MaterialData.cpp b/src/Magnum/Trade/MaterialData.cpp index 947e8613e5..b0e1ea0612 100644 --- a/src/Magnum/Trade/MaterialData.cpp +++ b/src/Magnum/Trade/MaterialData.cpp @@ -104,12 +104,29 @@ std::size_t materialAttributeTypeSize(const MaterialAttributeType type) { case MaterialAttributeType::Pointer: case MaterialAttributeType::MutablePointer: return sizeof(void*); + + case MaterialAttributeType::String: + CORRADE_ASSERT_UNREACHABLE("Trade::materialAttributeTypeSize(): string size is unknown", {}); } 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 */ { + /* Special handling for strings */ + if(type == MaterialAttributeType::String) { + const auto& stringValue = *static_cast(value); + /* The 4 extra bytes are for a null byte after both the name and value, + a type and a string size */ + CORRADE_ASSERT(name.size() + stringValue.size() + 4 <= Implementation::MaterialAttributeDataSize, + "Trade::MaterialAttributeData: name" << name << "and value" << stringValue << "too long, expected at most" << Implementation::MaterialAttributeDataSize - 4 << "bytes in total but got" << name.size() + stringValue.size(), ); + _data.type = MaterialAttributeType::String; + std::memcpy(_data.data + 1, name.data(), name.size()); + std::memcpy(_data.data + Implementation::MaterialAttributeDataSize - stringValue.size() - 2, stringValue.data(), stringValue.size()); + _data.data[Implementation::MaterialAttributeDataSize - 1] = stringValue.size(); + return; + } + /* 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(), ); @@ -118,22 +135,38 @@ MaterialAttributeData::MaterialAttributeData(const Containers::StringView name, 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 Containers::StringView name, const MaterialAttributeType type, const void* const value) noexcept: MaterialAttributeData{name, type, type == MaterialAttributeType::String ? ~std::size_t{} : 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, ); + + /* No builtin string attributes yet */ + CORRADE_INTERNAL_ASSERT(type != MaterialAttributeType::String); + _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 { + if(_data.type == MaterialAttributeType::String) + return _data.s.nameValue + Implementation::MaterialAttributeDataSize - _data.s.size - 3; return _data.data + Implementation::MaterialAttributeDataSize - materialAttributeTypeSize(_data.type); } +#ifndef DOXYGEN_GENERATING_OUTPUT +/* On Windows (MSVC, clang-cl and MinGw) it needs an explicit export otherwise + the symbol doesn't get exported. */ +template<> MAGNUM_TRADE_EXPORT Containers::StringView MaterialAttributeData::value() const { + CORRADE_ASSERT(_data.type == MaterialAttributeType::String, + "Trade::MaterialAttributeData::value():" << (_data.data + 1) << "of" << _data.type << "can't be retrieved as a string", {}); + return {_data.s.nameValue + Implementation::MaterialAttributeDataSize - _data.s.size - 3, _data.s.size, Containers::StringViewFlag::NullTerminated}; +} +#endif + MaterialData::MaterialData(const MaterialTypes types, Containers::Array&& data, const void* const importerState) noexcept: _data{std::move(data)}, _types{types}, _importerState{importerState} { #ifndef CORRADE_NO_ASSERT /* Not checking what's already done in MaterialAttributeData constructor. @@ -270,6 +303,20 @@ const void* MaterialData::attribute(const MaterialAttribute name) const { return attribute(string); } +#ifndef DOXYGEN_GENERATING_OUTPUT +/* On Windows (MSVC, clang-cl and MinGw) it needs an explicit export otherwise + the symbol doesn't get exported. */ +template<> MAGNUM_TRADE_EXPORT Containers::StringView MaterialData::attribute(const UnsignedInt id) const { + /* Can't delegate to attribute() returning const void* because that doesn't + include the size */ + CORRADE_ASSERT(id < _data.size(), + "Trade::MaterialData::attribute(): index" << id << "out of range for" << _data.size() << "attributes", {}); + CORRADE_ASSERT(_data[id]._data.type == MaterialAttributeType::String, + "Trade::MaterialData::attribute():" << (_data[id]._data.data + 1) << "of" << _data[id]._data.type << "can't be retrieved as a string", {}); + return {_data[id]._data.s.nameValue + Implementation::MaterialAttributeDataSize - _data[id]._data.s.size - 3, _data[id]._data.s.size, Containers::StringViewFlag::NullTerminated}; +} +#endif + const void* MaterialData::tryAttribute(const Containers::StringView name) const { const UnsignedInt id = attributeFor(name); if(id == ~UnsignedInt{}) return nullptr; @@ -355,6 +402,7 @@ Debug& operator<<(Debug& debug, const MaterialAttributeType value) { _c(Matrix4x3) _c(Pointer) _c(MutablePointer) + _c(String) #undef _c /* LCOV_EXCL_STOP */ } diff --git a/src/Magnum/Trade/MaterialData.h b/src/Magnum/Trade/MaterialData.h index 3d172ee802..83ab9c932e 100644 --- a/src/Magnum/Trade/MaterialData.h +++ b/src/Magnum/Trade/MaterialData.h @@ -332,11 +332,21 @@ enum class MaterialAttributeType: UnsignedByte { * `T` but the user has to ensure the type is correct. */ MutablePointer, + + /** + * Null-terminated string. Can be stored using any type convertible to + * @ref Corrade::Containers::StringView, retrieval has to be done using + * @ref Corrade::Containers::StringView. + */ + String }; /** @brief Byte size of a material attribute type @m_since_latest + +Can't be used with @ref MaterialAttributeType::String, as the size varies +depending on the value. */ MAGNUM_TRADE_EXPORT std::size_t materialAttributeTypeSize(MaterialAttributeType type); @@ -382,11 +392,33 @@ class MAGNUM_TRADE_EXPORT MaterialAttributeData { * don't need @cpp constexpr @ce, as it additionally checks that given * attribute has the expected type. */ - template constexpr /*implicit*/ MaterialAttributeData(Containers::StringView name, const T& value) noexcept; + template::value>::type + #endif + > constexpr /*implicit*/ MaterialAttributeData(Containers::StringView name, const T& value) noexcept; + + /** + * @brief Construct with a string name and value + * @param name Attribute name + * @param value Attribute value + * + * The combined length of @p name and @p value is expected to fit into + * 61 bytes. Type is set to @ref MaterialAttributeType::String. + * + * This function is useful in @cpp constexpr @ce contexts and for + * creating custom material attributes. For known attributes prefer to + * use @ref MaterialAttributeData(MaterialAttribute, const T&) if you + * don't need @cpp constexpr @ce, as it additionally checks that given + * attribute has the expected type. + */ + constexpr /*implicit*/ MaterialAttributeData(Containers::StringView name, Containers::StringView value) noexcept; + #ifndef DOXYGEN_GENERATING_OUTPUT - /* "Sure can't be constexpr" overload to avoid going through the + /* "Sure can't be constexpr" overloads to avoid going through the *insane* overload puzzle when not needed */ - template /*implicit*/ MaterialAttributeData(const char* name, const T& value) noexcept: MaterialAttributeData{name, Implementation::MaterialAttributeTypeFor::type(), sizeof(T), &value} {} + template::value>::type> /*implicit*/ MaterialAttributeData(const char* name, const T& value) noexcept: MaterialAttributeData{name, Implementation::MaterialAttributeTypeFor::type(), sizeof(T), &value} {} + /*implicit*/ MaterialAttributeData(const char* name, Containers::StringView value) noexcept: MaterialAttributeData{name, MaterialAttributeType::String, 0, &value} {} #endif /** @@ -402,7 +434,11 @@ class MAGNUM_TRADE_EXPORT MaterialAttributeData { * * @snippet MagnumTrade.cpp MaterialAttributeData-name */ - template /*implicit*/ MaterialAttributeData(MaterialAttribute name, const T& value) noexcept: MaterialAttributeData{name, Implementation::MaterialAttributeTypeFor::type(), &value} {} + template::value>::type + #endif + > /*implicit*/ MaterialAttributeData(MaterialAttribute name, const T& value) noexcept: MaterialAttributeData{name, Implementation::MaterialAttributeTypeFor::type(), &value} {} /** * @brief Construct from a type-erased value @@ -410,9 +446,15 @@ class MAGNUM_TRADE_EXPORT MaterialAttributeData { * @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. + * In case @p type is not @ref MaterialAttributeType::String, copies a + * number of bytes according to @ref materialAttributeTypeSize() from + * @p value. The @p name together with @p value is expected to fit into + * 62 bytes. + * + * In case @p type is @ref MaterialAttributeType::String, @p value is + * expected to point to a @ref Containers::StringView. The combined + * length of @p name and @p value strings is expected to fit into 61 + * bytes. */ /*implicit*/ MaterialAttributeData(Containers::StringView name, MaterialAttributeType type, const void* value) noexcept; @@ -447,6 +489,12 @@ class MAGNUM_TRADE_EXPORT MaterialAttributeData { * in case of a @ref MaterialAttributeType::Pointer or a * @ref MaterialAttributeType::MutablePointer, returns a * *pointer to a pointer*, not the pointer value itself. + * + * In case of a @ref MaterialAttributeType::String, returns a + * null-terminated @cpp const char* @ce (not a pointer to + * @ref Containers::StringView). This doesn't preserve the actual + * string size in case the string data contain zero bytes, thus prefer + * to use typed access in that case. */ const void* value() const; @@ -484,6 +532,14 @@ class MAGNUM_TRADE_EXPORT MaterialAttributeData { lookup fast and efficient, and data being at the end (instead of right after the null-terminated string) makes them accessible in O(1) as well. */ + struct StringData { + template constexpr explicit StringData(MaterialAttributeType type, Containers::StringView name, Containers::StringView value, Math::Implementation::Sequence): type{type}, nameValue{(sequence < name.size() ? name[sequence] : (sequence - (Implementation::MaterialAttributeDataSize - value.size() - 3) < value.size() ? value[sequence - (Implementation::MaterialAttributeDataSize - value.size() - 3)] : '\0'))...}, size{UnsignedByte(value.size())} {} + constexpr explicit StringData(MaterialAttributeType type, Containers::StringView name, Containers::StringView value): StringData{type, name, value, typename Math::Implementation::GenerateSequence::Type{}} {} + + MaterialAttributeType type; + char nameValue[Implementation::MaterialAttributeDataSize - 2]; + UnsignedByte size; + }; union ErasedScalar { constexpr explicit ErasedScalar(Float value): f{value} {} constexpr explicit ErasedScalar(Deg value): f{Float(value)} {} @@ -529,6 +585,8 @@ class MAGNUM_TRADE_EXPORT MaterialAttributeData { union CORRADE_ALIGNAS(8) Storage { constexpr explicit Storage() noexcept: data{} {} + constexpr explicit Storage(Containers::StringView name, Containers::StringView value) noexcept: s{MaterialAttributeType::String, name, value} {} + template constexpr explicit Storage(typename std::enable_if::type type, Containers::StringView name, const T& value) noexcept: _1{type, name, value} {} template constexpr explicit Storage(typename std::enable_if::value, MaterialAttributeType>::type type, Containers::StringView name, const T& value) noexcept: _4{type, name, value} {} template constexpr explicit Storage(typename std::enable_if::value && !std::is_pointer::value, MaterialAttributeType>::type type, Containers::StringView name, const T& value) noexcept: _8{type, name, value} {} @@ -544,6 +602,7 @@ class MAGNUM_TRADE_EXPORT MaterialAttributeData { MaterialAttributeType type; char data[Implementation::MaterialAttributeDataSize]; + StringData s; Data _1; Data p; Data _4; @@ -810,6 +869,12 @@ class MAGNUM_TRADE_EXPORT MaterialData { * in case of a @ref MaterialAttributeType::Pointer or a * @ref MaterialAttributeType::MutablePointer, returns a * *pointer to a pointer*, not the pointer value itself. + * + * In case of a @ref MaterialAttributeType::String returns a + * null-terminated @cpp const char* @ce (not a pointer to + * @ref Containers::StringView). This doesn't preserve the actual + * string size in case the string data contain zero bytes, thus prefer + * to use typed access in that case. */ const void* attribute(UnsignedInt id) const; @@ -821,6 +886,12 @@ class MAGNUM_TRADE_EXPORT MaterialData { * in case of a @ref MaterialAttributeType::Pointer or a * @ref MaterialAttributeType::MutablePointer, returns a * *pointer to a pointer*, not the pointer value itself. + * + * In case of a @ref MaterialAttributeType::String returns a + * null-terminated @cpp const char* @ce (not a pointer to + * @ref Containers::StringView). This doesn't preserve the actual + * string size in case the string data contain zero bytes, thus prefer + * to use typed access in that case. * @see @ref hasAttribute(), @ref tryAttribute(), @ref attributeOr() */ const void* attribute(Containers::StringView name) const; @@ -831,7 +902,8 @@ class MAGNUM_TRADE_EXPORT MaterialData { * * 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. + * for given @p id. In case of a string, the returned view always has + * @ref Corrade::Containers::StringViewFlag::NullTerminated set. */ template T attribute(UnsignedInt id) const; @@ -840,6 +912,8 @@ class MAGNUM_TRADE_EXPORT MaterialData { * * The @p name is expected to exist. Expects that @p T corresponds to * @ref attributeType(Containers::StringView) const for given @p name. + * In case of a string, the returned view always has + * @ref Corrade::Containers::StringViewFlag::NullTerminated set. * @see @ref hasAttribute() */ template T attribute(Containers::StringView name) const; @@ -996,6 +1070,8 @@ namespace Implementation { return MaterialAttributeType::MutablePointer; } }; + /* No specialization for StringView as this type trait should not be used + in that case */ #ifndef DOXYGEN_GENERATING_OUTPUT #define _c(type_) template<> struct MaterialAttributeTypeFor { \ constexpr static MaterialAttributeType type() { \ @@ -1035,7 +1111,15 @@ namespace Implementation { } /* The 2 extra bytes are for a null byte after the name and a type */ -template constexpr MaterialAttributeData::MaterialAttributeData(const Containers::StringView name, const T& value) noexcept: _data{Implementation::MaterialAttributeTypeFor::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::type() << "but got" << name.size()), name), value} {} +template constexpr MaterialAttributeData::MaterialAttributeData(const Containers::StringView name, const T& value) noexcept: _data{Implementation::MaterialAttributeTypeFor::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::type() << "but got" << name.size()), name), value} {} + +/* The 4 extra bytes are for a null byte after both the name and value, a type + and a string size */ +constexpr MaterialAttributeData::MaterialAttributeData(const Containers::StringView name, const Containers::StringView value) noexcept: _data{(CORRADE_CONSTEXPR_ASSERT(name.size() + value.size() + 4 <= Implementation::MaterialAttributeDataSize, "Trade::MaterialAttributeData: name" << name << "and value" << value << "too long, expected at most" << Implementation::MaterialAttributeDataSize - 4 << "bytes in total but got" << name.size() + value.size()), name), value} {} template T MaterialAttributeData::value() const { CORRADE_ASSERT(Implementation::MaterialAttributeTypeFor::type() == _data.type, @@ -1043,6 +1127,10 @@ template T MaterialAttributeData::value() const { return *reinterpret_cast(value()); } +#ifndef DOXYGEN_GENERATING_OUTPUT +template<> Containers::StringView MaterialAttributeData::value() const; +#endif + template T MaterialData::attribute(UnsignedInt id) const { const void* const value = attribute(id); #ifdef CORRADE_GRACEFUL_ASSERT @@ -1053,6 +1141,10 @@ template T MaterialData::attribute(UnsignedInt id) const { return *reinterpret_cast(value); } +#ifndef DOXYGEN_GENERATING_OUTPUT +template<> Containers::StringView MaterialData::attribute(UnsignedInt) const; +#endif + template T MaterialData::attribute(Containers::StringView name) const { const UnsignedInt id = attributeFor(name); CORRADE_ASSERT(id != ~UnsignedInt{}, diff --git a/src/Magnum/Trade/Test/MaterialDataTest.cpp b/src/Magnum/Trade/Test/MaterialDataTest.cpp index 8cfd7c9bfe..ddc690703f 100644 --- a/src/Magnum/Trade/Test/MaterialDataTest.cpp +++ b/src/Magnum/Trade/Test/MaterialDataTest.cpp @@ -57,13 +57,16 @@ class MaterialDataTest: public TestSuite::Tester { void constructAttributePointer(); void constructAttributeMutablePointer(); + void constructAttributeStringValue(); void constructAttributeInvalidName(); void constructAttributeWrongTypeForName(); void constructAttributeInvalidType(); void constructAttributeTooLarge(); + void constructAttributeTooLargeString(); void constructAttributeWrongAccessType(); void constructAttributeWrongAccessPointerType(); + void constructAttributeWrongAccessTypeString(); void construct(); void constructEmptyAttribute(); @@ -80,12 +83,14 @@ class MaterialDataTest: public TestSuite::Tester { void access(); void accessPointer(); + void accessString(); void accessOptional(); void accessOutOfBounds(); void accessInvalidAttributeName(); void accessNotFound(); void accessWrongType(); void accessWrongPointerType(); + void accessWrongTypeString(); void release(); @@ -162,13 +167,16 @@ MaterialDataTest::MaterialDataTest() { &MaterialDataTest::constructAttributePointer, &MaterialDataTest::constructAttributeMutablePointer, + &MaterialDataTest::constructAttributeStringValue, &MaterialDataTest::constructAttributeInvalidName, &MaterialDataTest::constructAttributeWrongTypeForName, &MaterialDataTest::constructAttributeInvalidType, &MaterialDataTest::constructAttributeTooLarge, + &MaterialDataTest::constructAttributeTooLargeString, &MaterialDataTest::constructAttributeWrongAccessType, &MaterialDataTest::constructAttributeWrongAccessPointerType, + &MaterialDataTest::constructAttributeWrongAccessTypeString, &MaterialDataTest::construct, &MaterialDataTest::constructEmptyAttribute}); @@ -188,12 +196,14 @@ MaterialDataTest::MaterialDataTest() { &MaterialDataTest::access, &MaterialDataTest::accessPointer, + &MaterialDataTest::accessString, &MaterialDataTest::accessOptional, &MaterialDataTest::accessOutOfBounds, &MaterialDataTest::accessInvalidAttributeName, &MaterialDataTest::accessNotFound, &MaterialDataTest::accessWrongType, &MaterialDataTest::accessWrongPointerType, + &MaterialDataTest::accessWrongTypeString, &MaterialDataTest::release, @@ -256,9 +266,11 @@ void MaterialDataTest::attributeTypeSizeInvalid() { Error redirectError{&out}; materialAttributeTypeSize(MaterialAttributeType(0x0)); materialAttributeTypeSize(MaterialAttributeType(0xfe)); + materialAttributeTypeSize(MaterialAttributeType::String); CORRADE_COMPARE(out.str(), "Trade::materialAttributeTypeSize(): invalid type Trade::MaterialAttributeType(0x0)\n" - "Trade::materialAttributeTypeSize(): invalid type Trade::MaterialAttributeType(0xfe)\n"); + "Trade::materialAttributeTypeSize(): invalid type Trade::MaterialAttributeType(0xfe)\n" + "Trade::materialAttributeTypeSize(): string size is unknown\n"); } void MaterialDataTest::attributeMap() { @@ -439,6 +451,41 @@ void MaterialDataTest::constructAttributeMutablePointer() { CORRADE_COMPARE(typeErased.value(), &data); } +void MaterialDataTest::constructAttributeStringValue() { + /* Explicitly using a non-null-terminated view on input to check the null + byte isn't read by accident*/ + MaterialAttributeData attribute{"name that's long", "and a value\0that's also long but still fits!!"_s.except(1)}; + CORRADE_COMPARE(attribute.name(), "name that's long"); + CORRADE_COMPARE(attribute.name().flags(), Containers::StringViewFlag::NullTerminated); + CORRADE_COMPARE(attribute.name()[attribute.name().size()], '\0'); + CORRADE_COMPARE(attribute.type(), MaterialAttributeType::String); + /* Pointer access will stop at the first null byte, but typed access won't */ + CORRADE_COMPARE(static_cast(attribute.value()), "and a value"_s); + CORRADE_COMPARE(attribute.value(), "and a value\0that's also long but still fits!"_s); + CORRADE_COMPARE(attribute.value().flags(), Containers::StringViewFlag::NullTerminated); + CORRADE_COMPARE(attribute.value()[attribute.value().size()], '\0'); + + constexpr MaterialAttributeData cattribute{"name that's long"_s, "and a value\0that's also long but still fits!!"_s.except(1)}; + CORRADE_COMPARE(cattribute.name(), "name that's long"); + CORRADE_COMPARE(cattribute.name().flags(), Containers::StringViewFlag::NullTerminated); + CORRADE_COMPARE(cattribute.name()[cattribute.name().size()], '\0'); + CORRADE_COMPARE(cattribute.type(), MaterialAttributeType::String); + CORRADE_COMPARE(cattribute.value(), "and a value\0that's also long but still fits!"_s); + CORRADE_COMPARE(cattribute.value().flags(), Containers::StringViewFlag::NullTerminated); + CORRADE_COMPARE(cattribute.value()[cattribute.value().size()], '\0'); + + /* Type-erased variant */ + const Containers::StringView value = "and a value\0that's also long but still fits!!"_s.except(1); + MaterialAttributeData typeErased{"name that's long", MaterialAttributeType::String, &value}; + CORRADE_COMPARE(typeErased.name(), "name that's long"); + CORRADE_COMPARE(typeErased.name().flags(), Containers::StringViewFlag::NullTerminated); + CORRADE_COMPARE(typeErased.name()[typeErased.name().size()], '\0'); + CORRADE_COMPARE(typeErased.type(), MaterialAttributeType::String); + CORRADE_COMPARE(typeErased.value(), "and a value\0that's also long but still fits!"_s); + CORRADE_COMPARE(typeErased.value().flags(), Containers::StringViewFlag::NullTerminated); + CORRADE_COMPARE(typeErased.value()[typeErased.value().size()], '\0'); +} + void MaterialDataTest::constructAttributeInvalidName() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); @@ -495,6 +542,22 @@ void MaterialDataTest::constructAttributeTooLarge() { "Trade::MaterialAttributeData: name attributeIsLong too long, expected at most 14 bytes for Trade::MaterialAttributeType::Matrix3x4 but got 15\n"); } +void MaterialDataTest::constructAttributeTooLargeString() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + std::ostringstream out; + Error redirectError{&out}; + MaterialAttributeData{"attribute is long", "This is a problem, got a long piece of text!"}; + /* Constexpr variant has the same assert, but in the header. It should have + the same output. */ + /*constexpr*/ MaterialAttributeData{"attribute is long"_s, "This is a problem, got a long piece of text!"_s}; + CORRADE_COMPARE(out.str(), + "Trade::MaterialAttributeData: name attribute is long and value This is a problem, got a long piece of text! too long, expected at most 60 bytes in total but got 61\n" + "Trade::MaterialAttributeData: name attribute is long and value This is a problem, got a long piece of text! too long, expected at most 60 bytes in total but got 61\n"); +} + void MaterialDataTest::constructAttributeWrongAccessType() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); @@ -523,6 +586,17 @@ void MaterialDataTest::constructAttributeWrongAccessPointerType() { "Trade::MaterialAttributeData::value(): improper type requested for boom of Trade::MaterialAttributeType::Pointer\n"); } +void MaterialDataTest::constructAttributeWrongAccessTypeString() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + std::ostringstream out; + Error redirectError{&out}; + MaterialAttributeData{"thing3", Matrix4x3{}}.value(); + CORRADE_COMPARE(out.str(), "Trade::MaterialAttributeData::value(): thing3 of Trade::MaterialAttributeType::Matrix4x3 can't be retrieved as a string\n"); +} + void MaterialDataTest::construct() { int state; MaterialData data{MaterialType::Phong, { @@ -818,6 +892,19 @@ void MaterialDataTest::accessPointer() { CORRADE_COMPARE(data.attribute("mutable"), &b); } +void MaterialDataTest::accessString() { + MaterialData data{{}, { + {"name?", "THIS IS\0WHO I AM!"_s} + }}; + CORRADE_COMPARE(data.attributeType("name?"), MaterialAttributeType::String); + + /* Pointer access will stop at the first null byte, but typed access won't */ + CORRADE_COMPARE(static_cast(data.attribute(0)), "THIS IS"_s); + CORRADE_COMPARE(data.attribute(0), "THIS IS\0WHO I AM!"_s); + CORRADE_COMPARE(data.attribute(0).flags(), Containers::StringViewFlag::NullTerminated); + CORRADE_COMPARE(data.attribute(0)[data.attribute(0).size()], '\0'); +} + void MaterialDataTest::accessOptional() { MaterialData data{{}, { {MaterialAttribute::AlphaMask, 0.5f}, @@ -859,10 +946,12 @@ void MaterialDataTest::accessOutOfBounds() { data.attributeType(2); data.attribute(2); data.attribute(2); + data.attribute(2); CORRADE_COMPARE(out.str(), "Trade::MaterialData::attributeName(): index 2 out of range for 2 attributes\n" "Trade::MaterialData::attributeType(): index 2 out of range for 2 attributes\n" "Trade::MaterialData::attribute(): index 2 out of range for 2 attributes\n" + "Trade::MaterialData::attribute(): index 2 out of range for 2 attributes\n" "Trade::MaterialData::attribute(): index 2 out of range for 2 attributes\n"); } @@ -990,6 +1079,36 @@ void MaterialDataTest::accessWrongPointerType() { "Trade::MaterialData::attribute(): improper type requested for pointer of Trade::MaterialAttributeType::Pointer\n"); } +void MaterialDataTest::accessWrongTypeString() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + MaterialData data{{}, { + {"Shininess", 0.0f} + }}; + + std::ostringstream out; + Error redirectError{&out}; + data.attribute(0); + data.attribute(MaterialAttribute::Shininess); + data.attribute("Shininess"); + data.tryAttribute(MaterialAttribute::Shininess); + data.tryAttribute("Shininess"); + data.attributeOr(MaterialAttribute::Shininess, Containers::StringView{}); + data.attributeOr("Shininess", Containers::StringView{}); + CORRADE_COMPARE(out.str(), + "Trade::MaterialData::attribute(): Shininess of Trade::MaterialAttributeType::Float can't be retrieved as a string\n" + "Trade::MaterialData::attribute(): Shininess of Trade::MaterialAttributeType::Float can't be retrieved as a string\n" + "Trade::MaterialData::attribute(): Shininess of Trade::MaterialAttributeType::Float can't be retrieved as a string\n" + /* tryAttribute() and attributeOr() delegate to attribute() so the + assert is the same */ + "Trade::MaterialData::attribute(): Shininess of Trade::MaterialAttributeType::Float can't be retrieved as a string\n" + "Trade::MaterialData::attribute(): Shininess of Trade::MaterialAttributeType::Float can't be retrieved as a string\n" + "Trade::MaterialData::attribute(): Shininess of Trade::MaterialAttributeType::Float can't be retrieved as a string\n" + "Trade::MaterialData::attribute(): Shininess of Trade::MaterialAttributeType::Float can't be retrieved as a string\n"); +} + void MaterialDataTest::release() { MaterialData data{{}, { {"DiffuseColor", 0xff3366aa_rgbaf}, From 0510449b36395ba337cc7606f7bf4dbf10c155c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 28 Jul 2020 23:46:47 +0200 Subject: [PATCH 10/36] Trade: material layer support in the new MaterialData. --- .../materialAttributeProperties.hpp | 1 + src/Magnum/Trade/MaterialData.cpp | 380 ++++++-- src/Magnum/Trade/MaterialData.h | 763 ++++++++++++--- src/Magnum/Trade/Test/MaterialDataTest.cpp | 907 ++++++++++++++++-- 4 files changed, 1783 insertions(+), 268 deletions(-) diff --git a/src/Magnum/Trade/Implementation/materialAttributeProperties.hpp b/src/Magnum/Trade/Implementation/materialAttributeProperties.hpp index 037219da0b..9b78c0a506 100644 --- a/src/Magnum/Trade/Implementation/materialAttributeProperties.hpp +++ b/src/Magnum/Trade/Implementation/materialAttributeProperties.hpp @@ -25,6 +25,7 @@ /* See Magnum/Trade/MaterialData.cpp and Magnum/Trade/Test/MaterialDataTest.cpp */ #ifdef _c +_cnt(LayerName,"$LayerName",String,Containers::StringView) _c(AlphaMask,Float) _ct(AlphaBlend,Bool,bool) _ct(DoubleSided,Bool,bool) diff --git a/src/Magnum/Trade/MaterialData.cpp b/src/Magnum/Trade/MaterialData.cpp index b0e1ea0612..2106b1acdb 100644 --- a/src/Magnum/Trade/MaterialData.cpp +++ b/src/Magnum/Trade/MaterialData.cpp @@ -48,9 +48,11 @@ constexpr struct { } AttributeMap[]{ #define _c(name, type) {#name ## _s, MaterialAttributeType::type, sizeof(type)}, #define _ct(name, typeName, type) {#name ## _s, MaterialAttributeType::typeName, sizeof(type)}, + #define _cnt(name, string, typeName, type) {string ## _s, MaterialAttributeType::typeName, 255}, #include "Magnum/Trade/Implementation/materialAttributeProperties.hpp" #undef _c #undef _ct + #undef _cnt }; #endif @@ -143,12 +145,25 @@ MaterialAttributeData::MaterialAttributeData(const MaterialAttribute name, const CORRADE_ASSERT(AttributeMap[UnsignedInt(name) - 1].type == type, "Trade::MaterialAttributeData: expected" << AttributeMap[UnsignedInt(name) - 1].type << "for" << name << "but got" << type, ); - /* No builtin string attributes yet */ - CORRADE_INTERNAL_ASSERT(type != MaterialAttributeType::String); - _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 Containers::StringView stringName = AttributeMap[UnsignedInt(name) - 1].name; + std::memcpy(_data.data + 1, stringName.data(), stringName.size()); + + /* Special handling for strings, in that case it's sot known in advance + that we fit and has to be checked */ + if(type == MaterialAttributeType::String) { + const auto& stringValue = *static_cast(value); + /* The 4 extra bytes are for a null byte after both the name and value, + a type and a string size */ + CORRADE_ASSERT(stringName.size() + stringValue.size() + 4 <= Implementation::MaterialAttributeDataSize, + "Trade::MaterialAttributeData: name" << stringName << "and value" << stringValue << "too long, expected at most" << Implementation::MaterialAttributeDataSize - 4 << "bytes in total but got" << stringName.size() + stringValue.size(), ); + _data.type = MaterialAttributeType::String; + std::memcpy(_data.data + Implementation::MaterialAttributeDataSize - stringValue.size() - 2, stringValue.data(), stringValue.size()); + _data.data[Implementation::MaterialAttributeDataSize - 1] = stringValue.size(); + return; + } else { + std::memcpy(_data.data + Implementation::MaterialAttributeDataSize - AttributeMap[UnsignedInt(name) - 1].size, value, AttributeMap[UnsignedInt(name) - 1].size); + } } const void* MaterialAttributeData::value() const { @@ -167,7 +182,7 @@ template<> MAGNUM_TRADE_EXPORT Containers::StringView MaterialAttributeData::val } #endif -MaterialData::MaterialData(const MaterialTypes types, Containers::Array&& data, const void* const importerState) noexcept: _data{std::move(data)}, _types{types}, _importerState{importerState} { +MaterialData::MaterialData(const MaterialTypes types, Containers::Array&& attributeData, Containers::Array&& layerData, const void* const importerState) noexcept: _data{std::move(attributeData)}, _layerOffsets{std::move(layerData)}, _types{types}, _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. */ @@ -176,40 +191,66 @@ MaterialData::MaterialData(const MaterialTypes types, Containers::Array 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; + /* Go through all layers and sort each independently */ + const UnsignedInt implicitLayerData[]{UnsignedInt(_data.size())}; + const Containers::ArrayView layerOffsets = + _layerOffsets ? _layerOffsets : Containers::arrayView(implicitLayerData); + UnsignedInt begin = 0; + for(std::size_t i = 0; i != layerOffsets.size(); ++i) { + const UnsignedInt end = layerOffsets[i]; + CORRADE_ASSERT(begin <= end && end <= _data.size(), + "Trade::MaterialData: invalid range (" << Debug::nospace << begin << Debug::nospace << "," << end << Debug::nospace <<") for layer" << i << "with" << _data.size() << "attributes in total", ); + + /* 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(end - begin > 1) for(std::size_t i = begin + 1; i != end; ++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; + } + + begin = end; } } -MaterialData::MaterialData(const MaterialTypes types, const std::initializer_list data, const void* const importerState): MaterialData{types, Implementation::initializerListToArrayWithDefaultDeleter(data), importerState} {} +MaterialData::MaterialData(const MaterialTypes types, const std::initializer_list attributeData, const std::initializer_list layerData, const void* const importerState): MaterialData{types, Implementation::initializerListToArrayWithDefaultDeleter(attributeData), Implementation::initializerListToArrayWithDefaultDeleter(layerData), importerState} {} -MaterialData::MaterialData(const MaterialTypes types, DataFlags, const Containers::ArrayView data, const void* const importerState) noexcept: _data{Containers::Array{const_cast(data.data()), data.size(), [](MaterialAttributeData*, std::size_t){}}}, _types{types}, _importerState{importerState} { +MaterialData::MaterialData(const MaterialTypes types, DataFlags, const Containers::ArrayView attributeData, DataFlags, Containers::ArrayView layerData, const void* const importerState) noexcept: _data{Containers::Array{const_cast(attributeData.data()), attributeData.size(), [](MaterialAttributeData*, std::size_t){}}}, _layerOffsets{Containers::Array{const_cast(layerData.data()), layerData.size(), [](UnsignedInt*, std::size_t){}}}, _types{types}, _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", ); + /* Go through all layers and verify that each is independently sorted */ + const UnsignedInt implicitLayerData[]{UnsignedInt(_data.size())}; + const Containers::ArrayView layerOffsets = + _layerOffsets ? _layerOffsets : Containers::arrayView(implicitLayerData); + UnsignedInt begin = 0; + for(std::size_t i = 0; i != layerOffsets.size(); ++i) { + const UnsignedInt end = layerOffsets[i]; + CORRADE_ASSERT(begin <= end && end <= _data.size(), + "Trade::MaterialData: invalid range (" << Debug::nospace << begin << Debug::nospace << "," << end << Debug::nospace <<") for layer" << i << "with" << _data.size() << "attributes in total", ); + + if(end - begin > 1) for(std::size_t i = begin + 1; i != end; ++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", ); + } + + begin = end; } #endif } @@ -228,105 +269,276 @@ Containers::StringView MaterialData::attributeString(const MaterialAttribute nam 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) { +UnsignedInt MaterialData::layerFor(const Containers::StringView layer) const { + for(std::size_t i = 1; i < _layerOffsets.size(); ++i) { + if(_layerOffsets[i] > _layerOffsets[i - 1] && + _data[_layerOffsets[i - 1]].name() == "$LayerName"_s && + _data[_layerOffsets[i - 1]].value() == layer) + return i; + } + return ~UnsignedInt{}; +} + +bool MaterialData::hasLayer(const Containers::StringView layer) const { + return layerFor(layer) != ~UnsignedInt{}; +} + +UnsignedInt MaterialData::layerId(const Containers::StringView layer) const { + const UnsignedInt id = layerFor(layer); + CORRADE_ASSERT(id != ~UnsignedInt{}, + "Trade::MaterialData::layerId(): layer" << layer << "not found", {}); + return id; +} + +Containers::StringView MaterialData::layerName(const UnsignedInt layer) const { + CORRADE_ASSERT(layer < layerCount(), + "Trade::MaterialData::layerName(): index" << layer << "out of range for" << layerCount() << "layers", {}); + /* Deliberately ignore this attribute in the base material */ + if(layer && _layerOffsets[layer] > _layerOffsets[layer - 1] && _data[_layerOffsets[layer - 1]].name() == "$LayerName") + return _data[_layerOffsets[layer - 1]].value(); + return {}; +} + +UnsignedInt MaterialData::attributeCount(const UnsignedInt layer) const { + CORRADE_ASSERT(layer < layerCount(), + "Trade::MaterialData::attributeCount(): index" << layer << "out of range for" << layerCount() << "layers", {}); + if(!_layerOffsets) return _data.size(); + if(!layer) return _layerOffsets[0]; + return _layerOffsets[layer] - _layerOffsets[layer - 1]; +} + +UnsignedInt MaterialData::attributeCount(const Containers::StringView layer) const { + const UnsignedInt layerId = layerFor(layer); + CORRADE_ASSERT(layerId != ~UnsignedInt{}, + "Trade::MaterialData::attributeCount(): layer" << layer << "not found", {}); + return attributeCount(layerId); +} + +UnsignedInt MaterialData::attributeFor(const UnsignedInt layer, const Containers::StringView name) const { + const MaterialAttributeData* begin = _data.begin() + + (layer && _layerOffsets ? _layerOffsets[layer - 1] : 0); + const MaterialAttributeData* end = + (_layerOffsets ? _data.begin() + _layerOffsets[layer] : _data.end()); + const MaterialAttributeData* const found = std::lower_bound(begin, 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(); + if(found == end || found->name() != name) return ~UnsignedInt{}; + return found - begin; } -bool MaterialData::hasAttribute(const Containers::StringView name) const { - return attributeFor(name) != ~UnsignedInt{}; +bool MaterialData::hasAttribute(const UnsignedInt layer, const Containers::StringView name) const { + CORRADE_ASSERT(layer < layerCount(), + "Trade::MaterialData::hasAttribute(): index" << layer << "out of range for" << layerCount() << "layers", {}); + return attributeFor(layer, name) != ~UnsignedInt{}; } -bool MaterialData::hasAttribute(const MaterialAttribute name) const { +bool MaterialData::hasAttribute(const UnsignedInt layer, const MaterialAttribute name) const { const Containers::StringView string = attributeString(name); CORRADE_ASSERT(string.data(), "Trade::MaterialData::hasAttribute(): invalid name" << name, {}); - return hasAttribute(string); + return hasAttribute(layer, string); +} + +bool MaterialData::hasAttribute(const Containers::StringView layer, const Containers::StringView name) const { + const UnsignedInt layerId = layerFor(layer); + CORRADE_ASSERT(layerId != ~UnsignedInt{}, + "Trade::MaterialData::hasAttribute(): layer" << layer << "not found", {}); + return hasAttribute(layerId, name); +} + +bool MaterialData::hasAttribute(const Containers::StringView layer, const MaterialAttribute name) const { + const Containers::StringView string = attributeString(name); + CORRADE_ASSERT(string.data(), "Trade::MaterialData::hasAttribute(): invalid name" << name, {}); + return hasAttribute(layer, string); +} + +UnsignedInt MaterialData::attributeId(const UnsignedInt layer, const Containers::StringView name) const { + CORRADE_ASSERT(layer < layerCount(), + "Trade::MaterialData::attributeId(): index" << layer << "out of range for" << layerCount() << "layers", {}); + const UnsignedInt id = attributeFor(layer, name); + CORRADE_ASSERT(id != ~UnsignedInt{}, + "Trade::MaterialData::attributeId(): attribute" << name << "not found in layer" << layer, {}); + return id; } -UnsignedInt MaterialData::attributeId(const Containers::StringView name) const { - const UnsignedInt id = attributeFor(name); +UnsignedInt MaterialData::attributeId(const UnsignedInt layer, const MaterialAttribute name) const { + const Containers::StringView string = attributeString(name); + CORRADE_ASSERT(string.data(), "Trade::MaterialData::attributeId(): invalid name" << name, {}); + return attributeId(layer, string); +} + +UnsignedInt MaterialData::attributeId(const Containers::StringView layer, const Containers::StringView name) const { + const UnsignedInt layerId = layerFor(layer); + CORRADE_ASSERT(layerId != ~UnsignedInt{}, + "Trade::MaterialData::attributeId(): layer" << layer << "not found", {}); + const UnsignedInt id = attributeFor(layerId, name); CORRADE_ASSERT(id != ~UnsignedInt{}, - "Trade::MaterialData::attributeId(): attribute" << name << "not found", {}); + "Trade::MaterialData::attributeId(): attribute" << name << "not found in layer" << layer, {}); return id; } -UnsignedInt MaterialData::attributeId(const MaterialAttribute name) const { +UnsignedInt MaterialData::attributeId(const Containers::StringView layer, const MaterialAttribute name) const { const Containers::StringView string = attributeString(name); CORRADE_ASSERT(string.data(), "Trade::MaterialData::attributeId(): invalid name" << name, {}); - return attributeId(string); + return attributeId(layer, string); +} + +Containers::StringView MaterialData::attributeName(const UnsignedInt layer, const UnsignedInt id) const { + CORRADE_ASSERT(layer < layerCount(), + "Trade::MaterialData::attributeName(): index" << layer << "out of range for" << layerCount() << "layers", {}); + CORRADE_ASSERT(id < attributeCount(layer), + "Trade::MaterialData::attributeName(): index" << id << "out of range for" << attributeCount(layer) << "attributes in layer" << layer, {}); + return _data[layerOffset(layer) + id]._data.data + 1; } -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; +Containers::StringView MaterialData::attributeName(const Containers::StringView layer, const UnsignedInt id) const { + const UnsignedInt layerId = layerFor(layer); + CORRADE_ASSERT(layerId != ~UnsignedInt{}, + "Trade::MaterialData::attributeName(): layer" << layer << "not found", {}); + CORRADE_ASSERT(id < attributeCount(layer), + "Trade::MaterialData::attributeName(): index" << id << "out of range for" << attributeCount(layer) << "attributes in layer" << layer, {}); + return _data[layerOffset(layerId) + id]._data.data + 1; +} + +MaterialAttributeType MaterialData::attributeType(const UnsignedInt layer, const UnsignedInt id) const { + CORRADE_ASSERT(layer < layerCount(), + "Trade::MaterialData::attributeType(): index" << layer << "out of range for" << layerCount() << "layers", {}); + CORRADE_ASSERT(id < attributeCount(layer), + "Trade::MaterialData::attributeType(): index" << id << "out of range for" << attributeCount(layer) << "attributes in layer" << layer, {}); + return _data[layerOffset(layer) + id]._data.type; +} + +MaterialAttributeType MaterialData::attributeType(const UnsignedInt layer, const Containers::StringView name) const { + CORRADE_ASSERT(layer < layerCount(), + "Trade::MaterialData::attributeType(): index" << layer << "out of range for" << layerCount() << "layers", {}); + const UnsignedInt id = attributeFor(layer, name); + CORRADE_ASSERT(id != ~UnsignedInt{}, + "Trade::MaterialData::attributeType(): attribute" << name << "not found in layer" << layer, {}); + return _data[layerOffset(layer) + id]._data.type; +} + +MaterialAttributeType MaterialData::attributeType(const UnsignedInt layer, const MaterialAttribute name) const { + const Containers::StringView string = attributeString(name); + CORRADE_ASSERT(string.data(), "Trade::MaterialData::attributeType(): invalid name" << name, {}); + return attributeType(layer, string); } -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 layer, const UnsignedInt id) const { + const UnsignedInt layerId = layerFor(layer); + CORRADE_ASSERT(layerId != ~UnsignedInt{}, + "Trade::MaterialData::attributeType(): layer" << layer << "not found", {}); + CORRADE_ASSERT(id < attributeCount(layer), + "Trade::MaterialData::attributeType(): index" << id << "out of range for" << attributeCount(layer) << "attributes in layer" << layer, {}); + return _data[layerOffset(layerId) + id]._data.type; } -MaterialAttributeType MaterialData::attributeType(const Containers::StringView name) const { - const UnsignedInt id = attributeFor(name); +MaterialAttributeType MaterialData::attributeType(const Containers::StringView layer, const Containers::StringView name) const { + const UnsignedInt layerId = layerFor(layer); + CORRADE_ASSERT(layerId != ~UnsignedInt{}, + "Trade::MaterialData::attributeType(): layer" << layer << "not found", {}); + const UnsignedInt id = attributeFor(layerId, name); CORRADE_ASSERT(id != ~UnsignedInt{}, - "Trade::MaterialData::attributeType(): attribute" << name << "not found", {}); - return _data[id]._data.type; + "Trade::MaterialData::attributeType(): attribute" << name << "not found in layer" << layer, {}); + return _data[layerOffset(layerId) + id]._data.type; } -MaterialAttributeType MaterialData::attributeType(const MaterialAttribute name) const { +MaterialAttributeType MaterialData::attributeType(const Containers::StringView layer, const MaterialAttribute name) const { const Containers::StringView string = attributeString(name); CORRADE_ASSERT(string.data(), "Trade::MaterialData::attributeType(): invalid name" << name, {}); - return attributeType(string); + return attributeType(layer, 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 UnsignedInt layer, const UnsignedInt id) const { + CORRADE_ASSERT(layer < layerCount(), + "Trade::MaterialData::attribute(): index" << layer << "out of range for" << layerCount() << "layers", {}); + CORRADE_ASSERT(id < attributeCount(layer), + "Trade::MaterialData::attribute(): index" << id << "out of range for" << attributeCount(layer) << "attributes in layer" << layer, {}); + return _data[layerOffset(layer) + id].value(); } -const void* MaterialData::attribute(const Containers::StringView name) const { - const UnsignedInt id = attributeFor(name); +const void* MaterialData::attribute(const UnsignedInt layer, const Containers::StringView name) const { + CORRADE_ASSERT(layer < layerCount(), + "Trade::MaterialData::attribute(): index" << layer << "out of range for" << layerCount() << "layers", {}); + const UnsignedInt id = attributeFor(layer, name); CORRADE_ASSERT(id != ~UnsignedInt{}, - "Trade::MaterialData::attribute(): attribute" << name << "not found", {}); - return _data[id].value(); + "Trade::MaterialData::attribute(): attribute" << name << "not found in layer" << layer, {}); + return _data[layerOffset(layer) + id].value(); } -const void* MaterialData::attribute(const MaterialAttribute name) const { +const void* MaterialData::attribute(const UnsignedInt layer, const MaterialAttribute name) const { const Containers::StringView string = attributeString(name); CORRADE_ASSERT(string.data(), "Trade::MaterialData::attribute(): invalid name" << name, {}); - return attribute(string); + return attribute(layer, string); +} + +const void* MaterialData::attribute(const Containers::StringView layer, const UnsignedInt id) const { + const UnsignedInt layerId = layerFor(layer); + CORRADE_ASSERT(layerId != ~UnsignedInt{}, + "Trade::MaterialData::attribute(): layer" << layer << "not found", {}); + CORRADE_ASSERT(id < attributeCount(layer), + "Trade::MaterialData::attribute(): index" << id << "out of range for" << attributeCount(layer) << "attributes in layer" << layer, {}); + return _data[layerOffset(layerId) + id].value(); +} + +const void* MaterialData::attribute(const Containers::StringView layer, const Containers::StringView name) const { + const UnsignedInt layerId = layerFor(layer); + CORRADE_ASSERT(layerId != ~UnsignedInt{}, + "Trade::MaterialData::attribute(): layer" << layer << "not found", {}); + const UnsignedInt id = attributeFor(layerId, name); + CORRADE_ASSERT(id != ~UnsignedInt{}, + "Trade::MaterialData::attribute(): attribute" << name << "not found in layer" << layer, {}); + return _data[layerOffset(layerId) + id].value(); +} + +const void* MaterialData::attribute(const Containers::StringView layer, const MaterialAttribute name) const { + const Containers::StringView string = attributeString(name); + CORRADE_ASSERT(string.data(), "Trade::MaterialData::attribute(): invalid name" << name, {}); + return attribute(layer, string); } #ifndef DOXYGEN_GENERATING_OUTPUT /* On Windows (MSVC, clang-cl and MinGw) it needs an explicit export otherwise the symbol doesn't get exported. */ -template<> MAGNUM_TRADE_EXPORT Containers::StringView MaterialData::attribute(const UnsignedInt id) const { +template<> MAGNUM_TRADE_EXPORT Containers::StringView MaterialData::attribute(const UnsignedInt layer, const UnsignedInt id) const { /* Can't delegate to attribute() returning const void* because that doesn't include the size */ - CORRADE_ASSERT(id < _data.size(), - "Trade::MaterialData::attribute(): index" << id << "out of range for" << _data.size() << "attributes", {}); - CORRADE_ASSERT(_data[id]._data.type == MaterialAttributeType::String, - "Trade::MaterialData::attribute():" << (_data[id]._data.data + 1) << "of" << _data[id]._data.type << "can't be retrieved as a string", {}); - return {_data[id]._data.s.nameValue + Implementation::MaterialAttributeDataSize - _data[id]._data.s.size - 3, _data[id]._data.s.size, Containers::StringViewFlag::NullTerminated}; + CORRADE_ASSERT(layer < layerCount(), + "Trade::MaterialData::attribute(): index" << layer << "out of range for" << layerCount() << "layers", {}); + CORRADE_ASSERT(id < attributeCount(layer), + "Trade::MaterialData::attribute(): index" << id << "out of range for" << attributeCount(layer) << "attributes in layer" << layer, {}); + const Trade::MaterialAttributeData& data = _data[layerOffset(layer) + id]; + CORRADE_ASSERT(data._data.type == MaterialAttributeType::String, + "Trade::MaterialData::attribute():" << (data._data.data + 1) << "of" << data._data.type << "can't be retrieved as a string", {}); + return {data._data.s.nameValue + Implementation::MaterialAttributeDataSize - data._data.s.size - 3, data._data.s.size, Containers::StringViewFlag::NullTerminated}; +} + +const void* MaterialData::tryAttribute(const UnsignedInt layer, const Containers::StringView name) const { + CORRADE_ASSERT(layer < layerCount(), + "Trade::MaterialData::tryAttribute(): index" << layer << "out of range for" << layerCount() << "layers", {}); + const UnsignedInt id = attributeFor(layer, name); + if(id == ~UnsignedInt{}) return nullptr; + return _data[layerOffset(layer) + id].value(); +} + +const void* MaterialData::tryAttribute(const UnsignedInt layer, const MaterialAttribute name) const { + const Containers::StringView string = attributeString(name); + CORRADE_ASSERT(string.data(), "Trade::MaterialData::tryAttribute(): invalid name" << name, {}); + return tryAttribute(layer, string); } #endif -const void* MaterialData::tryAttribute(const Containers::StringView name) const { - const UnsignedInt id = attributeFor(name); +const void* MaterialData::tryAttribute(const Containers::StringView layer, const Containers::StringView name) const { + const UnsignedInt layerId = layerFor(layer); + CORRADE_ASSERT(layerId != ~UnsignedInt{}, + "Trade::MaterialData::tryAttribute(): layer" << layer << "not found", {}); + const UnsignedInt id = attributeFor(layerId, name); if(id == ~UnsignedInt{}) return nullptr; - return _data[id].value(); + return _data[layerOffset(layerId) + id].value(); } -const void* MaterialData::tryAttribute(const MaterialAttribute name) const { +const void* MaterialData::tryAttribute(const Containers::StringView layer, const MaterialAttribute name) const { const Containers::StringView string = attributeString(name); CORRADE_ASSERT(string.data(), "Trade::MaterialData::tryAttribute(): invalid name" << name, {}); - return tryAttribute(string); + return tryAttribute(layer, string); } #ifdef MAGNUM_BUILD_DEPRECATED @@ -356,7 +568,11 @@ Float MaterialData::alphaMask() const { return attributeOr(MaterialAttribute::AlphaMask, 0.5f); } -Containers::Array MaterialData::release() { +Containers::Array MaterialData::releaseLayerData() { + return std::move(_layerOffsets); +} + +Containers::Array MaterialData::releaseAttributeData() { return std::move(_data); } @@ -366,7 +582,11 @@ Debug& operator<<(Debug& debug, const MaterialAttribute value) { if(UnsignedInt(value) - 1 >= Containers::arraySize(AttributeMap)) return debug << "(" << Debug::nospace << reinterpret_cast(UnsignedInt(value)) << Debug::nospace << ")"; - return debug << "::" << Debug::nospace << AttributeMap[UnsignedInt(value) - 1].name; + /* LayerName is prefixed with $, drop that */ + Containers::StringView string = AttributeMap[UnsignedInt(value) - 1].name; + if(string[0] == '$') string = string.suffix(1); + + return debug << "::" << Debug::nospace << string; } Debug& operator<<(Debug& debug, const MaterialAttributeType value) { diff --git a/src/Magnum/Trade/MaterialData.h b/src/Magnum/Trade/MaterialData.h index 83ab9c932e..1fe44c7330 100644 --- a/src/Magnum/Trade/MaterialData.h +++ b/src/Magnum/Trade/MaterialData.h @@ -46,9 +46,12 @@ 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 +Convenience aliases to actual attribute name strings. In most cases the alias +is in the same form and capitalization --- so for example +@ref MaterialAttribute::DoubleSided is an alias for @cpp "DoubleSided" @ce, the +only exception is @ref MaterialAttribute::LayerName which is @cpp "$LayerName" @ce. + +When this enum si used in @ref MaterialAttributeData constructors, the data are additionally checked for type compatibility. Other than that, there is no difference to the string variants. @@ -57,6 +60,17 @@ variants. enum class MaterialAttribute: UnsignedInt { /* Zero used for an invalid value */ + /** + * Layer name, @ref MaterialAttributeType::String. + * + * Unlike other attributes where string name matches the enum name, in this + * case the corresponding string is @cpp "$LayerName" @ce, done in order to + * have the layer name attribute appear first in each layer and thus + * simplify layer implementation. + * @see @ref MaterialData::layerName() + */ + LayerName = 1, + /** * Alpha mask, @ref MaterialAttributeType::Float. * @@ -67,7 +81,7 @@ enum class MaterialAttribute: UnsignedInt { * @see @ref MaterialAlphaMode, @ref MaterialData::alphaMode(), * @ref MaterialData::alphaMask() */ - AlphaMask = 1, + AlphaMask, /** * Alpha blending, @ref MaterialAttributeType::Bool. @@ -399,7 +413,7 @@ class MAGNUM_TRADE_EXPORT MaterialAttributeData { > constexpr /*implicit*/ MaterialAttributeData(Containers::StringView name, const T& value) noexcept; /** - * @brief Construct with a string name and value + * @brief Construct with a string name and string value * @param name Attribute name * @param value Attribute value * @@ -440,6 +454,21 @@ class MAGNUM_TRADE_EXPORT MaterialAttributeData { #endif > /*implicit*/ MaterialAttributeData(MaterialAttribute name, const T& value) noexcept: MaterialAttributeData{name, Implementation::MaterialAttributeTypeFor::type(), &value} {} + /** + * @brief Construct with a predefined name and string value + * @param name Attribute name + * @param value Attribute value + * + * Compared to @ref MaterialAttributeData(Containers::StringView, Containers::StringView) + * checks that the attribute is in expected type. The + * @ref MaterialAttribute gets converted to a corresponding string + * name. Apart from the type check, the following two instances are + * equivalent: + * + * @snippet MagnumTrade.cpp MaterialAttributeData-name + */ + /*implicit*/ MaterialAttributeData(MaterialAttribute name, Containers::StringView value) noexcept: MaterialAttributeData{name, MaterialAttributeType::String, &value} {} + /** * @brief Construct from a type-erased value * @param name Attribute name @@ -749,30 +778,74 @@ class MAGNUM_TRADE_EXPORT MaterialData { * @brief Construct * @param types Which material types are described by this * data. Can be an empty set. - * @param data Attribute data + * @param attributeData Attribute data * @param importerState Importer-specific state * - * The @p data gets sorted by name internally, expecting no duplicates. + * The @p attributeData gets sorted by name internally, expecting no + * duplicates. */ - explicit MaterialData(MaterialTypes types, Containers::Array&& data, const void* importerState = nullptr) noexcept; + explicit MaterialData(MaterialTypes types, Containers::Array&& attributeData, const void* importerState = nullptr) noexcept: MaterialData{types, std::move(attributeData), nullptr, importerState} {} /** @overload */ /* Not noexcept because allocation happens inside */ - explicit MaterialData(MaterialTypes types, std::initializer_list data, const void* importerState = nullptr); + explicit MaterialData(MaterialTypes types, std::initializer_list attributeData, const void* importerState = nullptr): MaterialData{types, attributeData, {}, importerState} {} /** * @brief Construct a non-owned material data + * @param types Which material types are described by + * this data. Can be an empty set. + * @param attributeDataFlags Ignored. Used only for a safer + * distinction from the owning constructor. + * @param attributeData Attribute data + * @param importerState Importer-specific state + * + * The @p attributeData is expected to be already sorted by name, + * without duplicates. + */ + explicit MaterialData(MaterialTypes types, DataFlags attributeDataFlags, Containers::ArrayView attributeData, const void* importerState = nullptr) noexcept: MaterialData{types, attributeDataFlags, attributeData, {}, nullptr, importerState} {} + + /** + * @brief Construct with layers * @param types Which material types are described by this * data. Can be an empty set. - * @param dataFlags Ignored. Used only for a safer distinction - * from the owning constructor. - * @param data Attribute data + * @param attributeData Attribute data + * @param layerData Layer offset data + * @param importerState Importer-specific state + * + * The @p attributeData gets sorted by name internally, expecting no + * duplicates inside each layer. The @p layerData is expected to be + * either empty or a monotonically non-decreasing sequence of offsets + * not larger than @p attributeData size, with *i*-th item specifying + * end offset of *i*-th layer. + */ + explicit MaterialData(MaterialTypes types, Containers::Array&& attributeData, Containers::Array&& layerData, const void* importerState = nullptr) noexcept; + + /** @overload */ + /* Not noexcept because allocation happens inside */ + explicit MaterialData(MaterialTypes types, std::initializer_list attributeData, std::initializer_list layerData, const void* importerState = nullptr); + + /** + * @brief Construct a non-owned material data with layers + * @param types Which material types are described by + * this data. Can be an empty set. + * @param attributeDataFlags Ignored. Used only for a safer + * distinction from the owning constructor. + * @param attributeData Attribute data + * @param layerDataFlags Ignored. Used only for a safer + * distinction from the owning constructor. + * @param layerData Layer offset data * @param importerState Importer-specific state * * The @p data is expected to be already sorted by name, without - * duplicates. + * duplicates inside each layer. The @p layerData is expected to be + * either empty or a monotonically non-decreasing sequence of offsets + * not larger than @p attributeData size, with *i*-th item specifying + * end offset of *i*-th layer. */ - explicit MaterialData(MaterialTypes types, DataFlags dataFlags, Containers::ArrayView data, const void* importerState = nullptr) noexcept; + /* The second (ignored) DataFlags is present in order to make it ready + for a possible extension where only one of the data is non-owned. + But so far I didn't see a need. */ + explicit MaterialData(MaterialTypes types, DataFlags attributeDataFlags, Containers::ArrayView attributeData, DataFlags layerDataFlags, Containers::ArrayView layerData, const void* importerState = nullptr) noexcept; ~MaterialData(); @@ -806,67 +879,256 @@ class MAGNUM_TRADE_EXPORT MaterialData { } #endif + /** + * @brief Raw layer offset data + * + * May return @cpp nullptr @ce if the material doesn't have any extra + * layers. + * @see @ref releaseLayerData() + */ + Containers::ArrayView layerData() const { return _layerOffsets; } + /** * @brief Raw attribute data * * Returns @cpp nullptr @ce if the material has no attributes. - * @see @ref release() + * @see @ref releaseAttributeData() */ - Containers::ArrayView data() const { return _data; } + Containers::ArrayView attributeData() const { return _data; } - /** @brief Attribute count */ - UnsignedInt attributeCount() const { return _data.size(); } + /** + * @brief Layer count + * + * There's always at least the base material, so this function returns + * at least @cpp 1 @ce. + */ + UnsignedInt layerCount() const { + return _layerOffsets.empty() ? 1 : _layerOffsets.size(); + } + + /** + * @brief Whether a material has given named layer + * + * Layers with no name assigned are skipped. The base material (layer + * @cpp 0 @ce is skipped as well) to avoid confusing base material with + * a layer. If you want to create a material consisting of just a + * layer, use @cpp 0 @ce for the first layer offset in the constructor. + * @see @ref hasAttribute() + */ + bool hasLayer(Containers::StringView layer) const; /** - * @brief Whether the material has given attribute + * @brief ID of a named layer * + * The @p layer is expected to exist. + * @see @ref hasLayer() + */ + UnsignedInt layerId(Containers::StringView layer) const; + + /** + * @brief Layer name + * + * Retrieves a @ref MaterialAttribute::LayerName attribute from given + * layer, if present. Returns a @cpp nullptr @ce view if the layer + * has no name, and an empty non-null view if the layer name is empty. + * The @p layer is expected to be smaller than @ref layerCount() const. + * + * The name, if present, is ignored for the base material (layer + * @cpp 0 @ce) to avoid confsing base material with a layer. If you + * want to create a material consisting of just a layer, use @cpp 0 @ce + * for the first layer offset in the constructor. + */ + Containers::StringView layerName(UnsignedInt layer) const; + + /** + * @brief Attribute count in given layer + * + * The @p layer is expected to be smaller than @ref layerCount() const. + */ + UnsignedInt attributeCount(UnsignedInt layer) const; + + /** + * @brief Attribute count in a named layer + * + * The @p layer is expected to exist. + * @see @ref hasLayer() + */ + UnsignedInt attributeCount(Containers::StringView layer) const; + + /** + * @brief Attribute count in the base material + * + * Equivalent to calling @ref attributeCount(UnsignedInt) const with + * @p layer set to @cpp 0 @ce. + */ + UnsignedInt attributeCount() const { return attributeCount(0); } + + /** + * @brief Whether a material layer has given attribute + * + * The @p layer is expected to be smaller than @ref layerCount() const. + * @see @ref tryAttribute(), @ref attributeOr(), @ref hasLayer() + */ + bool hasAttribute(UnsignedInt layer, Containers::StringView name) const; + bool hasAttribute(UnsignedInt layer, MaterialAttribute name) const; /**< @overload */ + + /** + * @brief Whether a named material layer has given attribute + * + * The @p layer is expected to exist. + * @see @ref tryAttribute(), @ref attributeOr(), @ref hasLayer() + */ + bool hasAttribute(Containers::StringView layer, Containers::StringView name) const; + bool hasAttribute(Containers::StringView layer, MaterialAttribute name) const; /**< @overload */ + + /** + * @brief Whether the base material has given attribute + * + * Equivalent to calling @ref hasAttribute(UnsignedInt, Containers::StringView) const + * with @p layer set to @cpp 0 @ce. * @see @ref tryAttribute(), @ref attributeOr() */ - bool hasAttribute(Containers::StringView name) const; - bool hasAttribute(MaterialAttribute name) const; /**< @overload */ + bool hasAttribute(Containers::StringView name) const { + return hasAttribute(0, name); + } + bool hasAttribute(MaterialAttribute name) const { + return hasAttribute(0, name); + } /**< @overload */ /** - * @brief ID of a named attribute + * @brief ID of a named attribute in given material layer * - * The @p name is expected to exist. + * The @p layer is expected to be smaller than @ref layerCount() const + * and @p name is expected to exist in that layer. + * @see @ref hasAttribute() */ - UnsignedInt attributeId(Containers::StringView name) const; - UnsignedInt attributeId(MaterialAttribute name) const; /**< @overload */ + UnsignedInt attributeId(UnsignedInt layer, Containers::StringView name) const; + UnsignedInt attributeId(UnsignedInt layer, MaterialAttribute name) const; /**< @overload */ /** - * @brief Attribute name + * @brief ID of a named attribute in a named material layer * - * The @p id is expected to be smaller than @ref attributeCount() const. - * The returned view always has + * The @p layer is expected to exist and @p name is expected to exist + * in that layer. + * @see @ref hasLayer(), @ref hasAttribute() + */ + UnsignedInt attributeId(Containers::StringView layer, Containers::StringView name) const; + UnsignedInt attributeId(Containers::StringView layer, MaterialAttribute name) const; /**< @overload */ + + /** + * @brief ID of a named attribute in the base material + * + * Equivalent to calling @ref attributeId(UnsignedInt, Containers::StringView) const + * with @p layer set to @cpp 0 @ce. + */ + UnsignedInt attributeId(Containers::StringView name) const { + return attributeId(0, name); + } + UnsignedInt attributeId(MaterialAttribute name) const { + return attributeId(0, name); + } /**< @overload */ + + /** + * @brief Name of an attribute in given material layer + * + * The @p layer is expected to be smaller than @ref layerCount() const + * and the @p id is expected to be smaller than @ref attributeCount(UnsignedInt) const + * in that layer. The returned view always has * @ref Corrade::Containers::StringViewFlag::NullTerminated set. * @see @ref attributeType() */ - Containers::StringView attributeName(UnsignedInt id) const; + Containers::StringView attributeName(UnsignedInt layer, UnsignedInt id) const; /** - * @brief Attribute type + * @brief Name of an attribute in a named material layer * - * The @p id is expected to be smaller than @ref attributeCount() const. + * The @p layer is expected to exist and the @p id is expected to be smaller than @ref attributeCount(UnsignedInt) const + * in that layer. + * @see @ref hasLayer() + */ + Containers::StringView attributeName(Containers::StringView layer, UnsignedInt id) const; + + /** + * @brief Name of an attribute in the base material + * + * Equivalent to calling @ref attributeName(UnsignedInt, UnsignedInt) const + * with @p layer set to @cpp 0 @ce. + */ + Containers::StringView attributeName(UnsignedInt id) const { + return attributeName(0, id); + } + + /** + * @brief Type of an attribute in given material layer + * + * The @p layer is expected to be smaller than @ref layerCount() const + * and @p id is expected to be smaller than @ref attributeCount(UnsignedInt) const + * in that layer. * @see @ref attributeName() */ - MaterialAttributeType attributeType(UnsignedInt id) const; + MaterialAttributeType attributeType(UnsignedInt layer, UnsignedInt id) const; /** - * @brief Type of a named attribute + * @brief Type of a named attribute in given material layer * - * The @p name is expected to exist. + * The @p layer is expected to be smaller than @ref layerCount() const + * and @p name is expected to exist in that layer. * @see @ref hasAttribute() */ - MaterialAttributeType attributeType(Containers::StringView name) const; + MaterialAttributeType attributeType(UnsignedInt layer, Containers::StringView name) const; /** @overload */ - MaterialAttributeType attributeType(MaterialAttribute name) const; + MaterialAttributeType attributeType(UnsignedInt layer, MaterialAttribute name) const; /** - * @brief Type-erased attribute value + * @brief Type of an attribute in a named material layer * - * The @p id is expected to be smaller than @ref attributeCount() const. - * Cast the pointer to a concrete type based on @ref type(). Note that - * in case of a @ref MaterialAttributeType::Pointer or a + * The @p layer is expected to exist and the @p id is expected to be smaller than @ref attributeCount(UnsignedInt) const + * in that layer. + * @see @ref hasLayer() + */ + MaterialAttributeType attributeType(Containers::StringView layer, UnsignedInt id) const; + + /** + * @brief Type of a named attribute in a named material layer + * + * The @p layer is expected to exist and @p name is expected to exist + * in that layer. + * @see @ref hasLayer(), @ref hasAttribute() + */ + MaterialAttributeType attributeType(Containers::StringView layer, Containers::StringView name) const; + MaterialAttributeType attributeType(Containers::StringView layer, MaterialAttribute name) const; /**< @overload */ + + /** + * @brief Type of an attribute in the base material + * + * Equivalent to calling @ref attributeType(UnsignedInt, UnsignedInt) const + * with @p layer set to @cpp 0 @ce. + */ + MaterialAttributeType attributeType(UnsignedInt id) const { + return attributeType(0, id); + } + + /** + * @brief Type of a named attribute in the base material + * + * Equivalent to calling @ref attributeType(UnsignedInt, Containers::StringView) const + * with @p layer set to @cpp 0 @ce. + */ + MaterialAttributeType attributeType(Containers::StringView name) const { + return attributeType(0, name); + } + MaterialAttributeType attributeType(MaterialAttribute name) const { + return attributeType(0, name); + } /**< @overload */ + + /** + * @brief Type-erased value of an attribute in given material layer + * + * The @p layer is expected to be smaller than @ref layerCount() const + * and @p id is expected to be smaller than @ref attributeCount(UnsignedInt) const + * in that layer. Cast the pointer to a concrete type based on + * @ref type(). Note that in case of a + * @ref MaterialAttributeType::Pointer or a * @ref MaterialAttributeType::MutablePointer, returns a * *pointer to a pointer*, not the pointer value itself. * @@ -876,13 +1138,14 @@ class MAGNUM_TRADE_EXPORT MaterialData { * string size in case the string data contain zero bytes, thus prefer * to use typed access in that case. */ - const void* attribute(UnsignedInt id) const; + const void* attribute(UnsignedInt layer, UnsignedInt id) const; /** - * @brief Type-erased value of a named attribute + * @brief Type-erased value of a named attribute in given material layer * - * The @p name is expected to exist. Cast the pointer to a concrete - * type based on @ref attributeType(). Note that + * The @p layer is expected to be smaller than @ref layerCount() const + * and @p name is expected to exist in that layer. Cast the pointer to + * a concrete type based on @ref attributeType(). Note that * in case of a @ref MaterialAttributeType::Pointer or a * @ref MaterialAttributeType::MutablePointer, returns a * *pointer to a pointer*, not the pointer value itself. @@ -894,64 +1157,263 @@ class MAGNUM_TRADE_EXPORT MaterialData { * to use typed access in that case. * @see @ref hasAttribute(), @ref tryAttribute(), @ref attributeOr() */ - const void* attribute(Containers::StringView name) const; - const void* attribute(MaterialAttribute name) const; /**< @overload */ + const void* attribute(UnsignedInt layer, Containers::StringView name) const; + const void* attribute(UnsignedInt layer, MaterialAttribute name) const; /**< @overload */ /** - * @brief Attribute value + * @brief Type-erased value of an attribute in a named material layer + * + * The @p layer is expected to exist and the @p id is expected to be smaller than @ref attributeCount(UnsignedInt) const + * in that layer. Cast the pointer to a concrete type based on + * @ref attributeType(). Note that in case of a + * @ref MaterialAttributeType::Pointer or a + * @ref MaterialAttributeType::MutablePointer, returns a + * *pointer to a pointer*, not the pointer value itself. + * + * In case of a @ref MaterialAttributeType::String returns a + * null-terminated @cpp const char* @ce (not a pointer to + * @ref Containers::StringView). This doesn't preserve the actual + * string size in case the string data contain zero bytes, thus prefer + * to use typed access in that case. + * @see @ref hasLayer() + */ + const void* attribute(Containers::StringView layer, UnsignedInt id) const; + + /** + * @brief Type-erased value of a named attribute in a named material layer + * + * The @p layer is expected to exist and @p name is expected to exist + * in that layer. Cast the pointer to a concrete type based on + * @ref attributeType(). Note that in case of a + * @ref MaterialAttributeType::Pointer or a + * @ref MaterialAttributeType::MutablePointer, returns a + * *pointer to a pointer*, not the pointer value itself. + * + * In case of a @ref MaterialAttributeType::String returns a + * null-terminated @cpp const char* @ce (not a pointer to + * @ref Containers::StringView). This doesn't preserve the actual + * string size in case the string data contain zero bytes, thus prefer + * to use typed access in that case. + * @see @ref hasLayer(), @ref hasAttribute() + */ + const void* attribute(Containers::StringView layer, Containers::StringView name) const; + const void* attribute(Containers::StringView layer, MaterialAttribute name) const; /**< @overload */ + + /** + * @brief Type-erased value of an attribute in the base material + * + * Equivalent to calling @ref attribute(UnsignedInt, UnsignedInt) const + * with @p layer set to @cpp 0 @ce. + */ + const void* attribute(UnsignedInt id) const { + return attribute(0, id); + } + + /** + * @brief Type-erased value of a named attribute in the base material + * + * Equivalent to calling @ref attribute(UnsignedInt, Containers::StringView) const + * with @p layer set to @cpp 0 @ce. + */ + const void* attribute(Containers::StringView name) const { + return attribute(0, name); + } + const void* attribute(MaterialAttribute name) const { + return attribute(0, name); + } /**< @overload */ + + /** + * @brief Value of an attribute in given material layer * - * The @p id is expected to be smaller than @ref attributeCount() const. - * Expects that @p T corresponds to @ref attributeType(UnsignedInt) const - * for given @p id. In case of a string, the returned view always has + * The @p layer is expected to be smaller than @ref layerCount() const + * and @p id is expected to be smaller than + * @ref attributeCount(UnsignedInt) const in that layer. Expects that + * @p T corresponds to @ref attributeType(UnsignedInt, UnsignedInt) const + * for given @p layer and @p id. In case of a string, the returned view + * always has @ref Corrade::Containers::StringViewFlag::NullTerminated + * set. + */ + template T attribute(UnsignedInt layer, UnsignedInt id) const; + + /** + * @brief Value of a named attribute in given material layer + * + * The @p layer is expected to be smaller than @ref layerCount() const + * and @p name is expected to exist in that layer. Expects that @p T + * corresponds to @ref attributeType(UnsignedInt, Containers::StringView) const + * for given @p layer and @p name. In case of a string, the returned + * view always has * @ref Corrade::Containers::StringViewFlag::NullTerminated set. + * @see @ref hasLayer(), @ref hasAttribute() */ - template T attribute(UnsignedInt id) const; + template T attribute(UnsignedInt layer, Containers::StringView name) const; + template T attribute(UnsignedInt layer, MaterialAttribute name) const; /**< @overload */ /** - * @brief Value of a named attribute + * @brief Value of an attribute in a named material layer * - * The @p name is expected to exist. Expects that @p T corresponds to - * @ref attributeType(Containers::StringView) const for given @p name. - * In case of a string, the returned view always has + * The @p layer is expected to exist and @p id is expected to be + * smaller than @ref attributeCount(UnsignedInt) const in that layer. + * Expects that @p T corresponds to + * @ref attributeType(Containers::StringView, UnsignedInt) const + * for given @p layer and @p id. In case of a string, the returned view + * always has @ref Corrade::Containers::StringViewFlag::NullTerminated + * set. + * @see @ref hasLayer() + */ + template T attribute(Containers::StringView layer, UnsignedInt id) const; + + /** + * @brief Value of a named attribute in a named material layer + * + * The @p layer is expected to exist and @p name is expected to exist + * in that layer. Expects that @p T corresponds to + * @ref attributeType(Containers::StringView, Containers::StringView) const + * for given @p layer and @p name. In case of a string, the returned + * view always has * @ref Corrade::Containers::StringViewFlag::NullTerminated set. - * @see @ref hasAttribute() + * @see @ref hasLayer(), @ref hasAttribute() + */ + template T attribute(Containers::StringView layer, Containers::StringView name) const; + template T attribute(Containers::StringView layer, MaterialAttribute name) const; /**< @overload */ + + /** + * @brief Value of an attribute in the base material + * + * Equivalent to calling @ref attribute(UnsignedInt, UnsignedInt) const + * with @p layer set to @cpp 0 @ce. + */ + template T attribute(UnsignedInt id) const { + return attribute(0, id); + } + + /** + * @brief Value of a named attribute in the base material + * + * Equivalent to calling @ref attribute(UnsignedInt, Containers::StringView) const + * with @p layer set to @cpp 0 @ce. */ - template T attribute(Containers::StringView name) const; - template T attribute(MaterialAttribute name) const; /**< @overload */ + template T attribute(Containers::StringView name) const { + return attribute(0, name); + } + template T attribute(MaterialAttribute name) const { + return attribute(0, name); + } /**< @overload */ /** - * @brief Type-erased attribute value, if exists + * @brief Type-erased attribute value in given material layer, if exists * - * Compared to @ref attribute(Containers::StringView name) const, if - * @p name doesn't exist, returns @cpp nullptr @ce instead of - * asserting. Cast the pointer to a concrete type based on - * @ref attributeType(). + * Compared to @ref attribute(UnsignedInt, Containers::StringView name) const, + * if @p name doesn't exist, returns @cpp nullptr @ce instead of + * asserting. Expects that @p layer is smaller than @ref layerCount() const. + * Cast the pointer to a concrete type based on @ref attributeType(). * @see @ref hasAttribute(), @ref attributeOr() */ - const void* tryAttribute(Containers::StringView name) const; - const void* tryAttribute(MaterialAttribute name) const; /**< @overload */ + const void* tryAttribute(UnsignedInt layer, Containers::StringView name) const; + const void* tryAttribute(UnsignedInt layer, MaterialAttribute name) const; /**< @overload */ /** - * @brief Value of a named attribute, if exists + * @brief Type-erased attribute value in a named material layer, if exists * - * Compared to @ref attribute(Containers::StringView name) const, if - * @p name doesn't exist, returns @ref Corrade::Containers::NullOpt - * instead of asserting. Expects that @p T corresponds to - * @ref attributeType(Containers::StringView) const for given @p name. + * Compared to @ref attribute(Containers::StringView, Containers::StringView name) const, + * if @p name doesn't exist, returns @cpp nullptr @ce instead of + * asserting. Expects that @p layer exists. Cast the pointer to a + * concrete type based on @ref attributeType(). + * @see @ref hasLayer(), @ref hasAttribute(), @ref attributeOr() */ - template Containers::Optional tryAttribute(Containers::StringView name) const; - template Containers::Optional tryAttribute(MaterialAttribute name) const; /**< @overload */ + const void* tryAttribute(Containers::StringView layer, Containers::StringView name) const; + const void* tryAttribute(Containers::StringView layer, MaterialAttribute name) const; /**< @overload */ /** - * @brief Value of a named attribute or a default + * @brief Value of a named attribute in given material layer, if exists * - * Compared to @ref attribute(Containers::StringView name) const, if - * @p name doesn't exist, returns @p defaultValue instead of asserting. - * Expects that @p T corresponds to - * @ref attributeType(Containers::StringView) const for given @p name. + * Compared to @ref attribute(UnsignedInt, Containers::StringView name) const, + * if @p name doesn't exist, returns @ref Corrade::Containers::NullOpt + * instead of asserting. Expects that @p layer is smaller than + * @ref layerCount() const and that @p T corresponds to + * @ref attributeType(UnsignedInt, Containers::StringView) const for + * given @p layer and @p name. */ - template T attributeOr(Containers::StringView name, const T& defaultValue) const; - template T attributeOr(MaterialAttribute name, const T& defaultValue) const; /**< @overload */ + template Containers::Optional tryAttribute(UnsignedInt layer, Containers::StringView name) const; + template Containers::Optional tryAttribute(UnsignedInt layer, MaterialAttribute name) const; /**< @overload */ + + /** + * @brief Value of a named attribute in a named material layer, if exists + * + * Compared to @ref attribute(Containers::StringView, Containers::StringView name) const, + * if @p name doesn't exist, returns @ref Corrade::Containers::NullOpt + * instead of asserting. Expects that @p layer exists and that @p T + * corresponds to @ref attributeType(Containers::StringView, Containers::StringView) const + * for given @p layer and @p name. + * @see @ref hasLayer() + */ + template Containers::Optional tryAttribute(Containers::StringView layer, Containers::StringView name) const; + template Containers::Optional tryAttribute(Containers::StringView layer, MaterialAttribute name) const; /**< @overload */ + + /** + * @brief Type-erased attribute value in the base material, if exists + * + * Equivalent to calling @ref tryAttribute(UnsignedInt, Containers::StringView) const + * with @p layer set to @cpp 0 @ce. + */ + const void* tryAttribute(Containers::StringView name) const { + return tryAttribute(0, name); + } + const void* tryAttribute(MaterialAttribute name) const { + return tryAttribute(0, name); + } /**< @overload */ + + /** + * @brief Value of a named attribute in the base material, if exists + * + * Equivalent to calling @ref tryAttribute(UnsignedInt, Containers::StringView) const + * with @p layer set to @cpp 0 @ce. + */ + template Containers::Optional tryAttribute(Containers::StringView name) const { + return tryAttribute(0, name); + } + template Containers::Optional tryAttribute(MaterialAttribute name) const { + return tryAttribute(0, name); + } /**< @overload */ + + /** + * @brief Value of a named attribute in given layer or a default + * + * Compared to @ref attribute(UnsignedInt, Containers::StringView name) const, + * if @p name doesn't exist, returns @p defaultValue instead of + * asserting. Expects that @p layer is smaller than @ref layerCount() + * const + * and that @p T corresponds to @ref attributeType(UnsignedInt, Containers::StringView) const + * for given @p layer and @p name. + */ + template T attributeOr(UnsignedInt layer, Containers::StringView name, const T& defaultValue) const; + template T attributeOr(UnsignedInt layer, MaterialAttribute name, const T& defaultValue) const; /**< @overload */ + + /** + * @brief Value of a named attribute in a named layer or a default + * + * Compared to @ref attribute(Containers::StringView, Containers::StringView name) const, + * if @p name doesn't exist, returns @p defaultValue instead of + * asserting. Expects that @p layer exists and that @p T corresponds to + * @ref attributeType(Containers::StringView, Containers::StringView) const + * for given @p layer and @p name. + * @see @ref hasLayer() + */ + template T attributeOr(Containers::StringView layer, Containers::StringView name, const T& defaultValue) const; + template T attributeOr(Containers::StringView layer, MaterialAttribute name, const T& defaultValue) const; /**< @overload */ + + /** + * @brief Value of a named attribute in the base material or a default + * + * Equivalent to calling @ref attributeOr(UnsignedInt, Containers::StringView, const T&) const + * with @p layer set to @cpp 0 @ce. + */ + template T attributeOr(Containers::StringView name, const T& defaultValue) const { + return attributeOr(0, name, defaultValue); + } + template T attributeOr(MaterialAttribute name, const T& defaultValue) const { + return attributeOr(0, name, defaultValue); + }/**< @overload */ /** * @brief Whether a material is double-sided @@ -995,16 +1457,38 @@ class MAGNUM_TRADE_EXPORT MaterialData { Float alphaMask() const; /** - * @brief Release data storage + * @brief Release layer 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 + * Releases the ownership of the layer offset array and resets internal + * layer-related state to default. The material then behaves like if it + * has no layers. Note that the returned array has a custom no-op * deleter when the data are not owned by the mesh, and while the * returned array type is mutable, the actual memory might be not. - * @see @ref data() + * + * @attention Querying attributes after calling @ref releaseLayerData() + * has undefined behavior and might lead to crashes. This is done + * intentionally in order to simplify the interaction between this + * function and @ref releaseAttributeData(). + * @see @ref layerData() */ - Containers::Array release(); + Containers::Array releaseLayerData(); + + /** + * @brief Release attribute data storage + * + * Releases the ownership of the attribute array and resets internal + * attribute-related state to default. The material then behaves like + * if it has no attributes. Note that the returned array has a custom + * no-op deleter when the data are not owned by the mesh, and while the + * returned array type is mutable, the actual memory might be not. + * + * @attention Querying layers after calling @ref releaseAttributeData() + * has undefined behavior and might lead to crashes. This is done + * intentionally in order to simplify the interaction between this + * function and @ref releaseLayerData(). + * @see @ref attributeData() + */ + Containers::Array releaseAttributeData(); /** * @brief Importer-specific state @@ -1015,10 +1499,15 @@ class MAGNUM_TRADE_EXPORT MaterialData { private: static Containers::StringView attributeString(MaterialAttribute name); - /* Internal helper that doesn't assert, unlike attributeId() */ - UnsignedInt attributeFor(Containers::StringView name) const; + /* Internal helpers that don't assert, unlike layerId() / attributeId() */ + UnsignedInt layerFor(Containers::StringView layer) const; + UnsignedInt layerOffset(UnsignedInt layer) const { + return layer && _layerOffsets ? _layerOffsets[layer - 1] : 0; + } + UnsignedInt attributeFor(UnsignedInt layer, Containers::StringView name) const; Containers::Array _data; + Containers::Array _layerOffsets; MaterialTypes _types; const void* _importerState; }; @@ -1131,55 +1620,113 @@ template T MaterialAttributeData::value() const { template<> Containers::StringView MaterialAttributeData::value() const; #endif -template T MaterialData::attribute(UnsignedInt id) const { - const void* const value = attribute(id); +template T MaterialData::attribute(const UnsignedInt layer, const UnsignedInt id) const { + const void* const value = attribute(layer, id); #ifdef CORRADE_GRACEFUL_ASSERT if(!value) return {}; #endif - CORRADE_ASSERT(Implementation::MaterialAttributeTypeFor::type() == _data[id]._data.type, - "Trade::MaterialData::attribute(): improper type requested for" << (_data[id]._data.data + 1) << "of" << _data[id]._data.type, {}); + const Trade::MaterialAttributeData& data = _data[layerOffset(layer) + id]; + CORRADE_ASSERT(Implementation::MaterialAttributeTypeFor::type() == data._data.type, + "Trade::MaterialData::attribute(): improper type requested for" << (data._data.data + 1) << "of" << data._data.type, {}); return *reinterpret_cast(value); } #ifndef DOXYGEN_GENERATING_OUTPUT -template<> Containers::StringView MaterialData::attribute(UnsignedInt) const; +template<> Containers::StringView MaterialData::attribute(UnsignedInt, UnsignedInt) const; #endif -template T MaterialData::attribute(Containers::StringView name) const { - const UnsignedInt id = attributeFor(name); +template T MaterialData::attribute(const UnsignedInt layer, const Containers::StringView name) const { + CORRADE_ASSERT(layer < layerCount(), + "Trade::MaterialData::attribute(): index" << layer << "out of range for" << layerCount() << "layers", {}); + const UnsignedInt id = attributeFor(layer, name); + CORRADE_ASSERT(id != ~UnsignedInt{}, + "Trade::MaterialData::attribute(): attribute" << name << "not found in layer" << layer, {}); + return attribute(layer, id); +} + +template T MaterialData::attribute(const UnsignedInt layer, const MaterialAttribute name) const { + const Containers::StringView string = attributeString(name); + CORRADE_ASSERT(string.data(), "Trade::MaterialData::attribute(): invalid name" << name, {}); + return attribute(layer, string); +} + +template T MaterialData::attribute(const Containers::StringView layer, const UnsignedInt id) const { + const UnsignedInt layerId = layerFor(layer); + CORRADE_ASSERT(layerId != ~UnsignedInt{}, + "Trade::MaterialData::attribute(): layer" << layer << "not found", {}); + CORRADE_ASSERT(id < attributeCount(layer), + "Trade::MaterialData::attribute(): index" << id << "out of range for" << attributeCount(layer) << "attributes in layer" << layer, {}); + return attribute(layerId, id); +} + +template T MaterialData::attribute(const Containers::StringView layer, const Containers::StringView name) const { + const UnsignedInt layerId = layerFor(layer); + CORRADE_ASSERT(layerId != ~UnsignedInt{}, + "Trade::MaterialData::attribute(): layer" << layer << "not found", {}); + const UnsignedInt id = attributeFor(layerId, name); CORRADE_ASSERT(id != ~UnsignedInt{}, - "Trade::MaterialData::attribute(): attribute" << name << "not found", {}); - return attribute(id); + "Trade::MaterialData::attribute(): attribute" << name << "not found in layer" << layer, {}); + return attribute(layerId, id); } -template T MaterialData::attribute(MaterialAttribute name) const { +template T MaterialData::attribute(const Containers::StringView layer, const MaterialAttribute name) const { const Containers::StringView string = attributeString(name); CORRADE_ASSERT(string.data(), "Trade::MaterialData::attribute(): invalid name" << name, {}); - return attribute(string); + return attribute(layer, string); } -template Containers::Optional MaterialData::tryAttribute(Containers::StringView name) const { - const UnsignedInt id = attributeFor(name); +template Containers::Optional MaterialData::tryAttribute(const UnsignedInt layer, const Containers::StringView name) const { + CORRADE_ASSERT(layer < layerCount(), + "Trade::MaterialData::tryAttribute(): index" << layer << "out of range for" << layerCount() << "layers", {}); + const UnsignedInt id = attributeFor(layer, name); if(id == ~UnsignedInt{}) return {}; - return attribute(id); + return attribute(layer, id); +} + +template Containers::Optional MaterialData::tryAttribute(const UnsignedInt layer, const MaterialAttribute name) const { + const Containers::StringView string = attributeString(name); + CORRADE_ASSERT(string.data(), "Trade::MaterialData::tryAttribute(): invalid name" << name, {}); + return tryAttribute(layer, string); +} + +template Containers::Optional MaterialData::tryAttribute(const Containers::StringView layer, const Containers::StringView name) const { + const UnsignedInt layerId = layerFor(layer); + CORRADE_ASSERT(layerId != ~UnsignedInt{}, + "Trade::MaterialData::tryAttribute(): layer" << layer << "not found", {}); + return tryAttribute(layerId, name); } -template Containers::Optional MaterialData::tryAttribute(MaterialAttribute name) const { +template Containers::Optional MaterialData::tryAttribute(const Containers::StringView layer, const MaterialAttribute name) const { const Containers::StringView string = attributeString(name); CORRADE_ASSERT(string.data(), "Trade::MaterialData::tryAttribute(): invalid name" << name, {}); - return tryAttribute(string); + return tryAttribute(layer, string); } -template T MaterialData::attributeOr(Containers::StringView name, const T& defaultValue) const { - const UnsignedInt id = attributeFor(name); +template T MaterialData::attributeOr(const UnsignedInt layer, const Containers::StringView name, const T& defaultValue) const { + CORRADE_ASSERT(layer < layerCount(), + "Trade::MaterialData::attributeOr(): index" << layer << "out of range for" << layerCount() << "layers", {}); + const UnsignedInt id = attributeFor(layer, name); if(id == ~UnsignedInt{}) return defaultValue; - return attribute(id); + return attribute(layer, id); +} + +template T MaterialData::attributeOr(const UnsignedInt layer, const MaterialAttribute name, const T& defaultValue) const { + const Containers::StringView string = attributeString(name); + CORRADE_ASSERT(string.data(), "Trade::MaterialData::attributeOr(): invalid name" << name, {}); + return attributeOr(layer, string, defaultValue); +} + +template T MaterialData::attributeOr(const Containers::StringView layer, const Containers::StringView name, const T& defaultValue) const { + const UnsignedInt layerId = layerFor(layer); + CORRADE_ASSERT(layerId != ~UnsignedInt{}, + "Trade::MaterialData::attributeOr(): layer" << layer << "not found", {}); + return attributeOr(layerId, name, defaultValue); } -template T MaterialData::attributeOr(MaterialAttribute name, const T& defaultValue) const { +template T MaterialData::attributeOr(const Containers::StringView layer, const MaterialAttribute name, const T& defaultValue) const { const Containers::StringView string = attributeString(name); CORRADE_ASSERT(string.data(), "Trade::MaterialData::attributeOr(): invalid name" << name, {}); - return attributeOr(string, defaultValue); + return attributeOr(layer, string, defaultValue); } }} diff --git a/src/Magnum/Trade/Test/MaterialDataTest.cpp b/src/Magnum/Trade/Test/MaterialDataTest.cpp index ddc690703f..1ed0388140 100644 --- a/src/Magnum/Trade/Test/MaterialDataTest.cpp +++ b/src/Magnum/Trade/Test/MaterialDataTest.cpp @@ -57,13 +57,15 @@ class MaterialDataTest: public TestSuite::Tester { void constructAttributePointer(); void constructAttributeMutablePointer(); - void constructAttributeStringValue(); + void constructAttributeStringNameStringValue(); + void constructAttributeNameStringValue(); void constructAttributeInvalidName(); void constructAttributeWrongTypeForName(); void constructAttributeInvalidType(); void constructAttributeTooLarge(); void constructAttributeTooLargeString(); + void constructAttributeTooLargeNameString(); void constructAttributeWrongAccessType(); void constructAttributeWrongAccessPointerType(); void constructAttributeWrongAccessTypeString(); @@ -73,10 +75,17 @@ class MaterialDataTest: public TestSuite::Tester { void constructDuplicateAttribute(); void constructFromImmutableSortedArray(); + void constructLayers(); + void constructLayersNotMonotonic(); + void constructLayersOffsetOutOfBounds(); + void constructNonOwned(); + void constructNonOwnedLayers(); void constructNonOwnedEmptyAttribute(); void constructNonOwnedNotSorted(); void constructNonOwnedDuplicateAttribute(); + void constructNonOwnedLayersNotMonotonic(); + void constructNonOwnedLayersOffsetOutOfBounds(); void constructCopy(); void constructMove(); @@ -86,13 +95,25 @@ class MaterialDataTest: public TestSuite::Tester { void accessString(); void accessOptional(); void accessOutOfBounds(); - void accessInvalidAttributeName(); void accessNotFound(); + void accessInvalidAttributeName(); void accessWrongType(); void accessWrongPointerType(); void accessWrongTypeString(); - void release(); + void accessLayerLayerNameInBaseMaterial(); + void accessLayerEmptyLayer(); + void accessLayerIndexOptional(); + void accessLayerStringOptional(); + void accessLayerOutOfBounds(); + void accessLayerNotFound(); + void accessOutOfBoundsInLayerIndex(); + void accessOutOfBoundsInLayerString(); + void accessNotFoundInLayerIndex(); + void accessNotFoundInLayerString(); + + void releaseAttributes(); + void releaseLayers(); #ifdef MAGNUM_BUILD_DEPRECATED void constructPhongDeprecated(); @@ -167,13 +188,15 @@ MaterialDataTest::MaterialDataTest() { &MaterialDataTest::constructAttributePointer, &MaterialDataTest::constructAttributeMutablePointer, - &MaterialDataTest::constructAttributeStringValue, + &MaterialDataTest::constructAttributeStringNameStringValue, + &MaterialDataTest::constructAttributeNameStringValue, &MaterialDataTest::constructAttributeInvalidName, &MaterialDataTest::constructAttributeWrongTypeForName, &MaterialDataTest::constructAttributeInvalidType, &MaterialDataTest::constructAttributeTooLarge, &MaterialDataTest::constructAttributeTooLargeString, + &MaterialDataTest::constructAttributeTooLargeNameString, &MaterialDataTest::constructAttributeWrongAccessType, &MaterialDataTest::constructAttributeWrongAccessPointerType, &MaterialDataTest::constructAttributeWrongAccessTypeString, @@ -186,10 +209,17 @@ MaterialDataTest::MaterialDataTest() { addTests({&MaterialDataTest::constructFromImmutableSortedArray, + &MaterialDataTest::constructLayers, + &MaterialDataTest::constructLayersNotMonotonic, + &MaterialDataTest::constructLayersOffsetOutOfBounds, + &MaterialDataTest::constructNonOwned, + &MaterialDataTest::constructNonOwnedLayers, &MaterialDataTest::constructNonOwnedEmptyAttribute, &MaterialDataTest::constructNonOwnedNotSorted, &MaterialDataTest::constructNonOwnedDuplicateAttribute, + &MaterialDataTest::constructNonOwnedLayersNotMonotonic, + &MaterialDataTest::constructNonOwnedLayersOffsetOutOfBounds, &MaterialDataTest::constructCopy, &MaterialDataTest::constructMove, @@ -199,13 +229,25 @@ MaterialDataTest::MaterialDataTest() { &MaterialDataTest::accessString, &MaterialDataTest::accessOptional, &MaterialDataTest::accessOutOfBounds, - &MaterialDataTest::accessInvalidAttributeName, &MaterialDataTest::accessNotFound, + &MaterialDataTest::accessInvalidAttributeName, &MaterialDataTest::accessWrongType, &MaterialDataTest::accessWrongPointerType, &MaterialDataTest::accessWrongTypeString, - &MaterialDataTest::release, + &MaterialDataTest::accessLayerLayerNameInBaseMaterial, + &MaterialDataTest::accessLayerEmptyLayer, + &MaterialDataTest::accessLayerIndexOptional, + &MaterialDataTest::accessLayerStringOptional, + &MaterialDataTest::accessLayerOutOfBounds, + &MaterialDataTest::accessLayerNotFound, + &MaterialDataTest::accessOutOfBoundsInLayerIndex, + &MaterialDataTest::accessOutOfBoundsInLayerString, + &MaterialDataTest::accessNotFoundInLayerIndex, + &MaterialDataTest::accessNotFoundInLayerString, + + &MaterialDataTest::releaseAttributes, + &MaterialDataTest::releaseLayers, #ifdef MAGNUM_BUILD_DEPRECATED &MaterialDataTest::constructPhongDeprecated, @@ -302,9 +344,14 @@ void MaterialDataTest::attributeMap() { CORRADE_COMPARE(materialAttributeTypeSize(MaterialAttributeType::typeName), sizeof(type)); \ CORRADE_COMPARE_AS(sizeof(type) + Containers::arraySize(#name_) + sizeof(MaterialAttributeType), sizeof(MaterialAttributeData), TestSuite::Compare::LessOrEqual); \ break; + #define _cnt(name_, string, typeName, type) \ + case MaterialAttribute::name_: \ + CORRADE_COMPARE((MaterialAttributeData{MaterialAttribute::name_, type{}}.name()), string); \ + break; #include "Magnum/Trade/Implementation/materialAttributeProperties.hpp" #undef _c #undef _ct + #undef _cnt } #ifdef __GNUC__ #pragma GCC diagnostic pop @@ -451,7 +498,7 @@ void MaterialDataTest::constructAttributeMutablePointer() { CORRADE_COMPARE(typeErased.value(), &data); } -void MaterialDataTest::constructAttributeStringValue() { +void MaterialDataTest::constructAttributeStringNameStringValue() { /* Explicitly using a non-null-terminated view on input to check the null byte isn't read by accident*/ MaterialAttributeData attribute{"name that's long", "and a value\0that's also long but still fits!!"_s.except(1)}; @@ -486,6 +533,33 @@ void MaterialDataTest::constructAttributeStringValue() { CORRADE_COMPARE(typeErased.value()[typeErased.value().size()], '\0'); } +void MaterialDataTest::constructAttributeNameStringValue() { + /* Explicitly using a non-null-terminated view on input to check the null + byte isn't read by accident*/ + + MaterialAttributeData attribute{MaterialAttribute::LayerName, "a value\0that's long but still fits!!"_s.except(1)}; + CORRADE_COMPARE(attribute.name(), "$LayerName"); + CORRADE_COMPARE(attribute.name().flags(), Containers::StringViewFlag::NullTerminated); + CORRADE_COMPARE(attribute.name()[attribute.name().size()], '\0'); + CORRADE_COMPARE(attribute.type(), MaterialAttributeType::String); + /* Pointer access will stop at the first null byte, but typed access won't */ + CORRADE_COMPARE(static_cast(attribute.value()), "a value"_s); + CORRADE_COMPARE(attribute.value(), "a value\0that's long but still fits!"_s); + CORRADE_COMPARE(attribute.value().flags(), Containers::StringViewFlag::NullTerminated); + CORRADE_COMPARE(attribute.value()[attribute.value().size()], '\0'); + + /* Type-erased variant */ + const Containers::StringView value = "a value\0that's long but still fits!!"_s.except(1); + MaterialAttributeData typeErased{MaterialAttribute::LayerName, MaterialAttributeType::String, &value}; + CORRADE_COMPARE(typeErased.name(), "$LayerName"); + CORRADE_COMPARE(typeErased.name().flags(), Containers::StringViewFlag::NullTerminated); + CORRADE_COMPARE(typeErased.name()[typeErased.name().size()], '\0'); + CORRADE_COMPARE(typeErased.type(), MaterialAttributeType::String); + CORRADE_COMPARE(typeErased.value(), "a value\0that's long but still fits!"_s); + CORRADE_COMPARE(typeErased.value().flags(), Containers::StringViewFlag::NullTerminated); + CORRADE_COMPARE(typeErased.value()[typeErased.value().size()], '\0'); +} + void MaterialDataTest::constructAttributeInvalidName() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); @@ -558,6 +632,18 @@ void MaterialDataTest::constructAttributeTooLargeString() { "Trade::MaterialAttributeData: name attribute is long and value This is a problem, got a long piece of text! too long, expected at most 60 bytes in total but got 61\n"); } +void MaterialDataTest::constructAttributeTooLargeNameString() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + std::ostringstream out; + Error redirectError{&out}; + MaterialAttributeData{MaterialAttribute::LayerName, "This is a problem, got a huge, yuuge value to store"}; + CORRADE_COMPARE(out.str(), + "Trade::MaterialAttributeData: name $LayerName and value This is a problem, got a huge, yuuge value to store too long, expected at most 60 bytes in total but got 61\n"); +} + void MaterialDataTest::constructAttributeWrongAccessType() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); @@ -607,10 +693,15 @@ void MaterialDataTest::construct() { }, &state}; CORRADE_COMPARE(data.types(), MaterialType::Phong); + CORRADE_COMPARE(data.layerCount(), 1); + CORRADE_VERIFY(!data.layerData()); CORRADE_COMPARE(data.attributeCount(), 4); - CORRADE_COMPARE(data.data().size(), 4); + CORRADE_COMPARE(data.attributeData().size(), 4); CORRADE_COMPARE(data.importerState(), &state); + CORRADE_COMPARE(data.layerName(0), ""); + CORRADE_VERIFY(!data.hasLayer("")); + /* Verify sorting */ CORRADE_COMPARE(data.attributeName(0), "AmbientTextureMatrix"); CORRADE_COMPARE(data.attributeName(1), "DiffuseTextureCoordinates"); @@ -734,6 +825,213 @@ void MaterialDataTest::constructFromImmutableSortedArray() { CORRADE_COMPARE(data.attributeName(1), "yay this is last"); } +void MaterialDataTest::constructLayers() { + int state; + MaterialData data{MaterialType::Phong, { + {MaterialAttribute::DoubleSided, true}, + {MaterialAttribute::DiffuseTextureCoordinates, 5u}, + + /* Layer name gets sorted first by the constructor */ + {"highlightColor", 0x335566ff_rgbaf}, + {MaterialAttribute::AlphaBlend, true}, + {MaterialAttribute::LayerName, "ClearCoat"}, + + /* Empty layer here */ + + /* Unnamed but nonempty layer */ + {"thickness", 0.015f}, + {MaterialAttribute::NormalTexture, 3u} + }, { + 2, 5, 5, 7 + }, &state}; + + CORRADE_COMPARE(data.types(), MaterialType::Phong); + CORRADE_COMPARE(data.importerState(), &state); + + CORRADE_COMPARE(data.layerCount(), 4); + CORRADE_COMPARE(data.layerData().size(), 4); + + CORRADE_COMPARE(data.attributeData().size(), 7); + CORRADE_COMPARE(data.attributeCount(0), 2); + CORRADE_COMPARE(data.attributeCount(1), 3); + CORRADE_COMPARE(data.attributeCount(2), 0); + CORRADE_COMPARE(data.attributeCount(3), 2); + CORRADE_COMPARE(data.attributeCount("ClearCoat"), 3); + + /* Layer access */ + CORRADE_COMPARE(data.layerName(0), ""); + CORRADE_COMPARE(data.layerName(1), "ClearCoat"); + CORRADE_COMPARE(data.layerName(2), ""); + CORRADE_COMPARE(data.layerName(3), ""); + + CORRADE_VERIFY(data.hasLayer("ClearCoat")); + CORRADE_VERIFY(!data.hasLayer("")); + CORRADE_VERIFY(!data.hasLayer("DoubleSided")); + + CORRADE_COMPARE(data.layerId("ClearCoat"), 1); + + /* Verify sorting in each layer */ + CORRADE_COMPARE(data.attributeName(0, 0), "DiffuseTextureCoordinates"); + CORRADE_COMPARE(data.attributeName(0, 1), "DoubleSided"); + + CORRADE_COMPARE(data.attributeName(1, 0), "$LayerName"); + CORRADE_COMPARE(data.attributeName(1, 1), "AlphaBlend"); + CORRADE_COMPARE(data.attributeName(1, 2), "highlightColor"); + + CORRADE_COMPARE(data.attributeName(3, 0), "NormalTexture"); + CORRADE_COMPARE(data.attributeName(3, 1), "thickness"); + + /* Access by layer ID and attribute ID */ + CORRADE_COMPARE(data.attributeType(0, 0), MaterialAttributeType::UnsignedInt); + CORRADE_COMPARE(data.attributeType(1, 2), MaterialAttributeType::Vector4); + CORRADE_COMPARE(data.attributeType(3, 1), MaterialAttributeType::Float); + + CORRADE_COMPARE(data.attribute(0, 0), 5); + CORRADE_COMPARE(data.attribute(1, 2), 0x335566ff_rgbaf); + CORRADE_COMPARE(data.attribute(3, 1), 0.015f); + + CORRADE_COMPARE(*static_cast(data.attribute(0, 0)), 5); + CORRADE_COMPARE(*static_cast(data.attribute(1, 2)), 0x335566ff_rgbaf); + CORRADE_COMPARE(*static_cast(data.attribute(3, 1)), 0.015f); + + /* Access by layer ID and attribute name */ + CORRADE_VERIFY(data.hasAttribute(0, MaterialAttribute::DiffuseTextureCoordinates)); + CORRADE_VERIFY(!data.hasAttribute(0, MaterialAttribute::AlphaBlend)); + CORRADE_VERIFY(data.hasAttribute(1, MaterialAttribute::AlphaBlend)); + CORRADE_VERIFY(data.hasAttribute(1, MaterialAttribute::LayerName)); + CORRADE_VERIFY(!data.hasAttribute(2, MaterialAttribute::LayerName)); + CORRADE_VERIFY(!data.hasAttribute(2, MaterialAttribute::NormalTexture)); + CORRADE_VERIFY(data.hasAttribute(3, MaterialAttribute::NormalTexture)); + + CORRADE_COMPARE(data.attributeId(0, MaterialAttribute::DiffuseTextureCoordinates), 0); + CORRADE_COMPARE(data.attributeId(1, MaterialAttribute::AlphaBlend), 1); + CORRADE_COMPARE(data.attributeId(1, MaterialAttribute::LayerName), 0); + CORRADE_COMPARE(data.attributeId(3, MaterialAttribute::NormalTexture), 0); + + CORRADE_COMPARE(data.attributeType(0, MaterialAttribute::DiffuseTextureCoordinates), MaterialAttributeType::UnsignedInt); + CORRADE_COMPARE(data.attributeType(1, MaterialAttribute::AlphaBlend), MaterialAttributeType::Bool); + CORRADE_COMPARE(data.attributeType(1, MaterialAttribute::LayerName), MaterialAttributeType::String); + CORRADE_COMPARE(data.attributeType(3, MaterialAttribute::NormalTexture), MaterialAttributeType::UnsignedInt); + + CORRADE_COMPARE(data.attribute(0, MaterialAttribute::DiffuseTextureCoordinates), 5); + CORRADE_COMPARE(data.attribute(1, MaterialAttribute::AlphaBlend), true); + CORRADE_COMPARE(data.attribute(1, MaterialAttribute::LayerName), "ClearCoat"); + CORRADE_COMPARE(data.attribute(3, MaterialAttribute::NormalTexture), 3); + + CORRADE_COMPARE(*static_cast(data.attribute(0, MaterialAttribute::DiffuseTextureCoordinates)), 5); + CORRADE_COMPARE(*static_cast(data.attribute(1, MaterialAttribute::AlphaBlend)), true); + CORRADE_COMPARE(static_cast(data.attribute(1, MaterialAttribute::LayerName)), "ClearCoat"_s); + CORRADE_COMPARE(*static_cast(data.attribute(3, MaterialAttribute::NormalTexture)), 3); + + /* Access by layer ID and attribute string */ + CORRADE_VERIFY(data.hasAttribute(0, "DoubleSided")); + CORRADE_VERIFY(!data.hasAttribute(0, "highlightColor")); + CORRADE_VERIFY(data.hasAttribute(1, "highlightColor")); + CORRADE_VERIFY(data.hasAttribute(1, "$LayerName")); + CORRADE_VERIFY(!data.hasAttribute(2, "$LayerName")); + CORRADE_VERIFY(!data.hasAttribute(2, "NormalTexture")); + CORRADE_VERIFY(data.hasAttribute(3, "NormalTexture")); + + CORRADE_COMPARE(data.attributeId(0, "DoubleSided"), 1); + CORRADE_COMPARE(data.attributeId(1, "highlightColor"), 2); + CORRADE_COMPARE(data.attributeId(1, "$LayerName"), 0); + CORRADE_COMPARE(data.attributeId(3, "NormalTexture"), 0); + + CORRADE_COMPARE(data.attributeType(0, "DoubleSided"), MaterialAttributeType::Bool); + CORRADE_COMPARE(data.attributeType(1, "highlightColor"), MaterialAttributeType::Vector4); + CORRADE_COMPARE(data.attributeType(1, "$LayerName"), MaterialAttributeType::String); + CORRADE_COMPARE(data.attributeType(3, "NormalTexture"), MaterialAttributeType::UnsignedInt); + + CORRADE_COMPARE(data.attribute(0, "DoubleSided"), true); + CORRADE_COMPARE(data.attribute(1, "highlightColor"), 0x335566ff_rgbaf); + CORRADE_COMPARE(data.attribute(1, "$LayerName"), "ClearCoat"); + CORRADE_COMPARE(data.attribute(3, "NormalTexture"), 3); + + CORRADE_COMPARE(*static_cast(data.attribute(0, "DoubleSided")), true); + CORRADE_COMPARE(*static_cast(data.attribute(1, "highlightColor")), 0x335566ff_rgbaf); + CORRADE_COMPARE(static_cast(data.attribute(1, "$LayerName")), "ClearCoat"_s); + CORRADE_COMPARE(*static_cast(data.attribute(3, "NormalTexture")), 3); + + /* Access by layer string and attribute ID */ + CORRADE_COMPARE(data.attributeName("ClearCoat", 1), "AlphaBlend"); + CORRADE_COMPARE(data.attributeName("ClearCoat", 2), "highlightColor"); + + CORRADE_COMPARE(data.attributeType("ClearCoat", 1), MaterialAttributeType::Bool); + CORRADE_COMPARE(data.attributeType("ClearCoat", 2), MaterialAttributeType::Vector4); + + CORRADE_COMPARE(data.attribute("ClearCoat", 1), true); + CORRADE_COMPARE(data.attribute("ClearCoat", 2), 0x335566ff_rgbaf); + + CORRADE_COMPARE(*static_cast(data.attribute("ClearCoat", 1)), true); + CORRADE_COMPARE(*static_cast(data.attribute("ClearCoat", 2)), 0x335566ff_rgbaf); + + /* Access by layer string and attribute name */ + CORRADE_VERIFY(data.hasAttribute("ClearCoat", MaterialAttribute::AlphaBlend)); + CORRADE_VERIFY(data.hasAttribute("ClearCoat", MaterialAttribute::LayerName)); + + CORRADE_COMPARE(data.attributeId("ClearCoat", MaterialAttribute::AlphaBlend), 1); + CORRADE_COMPARE(data.attributeId("ClearCoat", MaterialAttribute::LayerName), 0); + + CORRADE_COMPARE(data.attributeType("ClearCoat", MaterialAttribute::AlphaBlend), MaterialAttributeType::Bool); + CORRADE_COMPARE(data.attributeType("ClearCoat", MaterialAttribute::LayerName), MaterialAttributeType::String); + + CORRADE_COMPARE(data.attribute("ClearCoat", MaterialAttribute::AlphaBlend), true); + CORRADE_COMPARE(data.attribute("ClearCoat", MaterialAttribute::LayerName), "ClearCoat"); + + CORRADE_COMPARE(*static_cast(data.attribute("ClearCoat", MaterialAttribute::AlphaBlend)), true); + CORRADE_COMPARE(static_cast(data.attribute("ClearCoat", MaterialAttribute::LayerName)), "ClearCoat"_s); + + /* Access by layer string and attribute string */ + CORRADE_VERIFY(data.hasAttribute("ClearCoat", "highlightColor")); + CORRADE_VERIFY(data.hasAttribute("ClearCoat", "$LayerName")); + + CORRADE_COMPARE(data.attributeId("ClearCoat", "highlightColor"), 2); + CORRADE_COMPARE(data.attributeId("ClearCoat", "$LayerName"), 0); + + CORRADE_COMPARE(data.attributeType("ClearCoat", "highlightColor"), MaterialAttributeType::Vector4); + CORRADE_COMPARE(data.attributeType("ClearCoat", "$LayerName"), MaterialAttributeType::String); + + CORRADE_COMPARE(data.attribute("ClearCoat", "highlightColor"), 0x335566ff_rgbaf); + CORRADE_COMPARE(data.attribute("ClearCoat", "$LayerName"), "ClearCoat"); + + CORRADE_COMPARE(*static_cast(data.attribute("ClearCoat", "highlightColor")), 0x335566ff_rgbaf); + CORRADE_COMPARE(static_cast(data.attribute("ClearCoat", "$LayerName")), "ClearCoat"_s); +} + +void MaterialDataTest::constructLayersNotMonotonic() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + std::ostringstream out; + Error redirectError{&out}; + MaterialData data{MaterialType::Phong, { + {MaterialAttribute::DoubleSided, true}, + {MaterialAttribute::DiffuseTextureCoordinates, 5u}, + {MaterialAttribute::AlphaBlend, true}, + {MaterialAttribute::LayerName, "ClearCoat"}, + {MaterialAttribute::NormalTexture, 3u} + }, {2, 5, 4, 5}}; + CORRADE_COMPARE(out.str(), "Trade::MaterialData: invalid range (5, 4) for layer 2 with 5 attributes in total\n"); +} + +void MaterialDataTest::constructLayersOffsetOutOfBounds() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + std::ostringstream out; + Error redirectError{&out}; + MaterialData data{MaterialType::Phong, { + {MaterialAttribute::DoubleSided, true}, + {MaterialAttribute::DiffuseTextureCoordinates, 5u}, + {MaterialAttribute::AlphaBlend, true}, + {MaterialAttribute::LayerName, "ClearCoat"}, + {MaterialAttribute::NormalTexture, 3u} + }, {2, 6}}; + CORRADE_COMPARE(out.str(), "Trade::MaterialData: invalid range (2, 6) for layer 1 with 5 attributes in total\n"); +} + void MaterialDataTest::constructNonOwned() { constexpr MaterialAttributeData attributes[]{ {"AmbientTextureMatrix"_s, Matrix3{{0.5f, 0.0f, 0.0f}, @@ -749,9 +1047,11 @@ void MaterialDataTest::constructNonOwned() { /* Expecting the same output as in construct() */ CORRADE_COMPARE(data.types(), MaterialType::Phong); + CORRADE_COMPARE(data.layerCount(), 1); + CORRADE_VERIFY(!data.layerData()); CORRADE_COMPARE(data.attributeCount(), 4); - CORRADE_COMPARE(data.data().size(), 4); - CORRADE_COMPARE(data.data().data(), attributes); + CORRADE_COMPARE(data.attributeData().size(), 4); + CORRADE_COMPARE(data.attributeData().data(), attributes); CORRADE_COMPARE(data.importerState(), &state); /* We sorted the input already */ @@ -764,6 +1064,67 @@ void MaterialDataTest::constructNonOwned() { owned vs non-owned */ } +void MaterialDataTest::constructNonOwnedLayers() { + constexpr MaterialAttributeData attributes[]{ + {"DiffuseCoordinateSet"_s, 5u}, + {"DoubleSided"_s, true}, + + {"$LayerName"_s, "ClearCoat"_s}, + {"AlphaBlend"_s, true}, + {"highlightColor"_s, Vector4{0.2f, 0.6f, 0.4f, 1.0f}}, + + /* Empty layer here */ + + /* Unnamed but nonempty layer */ + {"NormalTexture"_s, 3u}, + {"thickness"_s, 0.015f} + }; + + constexpr UnsignedInt layers[]{ + 2, 5, 5, 7 + }; + + int state; + MaterialData data{MaterialType::Phong, + {}, attributes, + {}, layers, &state}; + + /* Expecting the same output as in constructLayers() */ + CORRADE_COMPARE(data.types(), MaterialType::Phong); + CORRADE_COMPARE(data.importerState(), &state); + + CORRADE_COMPARE(data.layerCount(), 4); + CORRADE_COMPARE(data.layerData().size(), 4); + CORRADE_COMPARE(data.layerData().data(), layers); + + CORRADE_COMPARE(data.attributeData().size(), 7); + CORRADE_COMPARE(data.attributeData().data(), attributes); + CORRADE_COMPARE(data.attributeCount(0), 2); + CORRADE_COMPARE(data.attributeCount(1), 3); + CORRADE_COMPARE(data.attributeCount(2), 0); + CORRADE_COMPARE(data.attributeCount(3), 2); + + /* Layer access */ + CORRADE_COMPARE(data.layerName(0), ""); + CORRADE_COMPARE(data.layerName(1), "ClearCoat"); + CORRADE_COMPARE(data.layerName(2), ""); + CORRADE_COMPARE(data.layerName(3), ""); + + /* We sorted the input already */ + CORRADE_COMPARE(data.attributeName(0, 0), "DiffuseCoordinateSet"); + CORRADE_COMPARE(data.attributeName(0, 1), "DoubleSided"); + + CORRADE_COMPARE(data.attributeName(1, 0), "$LayerName"); + CORRADE_COMPARE(data.attributeName(1, 1), "AlphaBlend"); + CORRADE_COMPARE(data.attributeName(1, 2), "highlightColor"); + + CORRADE_COMPARE(data.attributeName(3, 0), "NormalTexture"); + CORRADE_COMPARE(data.attributeName(3, 1), "thickness"); + + /* No need to verify the contents as there's no difference in access in + owned vs non-owned */ +} + void MaterialDataTest::constructNonOwnedEmptyAttribute() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); @@ -816,6 +1177,56 @@ void MaterialDataTest::constructNonOwnedDuplicateAttribute() { CORRADE_COMPARE(out.str(), "Trade::MaterialData: duplicate attribute DiffuseTextureCoordinates\n"); } +void MaterialDataTest::constructNonOwnedLayersNotMonotonic() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + MaterialAttributeData attributes[]{ + {MaterialAttribute::AlphaBlend, true}, + {MaterialAttribute::DiffuseTextureCoordinates, 5u}, + {MaterialAttribute::LayerName, "ClearCoat"}, + {MaterialAttribute::DoubleSided, true}, + {MaterialAttribute::NormalTexture, 3u} + }; + + UnsignedInt layers[]{ + 2, 5, 4, 5 + }; + + std::ostringstream out; + Error redirectError{&out}; + MaterialData data{MaterialType::Phong, + {}, attributes, + {}, layers}; + CORRADE_COMPARE(out.str(), "Trade::MaterialData: invalid range (5, 4) for layer 2 with 5 attributes in total\n"); +} + +void MaterialDataTest::constructNonOwnedLayersOffsetOutOfBounds() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + MaterialAttributeData attributes[]{ + {MaterialAttribute::AlphaBlend, true}, + {MaterialAttribute::DiffuseTextureCoordinates, 5u}, + {MaterialAttribute::LayerName, "ClearCoat"}, + {MaterialAttribute::DoubleSided, true}, + {MaterialAttribute::NormalTexture, 3u} + }; + + UnsignedInt layers[]{ + 2, 6 + }; + + std::ostringstream out; + Error redirectError{&out}; + MaterialData data{MaterialType::Phong, + {}, attributes, + {}, layers}; + CORRADE_COMPARE(out.str(), "Trade::MaterialData: invalid range (2, 6) for layer 1 with 5 attributes in total\n"); +} + void MaterialDataTest::constructCopy() { CORRADE_VERIFY(!(std::is_constructible{})); CORRADE_VERIFY(!(std::is_assignable{})); @@ -825,24 +1236,31 @@ void MaterialDataTest::constructMove() { int state; MaterialData a{MaterialType::Phong, { {MaterialAttribute::DoubleSided, true}, + {MaterialAttribute::AlphaBlend, true}, {"boredomFactor", 5} + }, { + 1, 1, 3 }, &state}; MaterialData b{std::move(a)}; + CORRADE_COMPARE(a.layerCount(), 1); CORRADE_COMPARE(a.attributeCount(), 0); CORRADE_COMPARE(b.types(), MaterialType::Phong); - CORRADE_COMPARE(b.attributeCount(), 2); - CORRADE_COMPARE(b.attributeName(0), "DoubleSided"); + CORRADE_COMPARE(b.layerCount(), 3); + CORRADE_COMPARE(b.attributeCount(2), 2); + CORRADE_COMPARE(b.attributeName(2, 0), "AlphaBlend"); CORRADE_COMPARE(b.importerState(), &state); MaterialData c{MaterialTypes{}, { {MaterialAttribute::AlphaMask, 0.5f} - }}; + }, {1}}; c = std::move(b); CORRADE_COMPARE(b.attributeCount(), 1); + CORRADE_COMPARE(b.layerCount(), 1); CORRADE_COMPARE(c.types(), MaterialType::Phong); - CORRADE_COMPARE(c.attributeCount(), 2); - CORRADE_COMPARE(c.attributeName(0), "DoubleSided"); + CORRADE_COMPARE(c.layerCount(), 3); + CORRADE_COMPARE(c.attributeCount(2), 2); + CORRADE_COMPARE(c.attributeName(2, 0), "AlphaBlend"); CORRADE_COMPARE(c.importerState(), &state); CORRADE_VERIFY(std::is_nothrow_move_constructible::value); @@ -948,55 +1366,11 @@ void MaterialDataTest::accessOutOfBounds() { data.attribute(2); data.attribute(2); CORRADE_COMPARE(out.str(), - "Trade::MaterialData::attributeName(): index 2 out of range for 2 attributes\n" - "Trade::MaterialData::attributeType(): index 2 out of range for 2 attributes\n" - "Trade::MaterialData::attribute(): index 2 out of range for 2 attributes\n" - "Trade::MaterialData::attribute(): index 2 out of range for 2 attributes\n" - "Trade::MaterialData::attribute(): index 2 out of range for 2 attributes\n"); -} - -void MaterialDataTest::accessInvalidAttributeName() { - #ifdef CORRADE_NO_ASSERT - CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); - #endif - - MaterialData data{{}, {}}; - - std::ostringstream out; - Error redirectError{&out}; - data.hasAttribute(MaterialAttribute(0x0)); - data.hasAttribute(MaterialAttribute(0xfefe)); - data.attributeId(MaterialAttribute(0x0)); - data.attributeId(MaterialAttribute(0xfefe)); - data.attributeType(MaterialAttribute(0x0)); - data.attributeType(MaterialAttribute(0xfefe)); - data.attribute(MaterialAttribute(0x0)); - data.attribute(MaterialAttribute(0xfefe)); - data.attribute(MaterialAttribute(0x0)); - data.attribute(MaterialAttribute(0xfefe)); - data.tryAttribute(MaterialAttribute(0x0)); - data.tryAttribute(MaterialAttribute(0xfefe)); - data.tryAttribute(MaterialAttribute(0x0)); - data.tryAttribute(MaterialAttribute(0xfefe)); - data.attributeOr(MaterialAttribute(0x0), 42); - data.attributeOr(MaterialAttribute(0xfefe), 42); - CORRADE_COMPARE(out.str(), - "Trade::MaterialData::hasAttribute(): invalid name Trade::MaterialAttribute(0x0)\n" - "Trade::MaterialData::hasAttribute(): invalid name Trade::MaterialAttribute(0xfefe)\n" - "Trade::MaterialData::attributeId(): invalid name Trade::MaterialAttribute(0x0)\n" - "Trade::MaterialData::attributeId(): invalid name Trade::MaterialAttribute(0xfefe)\n" - "Trade::MaterialData::attributeType(): invalid name Trade::MaterialAttribute(0x0)\n" - "Trade::MaterialData::attributeType(): invalid name Trade::MaterialAttribute(0xfefe)\n" - "Trade::MaterialData::attribute(): invalid name Trade::MaterialAttribute(0x0)\n" - "Trade::MaterialData::attribute(): invalid name Trade::MaterialAttribute(0xfefe)\n" - "Trade::MaterialData::attribute(): invalid name Trade::MaterialAttribute(0x0)\n" - "Trade::MaterialData::attribute(): invalid name Trade::MaterialAttribute(0xfefe)\n" - "Trade::MaterialData::tryAttribute(): invalid name Trade::MaterialAttribute(0x0)\n" - "Trade::MaterialData::tryAttribute(): invalid name Trade::MaterialAttribute(0xfefe)\n" - "Trade::MaterialData::tryAttribute(): invalid name Trade::MaterialAttribute(0x0)\n" - "Trade::MaterialData::tryAttribute(): invalid name Trade::MaterialAttribute(0xfefe)\n" - "Trade::MaterialData::attributeOr(): invalid name Trade::MaterialAttribute(0x0)\n" - "Trade::MaterialData::attributeOr(): invalid name Trade::MaterialAttribute(0xfefe)\n"); + "Trade::MaterialData::attributeName(): index 2 out of range for 2 attributes in layer 0\n" + "Trade::MaterialData::attributeType(): index 2 out of range for 2 attributes in layer 0\n" + "Trade::MaterialData::attribute(): index 2 out of range for 2 attributes in layer 0\n" + "Trade::MaterialData::attribute(): index 2 out of range for 2 attributes in layer 0\n" + "Trade::MaterialData::attribute(): index 2 out of range for 2 attributes in layer 0\n"); } void MaterialDataTest::accessNotFound() { @@ -1017,10 +1391,10 @@ void MaterialDataTest::accessNotFound() { data.attribute("DiffuseColour"); data.attribute("DiffuseColour"); CORRADE_COMPARE(out.str(), - "Trade::MaterialData::attributeId(): attribute DiffuseColour not found\n" - "Trade::MaterialData::attributeType(): attribute DiffuseColour not found\n" - "Trade::MaterialData::attribute(): attribute DiffuseColour not found\n" - "Trade::MaterialData::attribute(): attribute DiffuseColour not found\n"); + "Trade::MaterialData::attributeId(): attribute DiffuseColour not found in layer 0\n" + "Trade::MaterialData::attributeType(): attribute DiffuseColour not found in layer 0\n" + "Trade::MaterialData::attribute(): attribute DiffuseColour not found in layer 0\n" + "Trade::MaterialData::attribute(): attribute DiffuseColour not found in layer 0\n"); } void MaterialDataTest::accessWrongType() { @@ -1109,19 +1483,392 @@ void MaterialDataTest::accessWrongTypeString() { "Trade::MaterialData::attribute(): Shininess of Trade::MaterialAttributeType::Float can't be retrieved as a string\n"); } -void MaterialDataTest::release() { +void MaterialDataTest::accessLayerLayerNameInBaseMaterial() { + MaterialData data{{}, { + {MaterialAttribute::Shininess, 50.0f}, + {MaterialAttribute::LayerName, "base material name"} + }}; + + /* To avoid confusing the base material with a layer, LayerName is ignored + for the base material. */ + CORRADE_COMPARE(data.layerName(0), ""); + CORRADE_VERIFY(!data.hasLayer("base material name")); +} + +void MaterialDataTest::accessLayerEmptyLayer() { + /* If a layer is empty, its contents shouldn't leak into upper layers */ + MaterialData data{{}, { + {MaterialAttribute::NormalTexture, 3u}, + {MaterialAttribute::LayerName, "crumples"} + }, {0, 0, 2}}; + + CORRADE_COMPARE(data.layerName(0), ""); + CORRADE_COMPARE(data.layerName(1), ""); + CORRADE_COMPARE(data.layerName(2), "crumples"); + CORRADE_COMPARE(data.attributeCount(0), 0); + CORRADE_COMPARE(data.attributeCount(1), 0); + CORRADE_COMPARE(data.attributeCount(2), 2); + CORRADE_COMPARE(data.layerId("crumples"), 2); + CORRADE_COMPARE(data.attribute("crumples", MaterialAttribute::NormalTexture), 3u); +} + +void MaterialDataTest::accessLayerIndexOptional() { + MaterialData data{{}, { + {MaterialAttribute::DiffuseColor, 0x335566ff_rgbaf}, + {MaterialAttribute::AlphaMask, 0.5f}, + {MaterialAttribute::SpecularTexture, 3u} + }, {1, 3}}; + + /* This exists */ + CORRADE_VERIFY(data.tryAttribute(1, "SpecularTexture")); + CORRADE_VERIFY(data.tryAttribute(1, MaterialAttribute::SpecularTexture)); + CORRADE_COMPARE(*static_cast(data.tryAttribute(1, "SpecularTexture")), 3); + CORRADE_COMPARE(*static_cast(data.tryAttribute(1, MaterialAttribute::SpecularTexture)), 3); + CORRADE_COMPARE(data.tryAttribute(1, "SpecularTexture"), 3); + CORRADE_COMPARE(data.tryAttribute(1, MaterialAttribute::SpecularTexture), 3); + CORRADE_COMPARE(data.attributeOr(1, "SpecularTexture", 5u), 3); + CORRADE_COMPARE(data.attributeOr(1, MaterialAttribute::SpecularTexture, 5u), 3); + + /* This doesn't */ + CORRADE_VERIFY(!data.tryAttribute(1, "DiffuseTexture")); + CORRADE_VERIFY(!data.tryAttribute(1, MaterialAttribute::DiffuseTexture)); + CORRADE_VERIFY(!data.tryAttribute(1, "DiffuseTexture")); + CORRADE_VERIFY(!data.tryAttribute(1, MaterialAttribute::DiffuseTexture)); + CORRADE_COMPARE(data.attributeOr(1, "DiffuseTexture", 5u), 5); + CORRADE_COMPARE(data.attributeOr(1, MaterialAttribute::DiffuseTexture, 5u), 5); +} + +void MaterialDataTest::accessLayerStringOptional() { + MaterialData data{{}, { + {MaterialAttribute::DiffuseColor, 0x335566ff_rgbaf}, + {MaterialAttribute::LayerName, "ClearCoat"}, + {MaterialAttribute::AlphaMask, 0.5f}, + {MaterialAttribute::SpecularTexture, 3u} + }, {1, 4}}; + + /* This exists */ + CORRADE_VERIFY(data.tryAttribute("ClearCoat", "SpecularTexture")); + CORRADE_VERIFY(data.tryAttribute("ClearCoat", MaterialAttribute::SpecularTexture)); + CORRADE_COMPARE(*static_cast(data.tryAttribute("ClearCoat", "SpecularTexture")), 3); + CORRADE_COMPARE(*static_cast(data.tryAttribute("ClearCoat", MaterialAttribute::SpecularTexture)), 3); + CORRADE_COMPARE(data.tryAttribute("ClearCoat", "SpecularTexture"), 3); + CORRADE_COMPARE(data.tryAttribute("ClearCoat", MaterialAttribute::SpecularTexture), 3); + CORRADE_COMPARE(data.attributeOr("ClearCoat", "SpecularTexture", 5u), 3); + CORRADE_COMPARE(data.attributeOr("ClearCoat", MaterialAttribute::SpecularTexture, 5u), 3); + + /* This doesn't */ + CORRADE_VERIFY(!data.tryAttribute("ClearCoat", "DiffuseTexture")); + CORRADE_VERIFY(!data.tryAttribute("ClearCoat", MaterialAttribute::DiffuseTexture)); + CORRADE_VERIFY(!data.tryAttribute("ClearCoat", "DiffuseTexture")); + CORRADE_VERIFY(!data.tryAttribute("ClearCoat", MaterialAttribute::DiffuseTexture)); + CORRADE_COMPARE(data.attributeOr("ClearCoat", "DiffuseTexture", 5u), 5); + CORRADE_COMPARE(data.attributeOr("ClearCoat", MaterialAttribute::DiffuseTexture, 5u), 5); +} + +void MaterialDataTest::accessLayerOutOfBounds() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + MaterialData data{{}, { + {MaterialAttribute::AlphaMask, 0.5f}, + {MaterialAttribute::SpecularTexture, 3u} + }, {0, 2}}; + + std::ostringstream out; + Error redirectError{&out}; + data.layerName(2); + data.attributeCount(2); + data.hasAttribute(2, "AlphaMask"); + data.hasAttribute(2, MaterialAttribute::AlphaMask); + data.attributeId(2, "AlphaMask"); + data.attributeId(2, MaterialAttribute::AlphaMask); + data.attributeName(2, 0); + data.attributeType(2, 0); + data.attributeType(2, "AlphaMask"); + data.attributeType(2, MaterialAttribute::AlphaMask); + data.attribute(2, 0); + data.attribute(2, "AlphaMask"); + data.attribute(2, MaterialAttribute::AlphaMask); + data.attribute(2, 0); + data.attribute(2, "AlphaMask"); + data.attribute(2, MaterialAttribute::AlphaMask); + data.attribute(2, 0); + data.tryAttribute(2, "AlphaMask"); + data.tryAttribute(2, MaterialAttribute::AlphaMask); + data.tryAttribute(2, "AlphaMask"); + data.tryAttribute(2, MaterialAttribute::AlphaMask); + data.attributeOr(2, "AlphaMask", false); + data.attributeOr(2, MaterialAttribute::AlphaMask, false); + CORRADE_COMPARE(out.str(), + "Trade::MaterialData::layerName(): index 2 out of range for 2 layers\n" + "Trade::MaterialData::attributeCount(): index 2 out of range for 2 layers\n" + "Trade::MaterialData::hasAttribute(): index 2 out of range for 2 layers\n" + "Trade::MaterialData::hasAttribute(): index 2 out of range for 2 layers\n" + "Trade::MaterialData::attributeId(): index 2 out of range for 2 layers\n" + "Trade::MaterialData::attributeId(): index 2 out of range for 2 layers\n" + "Trade::MaterialData::attributeName(): index 2 out of range for 2 layers\n" + "Trade::MaterialData::attributeType(): index 2 out of range for 2 layers\n" + "Trade::MaterialData::attributeType(): index 2 out of range for 2 layers\n" + "Trade::MaterialData::attributeType(): index 2 out of range for 2 layers\n" + "Trade::MaterialData::attribute(): index 2 out of range for 2 layers\n" + "Trade::MaterialData::attribute(): index 2 out of range for 2 layers\n" + "Trade::MaterialData::attribute(): index 2 out of range for 2 layers\n" + "Trade::MaterialData::attribute(): index 2 out of range for 2 layers\n" + "Trade::MaterialData::attribute(): index 2 out of range for 2 layers\n" + "Trade::MaterialData::attribute(): index 2 out of range for 2 layers\n" + "Trade::MaterialData::attribute(): index 2 out of range for 2 layers\n" + "Trade::MaterialData::tryAttribute(): index 2 out of range for 2 layers\n" + "Trade::MaterialData::tryAttribute(): index 2 out of range for 2 layers\n" + "Trade::MaterialData::tryAttribute(): index 2 out of range for 2 layers\n" + "Trade::MaterialData::tryAttribute(): index 2 out of range for 2 layers\n" + "Trade::MaterialData::attributeOr(): index 2 out of range for 2 layers\n" + "Trade::MaterialData::attributeOr(): index 2 out of range for 2 layers\n"); +} + +void MaterialDataTest::accessLayerNotFound() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + MaterialData data{{}, { + {MaterialAttribute::LayerName, "clearCoat"}, + {MaterialAttribute::AlphaMask, 0.5f}, + }, {0, 2}}; + + std::ostringstream out; + Error redirectError{&out}; + data.attributeCount("ClearCoat"); + data.hasAttribute("ClearCoat", "AlphaMask"); + data.hasAttribute("ClearCoat", MaterialAttribute::AlphaMask); + data.attributeId("ClearCoat", "AlphaMask"); + data.attributeId("ClearCoat", MaterialAttribute::AlphaMask); + data.attributeName("ClearCoat", 0); + data.attributeType("ClearCoat", 0); + data.attributeType("ClearCoat", "AlphaMask"); + data.attributeType("ClearCoat", MaterialAttribute::AlphaMask); + data.attribute("ClearCoat", 0); + data.attribute("ClearCoat", "AlphaMask"); + data.attribute("ClearCoat", MaterialAttribute::AlphaMask); + data.attribute("ClearCoat", 0); + data.attribute("ClearCoat", "AlphaMask"); + data.attribute("ClearCoat", MaterialAttribute::AlphaMask); + data.attribute("ClearCoat", 0); + data.tryAttribute("ClearCoat", "AlphaMask"); + data.tryAttribute("ClearCoat", MaterialAttribute::AlphaMask); + data.tryAttribute("ClearCoat", "AlphaMask"); + data.tryAttribute("ClearCoat", MaterialAttribute::AlphaMask); + data.attributeOr("ClearCoat", "AlphaMask", false); + data.attributeOr("ClearCoat", MaterialAttribute::AlphaMask, false); + CORRADE_COMPARE(out.str(), + "Trade::MaterialData::attributeCount(): layer ClearCoat not found\n" + "Trade::MaterialData::hasAttribute(): layer ClearCoat not found\n" + "Trade::MaterialData::hasAttribute(): layer ClearCoat not found\n" + "Trade::MaterialData::attributeId(): layer ClearCoat not found\n" + "Trade::MaterialData::attributeId(): layer ClearCoat not found\n" + "Trade::MaterialData::attributeName(): layer ClearCoat not found\n" + "Trade::MaterialData::attributeType(): layer ClearCoat not found\n" + "Trade::MaterialData::attributeType(): layer ClearCoat not found\n" + "Trade::MaterialData::attributeType(): layer ClearCoat not found\n" + "Trade::MaterialData::attribute(): layer ClearCoat not found\n" + "Trade::MaterialData::attribute(): layer ClearCoat not found\n" + "Trade::MaterialData::attribute(): layer ClearCoat not found\n" + "Trade::MaterialData::attribute(): layer ClearCoat not found\n" + "Trade::MaterialData::attribute(): layer ClearCoat not found\n" + "Trade::MaterialData::attribute(): layer ClearCoat not found\n" + "Trade::MaterialData::attribute(): layer ClearCoat not found\n" + "Trade::MaterialData::tryAttribute(): layer ClearCoat not found\n" + "Trade::MaterialData::tryAttribute(): layer ClearCoat not found\n" + "Trade::MaterialData::tryAttribute(): layer ClearCoat not found\n" + "Trade::MaterialData::tryAttribute(): layer ClearCoat not found\n" + "Trade::MaterialData::attributeOr(): layer ClearCoat not found\n" + "Trade::MaterialData::attributeOr(): layer ClearCoat not found\n"); +} + +void MaterialDataTest::accessOutOfBoundsInLayerIndex() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + MaterialData data{{}, { + {MaterialAttribute::AlphaMask, 0.5f}, + {MaterialAttribute::SpecularTexture, 3u} + }, {0, 2}}; + + std::ostringstream out; + Error redirectError{&out}; + data.attributeName(1, 2); + data.attributeType(1, 2); + data.attribute(1, 2); + data.attribute(1, 2); + data.attribute(1, 2); + CORRADE_COMPARE(out.str(), + "Trade::MaterialData::attributeName(): index 2 out of range for 2 attributes in layer 1\n" + "Trade::MaterialData::attributeType(): index 2 out of range for 2 attributes in layer 1\n" + "Trade::MaterialData::attribute(): index 2 out of range for 2 attributes in layer 1\n" + "Trade::MaterialData::attribute(): index 2 out of range for 2 attributes in layer 1\n" + "Trade::MaterialData::attribute(): index 2 out of range for 2 attributes in layer 1\n"); +} + +void MaterialDataTest::accessOutOfBoundsInLayerString() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + MaterialData data{{}, { + {MaterialAttribute::LayerName, "ClearCoat"}, + {MaterialAttribute::AlphaMask, 0.5f} + }, {0, 2}}; + + std::ostringstream out; + Error redirectError{&out}; + data.attributeName("ClearCoat", 2); + data.attributeType("ClearCoat", 2); + data.attribute("ClearCoat", 2); + data.attribute("ClearCoat", 2); + data.attribute("ClearCoat", 2); + CORRADE_COMPARE(out.str(), + "Trade::MaterialData::attributeName(): index 2 out of range for 2 attributes in layer ClearCoat\n" + "Trade::MaterialData::attributeType(): index 2 out of range for 2 attributes in layer ClearCoat\n" + "Trade::MaterialData::attribute(): index 2 out of range for 2 attributes in layer ClearCoat\n" + "Trade::MaterialData::attribute(): index 2 out of range for 2 attributes in layer ClearCoat\n" + "Trade::MaterialData::attribute(): index 2 out of range for 2 attributes in layer ClearCoat\n"); +} + +void MaterialDataTest::accessNotFoundInLayerIndex() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + MaterialData data{{}, { + {"DiffuseColor", 0xff3366aa_rgbaf} + }, {0, 1}}; + + CORRADE_VERIFY(!data.hasAttribute(1, "DiffuseColour")); + + std::ostringstream out; + Error redirectError{&out}; + data.attributeId(1, "DiffuseColour"); + data.attributeType(1, "DiffuseColour"); + data.attribute(1, "DiffuseColour"); + data.attribute(1, "DiffuseColour"); + CORRADE_COMPARE(out.str(), + "Trade::MaterialData::attributeId(): attribute DiffuseColour not found in layer 1\n" + "Trade::MaterialData::attributeType(): attribute DiffuseColour not found in layer 1\n" + "Trade::MaterialData::attribute(): attribute DiffuseColour not found in layer 1\n" + "Trade::MaterialData::attribute(): attribute DiffuseColour not found in layer 1\n"); +} + +void MaterialDataTest::accessNotFoundInLayerString() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + MaterialData data{{}, { + {MaterialAttribute::LayerName, "ClearCoat"}, + {"DiffuseColor", 0xff3366aa_rgbaf} + }, {0, 1}}; + + CORRADE_VERIFY(!data.hasAttribute(1, "DiffuseColour")); + + std::ostringstream out; + Error redirectError{&out}; + data.attributeId("ClearCoat", "DiffuseColour"); + data.attributeType("ClearCoat", "DiffuseColour"); + data.attribute("ClearCoat", "DiffuseColour"); + data.attribute("ClearCoat", "DiffuseColour"); + CORRADE_COMPARE(out.str(), + "Trade::MaterialData::attributeId(): attribute DiffuseColour not found in layer ClearCoat\n" + "Trade::MaterialData::attributeType(): attribute DiffuseColour not found in layer ClearCoat\n" + "Trade::MaterialData::attribute(): attribute DiffuseColour not found in layer ClearCoat\n" + "Trade::MaterialData::attribute(): attribute DiffuseColour not found in layer ClearCoat\n"); +} + +void MaterialDataTest::accessInvalidAttributeName() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + MaterialData data{{}, {}}; + + /* The name should be converted to a string first and foremost and only + then delegated to another overload. Which means all asserts should + print the leaf function name. */ + std::ostringstream out; + Error redirectError{&out}; + data.hasAttribute(0, MaterialAttribute(0x0)); + data.hasAttribute("Layer", MaterialAttribute(0xfefe)); + data.attributeId(0, MaterialAttribute(0x0)); + data.attributeId("Layer", MaterialAttribute(0xfefe)); + data.attributeType(0, MaterialAttribute(0x0)); + data.attributeType("Layer", MaterialAttribute(0xfefe)); + data.attribute(0, MaterialAttribute(0x0)); + data.attribute("Layer", MaterialAttribute(0xfefe)); + data.attribute(0, MaterialAttribute(0x0)); + data.attribute("Layer", MaterialAttribute(0xfefe)); + data.tryAttribute(0, MaterialAttribute(0x0)); + data.tryAttribute("Layer", MaterialAttribute(0xfefe)); + data.tryAttribute(0, MaterialAttribute(0x0)); + data.tryAttribute("Layer", MaterialAttribute(0xfefe)); + data.attributeOr(0, MaterialAttribute(0x0), 42); + data.attributeOr("Layer", MaterialAttribute(0xfefe), 42); + CORRADE_COMPARE(out.str(), + "Trade::MaterialData::hasAttribute(): invalid name Trade::MaterialAttribute(0x0)\n" + "Trade::MaterialData::hasAttribute(): invalid name Trade::MaterialAttribute(0xfefe)\n" + "Trade::MaterialData::attributeId(): invalid name Trade::MaterialAttribute(0x0)\n" + "Trade::MaterialData::attributeId(): invalid name Trade::MaterialAttribute(0xfefe)\n" + "Trade::MaterialData::attributeType(): invalid name Trade::MaterialAttribute(0x0)\n" + "Trade::MaterialData::attributeType(): invalid name Trade::MaterialAttribute(0xfefe)\n" + "Trade::MaterialData::attribute(): invalid name Trade::MaterialAttribute(0x0)\n" + "Trade::MaterialData::attribute(): invalid name Trade::MaterialAttribute(0xfefe)\n" + "Trade::MaterialData::attribute(): invalid name Trade::MaterialAttribute(0x0)\n" + "Trade::MaterialData::attribute(): invalid name Trade::MaterialAttribute(0xfefe)\n" + "Trade::MaterialData::tryAttribute(): invalid name Trade::MaterialAttribute(0x0)\n" + "Trade::MaterialData::tryAttribute(): invalid name Trade::MaterialAttribute(0xfefe)\n" + "Trade::MaterialData::tryAttribute(): invalid name Trade::MaterialAttribute(0x0)\n" + "Trade::MaterialData::tryAttribute(): invalid name Trade::MaterialAttribute(0xfefe)\n" + "Trade::MaterialData::attributeOr(): invalid name Trade::MaterialAttribute(0x0)\n" + "Trade::MaterialData::attributeOr(): invalid name Trade::MaterialAttribute(0xfefe)\n"); +} + +void MaterialDataTest::releaseAttributes() { MaterialData data{{}, { {"DiffuseColor", 0xff3366aa_rgbaf}, {MaterialAttribute::NormalTexture, 0u} - }}; + }, {1, 2}}; - const void* pointer = data.data().data(); + const void* pointer = data.attributeData().data(); - Containers::Array released = data.release(); + Containers::Array released = data.releaseAttributeData(); CORRADE_COMPARE(released.data(), pointer); CORRADE_COMPARE(released.size(), 2); - CORRADE_VERIFY(!data.data()); - CORRADE_COMPARE(data.attributeCount(), 0); + CORRADE_VERIFY(data.layerData()); + CORRADE_COMPARE(data.layerCount(), 2); + CORRADE_VERIFY(!data.attributeData()); + /* This is based on the layer offsets, not an actual attribute count, so + it's inconsistent, yes */ + CORRADE_COMPARE(data.attributeCount(), 1); +} + +void MaterialDataTest::releaseLayers() { + MaterialData data{{}, { + {"DiffuseColor", 0xff3366aa_rgbaf}, + {MaterialAttribute::NormalTexture, 0u} + }, {1, 2}}; + + const void* pointer = data.layerData().data(); + + Containers::Array released = data.releaseLayerData(); + CORRADE_COMPARE(released.data(), pointer); + CORRADE_COMPARE(released.size(), 2); + CORRADE_VERIFY(!data.layerData()); + /* Returns always at least 1 (now it sees no layer data and thus thinks + there's just the implicit base material) */ + CORRADE_COMPARE(data.layerCount(), 1); + CORRADE_VERIFY(data.attributeData()); + /* No layer offsets anymore, so this is the total attribute count instead + of the base material attribute count. It's inconsistent, yes. */ + CORRADE_COMPARE(data.attributeCount(), 2); } #ifdef MAGNUM_BUILD_DEPRECATED @@ -1453,16 +2200,16 @@ void MaterialDataTest::phongAccessInvalidTextures() { data.normalTextureMatrix(); data.normalTextureCoordinates(); CORRADE_COMPARE(out.str(), - "Trade::MaterialData::attribute(): attribute AmbientTexture not found\n" + "Trade::MaterialData::attribute(): attribute AmbientTexture not found in layer 0\n" "Trade::PhongMaterialData::ambientTextureMatrix(): the material doesn't have an ambient texture\n" "Trade::PhongMaterialData::ambientTextureCoordinates(): the material doesn't have an ambient texture\n" - "Trade::MaterialData::attribute(): attribute DiffuseTexture not found\n" + "Trade::MaterialData::attribute(): attribute DiffuseTexture not found in layer 0\n" "Trade::PhongMaterialData::diffuseTextureMatrix(): the material doesn't have a diffuse texture\n" "Trade::PhongMaterialData::diffuseTextureCoordinates(): the material doesn't have a diffuse texture\n" - "Trade::MaterialData::attribute(): attribute SpecularTexture not found\n" + "Trade::MaterialData::attribute(): attribute SpecularTexture not found in layer 0\n" "Trade::PhongMaterialData::specularTextureMatrix(): the material doesn't have a specular texture\n" "Trade::PhongMaterialData::specularTextureCoordinates(): the material doesn't have a specular texture\n" - "Trade::MaterialData::attribute(): attribute NormalTexture not found\n" + "Trade::MaterialData::attribute(): attribute NormalTexture not found in layer 0\n" "Trade::PhongMaterialData::normalTextureMatrix(): the material doesn't have a normal texture\n" "Trade::PhongMaterialData::normalTextureCoordinates(): the material doesn't have a normal texture\n"); } @@ -1470,8 +2217,8 @@ void MaterialDataTest::phongAccessInvalidTextures() { void MaterialDataTest::debugAttribute() { std::ostringstream out; - Debug{&out} << MaterialAttribute::DiffuseTextureCoordinates << MaterialAttribute(0xfefe) << MaterialAttribute{}; - CORRADE_COMPARE(out.str(), "Trade::MaterialAttribute::DiffuseTextureCoordinates Trade::MaterialAttribute(0xfefe) Trade::MaterialAttribute(0x0)\n"); + Debug{&out} << MaterialAttribute::DiffuseTextureCoordinates << MaterialAttribute::LayerName << MaterialAttribute(0xfefe) << MaterialAttribute{}; + CORRADE_COMPARE(out.str(), "Trade::MaterialAttribute::DiffuseTextureCoordinates Trade::MaterialAttribute::LayerName Trade::MaterialAttribute(0xfefe) Trade::MaterialAttribute(0x0)\n"); } void MaterialDataTest::debugAttributeType() { From 0ec8e002fb7509cc9738fd8b88fca799eb02fe10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 26 Jul 2020 01:25:40 +0200 Subject: [PATCH 11/36] Trade: return Optional instead of Pointer from *Importer::material(). The plugin interface version got bumped to avoid ABI issues when loading plugins that weren't updated for the change, but apart from that this shouldn't be a breaking change, as the API returns a type that can be both an Optional and a Pointer. --- doc/changelog.dox | 6 ++ doc/snippets/MagnumTrade.cpp | 14 ++--- src/Magnum/Trade/AbstractImporter.cpp | 30 ++++++++-- src/Magnum/Trade/AbstractImporter.h | 38 +++++++++--- .../Trade/Test/AbstractImporterTest.cpp | 58 +++++++++++++++++-- .../AnyImageImporter/AnyImageImporter.cpp | 2 +- .../AnySceneImporter/AnySceneImporter.cpp | 6 +- .../AnySceneImporter/AnySceneImporter.h | 2 +- src/MagnumPlugins/ObjImporter/ObjImporter.cpp | 2 +- src/MagnumPlugins/TgaImporter/TgaImporter.cpp | 2 +- 10 files changed, 126 insertions(+), 34 deletions(-) diff --git a/doc/changelog.dox b/doc/changelog.dox index 2004f6f152..fd0811f870 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -150,6 +150,12 @@ See also: directly but rather generated on-the-fly from attribute data, which makes them less efficient than calling @ref Trade::MaterialData::hasAttribute() etc. +- @ref Trade::AbstractImporter::material() now returns + @ref Corrade::Containers::Optional instead of a @ref Corrade::Containers::Pointer, + as the new @ref Trade::MaterialData class isn't polymorphic anymore. If + @ref MAGNUM_BUILD_DEPRECATED is enabled, the returned type acts as a + @ref Corrade::Containers::Optional but has (deprecated) implicit conversion + to a @ref Corrade::Containers::Pointer to avoid breaking user code. @subsection changelog-latest-compatibility Potential compatibility breakages, removed APIs diff --git a/doc/snippets/MagnumTrade.cpp b/doc/snippets/MagnumTrade.cpp index d94679d9f1..2e524f8de2 100644 --- a/doc/snippets/MagnumTrade.cpp +++ b/doc/snippets/MagnumTrade.cpp @@ -42,7 +42,7 @@ #include "Magnum/Trade/ImageData.h" #include "Magnum/Trade/MeshData.h" #include "Magnum/Trade/ObjectData2D.h" -#include "Magnum/Trade/ObjectData3D.h" +#include "Magnum/Trade/MeshObjectData3D.h" #include "Magnum/Trade/PhongMaterialData.h" #ifdef MAGNUM_TARGET_GL #include "Magnum/GL/Texture.h" @@ -116,17 +116,17 @@ importer->openFile("scene.gltf"); // memory-maps all files { Containers::Pointer importer; -Float shininess; +Int materialIndex; /* [AbstractImporter-usage-cast] */ -Containers::Pointer data = importer->material(12); -if(data && data->type() == Trade::MaterialType::Phong) { - auto& phong = static_cast(*data); +Containers::Pointer data = importer->object3D(12); +if(data && data->instanceType() == Trade::ObjectInstanceType3D::Mesh) { + auto& mesh = static_cast(*data); - shininess = phong.shininess(); + materialIndex = mesh.material(); // ... } /* [AbstractImporter-usage-cast] */ -static_cast(shininess); +static_cast(materialIndex); } { diff --git a/src/Magnum/Trade/AbstractImporter.cpp b/src/Magnum/Trade/AbstractImporter.cpp index 1425b23779..404c275b9c 100644 --- a/src/Magnum/Trade/AbstractImporter.cpp +++ b/src/Magnum/Trade/AbstractImporter.cpp @@ -33,12 +33,12 @@ #include #include "Magnum/FileCallback.h" -#include "Magnum/Trade/AbstractMaterialData.h" #include "Magnum/Trade/AnimationData.h" #include "Magnum/Trade/ArrayAllocator.h" #include "Magnum/Trade/CameraData.h" #include "Magnum/Trade/ImageData.h" #include "Magnum/Trade/LightData.h" +#include "Magnum/Trade/MaterialData.h" #include "Magnum/Trade/MeshData.h" #include "Magnum/Trade/ObjectData2D.h" #include "Magnum/Trade/ObjectData3D.h" @@ -59,7 +59,7 @@ namespace Magnum { namespace Trade { std::string AbstractImporter::pluginInterface() { - return "cz.mosra.magnum.Trade.AbstractImporter/0.3.1"; + return "cz.mosra.magnum.Trade.AbstractImporter/0.3.2"; } #ifndef CORRADE_PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT @@ -671,17 +671,35 @@ std::string AbstractImporter::materialName(const UnsignedInt id) { std::string AbstractImporter::doMaterialName(UnsignedInt) { return {}; } -Containers::Pointer AbstractImporter::material(const UnsignedInt id) { +#if !defined(MAGNUM_BUILD_DEPRECATED) || defined(DOXYGEN_GENERATING_OUTPUT) +Containers::Optional +#else +OptionalButAlsoPointer +#endif +AbstractImporter::material(const UnsignedInt id) { CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::material(): no file opened", {}); CORRADE_ASSERT(id < doMaterialCount(), "Trade::AbstractImporter::material(): index" << id << "out of range for" << doMaterialCount() << "entries", {}); - return doMaterial(id); + + Containers::Optional material = doMaterial(id); + + /* GCC 4.8 and clang-cl needs an explicit conversion here */ + #ifdef MAGNUM_BUILD_DEPRECATED + return OptionalButAlsoPointer{std::move(material)}; + #else + return material; + #endif } -Containers::Pointer AbstractImporter::doMaterial(UnsignedInt) { +Containers::Optional AbstractImporter::doMaterial(UnsignedInt) { CORRADE_ASSERT_UNREACHABLE("Trade::AbstractImporter::material(): not implemented", {}); } -Containers::Pointer AbstractImporter::material(const std::string& name) { +#if !defined(MAGNUM_BUILD_DEPRECATED) || defined(DOXYGEN_GENERATING_OUTPUT) +Containers::Optional +#else +OptionalButAlsoPointer +#endif +AbstractImporter::material(const std::string& name) { CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::material(): no file opened", {}); const Int id = doMaterialForName(name); if(id == -1) { diff --git a/src/Magnum/Trade/AbstractImporter.h b/src/Magnum/Trade/AbstractImporter.h index 30e5adee4f..794503df95 100644 --- a/src/Magnum/Trade/AbstractImporter.h +++ b/src/Magnum/Trade/AbstractImporter.h @@ -129,6 +129,18 @@ MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, ImporterFlag value); */ MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, ImporterFlags value); +#if defined(MAGNUM_BUILD_DEPRECATED) && !defined(DOXYGEN_GENERATING_OUTPUT) +/* Could be a concrete type as only MaterialData need this, but that would + mean I'd need to include MaterialData here */ +template struct OptionalButAlsoPointer: Containers::Optional { + /*implicit*/ OptionalButAlsoPointer() = default; + /*implicit*/ OptionalButAlsoPointer(Containers::Optional&& optional): Containers::Optional{std::move(optional)} {} + CORRADE_DEPRECATED("use Containers::Optional instead of Containers::Pointer for a MaterialData") /*implicit*/ operator Containers::Pointer() && { + return Containers::Pointer{new T{std::move(**this)}}; + } +}; +#endif + /** @brief Base for importer plugins @@ -186,8 +198,8 @@ expose internal state through various accessors: - @ref importerState() can expose a pointer to the global importer state for currently opened file -- @ref AbstractMaterialData::importerState() can expose importer state for - given material imported by @ref material() +- @ref MaterialData::importerState() can expose importer state for given + material imported by @ref material() - @ref AnimationData::importerState() can expose importer state for given animation imported by @ref animation() - @ref CameraData::importerState() can expose importer state for a camera @@ -325,7 +337,7 @@ class MAGNUM_TRADE_EXPORT AbstractImporter: public PluginManager::AbstractManagi * @brief Plugin interface * * @code{.cpp} - * "cz.mosra.magnum.Trade.AbstractImporter/0.3.1" + * "cz.mosra.magnum.Trade.AbstractImporter/0.3.2" * @endcode */ static std::string pluginInterface(); @@ -1020,11 +1032,16 @@ class MAGNUM_TRADE_EXPORT AbstractImporter: public PluginManager::AbstractManagi * @brief Material * @param id Material ID, from range [0, @ref materialCount()). * - * Returns given material or @cpp nullptr @ce if importing failed. - * Expects that a file is opened. + * Returns given material or @ref Containers::NullOpt if importing + * failed. Expects that a file is opened. * @see @ref material(const std::string&) */ - Containers::Pointer material(UnsignedInt id); + #if !defined(MAGNUM_BUILD_DEPRECATED) || defined(DOXYGEN_GENERATING_OUTPUT) + Containers::Optional + #else + OptionalButAlsoPointer + #endif + material(UnsignedInt id); /** * @brief Material for given name @@ -1036,7 +1053,12 @@ class MAGNUM_TRADE_EXPORT AbstractImporter: public PluginManager::AbstractManagi * @ref Containers::NullOpt, otherwise propagates the result from * @ref material(UnsignedInt). Expects that a file is opened. */ - Containers::Pointer material(const std::string& name); + #if !defined(MAGNUM_BUILD_DEPRECATED) || defined(DOXYGEN_GENERATING_OUTPUT) + Containers::Optional + #else + OptionalButAlsoPointer + #endif + material(const std::string& name); /** * @brief Texture count @@ -1695,7 +1717,7 @@ class MAGNUM_TRADE_EXPORT AbstractImporter: public PluginManager::AbstractManagi virtual std::string doMaterialName(UnsignedInt id); /** @brief Implementation for @ref material() */ - virtual Containers::Pointer doMaterial(UnsignedInt id); + virtual Containers::Optional doMaterial(UnsignedInt id); /** * @brief Implementation for @ref textureCount() diff --git a/src/Magnum/Trade/Test/AbstractImporterTest.cpp b/src/Magnum/Trade/Test/AbstractImporterTest.cpp index d71d542109..d9f0f10097 100644 --- a/src/Magnum/Trade/Test/AbstractImporterTest.cpp +++ b/src/Magnum/Trade/Test/AbstractImporterTest.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -189,6 +190,9 @@ struct AbstractImporterTest: TestSuite::Tester { #endif void material(); + #ifdef MAGNUM_BUILD_DEPRECATED + void materialDeprecatedFallback(); + #endif void materialNameNotImplemented(); void materialNameOutOfRange(); void materialNotImplemented(); @@ -391,6 +395,9 @@ AbstractImporterTest::AbstractImporterTest() { #endif &AbstractImporterTest::material, + #ifdef MAGNUM_BUILD_DEPRECATED + &AbstractImporterTest::materialDeprecatedFallback, + #endif &AbstractImporterTest::materialNameNotImplemented, &AbstractImporterTest::materialNameOutOfRange, &AbstractImporterTest::materialNotImplemented, @@ -2989,12 +2996,8 @@ void AbstractImporterTest::material() { if(id == 7) return "eighth"; else return {}; } - Containers::Pointer doMaterial(UnsignedInt id) override { - if(id == 7) return Containers::pointer(new PhongMaterialData{{}, - {}, {}, - {}, {}, - {}, {}, {}, {}, - {}, {}, {}, &state}); + Containers::Optional doMaterial(UnsignedInt id) override { + if(id == 7) return Containers::optional(MaterialTypes{}, nullptr, &state); else return {}; } } importer; @@ -3013,6 +3016,49 @@ void AbstractImporterTest::material() { CORRADE_COMPARE(data->importerState(), &state); } } + +#ifdef MAGNUM_BUILD_DEPRECATED +void AbstractImporterTest::materialDeprecatedFallback() { + struct: AbstractImporter { + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return true; } + void doClose() override {} + + UnsignedInt doMaterialCount() const override { return 8; } + Int doMaterialForName(const std::string&) override { return 0; } + /* Using a deprecated PhongMaterialData constructor to verify that + propagating such instance works as well (array deleters etc.) */ + Containers::Optional doMaterial(UnsignedInt) override { + CORRADE_IGNORE_DEPRECATED_PUSH + return Containers::Optional{std::move(PhongMaterialData{{}, + {}, {}, + {}, {}, + {}, {}, + {}, + {}, {}, {}, {}, &state + })}; + CORRADE_IGNORE_DEPRECATED_POP + } + } importer; + + { + CORRADE_IGNORE_DEPRECATED_PUSH + Containers::Pointer data = importer.material(0); + CORRADE_IGNORE_DEPRECATED_POP + CORRADE_VERIFY(data); + CORRADE_COMPARE_AS(data->attributeCount(), 0, TestSuite::Compare::Greater); + CORRADE_COMPARE(data->importerState(), &state); + } { + CORRADE_IGNORE_DEPRECATED_PUSH + Containers::Pointer data = importer.material(""); + CORRADE_IGNORE_DEPRECATED_POP + CORRADE_VERIFY(data); + CORRADE_COMPARE_AS(data->attributeCount(), 0, TestSuite::Compare::Greater); + CORRADE_COMPARE(data->importerState(), &state); + } +} +#endif + void AbstractImporterTest::materialNameNotImplemented() { struct: AbstractImporter { ImporterFeatures doFeatures() const override { return {}; } diff --git a/src/MagnumPlugins/AnyImageImporter/AnyImageImporter.cpp b/src/MagnumPlugins/AnyImageImporter/AnyImageImporter.cpp index 635cc99233..b2a789627f 100644 --- a/src/MagnumPlugins/AnyImageImporter/AnyImageImporter.cpp +++ b/src/MagnumPlugins/AnyImageImporter/AnyImageImporter.cpp @@ -239,4 +239,4 @@ Containers::Optional AnyImageImporter::doImage2D(const UnsignedInt }} CORRADE_PLUGIN_REGISTER(AnyImageImporter, Magnum::Trade::AnyImageImporter, - "cz.mosra.magnum.Trade.AbstractImporter/0.3.1") + "cz.mosra.magnum.Trade.AbstractImporter/0.3.2") diff --git a/src/MagnumPlugins/AnySceneImporter/AnySceneImporter.cpp b/src/MagnumPlugins/AnySceneImporter/AnySceneImporter.cpp index 64043c433b..25048581b8 100644 --- a/src/MagnumPlugins/AnySceneImporter/AnySceneImporter.cpp +++ b/src/MagnumPlugins/AnySceneImporter/AnySceneImporter.cpp @@ -32,11 +32,11 @@ #include #include -#include "Magnum/Trade/AbstractMaterialData.h" #include "Magnum/Trade/AnimationData.h" #include "Magnum/Trade/CameraData.h" #include "Magnum/Trade/ImageData.h" #include "Magnum/Trade/LightData.h" +#include "Magnum/Trade/MaterialData.h" #include "Magnum/Trade/MeshData.h" #include "Magnum/Trade/ObjectData2D.h" #include "Magnum/Trade/ObjectData3D.h" @@ -217,7 +217,7 @@ CORRADE_IGNORE_DEPRECATED_POP UnsignedInt AnySceneImporter::doMaterialCount() const { return _in->materialCount(); } Int AnySceneImporter::doMaterialForName(const std::string& name) { return _in->materialForName(name); } std::string AnySceneImporter::doMaterialName(const UnsignedInt id) { return _in->materialName(id); } -Containers::Pointer AnySceneImporter::doMaterial(const UnsignedInt id) { return _in->material(id); } +Containers::Optional AnySceneImporter::doMaterial(const UnsignedInt id) { return _in->material(id); } UnsignedInt AnySceneImporter::doTextureCount() const { return _in->textureCount(); } Int AnySceneImporter::doTextureForName(const std::string& name) { return _in->textureForName(name); } @@ -245,4 +245,4 @@ Containers::Optional AnySceneImporter::doImage3D(const UnsignedInt }} CORRADE_PLUGIN_REGISTER(AnySceneImporter, Magnum::Trade::AnySceneImporter, - "cz.mosra.magnum.Trade.AbstractImporter/0.3.1") + "cz.mosra.magnum.Trade.AbstractImporter/0.3.2") diff --git a/src/MagnumPlugins/AnySceneImporter/AnySceneImporter.h b/src/MagnumPlugins/AnySceneImporter/AnySceneImporter.h index d267442899..6a68182a60 100644 --- a/src/MagnumPlugins/AnySceneImporter/AnySceneImporter.h +++ b/src/MagnumPlugins/AnySceneImporter/AnySceneImporter.h @@ -203,7 +203,7 @@ class MAGNUM_ANYSCENEIMPORTER_EXPORT AnySceneImporter: public AbstractImporter { MAGNUM_ANYSCENEIMPORTER_LOCAL UnsignedInt doMaterialCount() const override; MAGNUM_ANYSCENEIMPORTER_LOCAL Int doMaterialForName(const std::string& name) override; MAGNUM_ANYSCENEIMPORTER_LOCAL std::string doMaterialName(UnsignedInt id) override; - MAGNUM_ANYSCENEIMPORTER_LOCAL Containers::Pointer doMaterial(UnsignedInt id) override; + MAGNUM_ANYSCENEIMPORTER_LOCAL Containers::Optional doMaterial(UnsignedInt id) override; MAGNUM_ANYSCENEIMPORTER_LOCAL UnsignedInt doTextureCount() const override; MAGNUM_ANYSCENEIMPORTER_LOCAL Int doTextureForName(const std::string& name) override; diff --git a/src/MagnumPlugins/ObjImporter/ObjImporter.cpp b/src/MagnumPlugins/ObjImporter/ObjImporter.cpp index 1a609a2234..f6ceb3b35e 100644 --- a/src/MagnumPlugins/ObjImporter/ObjImporter.cpp +++ b/src/MagnumPlugins/ObjImporter/ObjImporter.cpp @@ -486,4 +486,4 @@ Containers::Optional ObjImporter::doMesh(UnsignedInt id, UnsignedInt) }} CORRADE_PLUGIN_REGISTER(ObjImporter, Magnum::Trade::ObjImporter, - "cz.mosra.magnum.Trade.AbstractImporter/0.3.1") + "cz.mosra.magnum.Trade.AbstractImporter/0.3.2") diff --git a/src/MagnumPlugins/TgaImporter/TgaImporter.cpp b/src/MagnumPlugins/TgaImporter/TgaImporter.cpp index 2f9e90a868..f37a236dee 100644 --- a/src/MagnumPlugins/TgaImporter/TgaImporter.cpp +++ b/src/MagnumPlugins/TgaImporter/TgaImporter.cpp @@ -206,4 +206,4 @@ Containers::Optional TgaImporter::doImage2D(UnsignedInt, UnsignedIn }} CORRADE_PLUGIN_REGISTER(TgaImporter, Magnum::Trade::TgaImporter, - "cz.mosra.magnum.Trade.AbstractImporter/0.3.1") + "cz.mosra.magnum.Trade.AbstractImporter/0.3.2") From b5fdfc2474942925006777e30e3940eeaa870df3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 1 Aug 2020 13:20:13 +0200 Subject: [PATCH 12/36] Trade: check for custom MaterialData deleters in AbstractImporter. And adapt MaterialData and the deprecated PhongMaterialData constructor to follow these rules. Basically the same as done for ImageData and MeshData. --- src/Magnum/Trade/AbstractImporter.cpp | 4 + src/Magnum/Trade/MaterialData.cpp | 3 +- src/Magnum/Trade/MaterialData.h | 5 ++ src/Magnum/Trade/PhongMaterialData.cpp | 3 + .../Trade/Test/AbstractImporterTest.cpp | 89 +++++++++++++++++++ 5 files changed, 103 insertions(+), 1 deletion(-) diff --git a/src/Magnum/Trade/AbstractImporter.cpp b/src/Magnum/Trade/AbstractImporter.cpp index 404c275b9c..0fda5dbd88 100644 --- a/src/Magnum/Trade/AbstractImporter.cpp +++ b/src/Magnum/Trade/AbstractImporter.cpp @@ -681,6 +681,10 @@ AbstractImporter::material(const UnsignedInt id) { CORRADE_ASSERT(id < doMaterialCount(), "Trade::AbstractImporter::material(): index" << id << "out of range for" << doMaterialCount() << "entries", {}); Containers::Optional material = doMaterial(id); + CORRADE_ASSERT(!material || ( + (!material->_data.deleter() || material->_data.deleter() == reinterpret_cast(Implementation::nonOwnedArrayDeleter)) && + (!material->_layerOffsets.deleter() || material->_layerOffsets.deleter() == reinterpret_cast(Implementation::nonOwnedArrayDeleter))), + "Trade::AbstractImporter::material(): implementation is not allowed to use a custom Array deleter", {}); /* GCC 4.8 and clang-cl needs an explicit conversion here */ #ifdef MAGNUM_BUILD_DEPRECATED diff --git a/src/Magnum/Trade/MaterialData.cpp b/src/Magnum/Trade/MaterialData.cpp index 2106b1acdb..a5ad23ba4d 100644 --- a/src/Magnum/Trade/MaterialData.cpp +++ b/src/Magnum/Trade/MaterialData.cpp @@ -32,6 +32,7 @@ #include "Magnum/Math/Vector4.h" #include "Magnum/Math/Matrix.h" +#include "Magnum/Trade/Data.h" #include "Magnum/Trade/Implementation/arrayUtilities.h" namespace Magnum { namespace Trade { @@ -226,7 +227,7 @@ MaterialData::MaterialData(const MaterialTypes types, Containers::Array attributeData, const std::initializer_list layerData, const void* const importerState): MaterialData{types, Implementation::initializerListToArrayWithDefaultDeleter(attributeData), Implementation::initializerListToArrayWithDefaultDeleter(layerData), importerState} {} -MaterialData::MaterialData(const MaterialTypes types, DataFlags, const Containers::ArrayView attributeData, DataFlags, Containers::ArrayView layerData, const void* const importerState) noexcept: _data{Containers::Array{const_cast(attributeData.data()), attributeData.size(), [](MaterialAttributeData*, std::size_t){}}}, _layerOffsets{Containers::Array{const_cast(layerData.data()), layerData.size(), [](UnsignedInt*, std::size_t){}}}, _types{types}, _importerState{importerState} { +MaterialData::MaterialData(const MaterialTypes types, DataFlags, const Containers::ArrayView attributeData, DataFlags, Containers::ArrayView layerData, const void* const importerState) noexcept: _data{Containers::Array{const_cast(attributeData.data()), attributeData.size(), reinterpret_cast(Implementation::nonOwnedArrayDeleter)}}, _layerOffsets{Containers::Array{const_cast(layerData.data()), layerData.size(), reinterpret_cast(Implementation::nonOwnedArrayDeleter)}}, _types{types}, _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) diff --git a/src/Magnum/Trade/MaterialData.h b/src/Magnum/Trade/MaterialData.h index 1fe44c7330..7b9d8e8cf0 100644 --- a/src/Magnum/Trade/MaterialData.h +++ b/src/Magnum/Trade/MaterialData.h @@ -1498,6 +1498,11 @@ class MAGNUM_TRADE_EXPORT MaterialData { const void* importerState() const { return _importerState; } private: + /* For custom deleter checks. Not done in the constructors here because + the restriction is pointless when used outside of plugin + implementations. */ + friend AbstractImporter; + static Containers::StringView attributeString(MaterialAttribute name); /* Internal helpers that don't assert, unlike layerId() / attributeId() */ UnsignedInt layerFor(Containers::StringView layer) const; diff --git a/src/Magnum/Trade/PhongMaterialData.cpp b/src/Magnum/Trade/PhongMaterialData.cpp index bf0b48915f..2c596b4a3d 100644 --- a/src/Magnum/Trade/PhongMaterialData.cpp +++ b/src/Magnum/Trade/PhongMaterialData.cpp @@ -91,6 +91,9 @@ PhongMaterialData::PhongMaterialData(const Flags flags, const Color4& ambientCol arrayAppend(data, Containers::InPlaceInit, MaterialAttribute::Shininess, shininess); + /* Convert back to a non-growable Array as importers don't allow custom + deleters in plugin implementations */ + arrayShrink(data, Containers::DefaultInit); return data; }(), importerState} { /* The data can't be filled here because it won't be sorted correctly */ diff --git a/src/Magnum/Trade/Test/AbstractImporterTest.cpp b/src/Magnum/Trade/Test/AbstractImporterTest.cpp index d9f0f10097..4a69917d1e 100644 --- a/src/Magnum/Trade/Test/AbstractImporterTest.cpp +++ b/src/Magnum/Trade/Test/AbstractImporterTest.cpp @@ -197,6 +197,9 @@ struct AbstractImporterTest: TestSuite::Tester { void materialNameOutOfRange(); void materialNotImplemented(); void materialOutOfRange(); + void materialNonOwningDeleters(); + void materialCustomAttributeDataDeleter(); + void materialCustomLayerDataDeleter(); void texture(); void textureNameNotImplemented(); @@ -402,6 +405,9 @@ AbstractImporterTest::AbstractImporterTest() { &AbstractImporterTest::materialNameOutOfRange, &AbstractImporterTest::materialNotImplemented, &AbstractImporterTest::materialOutOfRange, + &AbstractImporterTest::materialNonOwningDeleters, + &AbstractImporterTest::materialCustomAttributeDataDeleter, + &AbstractImporterTest::materialCustomLayerDataDeleter, &AbstractImporterTest::texture, &AbstractImporterTest::textureNameNotImplemented, @@ -3131,6 +3137,89 @@ void AbstractImporterTest::materialOutOfRange() { CORRADE_COMPARE(out.str(), "Trade::AbstractImporter::material(): index 8 out of range for 8 entries\n"); } +void AbstractImporterTest::materialNonOwningDeleters() { + struct: AbstractImporter { + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return true; } + void doClose() override {} + + UnsignedInt doMaterialCount() const override { return 1; } + Containers::Optional doMaterial(UnsignedInt) override { + return MaterialData{{}, {}, attributeData, {}, layerData}; + } + + UnsignedInt layerData[1]{}; + MaterialAttributeData attributeData[1]{ + {MaterialAttribute::DiffuseColor, Color4{}} + }; + } importer; + + auto data = importer.material(0); + CORRADE_VERIFY(data); + CORRADE_COMPARE(static_cast(data->attributeData()), importer.attributeData); + CORRADE_COMPARE(static_cast(data->layerData()), importer.layerData); +} + +void AbstractImporterTest::materialCustomAttributeDataDeleter() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + struct: AbstractImporter { + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return true; } + void doClose() override {} + + UnsignedInt doMaterialCount() const override { return 1; } + Int doMaterialForName(const std::string&) override { return 0; } + Containers::Optional doMaterial(UnsignedInt) override { + return MaterialData{{}, Containers::Array{attributeData, 1, [](MaterialAttributeData*, std::size_t) {}}}; + } + + MaterialAttributeData attributeData[1]{ + {MaterialAttribute::DiffuseColor, Color4{}} + }; + } importer; + + std::ostringstream out; + Error redirectError{&out}; + + importer.material(0); + importer.material(""); + CORRADE_COMPARE(out.str(), + "Trade::AbstractImporter::material(): implementation is not allowed to use a custom Array deleter\n" + "Trade::AbstractImporter::material(): implementation is not allowed to use a custom Array deleter\n"); +} + +void AbstractImporterTest::materialCustomLayerDataDeleter() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + struct: AbstractImporter { + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return true; } + void doClose() override {} + + UnsignedInt doMaterialCount() const override { return 1; } + Int doMaterialForName(const std::string&) override { return 0; } + Containers::Optional doMaterial(UnsignedInt) override { + return MaterialData{{}, nullptr, Containers::Array{layerData, 1, [](UnsignedInt*, std::size_t) {}}}; + } + + UnsignedInt layerData[1]{}; + } importer; + + std::ostringstream out; + Error redirectError{&out}; + + importer.material(0); + importer.material(""); + CORRADE_COMPARE(out.str(), + "Trade::AbstractImporter::material(): implementation is not allowed to use a custom Array deleter\n" + "Trade::AbstractImporter::material(): implementation is not allowed to use a custom Array deleter\n"); +} + void AbstractImporterTest::texture() { struct: AbstractImporter { ImporterFeatures doFeatures() const override { return {}; } From 33d1bad2e1be2c542f0f8d230a13083a367abfa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Thu, 30 Jul 2020 17:55:22 +0200 Subject: [PATCH 13/36] sceneconverter: print material info. --- src/Magnum/MeshTools/sceneconverter.cpp | 124 +++++++++++++++++++++++- 1 file changed, 119 insertions(+), 5 deletions(-) diff --git a/src/Magnum/MeshTools/sceneconverter.cpp b/src/Magnum/MeshTools/sceneconverter.cpp index 768349a560..303d05e4b0 100644 --- a/src/Magnum/MeshTools/sceneconverter.cpp +++ b/src/Magnum/MeshTools/sceneconverter.cpp @@ -39,6 +39,7 @@ #include "Magnum/Math/FunctionsBatch.h" #include "Magnum/MeshTools/RemoveDuplicates.h" #include "Magnum/Trade/AbstractImporter.h" +#include "Magnum/Trade/MaterialData.h" #include "Magnum/Trade/MeshData.h" #include "Magnum/Trade/MeshObjectData3D.h" #include "Magnum/Trade/AbstractSceneConverter.h" @@ -268,6 +269,13 @@ save its output; if no --converter is specified, AnySceneConverter is used.)") return 0; } + struct MaterialInfo { + UnsignedInt material; + UnsignedInt references; + Trade::MaterialData data{{}, {}}; + std::string name; + }; + struct MeshAttributeInfo { std::size_t offset; UnsignedInt stride, arraySize; @@ -290,17 +298,44 @@ save its output; if no --converter is specified, AnySceneConverter is used.)") /* Parse everything first to avoid errors interleaved with output */ - /* Scene properties. Currently just counting how much is each mesh - shared. */ + /* Scene properties. Currently just counting how much is each mesh / + material shared. */ + Containers::Array materialReferenceCount{importer->materialCount()}; Containers::Array meshReferenceCount{importer->meshCount()}; for(UnsignedInt i = 0; i != importer->object3DCount(); ++i) { Containers::Pointer object = importer->object3D(i); - if(object && object->instanceType() == Trade::ObjectInstanceType3D::Mesh && std::size_t(object->instance()) < meshReferenceCount.size()) - ++meshReferenceCount[object->instance()]; + if(object && object->instanceType() == Trade::ObjectInstanceType3D::Mesh) { + auto& meshObject = static_cast(*object); + if(std::size_t(meshObject.instance()) < meshReferenceCount.size()) + ++meshReferenceCount[meshObject.instance()]; + if(std::size_t(meshObject.material()) < materialReferenceCount.size()) + ++materialReferenceCount[meshObject.material()]; + } } - /* Mesh properties */ + /* Material properties */ bool error = false; + Containers::Array materialInfos; + for(UnsignedInt i = 0; i != importer->materialCount(); ++i) { + Containers::Optional material; + { + Duration d{importTime}; + if(!(material = importer->material(i))) { + error = true; + continue; + } + } + + MaterialInfo info{}; + info.material = i; + info.name = importer->materialName(i); + info.references = materialReferenceCount[i]; + info.data = *std::move(material); + + arrayAppend(materialInfos, std::move(info)); + } + + /* Mesh properties */ Containers::Array meshInfos; for(UnsignedInt i = 0; i != importer->meshCount(); ++i) { for(UnsignedInt j = 0; j != importer->meshLevelCount(i); ++j) { @@ -390,6 +425,85 @@ save its output; if no --converter is specified, AnySceneConverter is used.)") Containers::Array imageInfos = Trade::Implementation::imageInfo(*importer, error, compactImages); + for(const MaterialInfo& info: materialInfos) { + Debug d; + d << "Material" << info.material; + /* Print reference count only if there actually is a scene, + otherwise this information is useless */ + if(importer->object3DCount()) + d << Utility::formatString("(referenced by {} objects)", info.references); + d << Debug::nospace << ":"; + if(!info.name.empty()) d << info.name; + + d << Debug::newline << " Type:" << info.data.types(); + + for(UnsignedInt i = 0; i != info.data.layerCount(); ++i) { + /* Print extra layers with extra indent */ + const char* indent; + if(info.data.layerCount() != 1 && i != 0) { + d << Debug::newline << " Layer" << i << Debug::nospace << ":"; + if(!info.data.layerName(i).isEmpty()) + d << info.data.layerName(i); + indent = " "; + } else indent = " "; + + for(UnsignedInt j = 0; j != info.data.attributeCount(i); ++j) { + /* Ignore layer name (which is always first) unless it's in + the base material, in which case we print it as it + wouldn't otherwise be shown anywhere */ + if(i && !j && info.data.attributeName(i, j) == "$LayerName") + continue; + + d << Debug::newline << indent + << info.data.attributeName(i, j) << "@" + << info.data.attributeType(i, j) << Debug::nospace + << ":"; + switch(info.data.attributeType(i, j)) { + case Trade::MaterialAttributeType::Bool: + d << info.data.attribute(i, j); + break; + #define _c(type) case Trade::MaterialAttributeType::type: \ + d << info.data.attribute(i, j); \ + break; + _c(Float) + _c(Deg) + _c(Rad) + _c(UnsignedInt) + _c(Int) + _c(UnsignedLong) + _c(Long) + _c(Vector2) + _c(Vector2ui) + _c(Vector2i) + _c(Vector3) + _c(Vector3ui) + _c(Vector3i) + _c(Vector4) + _c(Vector4ui) + _c(Vector4i) + _c(Matrix2x2) + _c(Matrix2x3) + _c(Matrix2x4) + _c(Matrix3x2) + _c(Matrix3x3) + _c(Matrix3x4) + _c(Matrix4x2) + _c(Matrix4x3) + #undef _c + case Trade::MaterialAttributeType::Pointer: + d << info.data.attribute(i, j); + break; + case Trade::MaterialAttributeType::MutablePointer: + d << info.data.attribute(i, j); + break; + case Trade::MaterialAttributeType::String: + d << info.data.attribute(i, j); + break; + } + } + } + } + for(const MeshInfo& info: meshInfos) { Debug d; if(info.level == 0) { From d3b346eda55c8f7190ea721111c91ae57cad341c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 2 Aug 2020 23:28:07 +0200 Subject: [PATCH 14/36] Trade: add a MaterialTextureSwizzle enum. --- src/Magnum/MeshTools/sceneconverter.cpp | 3 ++ src/Magnum/Trade/MaterialData.cpp | 11 ++++ src/Magnum/Trade/MaterialData.h | 58 +++++++++++++++++++++- src/Magnum/Trade/Test/MaterialDataTest.cpp | 50 +++++++++++++++++++ src/Magnum/Trade/Trade.h | 1 + 5 files changed, 121 insertions(+), 2 deletions(-) diff --git a/src/Magnum/MeshTools/sceneconverter.cpp b/src/Magnum/MeshTools/sceneconverter.cpp index 303d05e4b0..4c94391a07 100644 --- a/src/Magnum/MeshTools/sceneconverter.cpp +++ b/src/Magnum/MeshTools/sceneconverter.cpp @@ -499,6 +499,9 @@ save its output; if no --converter is specified, AnySceneConverter is used.)") case Trade::MaterialAttributeType::String: d << info.data.attribute(i, j); break; + case Trade::MaterialAttributeType::TextureSwizzle: + d << info.data.attribute(i, j); + break; } } } diff --git a/src/Magnum/Trade/MaterialData.cpp b/src/Magnum/Trade/MaterialData.cpp index a5ad23ba4d..1e9f36c515 100644 --- a/src/Magnum/Trade/MaterialData.cpp +++ b/src/Magnum/Trade/MaterialData.cpp @@ -69,6 +69,7 @@ std::size_t materialAttributeTypeSize(const MaterialAttributeType type) { case MaterialAttributeType::Rad: case MaterialAttributeType::UnsignedInt: case MaterialAttributeType::Int: + case MaterialAttributeType::TextureSwizzle: return 4; case MaterialAttributeType::UnsignedLong: @@ -590,6 +591,15 @@ Debug& operator<<(Debug& debug, const MaterialAttribute value) { return debug << "::" << Debug::nospace << string; } +Debug& operator<<(Debug& debug, const MaterialTextureSwizzle value) { + /* The swizzle is encoded as a fourCC, so just print the numerical value as + a char. Worst case this will print nothing or four garbage letters. + Sorry in that case. GCC 4.8 doesn't understand just {}, wants to have + a full MaterialTextureSwizzle{} here. */ + MaterialTextureSwizzle values[]{value, MaterialTextureSwizzle{}}; + return debug << "Trade::MaterialTextureSwizzle::" << Debug::nospace << reinterpret_cast(values); +} + Debug& operator<<(Debug& debug, const MaterialAttributeType value) { debug << "Trade::MaterialAttributeType" << Debug::nospace; @@ -624,6 +634,7 @@ Debug& operator<<(Debug& debug, const MaterialAttributeType value) { _c(Pointer) _c(MutablePointer) _c(String) + _c(TextureSwizzle) #undef _c /* LCOV_EXCL_STOP */ } diff --git a/src/Magnum/Trade/MaterialData.h b/src/Magnum/Trade/MaterialData.h index 7b9d8e8cf0..894b7f2e45 100644 --- a/src/Magnum/Trade/MaterialData.h +++ b/src/Magnum/Trade/MaterialData.h @@ -26,7 +26,7 @@ */ /** @file - * @brief Class @ref Magnum::Trade::MaterialData, @ref Magnum::Trade::MaterialAttributeData, enum @ref Magnum::Trade::MaterialAttribute, @ref Magnum::Trade::MaterialAttributeType + * @brief Class @ref Magnum::Trade::MaterialData, @ref Magnum::Trade::MaterialAttributeData, enum @ref Magnum::Trade::MaterialAttribute, @ref Magnum::Trade::MaterialTextureSwizzle, @ref Magnum::Trade::MaterialAttributeType * @m_since_latest */ @@ -34,6 +34,7 @@ #include #include #include +#include #include "Magnum/Magnum.h" #include "Magnum/Math/RectangularMatrix.h" @@ -287,6 +288,50 @@ enum class MaterialAttribute: UnsignedInt { */ MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, MaterialAttribute value); +/** +@brief Material texture swizzle +@m_since_latest + +See @ref MaterialData for more information. +*/ +enum class MaterialTextureSwizzle: UnsignedInt { + /** Red component */ + R = Utility::Endianness::fourCC('R', '\0', '\0', '\0'), + + /** Green component */ + G = Utility::Endianness::fourCC('G', '\0', '\0', '\0'), + + /** Blue component */ + B = Utility::Endianness::fourCC('B', '\0', '\0', '\0'), + + /** Alpha component */ + A = Utility::Endianness::fourCC('A', '\0', '\0', '\0'), + + /** Red and green component */ + RG = Utility::Endianness::fourCC('R', 'G', '\0', '\0'), + + /** Green and blue component */ + GB = Utility::Endianness::fourCC('G', 'B', '\0', '\0'), + + /** Blue and alpha component */ + BA = Utility::Endianness::fourCC('B', 'A', '\0', '\0'), + + /** RGB components */ + RGB = Utility::Endianness::fourCC('R', 'G', 'B', '\0'), + + /** GBA components */ + GBA = Utility::Endianness::fourCC('G', 'B', 'A', '\0'), + + /** RGBA components */ + RGBA = Utility::Endianness::fourCC('R', 'G', 'B', 'A'), +}; + +/** +@debugoperatorenum{MaterialTextureSwizzle} +@m_since_latest +*/ +MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, MaterialTextureSwizzle value); + /** @brief Material attribute type @m_since_latest @@ -352,7 +397,10 @@ enum class MaterialAttributeType: UnsignedByte { * @ref Corrade::Containers::StringView, retrieval has to be done using * @ref Corrade::Containers::StringView. */ - String + String, + + /** One of the values from @ref MaterialTextureSwizzle */ + TextureSwizzle }; /** @@ -575,6 +623,7 @@ class MAGNUM_TRADE_EXPORT MaterialAttributeData { constexpr explicit ErasedScalar(Rad value): f{Float(value)} {} constexpr explicit ErasedScalar(UnsignedInt value): u{value} {} constexpr explicit ErasedScalar(Int value): i{value} {} + constexpr explicit ErasedScalar(MaterialTextureSwizzle value): u{UnsignedInt(value)} {} Float f; UnsignedInt u; @@ -1566,6 +1615,11 @@ namespace Implementation { }; /* No specialization for StringView as this type trait should not be used in that case */ + template<> struct MaterialAttributeTypeFor { + constexpr static MaterialAttributeType type() { + return MaterialAttributeType::TextureSwizzle; + } + }; #ifndef DOXYGEN_GENERATING_OUTPUT #define _c(type_) template<> struct MaterialAttributeTypeFor { \ constexpr static MaterialAttributeType type() { \ diff --git a/src/Magnum/Trade/Test/MaterialDataTest.cpp b/src/Magnum/Trade/Test/MaterialDataTest.cpp index 1ed0388140..fe45595d34 100644 --- a/src/Magnum/Trade/Test/MaterialDataTest.cpp +++ b/src/Magnum/Trade/Test/MaterialDataTest.cpp @@ -59,6 +59,7 @@ class MaterialDataTest: public TestSuite::Tester { void constructAttributeMutablePointer(); void constructAttributeStringNameStringValue(); void constructAttributeNameStringValue(); + void constructAttributeTextureSwizzle(); void constructAttributeInvalidName(); void constructAttributeWrongTypeForName(); @@ -93,6 +94,7 @@ class MaterialDataTest: public TestSuite::Tester { void access(); void accessPointer(); void accessString(); + void accessTextureSwizzle(); void accessOptional(); void accessOutOfBounds(); void accessNotFound(); @@ -133,6 +135,7 @@ class MaterialDataTest: public TestSuite::Tester { void phongAccessInvalidTextures(); void debugAttribute(); + void debugTextureSwizzle(); void debugAttributeType(); void debugType(); @@ -190,6 +193,7 @@ MaterialDataTest::MaterialDataTest() { &MaterialDataTest::constructAttributeMutablePointer, &MaterialDataTest::constructAttributeStringNameStringValue, &MaterialDataTest::constructAttributeNameStringValue, + &MaterialDataTest::constructAttributeTextureSwizzle, &MaterialDataTest::constructAttributeInvalidName, &MaterialDataTest::constructAttributeWrongTypeForName, @@ -227,6 +231,7 @@ MaterialDataTest::MaterialDataTest() { &MaterialDataTest::access, &MaterialDataTest::accessPointer, &MaterialDataTest::accessString, + &MaterialDataTest::accessTextureSwizzle, &MaterialDataTest::accessOptional, &MaterialDataTest::accessOutOfBounds, &MaterialDataTest::accessNotFound, @@ -267,6 +272,7 @@ MaterialDataTest::MaterialDataTest() { &MaterialDataTest::phongAccessInvalidTextures, &MaterialDataTest::debugAttribute, + &MaterialDataTest::debugTextureSwizzle, &MaterialDataTest::debugAttributeType, &MaterialDataTest::debugType, @@ -560,6 +566,27 @@ void MaterialDataTest::constructAttributeNameStringValue() { CORRADE_COMPARE(typeErased.value()[typeErased.value().size()], '\0'); } +void MaterialDataTest::constructAttributeTextureSwizzle() { + MaterialAttributeData attribute{"swizzle", MaterialTextureSwizzle::GBA}; + CORRADE_COMPARE(attribute.name(), "swizzle"); + CORRADE_COMPARE(attribute.type(), MaterialAttributeType::TextureSwizzle); + CORRADE_COMPARE(*static_cast(attribute.value()), MaterialTextureSwizzle::GBA); + CORRADE_COMPARE(attribute.value(), MaterialTextureSwizzle::GBA); + + constexpr MaterialAttributeData cattribute{"swizzle"_s, MaterialTextureSwizzle::GBA}; + CORRADE_COMPARE(cattribute.name(), "swizzle"); + CORRADE_COMPARE(cattribute.type(), MaterialAttributeType::TextureSwizzle); + CORRADE_COMPARE(*static_cast(cattribute.value()), MaterialTextureSwizzle::GBA); + CORRADE_COMPARE(cattribute.value(), MaterialTextureSwizzle::GBA); + + /* Type-erased variant */ + const MaterialTextureSwizzle swizzle = MaterialTextureSwizzle::GBA; + MaterialAttributeData typeErased{"swizzle", MaterialAttributeType::TextureSwizzle, &swizzle}; + CORRADE_COMPARE(typeErased.name(), "swizzle"); + CORRADE_COMPARE(typeErased.type(), MaterialAttributeType::TextureSwizzle); + CORRADE_COMPARE(typeErased.value(), MaterialTextureSwizzle::GBA); +} + void MaterialDataTest::constructAttributeInvalidName() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); @@ -1323,6 +1350,19 @@ void MaterialDataTest::accessString() { CORRADE_COMPARE(data.attribute(0)[data.attribute(0).size()], '\0'); } +void MaterialDataTest::accessTextureSwizzle() { + MaterialData data{{}, { + {"normalSwizzle", MaterialTextureSwizzle::BA} + }}; + CORRADE_COMPARE(data.attributeType("normalSwizzle"), MaterialAttributeType::TextureSwizzle); + + /* Pointer access will stop at the first null byte, printing the string + value */ + CORRADE_COMPARE(static_cast(data.attribute(0)), "BA"_s); + CORRADE_COMPARE(*static_cast(data.attribute(0)), MaterialTextureSwizzle::BA); + CORRADE_COMPARE(data.attribute(0), MaterialTextureSwizzle::BA); +} + void MaterialDataTest::accessOptional() { MaterialData data{{}, { {MaterialAttribute::AlphaMask, 0.5f}, @@ -2221,6 +2261,16 @@ void MaterialDataTest::debugAttribute() { CORRADE_COMPARE(out.str(), "Trade::MaterialAttribute::DiffuseTextureCoordinates Trade::MaterialAttribute::LayerName Trade::MaterialAttribute(0xfefe) Trade::MaterialAttribute(0x0)\n"); } +void MaterialDataTest::debugTextureSwizzle() { + std::ostringstream out; + + /* The swizzle is encoded as a fourCC, so it just prints the numerical + value as a char. Worst case this will print nothing or four garbage + letters. Sorry in that case. */ + Debug{&out} << MaterialTextureSwizzle::BA << MaterialTextureSwizzle{}; + CORRADE_COMPARE(out.str(), "Trade::MaterialTextureSwizzle::BA Trade::MaterialTextureSwizzle::\n"); +} + void MaterialDataTest::debugAttributeType() { std::ostringstream out; diff --git a/src/Magnum/Trade/Trade.h b/src/Magnum/Trade/Trade.h index c3541a1838..ac59202b5d 100644 --- a/src/Magnum/Trade/Trade.h +++ b/src/Magnum/Trade/Trade.h @@ -49,6 +49,7 @@ typedef CORRADE_DEPRECATED("use InputFileCallbackPolicy instead") InputFileCallb #endif enum class MaterialAttribute: UnsignedInt; +enum class MaterialTextureSwizzle: UnsignedInt; enum class MaterialAttributeType: UnsignedByte; enum class MaterialType: UnsignedInt; enum class MaterialAlphaMode: UnsignedByte; From 69f5f8111029afd02e8391f28ddd578b6dcaedba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 4 Aug 2020 18:39:35 +0200 Subject: [PATCH 15/36] Trade: expose basic PBR material properties and helper classes. Well, "basic". Practically mirrors glTF PBR materials: - builtin metallic/roughness - the KHR_materials_pbrSpecularGlossiness extension - extra normal/occlusion/emission maps - exposes the implicit metallic/roughness and specular/glossiness packing, but also allows separate maps with arbitrary packings as well as two-channel normal maps (instead of three-channel) - provides convenience checks for the most common packing schemes including MSFT_packing_normalRoughnessMetallic and the three variants of MSFT_packing_occlusionRoughnessMetallic - teaches PhongMaterialData to recognize packed specular/glossiness maps as well Next up is exposing at least one layer extension, and then I'm done here. --- doc/changelog.dox | 9 +- src/Magnum/Trade/CMakeLists.txt | 4 + .../materialAttributeProperties.hpp | 31 + src/Magnum/Trade/MaterialData.cpp | 6 +- src/Magnum/Trade/MaterialData.h | 519 +++++++- .../PbrMetallicRoughnessMaterialData.cpp | 302 +++++ .../Trade/PbrMetallicRoughnessMaterialData.h | 566 +++++++++ .../PbrSpecularGlossinessMaterialData.cpp | 251 ++++ .../Trade/PbrSpecularGlossinessMaterialData.h | 460 +++++++ src/Magnum/Trade/PhongMaterialData.cpp | 29 +- src/Magnum/Trade/PhongMaterialData.h | 60 +- src/Magnum/Trade/Test/MaterialDataTest.cpp | 1083 ++++++++++++++++- src/Magnum/Trade/Trade.h | 2 + 13 files changed, 3271 insertions(+), 51 deletions(-) create mode 100644 src/Magnum/Trade/PbrMetallicRoughnessMaterialData.cpp create mode 100644 src/Magnum/Trade/PbrMetallicRoughnessMaterialData.h create mode 100644 src/Magnum/Trade/PbrSpecularGlossinessMaterialData.cpp create mode 100644 src/Magnum/Trade/PbrSpecularGlossinessMaterialData.h diff --git a/doc/changelog.dox b/doc/changelog.dox index fd0811f870..f84a8d5c9e 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -76,7 +76,14 @@ See also: - A new, redesigned @ref Trade::MaterialData class allowing to store custom material attributes as well as more material types together in a single - instance. See [mosra/magnum#459](https://github.com/mosra/magnum/pull/459). + instance; plus new @ref Trade::PbrMetallicRoughnessMaterialData and + @ref Trade::PbrSpecularGlossinessMaterialData convenience accessor APIs + similar to @ref Trade::PhongMaterialData. See + [mosra/magnum#459](https://github.com/mosra/magnum/pull/459). +- Added @ref Trade::PhongMaterialData::hasSpecularTexture(), + @ref Trade::PhongMaterialData::specularTextureSwizzle() and + @ref Trade::PhongMaterialData::normalTextureSwizzle() to make new features + added for PBR materials recognizable also in classic Phong workflows. @subsection changelog-latest-changes Changes and improvements diff --git a/src/Magnum/Trade/CMakeLists.txt b/src/Magnum/Trade/CMakeLists.txt index e2019cb8fd..aa9a2a4a1b 100644 --- a/src/Magnum/Trade/CMakeLists.txt +++ b/src/Magnum/Trade/CMakeLists.txt @@ -45,6 +45,8 @@ set(MagnumTrade_GracefulAssert_SRCS MeshData.cpp ObjectData2D.cpp ObjectData3D.cpp + PbrMetallicRoughnessMaterialData.cpp + PbrSpecularGlossinessMaterialData.cpp PhongMaterialData.cpp) set(MagnumTrade_HEADERS @@ -63,6 +65,8 @@ set(MagnumTrade_HEADERS MeshObjectData3D.h ObjectData2D.h ObjectData3D.h + PbrMetallicRoughnessMaterialData.h + PbrSpecularGlossinessMaterialData.h PhongMaterialData.h SceneData.h TextureData.h diff --git a/src/Magnum/Trade/Implementation/materialAttributeProperties.hpp b/src/Magnum/Trade/Implementation/materialAttributeProperties.hpp index 9b78c0a506..c7c73e283f 100644 --- a/src/Magnum/Trade/Implementation/materialAttributeProperties.hpp +++ b/src/Magnum/Trade/Implementation/materialAttributeProperties.hpp @@ -39,12 +39,43 @@ _c(DiffuseTextureMatrix,Matrix3x3) _c(DiffuseTextureCoordinates,UnsignedInt) _c(SpecularColor,Vector4) _c(SpecularTexture,UnsignedInt) +_ct(SpecularTextureSwizzle,TextureSwizzle,MaterialTextureSwizzle) _c(SpecularTextureMatrix,Matrix3x3) _c(SpecularTextureCoordinates,UnsignedInt) _c(Shininess,Float) +_c(BaseColor,Vector4) +_c(BaseColorTexture,UnsignedInt) +_c(BaseColorTextureMatrix,Matrix3x3) +_c(BaseColorTextureCoordinates,UnsignedInt) +_c(Metalness,Float) +_c(MetalnessTexture,UnsignedInt) +_ct(MetalnessTextureSwizzle,TextureSwizzle,MaterialTextureSwizzle) +_c(MetalnessTextureMatrix,Matrix3x3) +_c(MetalnessTextureCoordinates,UnsignedInt) +_c(Roughness,Float) +_c(RoughnessTexture,UnsignedInt) +_ct(RoughnessTextureSwizzle,TextureSwizzle,MaterialTextureSwizzle) +_c(RoughnessTextureMatrix,Matrix3x3) +_c(RoughnessTextureCoordinates,UnsignedInt) +_c(MetallicRoughnessTexture,UnsignedInt) +_c(Glossiness,Float) +_c(GlossinessTexture,UnsignedInt) +_ct(GlossinessTextureSwizzle,TextureSwizzle,MaterialTextureSwizzle) +_c(GlossinessTextureMatrix,Matrix3x3) +_c(GlossinessTextureCoordinates,UnsignedInt) +_c(SpecularGlossinessTexture,UnsignedInt) _c(NormalTexture,UnsignedInt) +_ct(NormalTextureSwizzle,TextureSwizzle,MaterialTextureSwizzle) _c(NormalTextureMatrix,Matrix3x3) _c(NormalTextureCoordinates,UnsignedInt) +_c(OcclusionTexture,UnsignedInt) +_ct(OcclusionTextureSwizzle,TextureSwizzle,MaterialTextureSwizzle) +_c(OcclusionTextureMatrix,Matrix3x3) +_c(OcclusionTextureCoordinates,UnsignedInt) +_c(EmissiveColor,Vector3) +_c(EmissiveTexture,UnsignedInt) +_c(EmissiveTextureMatrix,Matrix3x3) +_c(EmissiveTextureCoordinates,UnsignedInt) _c(TextureMatrix,Matrix3x3) _c(TextureCoordinates,UnsignedInt) #endif diff --git a/src/Magnum/Trade/MaterialData.cpp b/src/Magnum/Trade/MaterialData.cpp index 1e9f36c515..5fc4ed97db 100644 --- a/src/Magnum/Trade/MaterialData.cpp +++ b/src/Magnum/Trade/MaterialData.cpp @@ -649,6 +649,8 @@ Debug& operator<<(Debug& debug, const MaterialType value) { /* LCOV_EXCL_START */ #define _c(value) case MaterialType::value: return debug << "::" #value; _c(Phong) + _c(PbrMetallicRoughness) + _c(PbrSpecularGlossiness) #undef _c /* LCOV_EXCL_STOP */ } @@ -658,7 +660,9 @@ Debug& operator<<(Debug& debug, const MaterialType value) { Debug& operator<<(Debug& debug, const MaterialTypes value) { return Containers::enumSetDebugOutput(debug, value, "Trade::MaterialTypes{}", { - MaterialType::Phong + MaterialType::Phong, + MaterialType::PbrMetallicRoughness, + MaterialType::PbrSpecularGlossiness }); } diff --git a/src/Magnum/Trade/MaterialData.h b/src/Magnum/Trade/MaterialData.h index 894b7f2e45..51b36b7655 100644 --- a/src/Magnum/Trade/MaterialData.h +++ b/src/Magnum/Trade/MaterialData.h @@ -144,80 +144,113 @@ enum class MaterialAttribute: UnsignedInt { AmbientTextureCoordinates, /** - * Diffuse color for Phong materials, @ref MaterialAttributeType::Vector4. + * Diffuse color for Phong or PBR specular/glossiness materials, + * @ref MaterialAttributeType::Vector4. * * If @ref MaterialAttribute::DiffuseTexture is present as well, these two * are multiplied together. - * @see @ref PhongMaterialData::diffuseColor() + * @see @ref PhongMaterialData::diffuseColor(), + * @ref PbrSpecularGlossinessMaterialData::diffuseColor() */ DiffuseColor, /** - * Diffuse texture index for Phong materials, + * Diffuse texture index for Phong or PBR specular/glossiness materials, * @ref MaterialAttributeType::UnsignedInt. * * If @ref MaterialAttribute::DiffuseColor is present as well, these two * are multiplied together. - * @see @ref PhongMaterialData::diffuseTexture() + * @see @ref PhongMaterialData::diffuseTexture(), + * @ref PbrSpecularGlossinessMaterialData::diffuseTexture() */ DiffuseTexture, /** - * Diffuse texture transformation matrix for Phong materials, - * @ref MaterialAttributeType::Matrix3x3. + * Diffuse texture transformation matrix for Phong or PBR + * specular/glossiness materials, @ref MaterialAttributeType::Matrix3x3. * * Has a precedence over @ref MaterialAttribute::TextureMatrix if both are * present. - * @see @ref PhongMaterialData::diffuseTextureMatrix() + * @see @ref PhongMaterialData::diffuseTextureMatrix(), + * @ref PbrSpecularGlossinessMaterialData::diffuseTextureMatrix() */ DiffuseTextureMatrix, /** - * Diffuse texture coordinate set index for Phong materials, - * @ref MaterialAttributeType::UnsignedInt. + * Diffuse texture coordinate set index for Phong or PBR + * specular/glossiness materials, @ref MaterialAttributeType::UnsignedInt. * * Has a precedence over @ref MaterialAttribute::TextureCoordinates if both * are present. - * @see @ref PhongMaterialData::diffuseTextureCoordinates() + * @see @ref PhongMaterialData::diffuseTextureCoordinates(), + * @ref PbrSpecularGlossinessMaterialData::diffuseTextureCoordinates() */ DiffuseTextureCoordinates, /** - * Specular color for Phong materials, @ref MaterialAttributeType::Vector4. + * Specular color for Phong or PBR specular/glossiness materials, + * @ref MaterialAttributeType::Vector4. Alpha is commonly zero to not + * interfere with alpha-masked objects, non-zero alpha can be for example + * used to render transparent material which are still expected to have + * specular highlights such as glass or soap bubbles. * - * If @ref MaterialAttribute::SpecularTexture is present as well, these two - * are multiplied together. - * @see @ref PhongMaterialData::specularColor() + * If @ref MaterialAttribute::SpecularTexture or + * @ref MaterialAttribute::SpecularGlossinessTexture is present as well, + * these two are multiplied together. + * @see @ref PhongMaterialData::specularColor(), + * @ref PbrSpecularGlossinessMaterialData::specularColor() */ SpecularColor, /** - * Specular texture index for Phong materials, + * Specular texture index for Phong or PBR specular/glossiness materials, * @ref MaterialAttributeType::UnsignedInt. * * If @ref MaterialAttribute::SpecularColor is present as well, these two - * are multiplied together. - * @see @ref PhongMaterialData::specularTexture() + * are multiplied together. Can be alternatively supplied as a packed + * @ref MaterialAttribute::SpecularGlossinessTexture. + * @see @ref PhongMaterialData::hasSpecularTexture(), + * @ref PhongMaterialData::specularTexture(), + * @ref PbrSpecularGlossinessMaterialData::hasSpecularTexture(), + * @ref PbrSpecularGlossinessMaterialData::hasSpecularGlossinessTexture(), + * @ref PbrSpecularGlossinessMaterialData::specularTexture() */ SpecularTexture, /** - * Specular texture transformation matrix for Phong materials, - * @ref MaterialAttributeType::Matrix3x3. + * Specular texture swizzle for Phong or PBR specular/glossiness materials, + * @ref MaterialAttributeType::TextureSwizzle. + * + * Can be used to describe whether the alpha channel of a + * @ref MaterialAttribute::SpecularTexture is used or not. Either + * @ref MaterialTextureSwizzle::RGBA or @ref MaterialTextureSwizzle::RGB + * (which is the default) is expected. Does not apply to + * @ref MaterialAttribute::SpecularGlossinessTexture --- in that case, + * the specular texture is always three-channel, regardless of this + * attribute. + * @see @ref PbrSpecularGlossinessMaterialData::specularTextureSwizzle() + */ + SpecularTextureSwizzle, + + /** + * Specular texture transformation matrix for Phong or PBR + * specular/glossiness materials, @ref MaterialAttributeType::Matrix3x3. * * Has a precedence over @ref MaterialAttribute::TextureMatrix if both are * present. - * @see @ref PhongMaterialData::specularTextureMatrix() + * @see @ref PhongMaterialData::specularTextureMatrix(), + * @ref PbrSpecularGlossinessMaterialData::glossinessTextureMatrix() */ SpecularTextureMatrix, /** - * Specular texture coordinate set index for Phong materials, - * @ref MaterialAttributeType::UnsignedInt. + * Specular texture coordinate set index for Phong or PBR + * specular/glossiness materials, @ref MaterialAttributeType::UnsignedInt. * * Has a precedence over @ref MaterialAttribute::TextureCoordinates if both * are present. - * @see @ref PhongMaterialData::specularTextureCoordinates() + * @see @ref PhongMaterialData::specularTextureCoordinates(), + * @ref PbrSpecularGlossinessMaterialData::specularTextureCoordinates() */ SpecularTextureCoordinates, @@ -228,20 +261,324 @@ enum class MaterialAttribute: UnsignedInt { */ Shininess, + /** + * Base color for PBR metallic/roughness materials, + * @ref MaterialAttributeType::Vector4. + * + * If @ref MaterialAttribute::BaseColorTexture is present as well, these + * two are multiplied together. + * @see @ref PbrMetallicRoughnessMaterialData::baseColor() + */ + BaseColor, + + /** + * Base color texture index for PBR metallic/roughness materials, + * @ref MaterialAttributeType::UnsignedInt. + * + * If @ref MaterialAttribute::BaseColor is present as well, these two are + * multiplied together. + * @see @ref PbrMetallicRoughnessMaterialData::baseColorTexture() + */ + BaseColorTexture, + + /** + * Base color texture transformation matrix for PBR metallic/roughness + * materials, @ref MaterialAttributeType::Matrix3x3. + * + * Has a precedence over @ref MaterialAttribute::TextureMatrix if both are + * present. + * @see @ref PbrMetallicRoughnessMaterialData::baseColorTextureMatrix() + */ + BaseColorTextureMatrix, + + /** + * Base color texture coordinate set index for PBR metallic/roughness + * materials, @ref MaterialAttributeType::UnsignedInt. + * + * Has a precedence over @ref MaterialAttribute::TextureCoordinates if both + * are present. + * @see @ref PbrMetallicRoughnessMaterialData::baseColorTextureCoordinates() + */ + BaseColorTextureCoordinates, + + /** + * Metalness for PBR metallic/roughness materials, + * @ref MaterialAttributeType::Float. + * + * If @ref MaterialAttribute::MetalnessTexture or + * @ref MaterialAttribute::MetallicRoughnessTexture is present as well, + * these two are multiplied together. + * @see @ref PbrMetallicRoughnessMaterialData::metalness() + */ + Metalness, + + /** + * Metalness texture index for PBR metallic/roughness materials, + * @ref MaterialAttributeType::UnsignedInt. + * + * If @ref MaterialAttribute::Metalness is present as well, these two are + * multiplied together. Can be alternatively supplied as a packed + * @ref MaterialAttribute::MetallicRoughnessTexture. + * @see @ref PbrMetallicRoughnessMaterialData::hasMetalnessTexture(), + * @ref PbrMetallicRoughnessMaterialData::hasMetallicRoughnessTexture(), + * @ref PbrMetallicRoughnessMaterialData::hasOcclusionRoughnessMetallicTexture(), + * @ref PbrMetallicRoughnessMaterialData::hasNormalRoughnessMetallicTexture() + * @ref PbrMetallicRoughnessMaterialData::metalnessTexture() + */ + MetalnessTexture, + + /** + * Metalness texture swizzle for PBR metallic/roughness materials, + * @ref MaterialAttributeType::TextureSwizzle. + * + * Can be used to express arbitrary packing of + * @ref MaterialAttribute::MetalnessTexture together with other maps in a + * single texture. A single-channel swizzle value is expected. If not + * present, @ref MaterialTextureSwizzle::R is assumed. Does not apply to + * @ref MaterialAttribute::MetallicRoughnessTexture --- in that case, the + * metalness is implicitly in the red channel regardless of this attribute. + * @see @ref PbrMetallicRoughnessMaterialData::hasMetallicRoughnessTexture(), + * @ref PbrMetallicRoughnessMaterialData::hasOcclusionRoughnessMetallicTexture(), + * @ref PbrMetallicRoughnessMaterialData::hasNormalRoughnessMetallicTexture() + * @ref PbrMetallicRoughnessMaterialData::metalnessTextureSwizzle() + */ + MetalnessTextureSwizzle, + + /** + * Metalness texture transformation matrix for PBR metallic/roughness + * materials, @ref MaterialAttributeType::Matrix3x3. + * + * Has a precedence over @ref MaterialAttribute::TextureMatrix if both are + * present. + * @see @ref PbrMetallicRoughnessMaterialData::metalnessTextureMatrix() + */ + MetalnessTextureMatrix, + + /** + * Metalness texture coordinate set index for PBR metallic/roughness + * materials, @ref MaterialAttributeType::UnsignedInt. + * + * Has a precedence over @ref MaterialAttribute::TextureCoordinates if both + * are present. + * @see @ref PbrMetallicRoughnessMaterialData::metalnessTextureCoordinates() + */ + MetalnessTextureCoordinates, + + /** + * Roughness for PBR metallic/roughness materials, + * @ref MaterialAttributeType::Float. + * + * If @ref MaterialAttribute::RoughnessTexture or + * @ref MaterialAttribute::MetallicRoughnessTexture is present as well, + * these two are multiplied together. + * @see @ref PbrMetallicRoughnessMaterialData::roughness() + */ + Roughness, + + /** + * Roughness texture index for PBR metallic/roughness materials, + * @ref MaterialAttributeType::UnsignedInt. + * + * If @ref MaterialAttribute::Roughness is present as well, these two are + * multiplied together. Can be alternatively supplied as a packed + * @ref MaterialAttribute::MetallicRoughnessTexture. + * @see @ref PbrMetallicRoughnessMaterialData::hasRoughnessTexture(), + * @ref PbrMetallicRoughnessMaterialData::hasMetallicRoughnessTexture(), + * @ref PbrMetallicRoughnessMaterialData::hasOcclusionRoughnessMetallicTexture(), + * @ref PbrMetallicRoughnessMaterialData::hasNormalRoughnessMetallicTexture() + * @ref PbrMetallicRoughnessMaterialData::roughnessTexture() + */ + RoughnessTexture, + + /** + * Roughness texture swizzle for PBR metallic/roughness materials, + * @ref MaterialAttributeType::TextureSwizzle. + * + * Can be used to express arbitrary packing of + * @ref MaterialAttribute::RoughnessTexture together with other maps in a + * single texture. A single-channel swizzle value is expected. If not + * present, @ref MaterialTextureSwizzle::R is assumed. Does not apply to + * @ref MaterialAttribute::MetallicRoughnessTexture --- in that case, the + * metalness is implicitly in the green channel regardless of this + * attribute. + * @see @ref PbrMetallicRoughnessMaterialData::hasMetallicRoughnessTexture(), + * @ref PbrMetallicRoughnessMaterialData::hasOcclusionRoughnessMetallicTexture(), + * @ref PbrMetallicRoughnessMaterialData::hasNormalRoughnessMetallicTexture() + * @ref PbrMetallicRoughnessMaterialData::roughnessTextureSwizzle() + */ + RoughnessTextureSwizzle, + + /** + * Roughness texture transformation matrix for PBR metallic/roughness + * materials, @ref MaterialAttributeType::Matrix3x3. + * + * Has a precedence over @ref MaterialAttribute::TextureMatrix if both are + * present. + * @see @ref PbrMetallicRoughnessMaterialData::roughnessTextureMatrix() + */ + RoughnessTextureMatrix, + + /** + * Roughness texture coordinate set index for PBR metallic/roughness + * materials, @ref MaterialAttributeType::UnsignedInt. + * + * Has a precedence over @ref MaterialAttribute::TextureCoordinates if both + * are present. + * @see @ref PbrMetallicRoughnessMaterialData::roughnessTextureCoordinates() + */ + RoughnessTextureCoordinates, + + /** + * Combined metallic/roughness texture index for PBR metallic/roughness + * materials with metalness in the red channel and roughness in the green + * channel, @ref MaterialAttributeType::UnsignedInt. + * + * If @ref MaterialAttribute::Metalness / @ref MaterialAttribute::Roughness + * is present as well, these two are multiplied together. Can be + * alternatively specified as a pair of @ref MaterialAttribute::MetalnessTexture + * / @ref MaterialAttribute::RoughnessTexture attributes together with + * @ref MaterialAttribute::MetalnessTextureSwizzle set to + * @ref MaterialTextureSwizzle::R (or omitted, since that's the default) + * and @ref MaterialAttribute::RoughnessTextureSwizzle set to + * @ref MaterialTextureSwizzle::G. Texture transformation and coordinate + * set, if needed, have to be specified either using the global + * @ref MaterialAttribute::TextureMatrix and + * @ref MaterialAttribute::TextureCoordinates attributes or the per-texture + * @ref MaterialAttribute::MetalnessTextureMatrix, + * @ref MaterialAttribute::RoughnessTextureMatrix, + * @ref MaterialAttribute::MetalnessTextureCoordinates and + * @ref MaterialAttribute::RoughnessTextureCoordinates variants. + * @see @ref PbrMetallicRoughnessMaterialData::hasMetallicRoughnessTexture(), + * @ref PbrMetallicRoughnessMaterialData::hasOcclusionRoughnessMetallicTexture(), + * @ref PbrMetallicRoughnessMaterialData::hasNormalRoughnessMetallicTexture() + * @ref PbrMetallicRoughnessMaterialData::metalnessTexture(), + * @ref PbrMetallicRoughnessMaterialData::roughnessTexture() + */ + MetallicRoughnessTexture, + + /* DiffuseColor, DiffuseTexture, DiffuseTextureMatrix, + DiffuseTextureCoordinates, SpecularColor, SpecularTexture, + SpecularTextureSwizzle, SpecularTextureMatrix, + SpecularTextureCoordinates specified above for Phong already */ + + /** + * Glossiness for PBR specular/glossiness materials, + * @ref MaterialAttributeType::Float. + * + * If @ref MaterialAttribute::GlossinessTexture or + * @ref MaterialAttribute::SpecularGlossinessTexture is present as well, + * these two are multiplied together. + * @see @ref PbrSpecularGlossinessMaterialData::glossiness() + */ + Glossiness, + + /** + * Glossiness texture index for PBR specular/glossiness materials, + * @ref MaterialAttributeType::UnsignedInt. + * + * If @ref MaterialAttribute::Glossiness is present as well, these two are + * multiplied together. Can be alternatively supplied as a packed + * @ref MaterialAttribute::SpecularGlossinessTexture. + * @see @ref PbrSpecularGlossinessMaterialData::hasGlossinessTexture(), + * @ref PbrSpecularGlossinessMaterialData::hasSpecularGlossinessTexture(), + * @ref PbrSpecularGlossinessMaterialData::glossinessTexture() + */ + GlossinessTexture, + + /** + * Glossiness texture swizzle for PBR specular/glossiness materials, + * @ref MaterialAttributeType::TextureSwizzle. + * + * Can be used to express arbitrary packing of + * @ref MaterialAttribute::GlossinessTexture together with other maps in a + * single texture. A single-channel swizzle value is expected. If not + * present, @ref MaterialTextureSwizzle::R is assumed. Does not apply to + * @ref MaterialAttribute::SpecularGlossinessTexture --- in that case, + * the glossiness is implicitly in the alpha channel regardless of this + * attribute. + * @see @ref PbrSpecularGlossinessMaterialData::hasSpecularGlossinessTexture(), + * @ref PbrSpecularGlossinessMaterialData::glossinessTextureSwizzle() + */ + GlossinessTextureSwizzle, + + /** + * Glossiness texture transformation matrix for PBR specular/glossiness + * materials, @ref MaterialAttributeType::Matrix3x3. + * + * Has a precedence over @ref MaterialAttribute::TextureMatrix if both are + * present. + * @see @ref PbrSpecularGlossinessMaterialData::glossinessTextureMatrix() + */ + GlossinessTextureMatrix, + + /** + * Glossiness texture coordinate set index for PBR specular/glossiness + * materials, @ref MaterialAttributeType::UnsignedInt. + * + * Has a precedence over @ref MaterialAttribute::TextureCoordinates if both + * are present. + * @see @ref PbrSpecularGlossinessMaterialData::glossinessTextureCoordinates() + */ + GlossinessTextureCoordinates, + + /** + * Combined specular/glossiness texture index for PBR specular/glossiness + * materials with specular color in the RGB channels and glossiness in + * alpha, @ref MaterialAttributeType::UnsignedInt. + * + * If @ref MaterialAttribute::SpecularColor / @ref MaterialAttribute::Glossiness + * is present as well, these two are multiplied together. Can be + * alternatively specified as a pair of @ref MaterialAttribute::SpecularTexture + * / @ref MaterialAttribute::GlossinessTexture attributes together with + * @ref MaterialAttribute::GlossinessTextureSwizzle set to + * @ref MaterialTextureSwizzle::A. Texture transformation and coordinate + * set, if needed, have to be specified either using the global + * @ref MaterialAttribute::TextureMatrix and + * @ref MaterialAttribute::TextureCoordinates attributes or the per-texture + * @ref MaterialAttribute::SpecularTextureMatrix, + * @ref MaterialAttribute::GlossinessTextureMatrix, + * @ref MaterialAttribute::SpecularTextureCoordinates and + * @ref MaterialAttribute::GlossinessTextureCoordinates variants. + * @see @ref PbrSpecularGlossinessMaterialData::hasSpecularGlossinessTexture(), + * @ref PbrSpecularGlossinessMaterialData::specularTexture(), + * @ref PbrSpecularGlossinessMaterialData::glossinessTexture() + */ + SpecularGlossinessTexture, + /** * Tangent-space normal map texture index, * @ref MaterialAttributeType::UnsignedInt. - * @see @ref PhongMaterialData::normalTexture() + * @see @ref PhongMaterialData::normalTexture(), + * @ref PbrMetallicRoughnessMaterialData::hasNormalRoughnessMetallicTexture(), + * @ref PbrMetallicRoughnessMaterialData::normalTexture(), + * @ref PbrSpecularGlossinessMaterialData::normalTexture() */ NormalTexture, + /** + * Normal texture swizzle, @ref MaterialAttributeType::TextureSwizzle. + * + * Can be used to express arbitrary packing together with other maps in a + * single texture. A two- or three-channel swizzle value is expected. If + * not present, @ref MaterialTextureSwizzle::RGB is assumed. + * + * If the texture is just two-component, the remaining component is + * implicit and calculated as @f$ z = \sqrt{1 - x^2 - y^2} @f$. + * @see @ref PbrMetallicRoughnessMaterialData::hasNormalRoughnessMetallicTexture(), + * @ref PbrMetallicRoughnessMaterialData::normalTextureSwizzle(), + * @ref PbrSpecularGlossinessMaterialData::normalTextureSwizzle() + */ + NormalTextureSwizzle, + /** * Normal texture transformation matrix, * @ref MaterialAttributeType::Matrix3x3. * * Has a precedence over @ref MaterialAttribute::TextureMatrix if both are * present. - * @see @ref PhongMaterialData::normalTextureMatrix() + * @see @ref PhongMaterialData::normalTextureMatrix(), + * @ref PbrMetallicRoughnessMaterialData::normalTextureMatrix(), + * @ref PbrSpecularGlossinessMaterialData::normalTextureMatrix() */ NormalTextureMatrix, @@ -251,10 +588,102 @@ enum class MaterialAttribute: UnsignedInt { * * Has a precedence over @ref MaterialAttribute::TextureCoordinates if both * are present. - * @see @ref PhongMaterialData::normalTextureCoordinates() + * @see @ref PhongMaterialData::normalTextureCoordinates(), + * @ref PbrMetallicRoughnessMaterialData::normalTextureCoordinates(), + * @ref PbrSpecularGlossinessMaterialData::normalTextureCoordinates() */ NormalTextureCoordinates, + /** + * Occlusion texture index, + * @ref MaterialAttributeType::UnsignedInt. + * @see @ref PbrMetallicRoughnessMaterialData::hasOcclusionRoughnessMetallicTexture(), + * @ref PbrMetallicRoughnessMaterialData::occlusionTexture(), + * @ref PbrSpecularGlossinessMaterialData::occlusionTexture() + */ + OcclusionTexture, + + /** + * Occlusion texture swizzle, @ref MaterialAttributeType::TextureSwizzle. + * + * Can be used to express arbitrary packing together with other maps in a + * single texture. A single-channel swizzle value is expected. If + * not present, @ref MaterialTextureSwizzle::R is assumed. + * @see @ref PbrMetallicRoughnessMaterialData::hasOcclusionRoughnessMetallicTexture(), + * @ref PbrMetallicRoughnessMaterialData::occlusionTextureSwizzle(), + * @ref PbrSpecularGlossinessMaterialData::occlusionTextureSwizzle() + */ + OcclusionTextureSwizzle, + + /** + * Occlusion texture transformation matrix, + * @ref MaterialAttributeType::Matrix3x3. + * + * Has a precedence over @ref MaterialAttribute::TextureMatrix if both are + * present. + * @see @ref PbrMetallicRoughnessMaterialData::occlusionTextureMatrix(), + * @ref PbrSpecularGlossinessMaterialData::occlusionTextureSwizzle() + */ + OcclusionTextureMatrix, + + /** + * Occlusion texture coordinate set index, + * @ref MaterialAttributeType::UnsignedInt. + * + * Has a precedence over @ref MaterialAttribute::TextureCoordinates if both + * are present. + * @see @ref PbrMetallicRoughnessMaterialData::occlusionTextureCoordinates(), + * @ref PbrSpecularGlossinessMaterialData::occlusionTextureCoordinates() + */ + OcclusionTextureCoordinates, + + /** + * Emissive color, + * @ref MaterialAttributeType::Vector3. + * + * If @ref MaterialAttribute::EmissiveTexture is present as well, these two + * are multiplied together. + * @see @ref PbrMetallicRoughnessMaterialData::emissiveColor(), + * @ref PbrSpecularGlossinessMaterialData::emissiveColor() + */ + EmissiveColor, + + /** + * Emissive texture index, + * @ref MaterialAttributeType::UnsignedInt. + * + * If @ref MaterialAttribute::EmissiveColor is present as well, these two + * are multiplied together. + * @see @ref PbrMetallicRoughnessMaterialData::emissiveTexture(), + * @ref PbrSpecularGlossinessMaterialData::emissiveTexture() + */ + EmissiveTexture, + + /** @todo EmissiveTextureSwizzle? It's a color and I'm not aware of any + existing packing schemes, so probably safe to assume it's always RGB */ + + /** + * Emissive texture transformation matrix, + * @ref MaterialAttributeType::Matrix3x3. + * + * Has a precedence over @ref MaterialAttribute::TextureMatrix if both are + * present. + * @see @ref PbrMetallicRoughnessMaterialData::emissiveTextureMatrix(), + * @ref PbrSpecularGlossinessMaterialData::emissiveTextureMatrix(), + */ + EmissiveTextureMatrix, + + /** + * Emissive texture coordinate set index, + * @ref MaterialAttributeType::UnsignedInt. + * + * Has a precedence over @ref MaterialAttribute::TextureCoordinates if both + * are present. + * @see @ref PbrMetallicRoughnessMaterialData::emissiveTextureCoordinates(), + * @ref PbrSpecularGlossinessMaterialData::emissiveTextureCoordinates() + */ + EmissiveTextureCoordinates, + /** * Common texture transformation matrix for all textures, * @ref MaterialAttributeType::Matrix3x3. @@ -262,9 +691,16 @@ enum class MaterialAttribute: UnsignedInt { * @ref MaterialAttribute::AmbientTextureMatrix / * @ref MaterialAttribute::DiffuseTextureMatrix / * @ref MaterialAttribute::SpecularTextureMatrix / - * @ref MaterialAttribute::NormalTextureMatrix have a precedence over this - * attribute for given texture, if present. - * @see @ref PhongMaterialData::textureMatrix() + * @ref MaterialAttribute::MetalnessTextureMatrix / + * @ref MaterialAttribute::RoughnessTextureMatrix / + * @ref MaterialAttribute::GlossinessTextureMatrix / + * @ref MaterialAttribute::NormalTextureMatrix / + * @ref MaterialAttribute::OcclusionTextureMatrix / + * @ref MaterialAttribute::EmissiveTextureMatrix have a precedence over + * this attribute for given texture, if present. + * @see @ref PhongMaterialData::textureMatrix(), + * @ref PbrMetallicRoughnessMaterialData::textureMatrix(), + * @ref PbrSpecularGlossinessMaterialData::textureMatrix() */ TextureMatrix, @@ -275,9 +711,16 @@ enum class MaterialAttribute: UnsignedInt { * @ref MaterialAttribute::AmbientTextureCoordinates / * @ref MaterialAttribute::DiffuseTextureCoordinates / * @ref MaterialAttribute::SpecularTextureCoordinates / - * @ref MaterialAttribute::NormalTextureCoordinates have a precedence + * @ref MaterialAttribute::MetalnessTextureCoordinates / + * @ref MaterialAttribute::RoughnessTextureCoordinates / + * @ref MaterialAttribute::GlossinessTextureCoordinates / + * @ref MaterialAttribute::NormalTextureCoordinates / + * @ref MaterialAttribute::OcclusionTextureCoordinates / + * @ref MaterialAttribute::EmissiveTextureCoordinates have a precedence * over this attribute for given texture, if present. - * @see @ref PhongMaterialData::textureCoordinates() + * @see @ref PhongMaterialData::textureCoordinates(), + * @ref PbrMetallicRoughnessMaterialData::textureCoordinates(), + * @ref PbrSpecularGlossinessMaterialData::textureCoordinates() */ TextureCoordinates, }; @@ -707,7 +1150,19 @@ enum class MaterialType: UnsignedInt { /** * Phong. Use @ref PhongMaterialData for convenience attribute access. */ - Phong = 1 << 0 + Phong = 1 << 0, + + /** + * PBR metallic/roughness. Use @ref PbrMetallicRoughnessMaterialData for + * convenience attribute access. + */ + PbrMetallicRoughness = 1 << 1, + + /** + * PBR specular/glossiness. Use @ref PbrSpecularGlossinessMaterialData for + * convenience attribute access. + */ + PbrSpecularGlossiness = 1 << 2 }; /** @debugoperatorenum{MaterialType} */ diff --git a/src/Magnum/Trade/PbrMetallicRoughnessMaterialData.cpp b/src/Magnum/Trade/PbrMetallicRoughnessMaterialData.cpp new file mode 100644 index 0000000000..951fd0f954 --- /dev/null +++ b/src/Magnum/Trade/PbrMetallicRoughnessMaterialData.cpp @@ -0,0 +1,302 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + 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 "PbrMetallicRoughnessMaterialData.h" + +#include "Magnum/Math/Color.h" +#include "Magnum/Math/Matrix3.h" + +namespace Magnum { namespace Trade { + +using namespace Math::Literals; + +bool PbrMetallicRoughnessMaterialData::hasMetalnessTexture() const { + return hasAttribute(MaterialAttribute::MetalnessTexture) || + hasAttribute(MaterialAttribute::MetallicRoughnessTexture); +} + +bool PbrMetallicRoughnessMaterialData::hasRoughnessTexture() const { + return hasAttribute(MaterialAttribute::RoughnessTexture) || + hasAttribute(MaterialAttribute::MetallicRoughnessTexture); +} + +bool PbrMetallicRoughnessMaterialData::hasMetallicRoughnessTexture() const { + return (hasAttribute(MaterialAttribute::MetallicRoughnessTexture) || + (hasAttribute(MaterialAttribute::MetalnessTexture) && + hasAttribute(MaterialAttribute::RoughnessTexture) && + metalnessTextureSwizzle() == MaterialTextureSwizzle::R && + roughnessTextureSwizzle() == MaterialTextureSwizzle::G)) && + metalnessTextureMatrix() == roughnessTextureMatrix() && + metalnessTextureCoordinates() == roughnessTextureCoordinates(); +} + +bool PbrMetallicRoughnessMaterialData::hasRoughnessMetallicOcclusionTexture() const { + if(!(hasAttribute(MaterialAttribute::RoughnessTexture) && + hasAttribute(MaterialAttribute::MetalnessTexture) && + hasAttribute(MaterialAttribute::OcclusionTexture) && + roughnessTextureSwizzle() == MaterialTextureSwizzle::R && + metalnessTextureSwizzle() == MaterialTextureSwizzle::G && + occlusionTextureSwizzle() == MaterialTextureSwizzle::B)) + return false; + + const Matrix3 roughnessTextureMatrix = this->roughnessTextureMatrix(); + const UnsignedInt roughnessTextureCoordinates = this->roughnessTextureCoordinates(); + return metalnessTextureMatrix() == roughnessTextureMatrix && + occlusionTextureMatrix() == roughnessTextureMatrix && + metalnessTextureCoordinates() == roughnessTextureCoordinates && + occlusionTextureCoordinates() == roughnessTextureCoordinates; +} + +bool PbrMetallicRoughnessMaterialData::hasOcclusionRoughnessMetallicTexture() const { + if(!(hasAttribute(MaterialAttribute::OcclusionTexture) && + hasAttribute(MaterialAttribute::RoughnessTexture) && + hasAttribute(MaterialAttribute::MetalnessTexture) && + occlusionTextureSwizzle() == MaterialTextureSwizzle::R && + roughnessTextureSwizzle() == MaterialTextureSwizzle::G && + metalnessTextureSwizzle() == MaterialTextureSwizzle::B)) + return false; + + const Matrix3 occlusionTextureMatrix = this->occlusionTextureMatrix(); + const UnsignedInt occlusionTextureCoordinates = this->occlusionTextureCoordinates(); + return roughnessTextureMatrix() == occlusionTextureMatrix && + metalnessTextureMatrix() == occlusionTextureMatrix && + roughnessTextureCoordinates() == occlusionTextureCoordinates && + metalnessTextureCoordinates() == occlusionTextureCoordinates; +} + +bool PbrMetallicRoughnessMaterialData::hasNormalRoughnessMetallicTexture() const { + if(!(hasAttribute(MaterialAttribute::NormalTexture) && + hasAttribute(MaterialAttribute::RoughnessTexture) && + hasAttribute(MaterialAttribute::MetalnessTexture) && + normalTextureSwizzle() == MaterialTextureSwizzle::RG && + roughnessTextureSwizzle() == MaterialTextureSwizzle::B && + metalnessTextureSwizzle() == MaterialTextureSwizzle::A)) + return false; + + const Matrix3 normalTextureMatrix = this->normalTextureMatrix(); + const UnsignedInt normalTextureCoordinates = this->normalTextureCoordinates(); + return roughnessTextureMatrix() == normalTextureMatrix && + metalnessTextureMatrix() == normalTextureMatrix && + roughnessTextureCoordinates() == normalTextureCoordinates && + metalnessTextureCoordinates() == normalTextureCoordinates; +} + +bool PbrMetallicRoughnessMaterialData::hasTextureTransformation() const { + return hasAttribute(MaterialAttribute::TextureMatrix) || + hasAttribute(MaterialAttribute::BaseColorTextureMatrix) || + hasAttribute(MaterialAttribute::MetalnessTextureMatrix) || + hasAttribute(MaterialAttribute::RoughnessTextureMatrix) || + hasAttribute(MaterialAttribute::NormalTextureMatrix) || + hasAttribute(MaterialAttribute::OcclusionTextureMatrix) || + hasAttribute(MaterialAttribute::EmissiveTextureMatrix);; +} + +bool PbrMetallicRoughnessMaterialData::hasTextureCoordinates() const { + return hasAttribute(MaterialAttribute::TextureCoordinates) || + hasAttribute(MaterialAttribute::BaseColorTextureCoordinates) || + hasAttribute(MaterialAttribute::MetalnessTextureCoordinates) || + hasAttribute(MaterialAttribute::RoughnessTextureCoordinates) || + hasAttribute(MaterialAttribute::NormalTextureCoordinates) || + hasAttribute(MaterialAttribute::OcclusionTextureCoordinates) || + hasAttribute(MaterialAttribute::EmissiveTextureCoordinates); +} + +Color4 PbrMetallicRoughnessMaterialData::baseColor() const { + return attributeOr(MaterialAttribute::BaseColor, 0xffffffff_rgbaf); +} + +UnsignedInt PbrMetallicRoughnessMaterialData::baseColorTexture() const { + return attribute(MaterialAttribute::BaseColorTexture); +} + +Matrix3 PbrMetallicRoughnessMaterialData::baseColorTextureMatrix() const { + CORRADE_ASSERT(hasAttribute(MaterialAttribute::BaseColorTexture), + "Trade::PbrMetallicRoughnessMaterialData::baseColorTextureMatrix(): the material doesn't have a base color texture", {}); + if(Containers::Optional value = tryAttribute(MaterialAttribute::BaseColorTextureMatrix)) + return *value; + return attributeOr(MaterialAttribute::TextureMatrix, Matrix3{}); +} + +UnsignedInt PbrMetallicRoughnessMaterialData::baseColorTextureCoordinates() const { + CORRADE_ASSERT(hasAttribute(MaterialAttribute::BaseColorTexture), + "Trade::PbrMetallicRoughnessMaterialData::baseColorTextureCoordinates(): the material doesn't have a base color texture", {}); + if(Containers::Optional value = tryAttribute(MaterialAttribute::BaseColorTextureCoordinates)) + return *value; + return attributeOr(MaterialAttribute::TextureCoordinates, 0u); +} + +Float PbrMetallicRoughnessMaterialData::metalness() const { + return attributeOr(MaterialAttribute::Metalness, 1.0f); +} + +UnsignedInt PbrMetallicRoughnessMaterialData::metalnessTexture() const { + if(Containers::Optional value = tryAttribute(MaterialAttribute::MetallicRoughnessTexture)) + return *value; + return attribute(MaterialAttribute::MetalnessTexture); +} + +MaterialTextureSwizzle PbrMetallicRoughnessMaterialData::metalnessTextureSwizzle() const { + CORRADE_ASSERT(hasMetalnessTexture(), + "Trade::PbrMetallicRoughnessMaterialData::metalnessTextureSwizzle(): the material doesn't have a metalness texture", {}); + if(hasAttribute(MaterialAttribute::MetallicRoughnessTexture)) + return MaterialTextureSwizzle::R; + return attributeOr(MaterialAttribute::MetalnessTextureSwizzle, MaterialTextureSwizzle::R); +} + +Matrix3 PbrMetallicRoughnessMaterialData::metalnessTextureMatrix() const { + CORRADE_ASSERT(hasMetalnessTexture(), + "Trade::PbrMetallicRoughnessMaterialData::metalnessTextureMatrix(): the material doesn't have a metalness texture", {}); + if(Containers::Optional value = tryAttribute(MaterialAttribute::MetalnessTextureMatrix)) + return *value; + return attributeOr(MaterialAttribute::TextureMatrix, Matrix3{}); +} + +UnsignedInt PbrMetallicRoughnessMaterialData::metalnessTextureCoordinates() const { + CORRADE_ASSERT(hasMetalnessTexture(), + "Trade::PbrMetallicRoughnessMaterialData::metalnessTextureCoordinates(): the material doesn't have a metalness texture", {}); + if(Containers::Optional value = tryAttribute(MaterialAttribute::MetalnessTextureCoordinates)) + return *value; + return attributeOr(MaterialAttribute::TextureCoordinates, 0u); +} + +Float PbrMetallicRoughnessMaterialData::roughness() const { + return attributeOr(MaterialAttribute::Roughness, 1.0f); +} + +UnsignedInt PbrMetallicRoughnessMaterialData::roughnessTexture() const { + if(Containers::Optional value = tryAttribute(MaterialAttribute::MetallicRoughnessTexture)) + return *value; + return attribute(MaterialAttribute::RoughnessTexture); +} + +MaterialTextureSwizzle PbrMetallicRoughnessMaterialData::roughnessTextureSwizzle() const { + CORRADE_ASSERT(hasRoughnessTexture(), + "Trade::PbrMetallicRoughnessMaterialData::roughnessTextureSwizzle(): the material doesn't have a roughness texture", {}); + if(hasAttribute(MaterialAttribute::MetallicRoughnessTexture)) + return MaterialTextureSwizzle::G; + return attributeOr(MaterialAttribute::RoughnessTextureSwizzle, MaterialTextureSwizzle::R); +} + +Matrix3 PbrMetallicRoughnessMaterialData::roughnessTextureMatrix() const { + CORRADE_ASSERT(hasRoughnessTexture(), + "Trade::PbrMetallicRoughnessMaterialData::roughnessTextureMatrix(): the material doesn't have a roughness texture", {}); + if(Containers::Optional value = tryAttribute(MaterialAttribute::RoughnessTextureMatrix)) + return *value; + return attributeOr(MaterialAttribute::TextureMatrix, Matrix3{}); +} + +UnsignedInt PbrMetallicRoughnessMaterialData::roughnessTextureCoordinates() const { + CORRADE_ASSERT(hasRoughnessTexture(), + "Trade::PbrMetallicRoughnessMaterialData::roughnessTextureCoordinates(): the material doesn't have a roughness texture", {}); + if(Containers::Optional value = tryAttribute(MaterialAttribute::RoughnessTextureCoordinates)) + return *value; + return attributeOr(MaterialAttribute::TextureCoordinates, 0u); +} + +UnsignedInt PbrMetallicRoughnessMaterialData::normalTexture() const { + return attribute(MaterialAttribute::NormalTexture); +} + +MaterialTextureSwizzle PbrMetallicRoughnessMaterialData::normalTextureSwizzle() const { + CORRADE_ASSERT(hasAttribute(MaterialAttribute::NormalTexture), + "Trade::PbrMetallicRoughnessMaterialData::normalTextureSwizzle(): the material doesn't have a normal texture", {}); + return attributeOr(MaterialAttribute::NormalTextureSwizzle, MaterialTextureSwizzle::RGB); +} + +Matrix3 PbrMetallicRoughnessMaterialData::normalTextureMatrix() const { + CORRADE_ASSERT(hasAttribute(MaterialAttribute::NormalTexture), + "Trade::PbrMetallicRoughnessMaterialData::normalTextureMatrix(): the material doesn't have a normal texture", {}); + if(Containers::Optional value = tryAttribute(MaterialAttribute::NormalTextureMatrix)) + return *value; + return attributeOr(MaterialAttribute::TextureMatrix, Matrix3{}); +} + +UnsignedInt PbrMetallicRoughnessMaterialData::normalTextureCoordinates() const { + CORRADE_ASSERT(hasAttribute(MaterialAttribute::NormalTexture), + "Trade::PbrMetallicRoughnessMaterialData::normalTextureCoordinates(): the material doesn't have a normal texture", {}); + if(Containers::Optional value = tryAttribute(MaterialAttribute::NormalTextureCoordinates)) + return *value; + return attributeOr(MaterialAttribute::TextureCoordinates, 0u); +} + +UnsignedInt PbrMetallicRoughnessMaterialData::occlusionTexture() const { + return attribute(MaterialAttribute::OcclusionTexture); +} + +MaterialTextureSwizzle PbrMetallicRoughnessMaterialData::occlusionTextureSwizzle() const { + CORRADE_ASSERT(hasAttribute(MaterialAttribute::OcclusionTexture), + "Trade::PbrMetallicRoughnessMaterialData::occlusionTextureSwizzle(): the material doesn't have an occlusion texture", {}); + return attributeOr(MaterialAttribute::OcclusionTextureSwizzle, MaterialTextureSwizzle::R); +} + +Matrix3 PbrMetallicRoughnessMaterialData::occlusionTextureMatrix() const { + CORRADE_ASSERT(hasAttribute(MaterialAttribute::OcclusionTexture), + "Trade::PbrMetallicRoughnessMaterialData::occlusionTextureMatrix(): the material doesn't have an occlusion texture", {}); + if(Containers::Optional value = tryAttribute(MaterialAttribute::OcclusionTextureMatrix)) + return *value; + return attributeOr(MaterialAttribute::TextureMatrix, Matrix3{}); +} + +UnsignedInt PbrMetallicRoughnessMaterialData::occlusionTextureCoordinates() const { + CORRADE_ASSERT(hasAttribute(MaterialAttribute::OcclusionTexture), + "Trade::PbrMetallicRoughnessMaterialData::occlusionTextureCoordinates(): the material doesn't have an occlusion texture", {}); + if(Containers::Optional value = tryAttribute(MaterialAttribute::OcclusionTextureCoordinates)) + return *value; + return attributeOr(MaterialAttribute::TextureCoordinates, 0u); +} + +Color3 PbrMetallicRoughnessMaterialData::emissiveColor() const { + return attributeOr(MaterialAttribute::EmissiveColor, 0x000000_srgbf); +} + +UnsignedInt PbrMetallicRoughnessMaterialData::emissiveTexture() const { + return attribute(MaterialAttribute::EmissiveTexture); +} + +Matrix3 PbrMetallicRoughnessMaterialData::emissiveTextureMatrix() const { + CORRADE_ASSERT(hasAttribute(MaterialAttribute::EmissiveTexture), + "Trade::PbrMetallicRoughnessMaterialData::emissiveTextureMatrix(): the material doesn't have an emissive texture", {}); + if(Containers::Optional value = tryAttribute(MaterialAttribute::EmissiveTextureMatrix)) + return *value; + return attributeOr(MaterialAttribute::TextureMatrix, Matrix3{}); +} + +UnsignedInt PbrMetallicRoughnessMaterialData::emissiveTextureCoordinates() const { + CORRADE_ASSERT(hasAttribute(MaterialAttribute::EmissiveTexture), + "Trade::PbrMetallicRoughnessMaterialData::emissiveTextureCoordinates(): the material doesn't have an emissive texture", {}); + if(Containers::Optional value = tryAttribute(MaterialAttribute::EmissiveTextureCoordinates)) + return *value; + return attributeOr(MaterialAttribute::TextureCoordinates, 0u); +} + +Matrix3 PbrMetallicRoughnessMaterialData::textureMatrix() const { + return attributeOr(MaterialAttribute::TextureMatrix, Matrix3{}); +} + +UnsignedInt PbrMetallicRoughnessMaterialData::textureCoordinates() const { + return attributeOr(MaterialAttribute::TextureCoordinates, 0u); +} + +}} diff --git a/src/Magnum/Trade/PbrMetallicRoughnessMaterialData.h b/src/Magnum/Trade/PbrMetallicRoughnessMaterialData.h new file mode 100644 index 0000000000..dcfb9f2deb --- /dev/null +++ b/src/Magnum/Trade/PbrMetallicRoughnessMaterialData.h @@ -0,0 +1,566 @@ +#ifndef Magnum_Trade_PbrMetallicRoughnessMaterialData_h +#define Magnum_Trade_PbrMetallicRoughnessMaterialData_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + 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::PbrMetallicRoughnessMaterialData + * @m_since_latest + */ + +#include "Magnum/Trade/MaterialData.h" + +namespace Magnum { namespace Trade { + +/** +@brief PBR metallic/roughness material data +@m_since_latest + +@see @ref AbstractImporter::material(), @ref PbrSpecularGlossinessMaterialData, + @ref PhongMaterialData +*/ +class MAGNUM_TRADE_EXPORT PbrMetallicRoughnessMaterialData: public MaterialData { + public: + #ifndef DOXYGEN_GENERATING_OUTPUT + /* Allow constructing subclasses directly. While not used in the + general Importer workflow, it allows users to create instances with + desired convenience APIs easier (and simplifies testing). It's + however hidden from the docs as constructing instances this way + isn't really common and it would add a lot of noise. */ + using MaterialData::MaterialData; + #endif + + /** + * @brief Whether the material has a metalness texture + * + * Returns @cpp true @ce if any of the + * @ref MaterialAttribute::MetalnessTexture or + * @ref MaterialAttribute::MetallicRoughnessTexture attributes is + * present, @cpp false @ce otherwise. + * @see @ref hasRoughnessTexture(), @ref hasMetallicRoughnessTexture() + */ + bool hasMetalnessTexture() const; + + /** + * @brief Whether the material has a roughness texture + * + * Returns @cpp true @ce if any of the + * @ref MaterialAttribute::RoughnessTexture or + * @ref MaterialAttribute::MetallicRoughnessTexture attributes is + * present, @cpp false @ce otherwise. + * @see @ref hasMetalnessTexture(), @ref hasMetallicRoughnessTexture() + */ + bool hasRoughnessTexture() const; + + /** + * @brief Whether the material has a combined metallic/roughness texture + * + * Returns @cpp true @ce if either the + * @ref MaterialAttribute::MetallicRoughnessTexture attribute is + * present or both @ref MaterialAttribute::MetalnessTexture and + * @ref MaterialAttribute::RoughnessTexture are present, point to + * the same texture ID, @ref MaterialAttribute::MetalnessTextureSwizzle + * is set to @ref MaterialTextureSwizzle::R (or omitted, in which case + * it's the default) and @ref MaterialAttribute::RoughnessTextureSwizzle + * is set to @ref MaterialTextureSwizzle::G, and ddditionally + * @ref MaterialAttribute::MetalnessTextureMatrix and + * @ref MaterialAttribute::RoughnessTextureMatrix are both either not + * present or have the same value, and + * @ref MaterialAttribute::MetalnessTextureCoordinates and + * @ref MaterialAttribute::RoughnessTextureCoordinates are both either + * not present or have the same value; @cpp false @ce otherwise. + * + * In other words, if this function returns @cpp true @ce, + * @ref metalnessTexture(), @ref metalnessTextureMatrix() and + * @ref metalnessTextureCoordinates() return values common for both + * metalness and roughness texture, and the two are packed together + * with metalness occupying the R channel and roughness the G channel. + * @see @ref hasMetalnessTexture(), @ref hasRoughnessTexture(), + * @ref hasOcclusionRoughnessMetallicTexture(), + * @ref hasRoughnessMetallicOcclusionTexture(), + * @ref hasNormalRoughnessMetallicTexture() + */ + bool hasMetallicRoughnessTexture() const; + + /** + * @brief Whether the material has a combined occlusion/roughness/metallic texture + * + * Returns @cpp true @ce if @ref MaterialAttribute::OcclusionTexture, + * @ref MaterialAttribute::RoughnessTexture and + * @ref MaterialAttribute::MetalnessTexture are all present, point to + * the same texture ID, @ref MaterialAttribute::OcclusionTextureSwizzle + * is set to @ref MaterialTextureSwizzle::R (or omitted, in which case + * it's the default), @ref MaterialAttribute::RoughnessTextureSwizzle + * is set to @ref MaterialTextureSwizzle::G and + * @ref MaterialAttribute::MetalnessTextureSwizzle is set to + * @ref MaterialTextureSwizzle::B, and additionally + * @ref MaterialAttribute::OcclusionTextureMatrix, + * @ref MaterialAttribute::RoughnessTextureMatrix and + * @ref MaterialAttribute::MetalnessTextureMatrix are all other not + * present or have the same value, and + * @ref MaterialAttribute::OcclusionTextureCoordinates, + * @ref MaterialAttribute::RoughnessTextureCoordinates and + * @ref MaterialAttribute::MetalnessTextureCoordinates are all either + * not present or have the same value; @cpp false @ce otherwise. + * + * In other words, if this function returns @cpp true @ce, + * @ref occlusionTexture(), @ref occlusionTextureMatrix() and + * @ref occlusionTextureCoordinates() return values common for + * occlusion, roughness and metalness textures, and the three are + * packed together with occlusion occupying the R channel, roughness + * the G channel and metalness the B channel. + * @see @ref hasMetalnessTexture(), @ref hasRoughnessTexture(), + * @ref hasMetallicRoughnessTexture(), + * @ref hasRoughnessMetallicOcclusionTexture(), + * @ref hasNormalRoughnessMetallicTexture() + */ + bool hasOcclusionRoughnessMetallicTexture() const; + + /** + * @brief Whether the material has a combined roughness/metallic/occlusion texture + * + * Returns @cpp true @ce if @ref MaterialAttribute::RoughnessTexture, + * @ref MaterialAttribute::MetalnessTexture and + * @ref MaterialAttribute::OcclusionTexture are all present, point to + * the same texture ID, @ref MaterialAttribute::RoughnessTextureSwizzle + * is set to @ref MaterialTextureSwizzle::R (or omitted, in which case + * it's the default), @ref MaterialAttribute::MetalnessTextureSwizzle + * is set to @ref MaterialTextureSwizzle::G and + * @ref MaterialAttribute::OcclusionTextureSwizzle is set to + * @ref MaterialTextureSwizzle::B, and additionally + * @ref MaterialAttribute::RoughnessTextureMatrix, + * @ref MaterialAttribute::MetalnessTextureMatrix and + * @ref MaterialAttribute::OcclusionTextureMatrix are all other not + * present or have the same value, and + * @ref MaterialAttribute::RoughnessTextureCoordinates, + * @ref MaterialAttribute::MetalnessTextureCoordinates and + * @ref MaterialAttribute::OcclusionTextureCoordinates are all either + * not present or have the same value; @cpp false @ce otherwise. + * + * In other words, if this function returns @cpp true @ce, + * @ref roughnessTexture(), @ref roughnessTextureMatrix() and + * @ref roughnessTextureCoordinates() return values common for + * roughness, metalness and occlusion textures, and the three are + * packed together with roughness occupying the R channel, metalness + * the G channel and occlusion the B channel. + * @see @ref hasMetalnessTexture(), @ref hasRoughnessTexture(), + * @ref hasMetallicRoughnessTexture(), + * @ref hasOcclusionRoughnessMetallicTexture(), + * @ref hasNormalRoughnessMetallicTexture() + */ + bool hasRoughnessMetallicOcclusionTexture() const; + + /** + * @brief Whether the material has a combined normal/roughness/metallic texture + * + * Returns @cpp true @ce if @ref MaterialAttribute::NormalTexture, + * @ref MaterialAttribute::RoughnessTexture and + * @ref MaterialAttribute::MetalnessTexture are all present, point to + * the same texture ID, @ref MaterialAttribute::NormalTextureSwizzle + * is set to @ref MaterialTextureSwizzle::RG (with the third channel + * implicit), @ref MaterialAttribute::RoughnessTextureSwizzle + * is set to @ref MaterialTextureSwizzle::B and + * @ref MaterialAttribute::MetalnessTextureSwizzle is set to + * @ref MaterialTextureSwizzle::A, and additionally + * @ref MaterialAttribute::NormalTextureMatrix, + * @ref MaterialAttribute::RoughnessTextureMatrix and + * @ref MaterialAttribute::MetalnessTextureMatrix are all other not + * present or have the same value, and + * @ref MaterialAttribute::NormalTextureCoordinates, + * @ref MaterialAttribute::RoughnessTextureCoordinates and + * @ref MaterialAttribute::MetalnessTextureCoordinates are all either + * not present or have the same value; @cpp false @ce otherwise. + * + * In other words, if this function returns @cpp true @ce, + * @ref normalTexture(), @ref normalTextureMatrix() and + * @ref normalTextureCoordinates() return values common for normal, + * roughness and metalness textures, and the three are packed together + * with normals occupying the RG channel, roughness the B channel and + * metalness the A channel. + * @see @ref hasMetalnessTexture(), @ref hasRoughnessTexture(), + * @ref hasMetallicRoughnessTexture(), + * @ref hasRoughnessMetallicOcclusionTexture(), + * @ref hasOcclusionRoughnessMetallicTexture() + */ + bool hasNormalRoughnessMetallicTexture() const; + + /** + * @brief Whether the material has texture transformation + * + * Returns @cpp true @ce if any of the + * @ref MaterialAttribute::BaseColorTextureMatrix, + * @ref MaterialAttribute::MetalnessTextureMatrix, + * @ref MaterialAttribute::RoughnessTextureMatrix, + * @ref MaterialAttribute::NormalTextureMatrix, + * @ref MaterialAttribute::OcclusionTextureMatrix, + * @ref MaterialAttribute::EmissiveTextureMatrix or + * @ref MaterialAttribute::TextureMatrix attributes is present, + * @cpp false @ce otherwise. + */ + bool hasTextureTransformation() const; + + /** + * @brief Whether the material uses extra texture coordinate sets + * + * Returns @cpp true @ce if any of the + * @ref MaterialAttribute::BaseColorTextureCoordinates, + * @ref MaterialAttribute::MetalnessTextureCoordinates, + * @ref MaterialAttribute::RoughnessTextureCoordinates, + * @ref MaterialAttribute::NormalTextureCoordinates, + * @ref MaterialAttribute::OcclusionTextureCoordinates, + * @ref MaterialAttribute::EmissiveTextureCoordinates or + * @ref MaterialAttribute::TextureCoordinates attributes is present and + * has a non-zero value, @cpp false @ce otherwise. + */ + bool hasTextureCoordinates() const; + + /** + * @brief Base color + * + * Convenience access to the @ref MaterialAttribute::BaseColor + * attribute. If not present, the default is @cpp 0xffffffff_srgbaf @ce. + * + * If the material has @ref MaterialAttribute::BaseColorTexture, the + * color and texture is meant to be multiplied together. + */ + Color4 baseColor() const; + + /** + * @brief Base color texture ID + * + * Available only if @ref MaterialAttribute::BaseColorTexture is + * present. Meant to be multiplied with @ref baseColor(). + * @see @ref hasAttribute(), @ref AbstractImporter::texture() + */ + UnsignedInt baseColorTexture() const; + + /** + * @brief Base color texture coordinate transformation matrix + * + * Convenience access to the @ref MaterialAttribute::BaseColorTextureMatrix + * / @ref MaterialAttribute::TextureMatrix attributes. If neither is + * present, the default is an identity matrix. Available only if the + * material has @ref MaterialAttribute::BaseColorTexture. + * @see @ref hasAttribute() + */ + Matrix3 baseColorTextureMatrix() const; + + /** + * @brief Base color texture coordinate set + * + * Convenience access to the @ref MaterialAttribute::BaseColorTextureCoordinates + * / @ref MaterialAttribute::TextureCoordinates attributes. If neither + * is present, the default is @cpp 0 @ce. Available only if the + * material has @ref MaterialAttribute::BaseColorTexture. + * @see @ref hasAttribute() + */ + UnsignedInt baseColorTextureCoordinates() const; + + /** + * @brief Metalness factor + * + * Convenience access to the @ref MaterialAttribute::Metalness + * attribute. If not present, the default is @cpp 1.0f @ce. + * + * If the material has a metalness texture, the factor and texture is + * meant to be multiplied together. + * @see @ref hasMetalnessTexture() + */ + Float metalness() const; + + /** + * @brief Metalness texture ID + * + * Available only if either @ref MaterialAttribute::MetalnessTexture or + * @ref MaterialAttribute::MetallicRoughnessTexture is present. Meant + * to be multiplied with @ref metalness(). + * @see @ref hasMetalnessTexture(), @ref AbstractImporter::texture() + */ + UnsignedInt metalnessTexture() const; + + /** + * @brief Metalness texture swizzle + * + * If @ref MaterialAttribute::MetallicRoughnessTexture is present, + * returns always @ref MaterialTextureSwizzle::R. Otherwise returns the + * @ref MaterialAttribute::MetalnessTextureSwizzle attribute, or + * @ref MaterialTextureSwizzle::R, if it's not present. Available only + * if the material has a metalness texture. + * @see @ref hasMetalnessTexture() + */ + MaterialTextureSwizzle metalnessTextureSwizzle() const; + + /** + * @brief Metalness texture coordinate transformation matrix + * + * Convenience access to the @ref MaterialAttribute::MetalnessTextureMatrix + * / @ref MaterialAttribute::TextureMatrix attributes. If neither is + * present, the default is an identity matrix. Available only if the + * material has a metalness texture. + * @see @ref hasMetalnessTexture() + */ + Matrix3 metalnessTextureMatrix() const; + + /** + * @brief Metalness texture coordinate set + * + * Convenience access to the @ref MaterialAttribute::MetalnessTextureCoordinates + * / @ref MaterialAttribute::TextureCoordinates attributes. If neither + * is present, the default is @cpp 0 @ce. Available only if the + * material has a metalness texture. + * @see @ref hasMetalnessTexture() + */ + UnsignedInt metalnessTextureCoordinates() const; + + /** + * @brief Roughness factor + * + * Convenience access to the @ref MaterialAttribute::Roughness + * attribute. If not present, the default is @cpp 1.0f @ce. + * + * If the material has a roughness texture, the factor and texture is + * meant to be multiplied together. + * @see @ref hasRoughnessTexture() + */ + Float roughness() const; + + /** + * @brief Roughness texture ID + * + * Available only if either @ref MaterialAttribute::RoughnessTexture or + * @ref MaterialAttribute::MetallicRoughnessTexture is present. Meant + * to be multiplied with @ref roughness(). + * @see @ref hasRoughnessTexture(), @ref AbstractImporter::texture() + */ + UnsignedInt roughnessTexture() const; + + /** + * @brief Roughness texture swizzle + * + * If @ref MaterialAttribute::MetallicRoughnessTexture is present, + * returns always @ref MaterialTextureSwizzle::G. Otherwise returns the + * @ref MaterialAttribute::RoughnessTextureSwizzle attribute, or + * @ref MaterialTextureSwizzle::R, if it's not present. Available only + * if the material has a roughness texture. + * @see @ref hasRoughnessTexture() + */ + MaterialTextureSwizzle roughnessTextureSwizzle() const; + + /** + * @brief Roughness texture coordinate transformation matrix + * + * Convenience access to the @ref MaterialAttribute::RoughnessTextureMatrix + * / @ref MaterialAttribute::TextureMatrix attributes. If neither is + * present, the default is an identity matrix. Available only if the + * material has a roughness texture. + * @see @ref hasRoughnessTexture() + */ + Matrix3 roughnessTextureMatrix() const; + + /** + * @brief Roughness texture coordinate set + * + * Convenience access to the @ref MaterialAttribute::RoughnessTextureCoordinates + * / @ref MaterialAttribute::TextureCoordinates attributes. If neither + * is present, the default is @cpp 0 @ce. Available only if the + * material has a roughness texture. + * @see @ref hasRoughnessTexture() + */ + UnsignedInt roughnessTextureCoordinates() const; + + /** + * @brief Normal texture ID + * + * Available only if @ref MaterialAttribute::NormalTexture is present. + * @see @ref hasAttribute(), @ref AbstractImporter::texture() + */ + UnsignedInt normalTexture() const; + + /** + * @brief Normal texture coordinate transformation matrix + * + * Convenience access to the @ref MaterialAttribute::NormalTextureMatrix + * / @ref MaterialAttribute::TextureMatrix attributes. If neither is + * present, the default is an identity matrix. Available only if the + * material has @ref MaterialAttribute::NormalTexture. + * @see @ref hasAttribute(), @ref AbstractImporter::texture() + */ + Matrix3 normalTextureMatrix() const; + + /** + * @brief Normal texture swizzle + * @m_since_latest + * + * Convenience access to the + * @ref MaterialAttribute::NormalTextureSwizzle attribute. If not + * present, the default is @ref MaterialTextureSwizzle::RGB. Available + * only if @ref MaterialAttribute::NormalTexture is present. + * @see @ref hasAttribute() + */ + MaterialTextureSwizzle normalTextureSwizzle() const; + + /** + * @brief Normal texture coordinate set + * + * Convenience access to the @ref MaterialAttribute::NormalTextureCoordinates + * / @ref MaterialAttribute::TextureCoordinates attributes. If neither is + * present, the default is @cpp 0 @ce. Available only if the material + * has @ref MaterialAttribute::NormalTexture. + * @see @ref hasAttribute(), @ref AbstractImporter::texture() + */ + UnsignedInt normalTextureCoordinates() const; + + /** + * @brief Occlusion texture ID + * + * Available only if @ref MaterialAttribute::OcclusionTexture is present. + * @see @ref hasAttribute(), @ref AbstractImporter::texture() + */ + UnsignedInt occlusionTexture() const; + + /** + * @brief Occlusion texture swizzle + * + * Convenience access to the + * @ref MaterialAttribute::OcclusionTextureSwizzle attribute. If not + * present, the default is @ref MaterialTextureSwizzle::R. Available + * only if @ref MaterialAttribute::OcclusionTexture is present. + * @see @ref hasAttribute() + */ + MaterialTextureSwizzle occlusionTextureSwizzle() const; + + /** + * @brief Occlusion texture coordinate transformation matrix + * + * Convenience access to the @ref MaterialAttribute::OcclusionTextureMatrix + * / @ref MaterialAttribute::TextureMatrix attributes. If neither is + * present, the default is an identity matrix. Available only if the + * material has @ref MaterialAttribute::OcclusionTexture. + * @see @ref hasAttribute() + */ + Matrix3 occlusionTextureMatrix() const; + + /** + * @brief Occlusion texture coordinate set + * + * Convenience access to the @ref MaterialAttribute::OcclusionTextureCoordinates + * / @ref MaterialAttribute::TextureCoordinates attributes. If neither + * is present, the default is @cpp 0 @ce. Available only if the + * material has @ref MaterialAttribute::OcclusionTexture. + * @see @ref hasAttribute() + */ + UnsignedInt occlusionTextureCoordinates() const; + + /** + * @brief Emissive color + * + * Convenience access to the @ref MaterialAttribute::EmissiveColor + * attribute. If not present, the default is @cpp 0x000000_srgbf @ce + * (i.e, no emission). + * + * If the material has @ref MaterialAttribute::EmissiveTexture, the + * color and texture is meant to be multiplied together. + */ + Color3 emissiveColor() const; + + /** + * @brief Emissive texture ID + * + * Available only if @ref MaterialAttribute::EmissiveTexture is present. + * Meant to be multiplied with @ref emissiveColor(). + * @see @ref hasAttribute(), @ref AbstractImporter::texture() + */ + UnsignedInt emissiveTexture() const; + + /* No EmissiveTextureSwizzle attribute right now (implicitly RGB) */ + + /** + * @brief Emissive texture coordinate transformation matrix + * + * Convenience access to the @ref MaterialAttribute::EmissiveTextureMatrix + * / @ref MaterialAttribute::TextureMatrix attributes. If neither is + * present, the default is an identity matrix. Available only if the + * material has @ref MaterialAttribute::EmissiveTexture. + * @see @ref hasAttribute() + */ + Matrix3 emissiveTextureMatrix() const; + + /** + * @brief Emissive texture coordinate set + * + * Convenience access to the @ref MaterialAttribute::EmissiveTextureCoordinates + * / @ref MaterialAttribute::TextureCoordinates attributes. If neither + * is present, the default is @cpp 0 @ce. Available only if the + * material has @ref MaterialAttribute::EmissiveTexture. + * @see @ref hasAttribute() + */ + UnsignedInt emissiveTextureCoordinates() const; + + /** + * @brief Common texture coordinate transformation matrix for all textures + * + * Convenience access to the @ref MaterialAttribute::TextureMatrix + * attribute. If not present, the default is an identity matrix. Note + * that the material may also define per-texture transformation using + * the @ref MaterialAttribute::BaseColorTextureMatrix, + * @ref MaterialAttribute::MetalnessTextureMatrix, + * @ref MaterialAttribute::RoughnessTextureMatrix, + * @ref MaterialAttribute::NormalTextureMatrix, + * @ref MaterialAttribute::OcclusionTextureMatrix and + * @ref MaterialAttribute::EmissiveTextureMatrix attributes, which then + * take precedence over the common one. + * @see @ref hasAttribute(), @ref baseColorTextureMatrix(), + * @ref metalnessTextureMatrix(), @ref roughnessTextureMatrix(), + * @ref normalTextureMatrix(), @ref occlusionTextureMatrix(), + * @ref emissiveTextureMatrix() + */ + Matrix3 textureMatrix() const; + + /** + * @brief Common texture coordinate set index for all textures + * + * Convenience access to the @ref MaterialAttribute::TextureCoordinates + * attribute. If not present, the default is @cpp 0 @ce. Note that the + * material may also define per-texture coordinate set using the + * @ref MaterialAttribute::BaseColorTextureCoordinates, + * @ref MaterialAttribute::MetalnessTextureCoordinates, + * @ref MaterialAttribute::RoughnessTextureCoordinates, + * @ref MaterialAttribute::NormalTextureCoordinates, + * @ref MaterialAttribute::OcclusionTextureCoordinates and + * @ref MaterialAttribute::EmissiveTextureCoordinates attributes, which + * then take precedence over the common one. + * @see @ref hasAttribute(), @ref baseColorTextureCoordinates(), + * @ref metalnessTextureCoordinates(), + * @ref roughnessTextureCoordinates(), + * @ref normalTextureCoordinates(), + * @ref occlusionTextureCoordinates(), + * @ref emissiveTextureCoordinates() + */ + UnsignedInt textureCoordinates() const; +}; + +}} + +#endif diff --git a/src/Magnum/Trade/PbrSpecularGlossinessMaterialData.cpp b/src/Magnum/Trade/PbrSpecularGlossinessMaterialData.cpp new file mode 100644 index 0000000000..d38cebaeef --- /dev/null +++ b/src/Magnum/Trade/PbrSpecularGlossinessMaterialData.cpp @@ -0,0 +1,251 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + 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 "PbrSpecularGlossinessMaterialData.h" + +#include "Magnum/Math/Color.h" +#include "Magnum/Math/Matrix3.h" + +namespace Magnum { namespace Trade { + +using namespace Math::Literals; + +bool PbrSpecularGlossinessMaterialData::hasSpecularTexture() const { + return hasAttribute(MaterialAttribute::SpecularTexture) || + hasAttribute(MaterialAttribute::SpecularGlossinessTexture); +} + +bool PbrSpecularGlossinessMaterialData::hasGlossinessTexture() const { + return hasAttribute(MaterialAttribute::GlossinessTexture) || + hasAttribute(MaterialAttribute::SpecularGlossinessTexture); +} + +bool PbrSpecularGlossinessMaterialData::hasSpecularGlossinessTexture() const { + return (hasAttribute(MaterialAttribute::SpecularGlossinessTexture) || + (hasAttribute(MaterialAttribute::SpecularTexture) && + hasAttribute(MaterialAttribute::GlossinessTexture) && + specularTextureSwizzle() == MaterialTextureSwizzle::RGB && + glossinessTextureSwizzle() == MaterialTextureSwizzle::A)) && + specularTextureMatrix() == glossinessTextureMatrix() && + specularTextureCoordinates() == glossinessTextureCoordinates(); +} + +bool PbrSpecularGlossinessMaterialData::hasTextureTransformation() const { + return hasAttribute(MaterialAttribute::TextureMatrix) || + hasAttribute(MaterialAttribute::DiffuseTextureMatrix) || + hasAttribute(MaterialAttribute::SpecularTextureMatrix) || + hasAttribute(MaterialAttribute::GlossinessTextureMatrix) || + hasAttribute(MaterialAttribute::NormalTextureMatrix) || + hasAttribute(MaterialAttribute::OcclusionTextureMatrix) || + hasAttribute(MaterialAttribute::EmissiveTextureMatrix); +} + +bool PbrSpecularGlossinessMaterialData::hasTextureCoordinates() const { + return hasAttribute(MaterialAttribute::TextureCoordinates) || + hasAttribute(MaterialAttribute::DiffuseTextureCoordinates) || + hasAttribute(MaterialAttribute::SpecularTextureCoordinates) || + hasAttribute(MaterialAttribute::GlossinessTextureCoordinates) || + hasAttribute(MaterialAttribute::NormalTextureCoordinates) || + hasAttribute(MaterialAttribute::OcclusionTextureCoordinates) || + hasAttribute(MaterialAttribute::EmissiveTextureCoordinates); +} + +Color4 PbrSpecularGlossinessMaterialData::diffuseColor() const { + return attributeOr(MaterialAttribute::DiffuseColor, 0xffffffff_srgbaf); +} + +UnsignedInt PbrSpecularGlossinessMaterialData::diffuseTexture() const { + return attribute(MaterialAttribute::DiffuseTexture); +} + +Matrix3 PbrSpecularGlossinessMaterialData::diffuseTextureMatrix() const { + CORRADE_ASSERT(hasAttribute(MaterialAttribute::DiffuseTexture), + "Trade::PbrSpecularGlossinessMaterialData::diffuseTextureMatrix(): the material doesn't have a diffuse texture", {}); + if(Containers::Optional value = tryAttribute(MaterialAttribute::DiffuseTextureMatrix)) + return *value; + return attributeOr(MaterialAttribute::TextureMatrix, Matrix3{}); +} + +UnsignedInt PbrSpecularGlossinessMaterialData::diffuseTextureCoordinates() const { + CORRADE_ASSERT(hasAttribute(MaterialAttribute::DiffuseTexture), + "Trade::PbrSpecularGlossinessMaterialData::diffuseTextureCoordinates(): the material doesn't have a diffuse texture", {}); + if(Containers::Optional value = tryAttribute(MaterialAttribute::DiffuseTextureCoordinates)) + return *value; + return attributeOr(MaterialAttribute::TextureCoordinates, 0u); +} + +Color4 PbrSpecularGlossinessMaterialData::specularColor() const { + return attributeOr(MaterialAttribute::SpecularColor, 0xffffff00_srgbaf); +} + +UnsignedInt PbrSpecularGlossinessMaterialData::specularTexture() const { + if(Containers::Optional value = tryAttribute(MaterialAttribute::SpecularGlossinessTexture)) + return *value; + return attribute(MaterialAttribute::SpecularTexture); +} + +MaterialTextureSwizzle PbrSpecularGlossinessMaterialData::specularTextureSwizzle() const { + CORRADE_ASSERT(hasSpecularTexture(), + "Trade::PbrSpecularGlossinessMaterialData::specularTextureSwizzle(): the material doesn't have a specular texture", {}); + if(hasAttribute(MaterialAttribute::SpecularGlossinessTexture)) + return MaterialTextureSwizzle::RGB; + return attributeOr(MaterialAttribute::SpecularTextureSwizzle, MaterialTextureSwizzle::RGB); +} + +Matrix3 PbrSpecularGlossinessMaterialData::specularTextureMatrix() const { + CORRADE_ASSERT(hasSpecularTexture(), + "Trade::PbrSpecularGlossinessMaterialData::specularTextureMatrix(): the material doesn't have a specular texture", {}); + if(Containers::Optional value = tryAttribute(MaterialAttribute::SpecularTextureMatrix)) + return *value; + return attributeOr(MaterialAttribute::TextureMatrix, Matrix3{}); +} + +UnsignedInt PbrSpecularGlossinessMaterialData::specularTextureCoordinates() const { + CORRADE_ASSERT(hasSpecularTexture(), + "Trade::PbrSpecularGlossinessMaterialData::specularTextureCoordinates(): the material doesn't have a specular texture", {}); + if(Containers::Optional value = tryAttribute(MaterialAttribute::SpecularTextureCoordinates)) + return *value; + return attributeOr(MaterialAttribute::TextureCoordinates, 0u); +} + +Float PbrSpecularGlossinessMaterialData::glossiness() const { + return attributeOr(MaterialAttribute::Glossiness, 1.0f); +} + +UnsignedInt PbrSpecularGlossinessMaterialData::glossinessTexture() const { + if(Containers::Optional value = tryAttribute(MaterialAttribute::SpecularGlossinessTexture)) + return *value; + return attribute(MaterialAttribute::GlossinessTexture); +} + +MaterialTextureSwizzle PbrSpecularGlossinessMaterialData::glossinessTextureSwizzle() const { + CORRADE_ASSERT(hasGlossinessTexture(), + "Trade::PbrSpecularGlossinessMaterialData::glossinessTextureSwizzle(): the material doesn't have a glossiness texture", {}); + if(hasAttribute(MaterialAttribute::SpecularGlossinessTexture)) + return MaterialTextureSwizzle::A; + return attributeOr(MaterialAttribute::GlossinessTextureSwizzle, MaterialTextureSwizzle::R); +} + +Matrix3 PbrSpecularGlossinessMaterialData::glossinessTextureMatrix() const { + CORRADE_ASSERT(hasGlossinessTexture(), + "Trade::PbrSpecularGlossinessMaterialData::glossinessTextureMatrix(): the material doesn't have a glossiness texture", {}); + if(Containers::Optional value = tryAttribute(MaterialAttribute::GlossinessTextureMatrix)) + return *value; + return attributeOr(MaterialAttribute::TextureMatrix, Matrix3{}); +} + +UnsignedInt PbrSpecularGlossinessMaterialData::glossinessTextureCoordinates() const { + CORRADE_ASSERT(hasGlossinessTexture(), + "Trade::PbrSpecularGlossinessMaterialData::glossinessTextureCoordinates(): the material doesn't have a glossiness texture", {}); + if(Containers::Optional value = tryAttribute(MaterialAttribute::GlossinessTextureCoordinates)) + return *value; + return attributeOr(MaterialAttribute::TextureCoordinates, 0u); +} + +UnsignedInt PbrSpecularGlossinessMaterialData::normalTexture() const { + return attribute(MaterialAttribute::NormalTexture); +} + +MaterialTextureSwizzle PbrSpecularGlossinessMaterialData::normalTextureSwizzle() const { + CORRADE_ASSERT(hasAttribute(MaterialAttribute::NormalTexture), + "Trade::PbrSpecularGlossinessMaterialData::normalTextureSwizzle(): the material doesn't have a normal texture", {}); + return attributeOr(MaterialAttribute::NormalTextureSwizzle, MaterialTextureSwizzle::RGB); +} + +Matrix3 PbrSpecularGlossinessMaterialData::normalTextureMatrix() const { + CORRADE_ASSERT(hasAttribute(MaterialAttribute::NormalTexture), + "Trade::PbrSpecularGlossinessMaterialData::normalTextureMatrix(): the material doesn't have a normal texture", {}); + if(Containers::Optional value = tryAttribute(MaterialAttribute::NormalTextureMatrix)) + return *value; + return attributeOr(MaterialAttribute::TextureMatrix, Matrix3{}); +} + +UnsignedInt PbrSpecularGlossinessMaterialData::normalTextureCoordinates() const { + CORRADE_ASSERT(hasAttribute(MaterialAttribute::NormalTexture), + "Trade::PbrSpecularGlossinessMaterialData::normalTextureCoordinates(): the material doesn't have a normal texture", {}); + if(Containers::Optional value = tryAttribute(MaterialAttribute::NormalTextureCoordinates)) + return *value; + return attributeOr(MaterialAttribute::TextureCoordinates, 0u); +} + +UnsignedInt PbrSpecularGlossinessMaterialData::occlusionTexture() const { + return attribute(MaterialAttribute::OcclusionTexture); +} + +MaterialTextureSwizzle PbrSpecularGlossinessMaterialData::occlusionTextureSwizzle() const { + CORRADE_ASSERT(hasAttribute(MaterialAttribute::OcclusionTexture), + "Trade::PbrSpecularGlossinessMaterialData::occlusionTextureSwizzle(): the material doesn't have an occlusion texture", {}); + return attributeOr(MaterialAttribute::OcclusionTextureSwizzle, MaterialTextureSwizzle::R); +} + +Matrix3 PbrSpecularGlossinessMaterialData::occlusionTextureMatrix() const { + CORRADE_ASSERT(hasAttribute(MaterialAttribute::OcclusionTexture), + "Trade::PbrSpecularGlossinessMaterialData::occlusionTextureMatrix(): the material doesn't have an occlusion texture", {}); + if(Containers::Optional value = tryAttribute(MaterialAttribute::OcclusionTextureMatrix)) + return *value; + return attributeOr(MaterialAttribute::TextureMatrix, Matrix3{}); +} + +UnsignedInt PbrSpecularGlossinessMaterialData::occlusionTextureCoordinates() const { + CORRADE_ASSERT(hasAttribute(MaterialAttribute::OcclusionTexture), + "Trade::PbrSpecularGlossinessMaterialData::occlusionTextureCoordinates(): the material doesn't have an occlusion texture", {}); + if(Containers::Optional value = tryAttribute(MaterialAttribute::OcclusionTextureCoordinates)) + return *value; + return attributeOr(MaterialAttribute::TextureCoordinates, 0u); +} + +Color3 PbrSpecularGlossinessMaterialData::emissiveColor() const { + return attributeOr(MaterialAttribute::EmissiveColor, 0x000000_srgbf); +} + +UnsignedInt PbrSpecularGlossinessMaterialData::emissiveTexture() const { + return attribute(MaterialAttribute::EmissiveTexture); +} + +Matrix3 PbrSpecularGlossinessMaterialData::emissiveTextureMatrix() const { + CORRADE_ASSERT(hasAttribute(MaterialAttribute::EmissiveTexture), + "Trade::PbrSpecularGlossinessMaterialData::emissiveTextureMatrix(): the material doesn't have an emissive texture", {}); + if(Containers::Optional value = tryAttribute(MaterialAttribute::EmissiveTextureMatrix)) + return *value; + return attributeOr(MaterialAttribute::TextureMatrix, Matrix3{}); +} + +UnsignedInt PbrSpecularGlossinessMaterialData::emissiveTextureCoordinates() const { + CORRADE_ASSERT(hasAttribute(MaterialAttribute::EmissiveTexture), + "Trade::PbrSpecularGlossinessMaterialData::emissiveTextureCoordinates(): the material doesn't have an emissive texture", {}); + if(Containers::Optional value = tryAttribute(MaterialAttribute::EmissiveTextureCoordinates)) + return *value; + return attributeOr(MaterialAttribute::TextureCoordinates, 0u); +} + +Matrix3 PbrSpecularGlossinessMaterialData::textureMatrix() const { + return attributeOr(MaterialAttribute::TextureMatrix, Matrix3{}); +} + +UnsignedInt PbrSpecularGlossinessMaterialData::textureCoordinates() const { + return attributeOr(MaterialAttribute::TextureCoordinates, 0u); +} + +}} diff --git a/src/Magnum/Trade/PbrSpecularGlossinessMaterialData.h b/src/Magnum/Trade/PbrSpecularGlossinessMaterialData.h new file mode 100644 index 0000000000..719d87f94f --- /dev/null +++ b/src/Magnum/Trade/PbrSpecularGlossinessMaterialData.h @@ -0,0 +1,460 @@ +#ifndef Magnum_Trade_PbrSpecularGlossinessMaterialData_h +#define Magnum_Trade_PbrSpecularGlossinessMaterialData_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + 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::PbrSpecularGlossinessMaterialData + * @m_since_latest + */ + +#include "Magnum/Trade/MaterialData.h" + +namespace Magnum { namespace Trade { + +/** +@brief PBR specular/glossiness material data +@m_since_latest + +@see @ref AbstractImporter::material(), @ref PbrMetallicRoughnessMaterialData, + @ref PhongMaterialData +*/ +class MAGNUM_TRADE_EXPORT PbrSpecularGlossinessMaterialData: public MaterialData { + public: + #ifndef DOXYGEN_GENERATING_OUTPUT + /* Allow constructing subclasses directly. While not used in the + general Importer workflow, it allows users to create instances with + desired convenience APIs easier (and simplifies testing). It's + however hidden from the docs as constructing instances this way + isn't really common and it would add a lot of noise. */ + using MaterialData::MaterialData; + #endif + + /** + * @brief Whether the material has a specular texture + * + * Returns @cpp true @ce if any of the + * @ref MaterialAttribute::SpecularTexture or + * @ref MaterialAttribute::SpecularGlossinessTexture attributes is + * present, @cpp false @ce otherwise. + * @see @ref hasGlossinessTexture(), + * @ref hasSpecularGlossinessTexture() + */ + bool hasSpecularTexture() const; + + /** + * @brief Whether the material has a glossiness texture + * + * Returns @cpp true @ce if any of the + * @ref MaterialAttribute::GlossinessTexture or + * @ref MaterialAttribute::SpecularGlossinessTexture attributes is + * present, @cpp false @ce otherwise. + * @see @ref hasSpecularTexture(), + * @ref hasSpecularGlossinessTexture() + */ + bool hasGlossinessTexture() const; + + /** + * @brief Whether the material has a combined specular/glossiness texture + * + * Returns @cpp true @ce if either the + * @ref MaterialAttribute::SpecularGlossinessTexture attribute is + * present or both @ref MaterialAttribute::SpecularTexture and + * @ref MaterialAttribute::GlossinessTexture are present, point to + * the same texture ID and @ref MaterialAttribute::GlossinessTextureSwizzle + * is set to @ref MaterialTextureSwizzle::A, and ddditionally + * @ref MaterialAttribute::SpecularTextureMatrix and + * @ref MaterialAttribute::GlossinessTextureMatrix are both either not + * present or have the same value, and + * @ref MaterialAttribute::SpecularTextureCoordinates and + * @ref MaterialAttribute::GlossinessTextureCoordinates are both either + * not present or have the same value; @cpp false @ce otherwise. + * + * In other words, if this function returns @cpp true @ce, + * @ref specularTexture(), @ref specularTextureMatrix() and + * @ref specularTextureCoordinates() return values common for both + * specular and glossiness texture, and the two are packed together + * with specular occupying the RGB channels and glossiness the alpha. + * @see @ref hasSpecularTexture(), @ref hasGlossinessTexture() + */ + bool hasSpecularGlossinessTexture() const; + + /** + * @brief Whether the material has texture transformation + * + * Returns @cpp true @ce if any of the + * @ref MaterialAttribute::DiffuseTextureMatrix, + * @ref MaterialAttribute::SpecularTextureMatrix, + * @ref MaterialAttribute::GlossinessTextureMatrix, + * @ref MaterialAttribute::NormalTextureMatrix, + * @ref MaterialAttribute::OcclusionTextureMatrix, + * @ref MaterialAttribute::EmissiveTextureMatrix or + * @ref MaterialAttribute::TextureMatrix attributes is present, + * @cpp false @ce otherwise. + */ + bool hasTextureTransformation() const; + + /** + * @brief Whether the material uses extra texture coordinate sets + * + * Returns @cpp true @ce if any of the + * @ref MaterialAttribute::DiffuseTextureCoordinates, + * @ref MaterialAttribute::SpecularTextureCoordinates, + * @ref MaterialAttribute::GlossinessTextureCoordinates, + * @ref MaterialAttribute::NormalTextureCoordinates, + * @ref MaterialAttribute::OcclusionTextureCoordinates, + * @ref MaterialAttribute::EmissiveTextureCoordinates or + * @ref MaterialAttribute::TextureCoordinates attributes is present and + * has a non-zero value, @cpp false @ce otherwise. + */ + bool hasTextureCoordinates() const; + + /** + * @brief Base color + * + * Convenience access to the @ref MaterialAttribute::DiffuseColor + * attribute. If not present, the default is @cpp 0xffffffff_srgbaf @ce. + * + * If the material has @ref MaterialAttribute::DiffuseTexture, the + * color and texture is meant to be multiplied together. + */ + Color4 diffuseColor() const; + + /** + * @brief Base color texture ID + * + * Available only if @ref MaterialAttribute::DiffuseTexture is + * present. Meant to be multiplied with @ref diffuseColor(). + * @see @ref hasAttribute(), @ref AbstractImporter::texture() + */ + UnsignedInt diffuseTexture() const; + + /** + * @brief Base color texture coordinate transformation matrix + * + * Convenience access to the @ref MaterialAttribute::DiffuseTextureMatrix + * / @ref MaterialAttribute::TextureMatrix attributes. If neither is + * present, the default is an identity matrix. Available only if the + * material has @ref MaterialAttribute::DiffuseTexture. + * @see @ref hasAttribute() + */ + Matrix3 diffuseTextureMatrix() const; + + /** + * @brief Base color texture coordinate set + * + * Convenience access to the @ref MaterialAttribute::DiffuseTextureCoordinates + * / @ref MaterialAttribute::TextureCoordinates attributes. If neither is + * present, the default is @cpp 0 @ce. Available only if the material + * has @ref MaterialAttribute::DiffuseTexture. + * @see @ref hasAttribute() + */ + UnsignedInt diffuseTextureCoordinates() const; + + /** + * @brief Specular color + * + * Convenience access to the @ref MaterialAttribute::SpecularColor + * attribute. If not present, the default is @cpp 0xffffff00_srgbaf @ce. + * + * If the material has a specular texture, the color and texture is + * meant to be multiplied together. + * @see @ref hasSpecularTexture() + */ + Color4 specularColor() const; + + /** + * @brief Specular texture ID + * + * Available only if either @ref MaterialAttribute::SpecularTexture or + * @ref MaterialAttribute::SpecularGlossinessTexture is present. Meant + * to be multiplied with @ref specularColor(). + * @see @ref hasSpecularTexture(), @ref AbstractImporter::texture() + */ + UnsignedInt specularTexture() const; + + /** + * @brief Specular texture swizzle + * + * If @ref MaterialAttribute::SpecularGlossinessTexture is present, + * returns always @ref MaterialTextureSwizzle::RGB. Otherwise returns + * the @ref MaterialAttribute::SpecularTextureSwizzle attribute, or + * @ref MaterialTextureSwizzle::RGB if it's not present. Available only + * if the material has a specular texture. + * @see @ref hasSpecularTexture() + */ + MaterialTextureSwizzle specularTextureSwizzle() const; + + /** + * @brief Specular texture coordinate transformation matrix + * + * Convenience access to the @ref MaterialAttribute::SpecularTextureMatrix + * / @ref MaterialAttribute::TextureMatrix attributes. If neither is + * present, the default is an identity matrix. Available only if the + * material has a specular texture. + * @see @ref hasSpecularTexture() + */ + Matrix3 specularTextureMatrix() const; + + /** + * @brief Specular texture coordinate set + * + * Convenience access to the @ref MaterialAttribute::SpecularTextureCoordinates + * / @ref MaterialAttribute::TextureCoordinates attributes. If neither is + * present, the default is @cpp 0 @ce. Available only if the material + * has a specular texture. + * @see @ref hasSpecularTexture() + */ + UnsignedInt specularTextureCoordinates() const; + + /** + * @brief Glossiness factor + * + * Convenience access to the @ref MaterialAttribute::Glossiness + * attribute. If not present, the default is @cpp 1.0f @ce. + * + * If the material has a glossiness texture, the factor and texture is + * meant to be multiplied together. + * @see @ref hasGlossinessTexture() + */ + Float glossiness() const; + + /** + * @brief Glossiness texture ID + * + * Available only if either @ref MaterialAttribute::GlossinessTexture or + * @ref MaterialAttribute::SpecularGlossinessTexture is present. Meant + * to be multiplied with @ref glossiness(). + * @see @ref hasGlossinessTexture(), @ref AbstractImporter::texture() + */ + UnsignedInt glossinessTexture() const; + + /** + * @brief Glossiness texture swizzle + * + * If @ref MaterialAttribute::SpecularGlossinessTexture is present, + * returns always @ref MaterialTextureSwizzle::A. Otherwise returns the + * @ref MaterialAttribute::GlossinessTextureSwizzle attribute, or + * @ref MaterialTextureSwizzle::R if it's not present. Available only + * if the material has a glossiness texture. + * @see @ref hasGlossinessTexture() + */ + MaterialTextureSwizzle glossinessTextureSwizzle() const; + + /** + * @brief Glossiness texture coordinate transformation matrix + * + * Convenience access to the @ref MaterialAttribute::GlossinessTextureMatrix + * / @ref MaterialAttribute::TextureMatrix attributes. If neither is + * present, the default is an identity matrix. Available only if the + * material has a glossiness texture. + * @see @ref hasGlossinessTexture() + */ + Matrix3 glossinessTextureMatrix() const; + + /** + * @brief Glossiness texture coordinate set + * + * Convenience access to the @ref MaterialAttribute::GlossinessTextureCoordinates + * / @ref MaterialAttribute::TextureCoordinates attributes. If neither is + * present, the default is @cpp 0 @ce. Available only if the material + * has a glossiness texture. + * @see @ref hasGlossinessTexture() + */ + UnsignedInt glossinessTextureCoordinates() const; + + /** + * @brief Normal texture ID + * + * Available only if @ref MaterialAttribute::NormalTexture is present. + * @see @ref hasAttribute(), @ref AbstractImporter::texture() + */ + UnsignedInt normalTexture() const; + + /** + * @brief Normal texture swizzle + * + * Convenience access to the + * @ref MaterialAttribute::NormalTextureSwizzle attribute. If not + * present, the default is @ref MaterialTextureSwizzle::RGB. Available + * only if @ref MaterialAttribute::NormalTexture is present. + * @see @ref hasAttribute() + */ + MaterialTextureSwizzle normalTextureSwizzle() const; + + /** + * @brief Normal texture coordinate transformation matrix + * + * Convenience access to the @ref MaterialAttribute::NormalTextureMatrix + * / @ref MaterialAttribute::TextureMatrix attributes. If neither is + * present, the default is an identity matrix. Available only if the + * material has @ref MaterialAttribute::NormalTexture. + * @see @ref hasAttribute() + */ + Matrix3 normalTextureMatrix() const; + + /** + * @brief Normal texture coordinate set + * + * Convenience access to the @ref MaterialAttribute::NormalTextureCoordinates + * / @ref MaterialAttribute::TextureCoordinates attributes. If neither is + * present, the default is @cpp 0 @ce. Available only if the material + * has @ref MaterialAttribute::NormalTexture. + * @see @ref hasAttribute() + */ + UnsignedInt normalTextureCoordinates() const; + + /** + * @brief Occlusion texture ID + * + * Available only if @ref MaterialAttribute::OcclusionTexture is present. + * @see @ref hasAttribute(), @ref AbstractImporter::texture() + */ + UnsignedInt occlusionTexture() const; + + /** + * @brief Occlusion texture swizzle + * + * Convenience access to the + * @ref MaterialAttribute::OcclusionTextureSwizzle attribute. If not + * present, the default is @ref MaterialTextureSwizzle::R. Available + * only if @ref MaterialAttribute::OcclusionTexture is present. + * @see @ref hasAttribute() + */ + MaterialTextureSwizzle occlusionTextureSwizzle() const; + + /** + * @brief Occlusion texture coordinate transformation matrix + * + * Convenience access to the @ref MaterialAttribute::OcclusionTextureMatrix + * / @ref MaterialAttribute::TextureMatrix attributes. If neither is + * present, the default is an identity matrix. Available only if the + * material has @ref MaterialAttribute::OcclusionTexture. + * @see @ref hasAttribute(), @ref AbstractImporter::texture() + */ + Matrix3 occlusionTextureMatrix() const; + + /** + * @brief Occlusion texture coordinate set + * + * Convenience access to the @ref MaterialAttribute::OcclusionTextureCoordinates + * / @ref MaterialAttribute::TextureCoordinates attributes. If neither is + * present, the default is @cpp 0 @ce. Available only if the material + * has @ref MaterialAttribute::OcclusionTexture. + * @see @ref hasAttribute(), @ref AbstractImporter::texture() + */ + UnsignedInt occlusionTextureCoordinates() const; + + /** + * @brief Emissive color + * + * Convenience access to the @ref MaterialAttribute::EmissiveColor + * attribute. If not present, the default is @cpp 0x000000_srgbf @ce + * (i.e, no emission). + * + * If the material has @ref MaterialAttribute::EmissiveTexture, the + * color and texture is meant to be multiplied together. + */ + Color3 emissiveColor() const; + + /** + * @brief Emissive texture ID + * + * Available only if @ref MaterialAttribute::EmissiveTexture is present. + * Meant to be multiplied with @ref emissiveColor(). + * @see @ref hasAttribute(), @ref AbstractImporter::texture() + */ + UnsignedInt emissiveTexture() const; + + /* No EmissiveTextureSwizzle attribute right now (implicitly RGB) */ + + /** + * @brief Emissive texture coordinate transformation matrix + * + * Convenience access to the @ref MaterialAttribute::EmissiveTextureMatrix + * / @ref MaterialAttribute::TextureMatrix attributes. If neither is + * present, the default is an identity matrix. Available only if the + * material has @ref MaterialAttribute::EmissiveTexture. + * @see @ref hasAttribute(), @ref AbstractImporter::texture() + */ + Matrix3 emissiveTextureMatrix() const; + + /** + * @brief Emissive texture coordinate set + * + * Convenience access to the @ref MaterialAttribute::EmissiveTextureCoordinates + * / @ref MaterialAttribute::TextureCoordinates attributes. If neither + * is present, the default is @cpp 0 @ce. Available only if the + * material has @ref MaterialAttribute::EmissiveTexture. + * @see @ref hasAttribute(), @ref AbstractImporter::texture() + */ + UnsignedInt emissiveTextureCoordinates() const; + + /** + * @brief Common texture coordinate transformation matrix for all textures + * + * Convenience access to the @ref MaterialAttribute::TextureMatrix + * attribute. If not present, the default is an identity matrix. Note + * that the material may also define per-texture transformation using + * the @ref MaterialAttribute::DiffuseTextureMatrix, + * @ref MaterialAttribute::SpecularTextureMatrix, + * @ref MaterialAttribute::GlossinessTextureMatrix, + * @ref MaterialAttribute::NormalTextureMatrix, + * @ref MaterialAttribute::OcclusionTextureMatrix and + * @ref MaterialAttribute::EmissiveTextureMatrix attributes, which then + * take precedence over the common one. + * @see @ref hasAttribute(), @ref diffuseTextureMatrix(), + * @ref specularTextureMatrix(), @ref glossinessTextureMatrix(), + * @ref normalTextureMatrix(), @ref occlusionTextureMatrix(), + * @ref emissiveTextureMatrix() + */ + Matrix3 textureMatrix() const; + + /** + * @brief Common texture coordinate set index for all textures + * + * Convenience access to the @ref MaterialAttribute::TextureCoordinates + * attribute. If not present, the default is @cpp 0 @ce. Note that the + * material may also define per-texture coordinate set using the + * @ref MaterialAttribute::DiffuseTextureCoordinates, + * @ref MaterialAttribute::SpecularTextureCoordinates, + * @ref MaterialAttribute::GlossinessTextureCoordinates, + * @ref MaterialAttribute::NormalTextureCoordinates, + * @ref MaterialAttribute::OcclusionTextureCoordinates and + * @ref MaterialAttribute::EmissiveTextureCoordinates attributes, which + * then take precedence over the common one. + * @see @ref hasAttribute(), @ref diffuseTextureCoordinates(), + * @ref specularTextureCoordinates(), + * @ref glossinessTextureCoordinates(), + * @ref normalTextureCoordinates(), + * @ref occlusionTextureCoordinates(), + * @ref emissiveTextureCoordinates() + */ + UnsignedInt textureCoordinates() const; +}; + +}} + +#endif diff --git a/src/Magnum/Trade/PhongMaterialData.cpp b/src/Magnum/Trade/PhongMaterialData.cpp index 2c596b4a3d..a251a7546b 100644 --- a/src/Magnum/Trade/PhongMaterialData.cpp +++ b/src/Magnum/Trade/PhongMaterialData.cpp @@ -115,7 +115,7 @@ PhongMaterialData::Flags PhongMaterialData::flags() const { flags |= Flag::AmbientTexture; if(hasAttribute(MaterialAttribute::DiffuseTexture)) flags |= Flag::DiffuseTexture; - if(hasAttribute(MaterialAttribute::SpecularTexture)) + if(hasSpecularTexture()) flags |= Flag::SpecularTexture; if(hasAttribute(MaterialAttribute::NormalTexture)) flags |= Flag::NormalTexture; @@ -129,6 +129,11 @@ PhongMaterialData::Flags PhongMaterialData::flags() const { CORRADE_IGNORE_DEPRECATED_POP #endif +bool PhongMaterialData::hasSpecularTexture() const { + return hasAttribute(MaterialAttribute::SpecularTexture) || + hasAttribute(MaterialAttribute::SpecularGlossinessTexture); +} + bool PhongMaterialData::hasTextureTransformation() const { return hasAttribute(MaterialAttribute::AmbientTextureMatrix) || hasAttribute(MaterialAttribute::DiffuseTextureMatrix) || @@ -195,15 +200,25 @@ UnsignedInt PhongMaterialData::diffuseTextureCoordinates() const { } Color4 PhongMaterialData::specularColor() const { - return attributeOr(MaterialAttribute::SpecularColor, 0xffffffff_rgbaf); + return attributeOr(MaterialAttribute::SpecularColor, 0xffffff00_rgbaf); } UnsignedInt PhongMaterialData::specularTexture() const { + if(Containers::Optional value = tryAttribute(MaterialAttribute::SpecularGlossinessTexture)) + return *value; return attribute(MaterialAttribute::SpecularTexture); } +MaterialTextureSwizzle PhongMaterialData::specularTextureSwizzle() const { + CORRADE_ASSERT(hasSpecularTexture(), + "Trade::PhongMaterialData::specularTextureSwizzle(): the material doesn't have a specular texture", {}); + if(hasAttribute(MaterialAttribute::SpecularGlossinessTexture)) + return MaterialTextureSwizzle::RGB; + return attributeOr(MaterialAttribute::SpecularTextureSwizzle, MaterialTextureSwizzle::RGB); +} + Matrix3 PhongMaterialData::specularTextureMatrix() const { - CORRADE_ASSERT(hasAttribute(MaterialAttribute::SpecularTexture), + CORRADE_ASSERT(hasSpecularTexture(), "Trade::PhongMaterialData::specularTextureMatrix(): the material doesn't have a specular texture", {}); if(Containers::Optional set = tryAttribute(MaterialAttribute::SpecularTextureMatrix)) return *set; @@ -211,7 +226,7 @@ Matrix3 PhongMaterialData::specularTextureMatrix() const { } UnsignedInt PhongMaterialData::specularTextureCoordinates() const { - CORRADE_ASSERT(hasAttribute(MaterialAttribute::SpecularTexture), + CORRADE_ASSERT(hasSpecularTexture(), "Trade::PhongMaterialData::specularTextureCoordinates(): the material doesn't have a specular texture", {}); if(Containers::Optional set = tryAttribute(MaterialAttribute::SpecularTextureCoordinates)) return *set; @@ -222,6 +237,12 @@ UnsignedInt PhongMaterialData::normalTexture() const { return attribute(MaterialAttribute::NormalTexture); } +MaterialTextureSwizzle PhongMaterialData::normalTextureSwizzle() const { + CORRADE_ASSERT(hasAttribute(MaterialAttribute::NormalTexture), + "Trade::PhongMaterialData::normalTextureSwizzle(): the material doesn't have a normal texture", {}); + return attributeOr(MaterialAttribute::NormalTextureSwizzle, MaterialTextureSwizzle::RGB); +} + Matrix3 PhongMaterialData::normalTextureMatrix() const { CORRADE_ASSERT(hasAttribute(MaterialAttribute::NormalTexture), "Trade::PhongMaterialData::normalTextureMatrix(): the material doesn't have a normal texture", {}); diff --git a/src/Magnum/Trade/PhongMaterialData.h b/src/Magnum/Trade/PhongMaterialData.h index d7d8cce3f9..6460475562 100644 --- a/src/Magnum/Trade/PhongMaterialData.h +++ b/src/Magnum/Trade/PhongMaterialData.h @@ -37,7 +37,8 @@ namespace Magnum { namespace Trade { /** @brief Phong material data -@see @ref AbstractImporter::material() +@see @ref AbstractImporter::material(), @ref PbrMetallicRoughnessMaterialData, + @ref PbrSpecularGlossinessMaterialData */ class MAGNUM_TRADE_EXPORT PhongMaterialData: public MaterialData { public: @@ -227,6 +228,17 @@ class MAGNUM_TRADE_EXPORT PhongMaterialData: public MaterialData { CORRADE_IGNORE_DEPRECATED_POP #endif + /** + * @brief Whether the material has a specular texture + * @m_since_latest + * + * Returns @cpp true @ce if any of the + * @ref MaterialAttribute::SpecularTexture or + * @ref MaterialAttribute::SpecularGlossinessTexture attributes is + * present, @cpp false @ce otherwise. + */ + bool hasSpecularTexture() const; + /** * @brief Whether the material has texture transformation * @m_since_latest @@ -390,23 +402,37 @@ class MAGNUM_TRADE_EXPORT PhongMaterialData: public MaterialData { * @brief Specular color * * Convenience access to the @ref MaterialAttribute::SpecularColor - * attribute. If not present, the default is @cpp 0xffffffff_rgbaf @ce. + * attribute. If not present, the default is @cpp 0xffffff00_rgbaf @ce. * - * If the material has @ref MaterialAttribute::SpecularTexture, the - * color and texture is meant to be multiplied together. - * @see @ref hasAttribute() + * If the material has a specular texture, the color and texture is + * meant to be multiplied together. + * @see @ref hasSpecularTexture() */ Color4 specularColor() const; /** * @brief Specular texture ID * - * Available only if @ref MaterialAttribute::SpecularTexture is - * present. Meant to be multiplied with @ref specularColor(). + * Available only if either @ref MaterialAttribute::SpecularTexture or + * @ref MaterialAttribute::SpecularGlossinessTexture is present. Meant + * to be multiplied with @ref specularColor(). * @see @ref hasAttribute(), @ref AbstractImporter::texture() */ UnsignedInt specularTexture() const; + /** + * @brief Specular texture swizzle + * @m_since_latest + * + * If @ref MaterialAttribute::SpecularGlossinessTexture is present, + * returns always @ref MaterialTextureSwizzle::RGB. Otherwise returns + * the @ref MaterialAttribute::SpecularTextureSwizzle attribute, or + * @ref MaterialTextureSwizzle::RGB if it's not present. Available only + * if the material has a specular texture. + * @see @ref hasSpecularTexture() + */ + MaterialTextureSwizzle specularTextureSwizzle() const; + /** * @brief Specular texture coordinate transformation matrix * @m_since_latest @@ -414,8 +440,8 @@ class MAGNUM_TRADE_EXPORT PhongMaterialData: public MaterialData { * Convenience access to the @ref MaterialAttribute::SpecularTextureMatrix * / @ref MaterialAttribute::TextureMatrix attributes. If neither is * present, the default is an identity matrix. Available only if the - * material has @ref MaterialAttribute::SpecularTexture. - * @see @ref hasAttribute() + * material has a specular texture. + * @see @ref hasSpecularTexture() */ Matrix3 specularTextureMatrix() const; @@ -426,8 +452,8 @@ class MAGNUM_TRADE_EXPORT PhongMaterialData: public MaterialData { * Convenience access to the @ref MaterialAttribute::SpecularTextureCoordinates * / @ref MaterialAttribute::TextureCoordinates attributes. If neither * is present, the default is @cpp 0 @ce. Available only if the - * material has @ref MaterialAttribute::SpecularTexture. - * @see @ref hasAttribute() + * material has a specular texture. + * @see @ref hasSpecularTexture() */ UnsignedInt specularTextureCoordinates() const; @@ -451,6 +477,18 @@ class MAGNUM_TRADE_EXPORT PhongMaterialData: public MaterialData { */ UnsignedInt normalTexture() const; + /** + * @brief Normal texture swizzle + * @m_since_latest + * + * Convenience access to the + * @ref MaterialAttribute::NormalTextureSwizzle attribute. If not + * present, the default is @ref MaterialTextureSwizzle::RGB. Available + * only if @ref MaterialAttribute::NormalTexture is present. + * @see @ref hasAttribute() + */ + MaterialTextureSwizzle normalTextureSwizzle() const; + /** * @brief Normal texture coordinate transformation matrix * @m_since_latest diff --git a/src/Magnum/Trade/Test/MaterialDataTest.cpp b/src/Magnum/Trade/Test/MaterialDataTest.cpp index fe45595d34..783c589aad 100644 --- a/src/Magnum/Trade/Test/MaterialDataTest.cpp +++ b/src/Magnum/Trade/Test/MaterialDataTest.cpp @@ -34,6 +34,8 @@ #include "Magnum/Math/Color.h" #include "Magnum/Math/Matrix3.h" #include "Magnum/Trade/MaterialData.h" +#include "Magnum/Trade/PbrMetallicRoughnessMaterialData.h" +#include "Magnum/Trade/PbrSpecularGlossinessMaterialData.h" #include "Magnum/Trade/PhongMaterialData.h" namespace Magnum { namespace Trade { namespace Test { namespace { @@ -127,11 +129,33 @@ class MaterialDataTest: public TestSuite::Tester { void constructPhongDeprecatedNoTextureCoordinatesFlag(); #endif + void pbrMetallicRoughnessAccess(); + void pbrMetallicRoughnessAccessDefaults(); + void pbrMetallicRoughnessAccessTextured(); + void pbrMetallicRoughnessAccessTexturedDefaults(); + void pbrMetallicRoughnessAccessTexturedImplicitPackedMetallicRoughness(); + void pbrMetallicRoughnessAccessTexturedExplicitPackedMetallicRoughness(); + void pbrMetallicRoughnessAccessTexturedExplicitPackedRoughnessMetallicOcclusion(); + void pbrMetallicRoughnessAccessTexturedExplicitPackedOcclusionRoughnessMetallic(); + void pbrMetallicRoughnessAccessTexturedExplicitPackedNormalRoughnessMetallic(); + void pbrMetallicRoughnessAccessTexturedSingleMatrixCoordinates(); + void pbrMetallicRoughnessAccessInvalidTextures(); + + void pbrSpecularGlossinessAccess(); + void pbrSpecularGlossinessAccessDefaults(); + void pbrSpecularGlossinessAccessTextured(); + void pbrSpecularGlossinessAccessTexturedDefaults(); + void pbrSpecularGlossinessAccessTexturedImplicitPackedSpecularGlossiness(); + void pbrSpecularGlossinessAccessTexturedExplicitPackedSpecularGlossiness(); + void pbrSpecularGlossinessAccessTexturedSingleMatrixCoordinates(); + void pbrSpecularGlossinessAccessInvalidTextures(); + void phongAccess(); void phongAccessDefaults(); void phongAccessTextured(); void phongAccessTexturedDefaults(); void phongAccessTexturedSingleMatrixCoordinates(); + void phongAccessTexturedImplicitPackedSpecularGlossiness(); void phongAccessInvalidTextures(); void debugAttribute(); @@ -264,11 +288,33 @@ MaterialDataTest::MaterialDataTest() { &MaterialDataTest::constructPhongDeprecatedNoTextureCoordinatesFlag, #endif + &MaterialDataTest::pbrMetallicRoughnessAccess, + &MaterialDataTest::pbrMetallicRoughnessAccessDefaults, + &MaterialDataTest::pbrMetallicRoughnessAccessTextured, + &MaterialDataTest::pbrMetallicRoughnessAccessTexturedDefaults, + &MaterialDataTest::pbrMetallicRoughnessAccessTexturedImplicitPackedMetallicRoughness, + &MaterialDataTest::pbrMetallicRoughnessAccessTexturedExplicitPackedMetallicRoughness, + &MaterialDataTest::pbrMetallicRoughnessAccessTexturedExplicitPackedRoughnessMetallicOcclusion, + &MaterialDataTest::pbrMetallicRoughnessAccessTexturedExplicitPackedOcclusionRoughnessMetallic, + &MaterialDataTest::pbrMetallicRoughnessAccessTexturedExplicitPackedNormalRoughnessMetallic, + &MaterialDataTest::pbrMetallicRoughnessAccessTexturedSingleMatrixCoordinates, + &MaterialDataTest::pbrMetallicRoughnessAccessInvalidTextures, + + &MaterialDataTest::pbrSpecularGlossinessAccess, + &MaterialDataTest::pbrSpecularGlossinessAccessDefaults, + &MaterialDataTest::pbrSpecularGlossinessAccessTextured, + &MaterialDataTest::pbrSpecularGlossinessAccessTexturedDefaults, + &MaterialDataTest::pbrSpecularGlossinessAccessTexturedImplicitPackedSpecularGlossiness, + &MaterialDataTest::pbrSpecularGlossinessAccessTexturedExplicitPackedSpecularGlossiness, + &MaterialDataTest::pbrSpecularGlossinessAccessTexturedSingleMatrixCoordinates, + &MaterialDataTest::pbrSpecularGlossinessAccessInvalidTextures, + &MaterialDataTest::phongAccess, &MaterialDataTest::phongAccessDefaults, &MaterialDataTest::phongAccessTextured, &MaterialDataTest::phongAccessTexturedDefaults, &MaterialDataTest::phongAccessTexturedSingleMatrixCoordinates, + &MaterialDataTest::phongAccessTexturedImplicitPackedSpecularGlossiness, &MaterialDataTest::phongAccessInvalidTextures, &MaterialDataTest::debugAttribute, @@ -2086,6 +2132,1003 @@ void MaterialDataTest::constructPhongDeprecatedNoTextureCoordinatesFlag() { } #endif +void MaterialDataTest::pbrMetallicRoughnessAccess() { + MaterialData base{MaterialType::PbrMetallicRoughness, { + {MaterialAttribute::BaseColor, 0xccffbbff_rgbaf}, + {MaterialAttribute::Metalness, 0.5f}, + {MaterialAttribute::Roughness, 0.79f}, + {MaterialAttribute::EmissiveColor, 0x111111_rgbf} + }}; + + CORRADE_COMPARE(base.types(), MaterialType::PbrMetallicRoughness); + const PbrMetallicRoughnessMaterialData& data = static_cast(base); + + CORRADE_VERIFY(!data.hasMetalnessTexture()); + CORRADE_VERIFY(!data.hasRoughnessTexture()); + CORRADE_VERIFY(!data.hasMetallicRoughnessTexture()); + CORRADE_VERIFY(!data.hasRoughnessMetallicOcclusionTexture()); + CORRADE_VERIFY(!data.hasOcclusionRoughnessMetallicTexture()); + CORRADE_VERIFY(!data.hasNormalRoughnessMetallicTexture()); + CORRADE_VERIFY(!data.hasTextureTransformation()); + CORRADE_VERIFY(!data.hasTextureCoordinates()); + CORRADE_COMPARE(data.baseColor(), 0xccffbbff_rgbaf); + CORRADE_COMPARE(data.metalness(), 0.5f); + CORRADE_COMPARE(data.roughness(), 0.79f); + CORRADE_COMPARE(data.emissiveColor(), 0x111111_rgbf); +} + +void MaterialDataTest::pbrMetallicRoughnessAccessDefaults() { + MaterialData base{{}, {}}; + + CORRADE_COMPARE(base.types(), MaterialTypes{}); + /* Casting is fine even if the type doesn't include PbrMetallicRoughness */ + const PbrMetallicRoughnessMaterialData& data = static_cast(base); + + CORRADE_VERIFY(!data.hasMetalnessTexture()); + CORRADE_VERIFY(!data.hasRoughnessTexture()); + CORRADE_VERIFY(!data.hasMetallicRoughnessTexture()); + CORRADE_VERIFY(!data.hasRoughnessMetallicOcclusionTexture()); + CORRADE_VERIFY(!data.hasOcclusionRoughnessMetallicTexture()); + CORRADE_VERIFY(!data.hasNormalRoughnessMetallicTexture()); + CORRADE_VERIFY(!data.hasTextureTransformation()); + CORRADE_VERIFY(!data.hasTextureCoordinates()); + CORRADE_COMPARE(data.baseColor(), 0xffffffff_rgbaf); + CORRADE_COMPARE(data.metalness(), 1.0f); + CORRADE_COMPARE(data.roughness(), 1.0f); + CORRADE_COMPARE(data.emissiveColor(), 0x000000_rgbf); + CORRADE_COMPARE(data.textureMatrix(), Matrix3{}); + CORRADE_COMPARE(data.textureCoordinates(), 0); +} + +void MaterialDataTest::pbrMetallicRoughnessAccessTextured() { + PbrMetallicRoughnessMaterialData data{{}, { + {MaterialAttribute::BaseColor, 0xccffbbff_rgbaf}, + {MaterialAttribute::BaseColorTexture, 0u}, + {MaterialAttribute::BaseColorTextureMatrix, Matrix3::scaling({0.5f, 1.0f})}, + {MaterialAttribute::BaseColorTextureCoordinates, 2u}, + {MaterialAttribute::Metalness, 0.5f}, + {MaterialAttribute::MetalnessTexture, 1u}, + {MaterialAttribute::MetalnessTextureSwizzle, MaterialTextureSwizzle::G}, + {MaterialAttribute::MetalnessTextureMatrix, Matrix3::scaling({0.5f, 0.5f})}, + {MaterialAttribute::MetalnessTextureCoordinates, 3u}, + {MaterialAttribute::Roughness, 0.79f}, + {MaterialAttribute::RoughnessTexture, 2u}, + {MaterialAttribute::RoughnessTextureSwizzle, MaterialTextureSwizzle::A}, + {MaterialAttribute::RoughnessTextureMatrix, Matrix3::scaling({1.0f, 1.0f})}, + {MaterialAttribute::RoughnessTextureCoordinates, 4u}, + {MaterialAttribute::NormalTexture, 3u}, + {MaterialAttribute::NormalTextureSwizzle, MaterialTextureSwizzle::BA}, + {MaterialAttribute::NormalTextureMatrix, Matrix3::scaling({1.0f, 0.5f})}, + {MaterialAttribute::NormalTextureCoordinates, 5u}, + {MaterialAttribute::OcclusionTexture, 4u}, + {MaterialAttribute::OcclusionTextureSwizzle, MaterialTextureSwizzle::B}, + {MaterialAttribute::OcclusionTextureMatrix, Matrix3::scaling({1.0f, 0.75f})}, + {MaterialAttribute::OcclusionTextureCoordinates, 6u}, + {MaterialAttribute::EmissiveColor, 0x111111_rgbf}, + {MaterialAttribute::EmissiveTexture, 5u}, + {MaterialAttribute::EmissiveTextureMatrix, Matrix3::scaling({0.75f, 0.5f})}, + {MaterialAttribute::EmissiveTextureCoordinates, 7u} + }}; + + CORRADE_VERIFY(data.hasMetalnessTexture()); + CORRADE_VERIFY(data.hasRoughnessTexture()); + CORRADE_VERIFY(!data.hasMetallicRoughnessTexture()); + CORRADE_VERIFY(!data.hasRoughnessMetallicOcclusionTexture()); + CORRADE_VERIFY(!data.hasOcclusionRoughnessMetallicTexture()); + CORRADE_VERIFY(!data.hasNormalRoughnessMetallicTexture()); + CORRADE_VERIFY(data.hasTextureTransformation()); + CORRADE_VERIFY(data.hasTextureCoordinates()); + CORRADE_COMPARE(data.baseColor(), 0xccffbbff_rgbaf); + CORRADE_COMPARE(data.baseColorTexture(), 0); + CORRADE_COMPARE(data.baseColorTextureMatrix(), Matrix3::scaling({0.5f, 1.0f})); + CORRADE_COMPARE(data.baseColorTextureCoordinates(), 2); + CORRADE_COMPARE(data.metalness(), 0.5f); + CORRADE_COMPARE(data.metalnessTexture(), 1); + CORRADE_COMPARE(data.metalnessTextureSwizzle(), MaterialTextureSwizzle::G); + CORRADE_COMPARE(data.metalnessTextureMatrix(), Matrix3::scaling({0.5f, 0.5f})); + CORRADE_COMPARE(data.metalnessTextureCoordinates(), 3); + CORRADE_COMPARE(data.roughness(), 0.79f); + CORRADE_COMPARE(data.roughnessTexture(), 2); + CORRADE_COMPARE(data.roughnessTextureSwizzle(), MaterialTextureSwizzle::A); + CORRADE_COMPARE(data.roughnessTextureMatrix(), Matrix3::scaling({1.0f, 1.0f})); + CORRADE_COMPARE(data.roughnessTextureCoordinates(), 4); + CORRADE_COMPARE(data.normalTexture(), 3); + CORRADE_COMPARE(data.normalTextureSwizzle(), MaterialTextureSwizzle::BA); + CORRADE_COMPARE(data.normalTextureMatrix(), Matrix3::scaling({1.0f, 0.5f})); + CORRADE_COMPARE(data.normalTextureCoordinates(), 5); + CORRADE_COMPARE(data.occlusionTexture(), 4); + CORRADE_COMPARE(data.occlusionTextureMatrix(), Matrix3::scaling({1.0f, 0.75f})); + CORRADE_COMPARE(data.occlusionTextureSwizzle(), MaterialTextureSwizzle::B); + CORRADE_COMPARE(data.occlusionTextureCoordinates(), 6); + CORRADE_COMPARE(data.emissiveColor(), 0x111111_rgbf); + CORRADE_COMPARE(data.emissiveTextureMatrix(), Matrix3::scaling({0.75f, 0.5f})); + CORRADE_COMPARE(data.emissiveTexture(), 5); + CORRADE_COMPARE(data.emissiveTextureCoordinates(), 7); + + CORRADE_COMPARE(data.textureMatrix(), Matrix3{}); + CORRADE_COMPARE(data.textureCoordinates(), 0); +} + +void MaterialDataTest::pbrMetallicRoughnessAccessTexturedDefaults() { + PbrMetallicRoughnessMaterialData data{{}, { + {MaterialAttribute::BaseColorTexture, 1u}, + {MaterialAttribute::MetalnessTexture, 2u}, + {MaterialAttribute::RoughnessTexture, 3u}, + {MaterialAttribute::NormalTexture, 4u}, + {MaterialAttribute::OcclusionTexture, 5u}, + {MaterialAttribute::EmissiveTexture, 6u} + }}; + + CORRADE_VERIFY(data.hasMetalnessTexture()); + CORRADE_VERIFY(data.hasRoughnessTexture()); + CORRADE_VERIFY(!data.hasMetallicRoughnessTexture()); + CORRADE_VERIFY(!data.hasRoughnessMetallicOcclusionTexture()); + CORRADE_VERIFY(!data.hasOcclusionRoughnessMetallicTexture()); + CORRADE_VERIFY(!data.hasNormalRoughnessMetallicTexture()); + CORRADE_VERIFY(!data.hasTextureTransformation()); + CORRADE_VERIFY(!data.hasTextureCoordinates()); + CORRADE_COMPARE(data.baseColor(), 0xffffffff_rgbaf); + CORRADE_COMPARE(data.baseColorTexture(), 1); + CORRADE_COMPARE(data.baseColorTextureMatrix(), Matrix3{}); + CORRADE_COMPARE(data.baseColorTextureCoordinates(), 0); + CORRADE_COMPARE(data.metalness(), 1.0f); + CORRADE_COMPARE(data.metalnessTexture(), 2); + CORRADE_COMPARE(data.metalnessTextureSwizzle(), MaterialTextureSwizzle::R); + CORRADE_COMPARE(data.metalnessTextureMatrix(), Matrix3{}); + CORRADE_COMPARE(data.metalnessTextureCoordinates(), 0); + CORRADE_COMPARE(data.roughness(), 1.0f); + CORRADE_COMPARE(data.roughnessTexture(), 3); + CORRADE_COMPARE(data.roughnessTextureSwizzle(), MaterialTextureSwizzle::R); + CORRADE_COMPARE(data.roughnessTextureMatrix(), Matrix3{}); + CORRADE_COMPARE(data.roughnessTextureCoordinates(), 0); + CORRADE_COMPARE(data.normalTexture(), 4); + CORRADE_COMPARE(data.normalTextureSwizzle(), MaterialTextureSwizzle::RGB); + CORRADE_COMPARE(data.normalTextureMatrix(), Matrix3{}); + CORRADE_COMPARE(data.normalTextureCoordinates(), 0); + CORRADE_COMPARE(data.occlusionTexture(), 5); + CORRADE_COMPARE(data.occlusionTextureSwizzle(), MaterialTextureSwizzle::R); + CORRADE_COMPARE(data.occlusionTextureMatrix(), Matrix3{}); + CORRADE_COMPARE(data.occlusionTextureCoordinates(), 0); + CORRADE_COMPARE(data.emissiveColor(), 0x000000_rgbf); + CORRADE_COMPARE(data.emissiveTexture(), 6); + CORRADE_COMPARE(data.emissiveTextureMatrix(), Matrix3{}); + CORRADE_COMPARE(data.emissiveTextureCoordinates(), 0); + + CORRADE_COMPARE(data.textureMatrix(), Matrix3{}); + CORRADE_COMPARE(data.textureCoordinates(), 0); +} + +void MaterialDataTest::pbrMetallicRoughnessAccessTexturedSingleMatrixCoordinates() { + PbrMetallicRoughnessMaterialData data{{}, { + {MaterialAttribute::BaseColorTexture, 1u}, + {MaterialAttribute::MetalnessTexture, 2u}, + {MaterialAttribute::RoughnessTexture, 3u}, + {MaterialAttribute::NormalTexture, 4u}, + {MaterialAttribute::OcclusionTexture, 5u}, + {MaterialAttribute::EmissiveTexture, 6u}, + {MaterialAttribute::TextureMatrix, Matrix3::scaling({0.5f, 0.5f})}, + {MaterialAttribute::TextureCoordinates, 7u} + }}; + + CORRADE_COMPARE(data.baseColorTextureMatrix(), Matrix3::scaling({0.5f, 0.5f})); + CORRADE_COMPARE(data.baseColorTextureCoordinates(), 7); + CORRADE_COMPARE(data.metalnessTextureMatrix(), Matrix3::scaling({0.5f, 0.5f})); + CORRADE_COMPARE(data.metalnessTextureCoordinates(), 7); + CORRADE_COMPARE(data.roughnessTextureMatrix(), Matrix3::scaling({0.5f, 0.5f})); + CORRADE_COMPARE(data.roughnessTextureCoordinates(), 7); + CORRADE_COMPARE(data.normalTextureMatrix(), Matrix3::scaling({0.5f, 0.5f})); + CORRADE_COMPARE(data.normalTextureCoordinates(), 7); + CORRADE_COMPARE(data.occlusionTextureMatrix(), Matrix3::scaling({0.5f, 0.5f})); + CORRADE_COMPARE(data.occlusionTextureCoordinates(), 7); + CORRADE_COMPARE(data.emissiveTextureMatrix(), Matrix3::scaling({0.5f, 0.5f})); + CORRADE_COMPARE(data.emissiveTextureCoordinates(), 7); + + CORRADE_COMPARE(data.textureMatrix(), Matrix3::scaling({0.5f, 0.5f})); + CORRADE_COMPARE(data.textureCoordinates(), 7); +} + +void MaterialDataTest::pbrMetallicRoughnessAccessTexturedImplicitPackedMetallicRoughness() { + /* Just the texture ID, the rest is implicit */ + { + PbrMetallicRoughnessMaterialData data{{}, { + {MaterialAttribute::MetallicRoughnessTexture, 2u}, + }}; + CORRADE_VERIFY(data.hasMetallicRoughnessTexture()); + CORRADE_COMPARE(data.metalnessTexture(), 2); + CORRADE_COMPARE(data.metalnessTextureSwizzle(), MaterialTextureSwizzle::R); + CORRADE_COMPARE(data.metalnessTextureMatrix(), Matrix3{}); + CORRADE_COMPARE(data.metalnessTextureCoordinates(), 0); + CORRADE_COMPARE(data.roughnessTexture(), 2); + CORRADE_COMPARE(data.roughnessTextureSwizzle(), MaterialTextureSwizzle::G); + CORRADE_COMPARE(data.roughnessTextureMatrix(), Matrix3{}); + CORRADE_COMPARE(data.roughnessTextureCoordinates(), 0); + + /* Explicit parameters for everything, but all the same */ + } { + PbrMetallicRoughnessMaterialData data{{}, { + {MaterialAttribute::MetallicRoughnessTexture, 2u}, + {MaterialAttribute::MetalnessTextureMatrix, Matrix3::scaling({0.5f, 0.5f})}, + {MaterialAttribute::MetalnessTextureSwizzle, MaterialTextureSwizzle::R}, + {MaterialAttribute::MetalnessTextureCoordinates, 3u}, + {MaterialAttribute::RoughnessTextureSwizzle, MaterialTextureSwizzle::G}, + {MaterialAttribute::RoughnessTextureMatrix, Matrix3::scaling({0.5f, 0.5f})}, + {MaterialAttribute::RoughnessTextureCoordinates, 3u} + }}; + CORRADE_VERIFY(data.hasMetallicRoughnessTexture()); + CORRADE_COMPARE(data.metalnessTexture(), 2); + CORRADE_COMPARE(data.metalnessTextureSwizzle(), MaterialTextureSwizzle::R); + CORRADE_COMPARE(data.metalnessTextureMatrix(), Matrix3::scaling({0.5f, 0.5f})); + CORRADE_COMPARE(data.metalnessTextureCoordinates(), 3); + CORRADE_COMPARE(data.roughnessTexture(), 2); + CORRADE_COMPARE(data.roughnessTextureSwizzle(), MaterialTextureSwizzle::G); + CORRADE_COMPARE(data.roughnessTextureMatrix(), Matrix3::scaling({0.5f, 0.5f})); + CORRADE_COMPARE(data.roughnessTextureCoordinates(), 3); + + /* Swizzle is ignored when the combined texture is specified, so this is + fine */ + } { + PbrMetallicRoughnessMaterialData data{{}, { + {MaterialAttribute::MetallicRoughnessTexture, 2u}, + {MaterialAttribute::RoughnessTextureSwizzle, MaterialTextureSwizzle::B}, + }}; + CORRADE_VERIFY(data.hasMetallicRoughnessTexture()); + + /* Unexpected texture matrix */ + } { + PbrMetallicRoughnessMaterialData data{{}, { + {MaterialAttribute::MetallicRoughnessTexture, 2u}, + {MaterialAttribute::MetalnessTextureMatrix, Matrix3::scaling({0.5f, 1.0f})}, + }}; + CORRADE_VERIFY(!data.hasMetallicRoughnessTexture()); + + /* Unexpected texture coordinates */ + } { + PbrMetallicRoughnessMaterialData data{{}, { + {MaterialAttribute::MetallicRoughnessTexture, 2u}, + {MaterialAttribute::RoughnessTextureCoordinates, 1u}, + }}; + CORRADE_VERIFY(!data.hasMetallicRoughnessTexture()); + } +} + +void MaterialDataTest::pbrMetallicRoughnessAccessTexturedExplicitPackedMetallicRoughness() { + /* Just the texture IDs and swizzles, the rest is implicit */ + { + PbrMetallicRoughnessMaterialData data{{}, { + {MaterialAttribute::MetalnessTexture, 2u}, + {MaterialAttribute::RoughnessTexture, 2u}, + {MaterialAttribute::RoughnessTextureSwizzle, MaterialTextureSwizzle::G} + }}; + CORRADE_VERIFY(data.hasMetallicRoughnessTexture()); + + /* Explicit parameters for everything, but all the same */ + } { + PbrMetallicRoughnessMaterialData data{{}, { + {MaterialAttribute::MetalnessTexture, 2u}, + {MaterialAttribute::MetalnessTextureMatrix, Matrix3::scaling({0.5f, 0.5f})}, + {MaterialAttribute::MetalnessTextureSwizzle, MaterialTextureSwizzle::R}, + {MaterialAttribute::MetalnessTextureCoordinates, 3u}, + {MaterialAttribute::RoughnessTexture, 2u}, + {MaterialAttribute::RoughnessTextureSwizzle, MaterialTextureSwizzle::G}, + {MaterialAttribute::RoughnessTextureMatrix, Matrix3::scaling({0.5f, 0.5f})}, + {MaterialAttribute::RoughnessTextureCoordinates, 3u} + }}; + CORRADE_VERIFY(data.hasMetallicRoughnessTexture()); + + /* One texture missing */ + } { + PbrMetallicRoughnessMaterialData data{{}, { + {MaterialAttribute::RoughnessTexture, 2u}, + {MaterialAttribute::RoughnessTextureSwizzle, MaterialTextureSwizzle::B}, + }}; + CORRADE_VERIFY(!data.hasMetallicRoughnessTexture()); + + /* Unexpected swizzle */ + } { + PbrMetallicRoughnessMaterialData data{{}, { + {MaterialAttribute::MetalnessTexture, 2u}, + {MaterialAttribute::RoughnessTexture, 2u}, + {MaterialAttribute::RoughnessTextureSwizzle, MaterialTextureSwizzle::B}, + }}; + CORRADE_VERIFY(!data.hasMetallicRoughnessTexture()); + + /* Unexpected texture matrix */ + } { + PbrMetallicRoughnessMaterialData data{{}, { + {MaterialAttribute::MetalnessTexture, 2u}, + {MaterialAttribute::MetalnessTextureMatrix, Matrix3::scaling({0.5f, 1.0f})}, + {MaterialAttribute::RoughnessTexture, 2u}, + {MaterialAttribute::RoughnessTextureSwizzle, MaterialTextureSwizzle::G}, + }}; + CORRADE_VERIFY(!data.hasMetallicRoughnessTexture()); + + /* Unexpected texture coordinates */ + } { + PbrMetallicRoughnessMaterialData data{{}, { + {MaterialAttribute::MetalnessTexture, 2u}, + {MaterialAttribute::RoughnessTexture, 2u}, + {MaterialAttribute::RoughnessTextureSwizzle, MaterialTextureSwizzle::G}, + {MaterialAttribute::RoughnessTextureCoordinates, 1u}, + }}; + CORRADE_VERIFY(!data.hasMetallicRoughnessTexture()); + } +} + +void MaterialDataTest::pbrMetallicRoughnessAccessTexturedExplicitPackedRoughnessMetallicOcclusion() { + /* Just the texture IDs and swizzles, the rest is implicit */ + { + PbrMetallicRoughnessMaterialData data{{}, { + {MaterialAttribute::RoughnessTexture, 2u}, + {MaterialAttribute::MetalnessTexture, 2u}, + {MaterialAttribute::MetalnessTextureSwizzle, MaterialTextureSwizzle::G}, + {MaterialAttribute::OcclusionTexture, 2u}, + {MaterialAttribute::OcclusionTextureSwizzle, MaterialTextureSwizzle::B}, + }}; + CORRADE_VERIFY(data.hasRoughnessMetallicOcclusionTexture()); + + /* Explicit parameters for everything, but all the same */ + } { + PbrMetallicRoughnessMaterialData data{{}, { + {MaterialAttribute::RoughnessTexture, 2u}, + {MaterialAttribute::RoughnessTextureSwizzle, MaterialTextureSwizzle::R}, + {MaterialAttribute::RoughnessTextureMatrix, Matrix3::scaling({0.5f, 0.5f})}, + {MaterialAttribute::RoughnessTextureCoordinates, 3u}, + {MaterialAttribute::MetalnessTexture, 2u}, + {MaterialAttribute::MetalnessTextureMatrix, Matrix3::scaling({0.5f, 0.5f})}, + {MaterialAttribute::MetalnessTextureSwizzle, MaterialTextureSwizzle::G}, + {MaterialAttribute::MetalnessTextureCoordinates, 3u}, + {MaterialAttribute::OcclusionTexture, 2u}, + {MaterialAttribute::OcclusionTextureSwizzle, MaterialTextureSwizzle::B}, + {MaterialAttribute::OcclusionTextureMatrix, Matrix3::scaling({0.5f, 0.5f})}, + {MaterialAttribute::OcclusionTextureCoordinates, 3u} + }}; + CORRADE_VERIFY(data.hasRoughnessMetallicOcclusionTexture()); + + /* One texture missing */ + } { + PbrMetallicRoughnessMaterialData data{{}, { + {MaterialAttribute::RoughnessTexture, 2u}, + {MaterialAttribute::MetalnessTexture, 2u}, + {MaterialAttribute::MetalnessTextureSwizzle, MaterialTextureSwizzle::G}, + {MaterialAttribute::OcclusionTextureSwizzle, MaterialTextureSwizzle::B}, + }}; + CORRADE_VERIFY(!data.hasRoughnessMetallicOcclusionTexture()); + + /* Unexpected swizzle */ + } { + PbrMetallicRoughnessMaterialData data{{}, { + {MaterialAttribute::RoughnessTexture, 2u}, + {MaterialAttribute::RoughnessTextureSwizzle, MaterialTextureSwizzle::A}, + {MaterialAttribute::MetalnessTexture, 2u}, + {MaterialAttribute::MetalnessTextureSwizzle, MaterialTextureSwizzle::G}, + {MaterialAttribute::OcclusionTexture, 2u}, + {MaterialAttribute::OcclusionTextureSwizzle, MaterialTextureSwizzle::B}, + }}; + CORRADE_VERIFY(!data.hasRoughnessMetallicOcclusionTexture()); + + /* Unexpected texture matrix */ + } { + PbrMetallicRoughnessMaterialData data{{}, { + {MaterialAttribute::RoughnessTexture, 2u}, + {MaterialAttribute::MetalnessTexture, 2u}, + {MaterialAttribute::MetalnessTextureSwizzle, MaterialTextureSwizzle::G}, + {MaterialAttribute::OcclusionTexture, 2u}, + {MaterialAttribute::OcclusionTextureSwizzle, MaterialTextureSwizzle::B}, + {MaterialAttribute::OcclusionTextureMatrix, Matrix3::scaling({0.5f, 1.0f})}, + }}; + CORRADE_VERIFY(!data.hasRoughnessMetallicOcclusionTexture()); + + /* Unexpected texture coordinates */ + } { + PbrMetallicRoughnessMaterialData data{{}, { + {MaterialAttribute::RoughnessTexture, 2u}, + {MaterialAttribute::MetalnessTexture, 2u}, + {MaterialAttribute::MetalnessTextureSwizzle, MaterialTextureSwizzle::G}, + {MaterialAttribute::OcclusionTexture, 2u}, + {MaterialAttribute::OcclusionTextureSwizzle, MaterialTextureSwizzle::B}, + {MaterialAttribute::MetalnessTextureCoordinates, 1u}, + }}; + CORRADE_VERIFY(!data.hasRoughnessMetallicOcclusionTexture()); + } +} + +void MaterialDataTest::pbrMetallicRoughnessAccessTexturedExplicitPackedOcclusionRoughnessMetallic() { + /* Just the texture IDs and swizzles, the rest is implicit */ + { + PbrMetallicRoughnessMaterialData data{{}, { + {MaterialAttribute::OcclusionTexture, 2u}, + {MaterialAttribute::RoughnessTexture, 2u}, + {MaterialAttribute::RoughnessTextureSwizzle, MaterialTextureSwizzle::G}, + {MaterialAttribute::MetalnessTexture, 2u}, + {MaterialAttribute::MetalnessTextureSwizzle, MaterialTextureSwizzle::B}, + }}; + CORRADE_VERIFY(data.hasOcclusionRoughnessMetallicTexture()); + + /* Explicit parameters for everything, but all the same */ + } { + PbrMetallicRoughnessMaterialData data{{}, { + {MaterialAttribute::OcclusionTexture, 2u}, + {MaterialAttribute::OcclusionTextureSwizzle, MaterialTextureSwizzle::R}, + {MaterialAttribute::OcclusionTextureMatrix, Matrix3::scaling({0.5f, 0.5f})}, + {MaterialAttribute::OcclusionTextureCoordinates, 3u}, + {MaterialAttribute::RoughnessTexture, 2u}, + {MaterialAttribute::RoughnessTextureSwizzle, MaterialTextureSwizzle::G}, + {MaterialAttribute::RoughnessTextureMatrix, Matrix3::scaling({0.5f, 0.5f})}, + {MaterialAttribute::RoughnessTextureCoordinates, 3u}, + {MaterialAttribute::MetalnessTexture, 2u}, + {MaterialAttribute::MetalnessTextureMatrix, Matrix3::scaling({0.5f, 0.5f})}, + {MaterialAttribute::MetalnessTextureSwizzle, MaterialTextureSwizzle::B}, + {MaterialAttribute::MetalnessTextureCoordinates, 3u} + }}; + CORRADE_VERIFY(data.hasOcclusionRoughnessMetallicTexture()); + + /* One texture missing */ + } { + PbrMetallicRoughnessMaterialData data{{}, { + {MaterialAttribute::RoughnessTexture, 2u}, + {MaterialAttribute::RoughnessTextureSwizzle, MaterialTextureSwizzle::G}, + {MaterialAttribute::MetalnessTexture, 2u}, + {MaterialAttribute::MetalnessTextureSwizzle, MaterialTextureSwizzle::B}, + }}; + CORRADE_VERIFY(!data.hasOcclusionRoughnessMetallicTexture()); + + /* Unexpected swizzle */ + } { + PbrMetallicRoughnessMaterialData data{{}, { + {MaterialAttribute::OcclusionTexture, 2u}, + {MaterialAttribute::OcclusionTextureSwizzle, MaterialTextureSwizzle::A}, + {MaterialAttribute::RoughnessTexture, 2u}, + {MaterialAttribute::RoughnessTextureSwizzle, MaterialTextureSwizzle::G}, + {MaterialAttribute::MetalnessTexture, 2u}, + {MaterialAttribute::MetalnessTextureSwizzle, MaterialTextureSwizzle::B}, + }}; + CORRADE_VERIFY(!data.hasOcclusionRoughnessMetallicTexture()); + + /* Unexpected texture matrix */ + } { + PbrMetallicRoughnessMaterialData data{{}, { + {MaterialAttribute::OcclusionTexture, 2u}, + {MaterialAttribute::OcclusionTextureMatrix, Matrix3::scaling({0.5f, 1.0f})}, + {MaterialAttribute::RoughnessTexture, 2u}, + {MaterialAttribute::RoughnessTextureSwizzle, MaterialTextureSwizzle::G}, + {MaterialAttribute::MetalnessTexture, 2u}, + {MaterialAttribute::MetalnessTextureSwizzle, MaterialTextureSwizzle::B}, + }}; + CORRADE_VERIFY(!data.hasOcclusionRoughnessMetallicTexture()); + + /* Unexpected texture coordinates */ + } { + PbrMetallicRoughnessMaterialData data{{}, { + {MaterialAttribute::OcclusionTexture, 2u}, + {MaterialAttribute::RoughnessTexture, 2u}, + {MaterialAttribute::RoughnessTextureSwizzle, MaterialTextureSwizzle::G}, + {MaterialAttribute::MetalnessTexture, 2u}, + {MaterialAttribute::MetalnessTextureSwizzle, MaterialTextureSwizzle::B}, + {MaterialAttribute::MetalnessTextureCoordinates, 1u}, + }}; + CORRADE_VERIFY(!data.hasOcclusionRoughnessMetallicTexture()); + } +} + +void MaterialDataTest::pbrMetallicRoughnessAccessTexturedExplicitPackedNormalRoughnessMetallic() { + /* Just the texture IDs and swizzles, the rest is implicit */ + { + PbrMetallicRoughnessMaterialData data{{}, { + {MaterialAttribute::NormalTexture, 2u}, + {MaterialAttribute::NormalTextureSwizzle, MaterialTextureSwizzle::RG}, + {MaterialAttribute::RoughnessTexture, 2u}, + {MaterialAttribute::RoughnessTextureSwizzle, MaterialTextureSwizzle::B}, + {MaterialAttribute::MetalnessTexture, 2u}, + {MaterialAttribute::MetalnessTextureSwizzle, MaterialTextureSwizzle::A} + }}; + CORRADE_VERIFY(data.hasNormalRoughnessMetallicTexture()); + + /* Explicit parameters for everything, but all the same */ + } { + PbrMetallicRoughnessMaterialData data{{}, { + {MaterialAttribute::NormalTexture, 2u}, + {MaterialAttribute::NormalTextureSwizzle, MaterialTextureSwizzle::RG}, + {MaterialAttribute::NormalTextureMatrix, Matrix3::scaling({0.5f, 0.5f})}, + {MaterialAttribute::NormalTextureCoordinates, 3u}, + {MaterialAttribute::RoughnessTexture, 2u}, + {MaterialAttribute::RoughnessTextureSwizzle, MaterialTextureSwizzle::B}, + {MaterialAttribute::RoughnessTextureMatrix, Matrix3::scaling({0.5f, 0.5f})}, + {MaterialAttribute::RoughnessTextureCoordinates, 3u}, + {MaterialAttribute::MetalnessTexture, 2u}, + {MaterialAttribute::MetalnessTextureMatrix, Matrix3::scaling({0.5f, 0.5f})}, + {MaterialAttribute::MetalnessTextureSwizzle, MaterialTextureSwizzle::A}, + {MaterialAttribute::MetalnessTextureCoordinates, 3u} + }}; + CORRADE_VERIFY(data.hasNormalRoughnessMetallicTexture()); + + /* One texture missing */ + } { + PbrMetallicRoughnessMaterialData data{{}, { + {MaterialAttribute::RoughnessTexture, 2u}, + {MaterialAttribute::RoughnessTextureSwizzle, MaterialTextureSwizzle::B}, + {MaterialAttribute::MetalnessTexture, 2u}, + {MaterialAttribute::MetalnessTextureSwizzle, MaterialTextureSwizzle::A}, + }}; + CORRADE_VERIFY(!data.hasNormalRoughnessMetallicTexture()); + + /* Unexpected swizzle */ + } { + PbrMetallicRoughnessMaterialData data{{}, { + {MaterialAttribute::NormalTexture, 2u}, + {MaterialAttribute::NormalTextureSwizzle, MaterialTextureSwizzle::RGB}, + {MaterialAttribute::RoughnessTexture, 2u}, + {MaterialAttribute::RoughnessTextureSwizzle, MaterialTextureSwizzle::B}, + {MaterialAttribute::MetalnessTexture, 2u}, + {MaterialAttribute::MetalnessTextureSwizzle, MaterialTextureSwizzle::A}, + }}; + CORRADE_VERIFY(!data.hasNormalRoughnessMetallicTexture()); + + /* Unexpected texture matrix */ + } { + PbrMetallicRoughnessMaterialData data{{}, { + {MaterialAttribute::NormalTexture, 2u}, + {MaterialAttribute::NormalTextureSwizzle, MaterialTextureSwizzle::RG}, + {MaterialAttribute::NormalTextureMatrix, Matrix3::scaling({0.5f, 1.0f})}, + {MaterialAttribute::RoughnessTexture, 2u}, + {MaterialAttribute::RoughnessTextureSwizzle, MaterialTextureSwizzle::B}, + {MaterialAttribute::MetalnessTexture, 2u}, + {MaterialAttribute::MetalnessTextureSwizzle, MaterialTextureSwizzle::A}, + }}; + CORRADE_VERIFY(!data.hasNormalRoughnessMetallicTexture()); + + /* Unexpected texture coordinates */ + } { + PbrMetallicRoughnessMaterialData data{{}, { + {MaterialAttribute::NormalTexture, 2u}, + {MaterialAttribute::NormalTextureSwizzle, MaterialTextureSwizzle::RG}, + {MaterialAttribute::RoughnessTexture, 2u}, + {MaterialAttribute::RoughnessTextureSwizzle, MaterialTextureSwizzle::B}, + {MaterialAttribute::MetalnessTexture, 2u}, + {MaterialAttribute::MetalnessTextureSwizzle, MaterialTextureSwizzle::A}, + {MaterialAttribute::MetalnessTextureCoordinates, 1u}, + }}; + CORRADE_VERIFY(!data.hasNormalRoughnessMetallicTexture()); + } +} + +void MaterialDataTest::pbrMetallicRoughnessAccessInvalidTextures() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + PbrMetallicRoughnessMaterialData data{{}, {}}; + + std::ostringstream out; + Error redirectError{&out}; + data.baseColorTexture(); + data.baseColorTextureMatrix(); + data.baseColorTextureCoordinates(); + data.metalnessTexture(); + data.metalnessTextureSwizzle(); + data.metalnessTextureMatrix(); + data.metalnessTextureCoordinates(); + data.roughnessTexture(); + data.roughnessTextureSwizzle(); + data.roughnessTextureMatrix(); + data.roughnessTextureCoordinates(); + data.normalTexture(); + data.normalTextureSwizzle(); + data.normalTextureMatrix(); + data.normalTextureCoordinates(); + data.occlusionTexture(); + data.occlusionTextureSwizzle(); + data.occlusionTextureMatrix(); + data.occlusionTextureCoordinates(); + data.emissiveTexture(); + data.emissiveTextureMatrix(); + data.emissiveTextureCoordinates(); + CORRADE_COMPARE(out.str(), + "Trade::MaterialData::attribute(): attribute BaseColorTexture not found in layer 0\n" + "Trade::PbrMetallicRoughnessMaterialData::baseColorTextureMatrix(): the material doesn't have a base color texture\n" + "Trade::PbrMetallicRoughnessMaterialData::baseColorTextureCoordinates(): the material doesn't have a base color texture\n" + "Trade::MaterialData::attribute(): attribute MetalnessTexture not found in layer 0\n" + "Trade::PbrMetallicRoughnessMaterialData::metalnessTextureSwizzle(): the material doesn't have a metalness texture\n" + "Trade::PbrMetallicRoughnessMaterialData::metalnessTextureMatrix(): the material doesn't have a metalness texture\n" + "Trade::PbrMetallicRoughnessMaterialData::metalnessTextureCoordinates(): the material doesn't have a metalness texture\n" + "Trade::MaterialData::attribute(): attribute RoughnessTexture not found in layer 0\n" + "Trade::PbrMetallicRoughnessMaterialData::roughnessTextureSwizzle(): the material doesn't have a roughness texture\n" + "Trade::PbrMetallicRoughnessMaterialData::roughnessTextureMatrix(): the material doesn't have a roughness texture\n" + "Trade::PbrMetallicRoughnessMaterialData::roughnessTextureCoordinates(): the material doesn't have a roughness texture\n" + "Trade::MaterialData::attribute(): attribute NormalTexture not found in layer 0\n" + "Trade::PbrMetallicRoughnessMaterialData::normalTextureSwizzle(): the material doesn't have a normal texture\n" + "Trade::PbrMetallicRoughnessMaterialData::normalTextureMatrix(): the material doesn't have a normal texture\n" + "Trade::PbrMetallicRoughnessMaterialData::normalTextureCoordinates(): the material doesn't have a normal texture\n" + "Trade::MaterialData::attribute(): attribute OcclusionTexture not found in layer 0\n" + "Trade::PbrMetallicRoughnessMaterialData::occlusionTextureSwizzle(): the material doesn't have an occlusion texture\n" + "Trade::PbrMetallicRoughnessMaterialData::occlusionTextureMatrix(): the material doesn't have an occlusion texture\n" + "Trade::PbrMetallicRoughnessMaterialData::occlusionTextureCoordinates(): the material doesn't have an occlusion texture\n" + "Trade::MaterialData::attribute(): attribute EmissiveTexture not found in layer 0\n" + "Trade::PbrMetallicRoughnessMaterialData::emissiveTextureMatrix(): the material doesn't have an emissive texture\n" + "Trade::PbrMetallicRoughnessMaterialData::emissiveTextureCoordinates(): the material doesn't have an emissive texture\n"); +} + +void MaterialDataTest::pbrSpecularGlossinessAccess() { + MaterialData base{MaterialType::PbrSpecularGlossiness, { + {MaterialAttribute::DiffuseColor, 0xccffbbff_rgbaf}, + {MaterialAttribute::SpecularColor, 0xff336600_rgbaf}, + {MaterialAttribute::Glossiness, 0.79f}, + {MaterialAttribute::EmissiveColor, 0x111111_rgbf} + }}; + + CORRADE_COMPARE(base.types(), MaterialType::PbrSpecularGlossiness); + const PbrSpecularGlossinessMaterialData& data = static_cast(base); + + CORRADE_VERIFY(!data.hasSpecularTexture()); + CORRADE_VERIFY(!data.hasGlossinessTexture()); + CORRADE_VERIFY(!data.hasSpecularGlossinessTexture()); + CORRADE_VERIFY(!data.hasTextureTransformation()); + CORRADE_VERIFY(!data.hasTextureCoordinates()); + CORRADE_COMPARE(data.diffuseColor(), 0xccffbbff_rgbaf); + CORRADE_COMPARE(data.specularColor(), 0xff336600_rgbaf); + CORRADE_COMPARE(data.glossiness(), 0.79f); +} + +void MaterialDataTest::pbrSpecularGlossinessAccessDefaults() { + MaterialData base{{}, {}}; + + CORRADE_COMPARE(base.types(), MaterialTypes{}); + /* Casting is fine even if the type doesn't include PbrMetallicRoughness */ + const PbrSpecularGlossinessMaterialData& data = static_cast(base); + + CORRADE_VERIFY(!data.hasSpecularTexture()); + CORRADE_VERIFY(!data.hasGlossinessTexture()); + CORRADE_VERIFY(!data.hasSpecularGlossinessTexture()); + CORRADE_VERIFY(!data.hasTextureTransformation()); + CORRADE_VERIFY(!data.hasTextureCoordinates()); + CORRADE_COMPARE(data.diffuseColor(), 0xffffffff_rgbaf); + CORRADE_COMPARE(data.specularColor(), 0xffffff00_rgbaf); + CORRADE_COMPARE(data.glossiness(), 1.0f); +} + +void MaterialDataTest::pbrSpecularGlossinessAccessTextured() { + PbrSpecularGlossinessMaterialData data{{}, { + {MaterialAttribute::DiffuseColor, 0xccffbbff_rgbaf}, + {MaterialAttribute::DiffuseTexture, 0u}, + {MaterialAttribute::DiffuseTextureMatrix, Matrix3::scaling({0.5f, 1.0f})}, + {MaterialAttribute::DiffuseTextureCoordinates, 2u}, + {MaterialAttribute::SpecularColor, 0x33556600_rgbaf}, + {MaterialAttribute::SpecularTexture, 1u}, + {MaterialAttribute::SpecularTextureSwizzle, MaterialTextureSwizzle::RGBA}, + {MaterialAttribute::SpecularTextureMatrix, Matrix3::scaling({0.5f, 0.5f})}, + {MaterialAttribute::SpecularTextureCoordinates, 3u}, + {MaterialAttribute::Glossiness, 0.79f}, + {MaterialAttribute::GlossinessTexture, 2u}, + {MaterialAttribute::GlossinessTextureSwizzle, MaterialTextureSwizzle::A}, + {MaterialAttribute::GlossinessTextureMatrix, Matrix3::scaling({1.0f, 1.0f})}, + {MaterialAttribute::GlossinessTextureCoordinates, 4u}, + {MaterialAttribute::NormalTexture, 3u}, + {MaterialAttribute::NormalTextureSwizzle, MaterialTextureSwizzle::BA}, + {MaterialAttribute::NormalTextureMatrix, Matrix3::scaling({1.0f, 0.5f})}, + {MaterialAttribute::NormalTextureCoordinates, 5u}, + {MaterialAttribute::OcclusionTexture, 4u}, + {MaterialAttribute::OcclusionTextureSwizzle, MaterialTextureSwizzle::B}, + {MaterialAttribute::OcclusionTextureMatrix, Matrix3::scaling({1.0f, 0.75f})}, + {MaterialAttribute::OcclusionTextureCoordinates, 6u}, + {MaterialAttribute::EmissiveColor, 0x111111_rgbf}, + {MaterialAttribute::EmissiveTexture, 5u}, + {MaterialAttribute::EmissiveTextureMatrix, Matrix3::scaling({0.75f, 0.5f})}, + {MaterialAttribute::EmissiveTextureCoordinates, 7u} + }}; + + CORRADE_VERIFY(data.hasSpecularTexture()); + CORRADE_VERIFY(data.hasGlossinessTexture()); + CORRADE_VERIFY(!data.hasSpecularGlossinessTexture()); + CORRADE_VERIFY(data.hasTextureTransformation()); + CORRADE_VERIFY(data.hasTextureCoordinates()); + CORRADE_COMPARE(data.diffuseColor(), 0xccffbbff_rgbaf); + CORRADE_COMPARE(data.diffuseTexture(), 0); + CORRADE_COMPARE(data.diffuseTextureMatrix(), Matrix3::scaling({0.5f, 1.0f})); + CORRADE_COMPARE(data.diffuseTextureCoordinates(), 2); + CORRADE_COMPARE(data.specularColor(), 0x33556600_rgbaf); + CORRADE_COMPARE(data.specularTexture(), 1); + CORRADE_COMPARE(data.specularTextureSwizzle(), MaterialTextureSwizzle::RGBA); + CORRADE_COMPARE(data.specularTextureMatrix(), Matrix3::scaling({0.5f, 0.5f})); + CORRADE_COMPARE(data.specularTextureCoordinates(), 3); + CORRADE_COMPARE(data.glossiness(), 0.79f); + CORRADE_COMPARE(data.glossinessTexture(), 2); + CORRADE_COMPARE(data.glossinessTextureSwizzle(), MaterialTextureSwizzle::A); + CORRADE_COMPARE(data.glossinessTextureMatrix(), Matrix3::scaling({1.0f, 1.0f})); + CORRADE_COMPARE(data.glossinessTextureCoordinates(), 4); + CORRADE_COMPARE(data.normalTexture(), 3); + CORRADE_COMPARE(data.normalTextureSwizzle(), MaterialTextureSwizzle::BA); + CORRADE_COMPARE(data.normalTextureMatrix(), Matrix3::scaling({1.0f, 0.5f})); + CORRADE_COMPARE(data.normalTextureCoordinates(), 5); + CORRADE_COMPARE(data.occlusionTexture(), 4); + CORRADE_COMPARE(data.occlusionTextureMatrix(), Matrix3::scaling({1.0f, 0.75f})); + CORRADE_COMPARE(data.occlusionTextureSwizzle(), MaterialTextureSwizzle::B); + CORRADE_COMPARE(data.occlusionTextureCoordinates(), 6); + CORRADE_COMPARE(data.emissiveColor(), 0x111111_rgbf); + CORRADE_COMPARE(data.emissiveTextureMatrix(), Matrix3::scaling({0.75f, 0.5f})); + CORRADE_COMPARE(data.emissiveTexture(), 5); + CORRADE_COMPARE(data.emissiveTextureCoordinates(), 7); + + CORRADE_COMPARE(data.textureMatrix(), Matrix3{}); + CORRADE_COMPARE(data.textureCoordinates(), 0); +} + +void MaterialDataTest::pbrSpecularGlossinessAccessTexturedDefaults() { + PbrSpecularGlossinessMaterialData data{{}, { + {MaterialAttribute::DiffuseTexture, 1u}, + {MaterialAttribute::SpecularTexture, 2u}, + {MaterialAttribute::GlossinessTexture, 3u}, + {MaterialAttribute::NormalTexture, 4u}, + {MaterialAttribute::OcclusionTexture, 5u}, + {MaterialAttribute::EmissiveTexture, 6u} + }}; + + CORRADE_VERIFY(data.hasSpecularTexture()); + CORRADE_VERIFY(data.hasGlossinessTexture()); + CORRADE_VERIFY(!data.hasSpecularGlossinessTexture()); + CORRADE_VERIFY(!data.hasTextureTransformation()); + CORRADE_VERIFY(!data.hasTextureCoordinates()); + CORRADE_COMPARE(data.diffuseColor(), 0xffffffff_rgbaf); + CORRADE_COMPARE(data.diffuseTexture(), 1); + CORRADE_COMPARE(data.diffuseTextureMatrix(), Matrix3{}); + CORRADE_COMPARE(data.diffuseTextureCoordinates(), 0); + CORRADE_COMPARE(data.specularColor(), 0xffffff00_rgbaf); + CORRADE_COMPARE(data.specularTexture(), 2); + CORRADE_COMPARE(data.specularTextureSwizzle(), MaterialTextureSwizzle::RGB); + CORRADE_COMPARE(data.specularTextureMatrix(), Matrix3{}); + CORRADE_COMPARE(data.specularTextureCoordinates(), 0); + CORRADE_COMPARE(data.glossiness(), 1.0f); + CORRADE_COMPARE(data.glossinessTexture(), 3); + CORRADE_COMPARE(data.glossinessTextureSwizzle(), MaterialTextureSwizzle::R); + CORRADE_COMPARE(data.glossinessTextureMatrix(), Matrix3{}); + CORRADE_COMPARE(data.glossinessTextureCoordinates(), 0); + CORRADE_COMPARE(data.normalTexture(), 4); + CORRADE_COMPARE(data.normalTextureSwizzle(), MaterialTextureSwizzle::RGB); + CORRADE_COMPARE(data.normalTextureMatrix(), Matrix3{}); + CORRADE_COMPARE(data.normalTextureCoordinates(), 0); + CORRADE_COMPARE(data.occlusionTexture(), 5); + CORRADE_COMPARE(data.occlusionTextureMatrix(), Matrix3{}); + CORRADE_COMPARE(data.occlusionTextureSwizzle(), MaterialTextureSwizzle::R); + CORRADE_COMPARE(data.occlusionTextureCoordinates(), 0); + CORRADE_COMPARE(data.emissiveColor(), 0x000000_rgbf); + CORRADE_COMPARE(data.emissiveTextureMatrix(), Matrix3{}); + CORRADE_COMPARE(data.emissiveTexture(), 6); + CORRADE_COMPARE(data.emissiveTextureCoordinates(), 0); + + CORRADE_COMPARE(data.textureMatrix(), Matrix3{}); + CORRADE_COMPARE(data.textureCoordinates(), 0); +} + +void MaterialDataTest::pbrSpecularGlossinessAccessTexturedSingleMatrixCoordinates() { + PbrSpecularGlossinessMaterialData data{{}, { + {MaterialAttribute::DiffuseTexture, 1u}, + {MaterialAttribute::SpecularTexture, 2u}, + {MaterialAttribute::GlossinessTexture, 3u}, + {MaterialAttribute::NormalTexture, 4u}, + {MaterialAttribute::OcclusionTexture, 5u}, + {MaterialAttribute::EmissiveTexture, 6u}, + {MaterialAttribute::TextureMatrix, Matrix3::scaling({0.5f, 0.5f})}, + {MaterialAttribute::TextureCoordinates, 7u} + }}; + + CORRADE_VERIFY(data.hasTextureTransformation()); + CORRADE_VERIFY(data.hasTextureCoordinates()); + CORRADE_COMPARE(data.diffuseTextureMatrix(), Matrix3::scaling({0.5f, 0.5f})); + CORRADE_COMPARE(data.diffuseTextureCoordinates(), 7); + CORRADE_COMPARE(data.specularTextureMatrix(), Matrix3::scaling({0.5f, 0.5f})); + CORRADE_COMPARE(data.specularTextureCoordinates(), 7); + CORRADE_COMPARE(data.glossinessTextureMatrix(), Matrix3::scaling({0.5f, 0.5f})); + CORRADE_COMPARE(data.glossinessTextureCoordinates(), 7); + CORRADE_COMPARE(data.normalTextureMatrix(), Matrix3::scaling({0.5f, 0.5f})); + CORRADE_COMPARE(data.normalTextureCoordinates(), 7); + CORRADE_COMPARE(data.occlusionTextureMatrix(), Matrix3::scaling({0.5f, 0.5f})); + CORRADE_COMPARE(data.occlusionTextureCoordinates(), 7); + CORRADE_COMPARE(data.emissiveTextureMatrix(), Matrix3::scaling({0.5f, 0.5f})); + CORRADE_COMPARE(data.emissiveTextureCoordinates(), 7); + + CORRADE_COMPARE(data.textureMatrix(), Matrix3::scaling({0.5f, 0.5f})); + CORRADE_COMPARE(data.textureCoordinates(), 7); +} + +void MaterialDataTest::pbrSpecularGlossinessAccessTexturedImplicitPackedSpecularGlossiness() { + /* Just the texture ID, the rest is implicit */ + { + PbrSpecularGlossinessMaterialData data{{}, { + {MaterialAttribute::SpecularGlossinessTexture, 2u}, + }}; + CORRADE_VERIFY(data.hasSpecularGlossinessTexture()); + CORRADE_COMPARE(data.specularTexture(), 2); + CORRADE_COMPARE(data.specularTextureSwizzle(), MaterialTextureSwizzle::RGB); + CORRADE_COMPARE(data.specularTextureMatrix(), Matrix3{}); + CORRADE_COMPARE(data.specularTextureCoordinates(), 0); + CORRADE_COMPARE(data.glossinessTexture(), 2); + CORRADE_COMPARE(data.glossinessTextureSwizzle(), MaterialTextureSwizzle::A); + CORRADE_COMPARE(data.glossinessTextureMatrix(), Matrix3{}); + CORRADE_COMPARE(data.glossinessTextureCoordinates(), 0); + + /* Explicit parameters for everything, but all the same */ + } { + PbrSpecularGlossinessMaterialData data{{}, { + {MaterialAttribute::SpecularGlossinessTexture, 2u}, + {MaterialAttribute::SpecularTextureSwizzle, MaterialTextureSwizzle::RGB}, + {MaterialAttribute::SpecularTextureMatrix, Matrix3::scaling({0.5f, 0.5f})}, + {MaterialAttribute::SpecularTextureCoordinates, 3u}, + {MaterialAttribute::GlossinessTextureSwizzle, MaterialTextureSwizzle::A}, + {MaterialAttribute::GlossinessTextureMatrix, Matrix3::scaling({0.5f, 0.5f})}, + {MaterialAttribute::GlossinessTextureCoordinates, 3u} + }}; + CORRADE_VERIFY(data.hasSpecularGlossinessTexture()); + CORRADE_COMPARE(data.specularTexture(), 2); + CORRADE_COMPARE(data.specularTextureSwizzle(), MaterialTextureSwizzle::RGB); + CORRADE_COMPARE(data.specularTextureMatrix(), Matrix3::scaling({0.5f, 0.5f})); + CORRADE_COMPARE(data.specularTextureCoordinates(), 3); + CORRADE_COMPARE(data.glossinessTexture(), 2); + CORRADE_COMPARE(data.glossinessTextureSwizzle(), MaterialTextureSwizzle::A); + CORRADE_COMPARE(data.glossinessTextureMatrix(), Matrix3::scaling({0.5f, 0.5f})); + CORRADE_COMPARE(data.glossinessTextureCoordinates(), 3); + + /* Swizzle is ignored when the combined texture is specified, so this is + fine. */ + } { + PbrSpecularGlossinessMaterialData data{{}, { + {MaterialAttribute::SpecularGlossinessTexture, 2u}, + {MaterialAttribute::GlossinessTextureSwizzle, MaterialTextureSwizzle::B}, + }}; + CORRADE_VERIFY(data.hasSpecularGlossinessTexture()); + + /* Unexpected texture matrix */ + } { + PbrSpecularGlossinessMaterialData data{{}, { + {MaterialAttribute::SpecularGlossinessTexture, 2u}, + {MaterialAttribute::SpecularTextureMatrix, Matrix3::scaling({0.5f, 1.0f})}, + }}; + CORRADE_VERIFY(!data.hasSpecularGlossinessTexture()); + + /* Unexpected texture coordinates */ + } { + PbrSpecularGlossinessMaterialData data{{}, { + {MaterialAttribute::SpecularGlossinessTexture, 2u}, + {MaterialAttribute::GlossinessTextureCoordinates, 1u}, + }}; + CORRADE_VERIFY(!data.hasSpecularGlossinessTexture()); + } +} + +void MaterialDataTest::pbrSpecularGlossinessAccessTexturedExplicitPackedSpecularGlossiness() { + /* Just the texture ID and swizzles, the rest is implicit */ + { + PbrSpecularGlossinessMaterialData data{{}, { + {MaterialAttribute::SpecularTexture, 2u}, + {MaterialAttribute::GlossinessTexture, 2u}, + {MaterialAttribute::GlossinessTextureSwizzle, MaterialTextureSwizzle::A} + }}; + CORRADE_VERIFY(data.hasSpecularGlossinessTexture()); + CORRADE_COMPARE(data.specularTexture(), 2); + CORRADE_COMPARE(data.specularTextureSwizzle(), MaterialTextureSwizzle::RGB); + CORRADE_COMPARE(data.specularTextureMatrix(), Matrix3{}); + CORRADE_COMPARE(data.specularTextureCoordinates(), 0); + CORRADE_COMPARE(data.glossinessTexture(), 2); + CORRADE_COMPARE(data.glossinessTextureSwizzle(), MaterialTextureSwizzle::A); + CORRADE_COMPARE(data.glossinessTextureMatrix(), Matrix3{}); + CORRADE_COMPARE(data.glossinessTextureCoordinates(), 0); + + /* Explicit parameters for everything, but all the same */ + } { + PbrSpecularGlossinessMaterialData data{{}, { + {MaterialAttribute::SpecularTexture, 2u}, + {MaterialAttribute::SpecularTextureSwizzle, MaterialTextureSwizzle::RGB}, + {MaterialAttribute::SpecularTextureMatrix, Matrix3::scaling({0.5f, 0.5f})}, + {MaterialAttribute::SpecularTextureCoordinates, 3u}, + {MaterialAttribute::GlossinessTexture, 2u}, + {MaterialAttribute::GlossinessTextureSwizzle, MaterialTextureSwizzle::A}, + {MaterialAttribute::GlossinessTextureMatrix, Matrix3::scaling({0.5f, 0.5f})}, + {MaterialAttribute::GlossinessTextureCoordinates, 3u} + }}; + CORRADE_VERIFY(data.hasSpecularGlossinessTexture()); + CORRADE_COMPARE(data.specularTexture(), 2); + CORRADE_COMPARE(data.specularTextureSwizzle(), MaterialTextureSwizzle::RGB); + CORRADE_COMPARE(data.specularTextureMatrix(), Matrix3::scaling({0.5f, 0.5f})); + CORRADE_COMPARE(data.specularTextureCoordinates(), 3); + CORRADE_COMPARE(data.glossinessTexture(), 2); + CORRADE_COMPARE(data.glossinessTextureSwizzle(), MaterialTextureSwizzle::A); + CORRADE_COMPARE(data.glossinessTextureMatrix(), Matrix3::scaling({0.5f, 0.5f})); + CORRADE_COMPARE(data.glossinessTextureCoordinates(), 3); + + /* Unexpected swizzle 1 */ + } { + PbrSpecularGlossinessMaterialData data{{}, { + {MaterialAttribute::SpecularTexture, 2u}, + {MaterialAttribute::SpecularTextureSwizzle, MaterialTextureSwizzle::RGBA}, + {MaterialAttribute::GlossinessTexture, 2u}, + {MaterialAttribute::GlossinessTextureSwizzle, MaterialTextureSwizzle::A} + }}; + CORRADE_VERIFY(!data.hasSpecularGlossinessTexture()); + + /* Unexpected swizzle 2 */ + } { + PbrSpecularGlossinessMaterialData data{{}, { + {MaterialAttribute::SpecularTexture, 2u}, + {MaterialAttribute::GlossinessTexture, 2u}, + {MaterialAttribute::GlossinessTextureSwizzle, MaterialTextureSwizzle::B} + }}; + CORRADE_VERIFY(!data.hasSpecularGlossinessTexture()); + + /* Unexpected texture matrix */ + } { + PbrSpecularGlossinessMaterialData data{{}, { + {MaterialAttribute::SpecularTexture, 2u}, + {MaterialAttribute::SpecularTextureMatrix, Matrix3::scaling({0.5f, 1.0f})}, + {MaterialAttribute::GlossinessTexture, 2u}, + {MaterialAttribute::GlossinessTextureSwizzle, MaterialTextureSwizzle::A} + }}; + CORRADE_VERIFY(!data.hasSpecularGlossinessTexture()); + + /* Unexpected texture coordinates */ + } { + PbrSpecularGlossinessMaterialData data{{}, { + {MaterialAttribute::SpecularTexture, 2u}, + {MaterialAttribute::GlossinessTexture, 2u}, + {MaterialAttribute::GlossinessTextureSwizzle, MaterialTextureSwizzle::A}, + {MaterialAttribute::GlossinessTextureCoordinates, 1u} + }}; + CORRADE_VERIFY(!data.hasSpecularGlossinessTexture()); + } +} + +void MaterialDataTest::pbrSpecularGlossinessAccessInvalidTextures() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + PbrSpecularGlossinessMaterialData data{{}, {}}; + + std::ostringstream out; + Error redirectError{&out}; + data.diffuseTexture(); + data.diffuseTextureMatrix(); + data.diffuseTextureCoordinates(); + data.specularTexture(); + data.specularTextureSwizzle(); + data.specularTextureMatrix(); + data.specularTextureCoordinates(); + data.glossinessTexture(); + data.glossinessTextureSwizzle(); + data.glossinessTextureMatrix(); + data.glossinessTextureCoordinates(); + data.normalTexture(); + data.normalTextureSwizzle(); + data.normalTextureMatrix(); + data.normalTextureCoordinates(); + data.occlusionTexture(); + data.occlusionTextureSwizzle(); + data.occlusionTextureMatrix(); + data.occlusionTextureCoordinates(); + data.emissiveTexture(); + data.emissiveTextureMatrix(); + data.emissiveTextureCoordinates(); + CORRADE_COMPARE(out.str(), + "Trade::MaterialData::attribute(): attribute DiffuseTexture not found in layer 0\n" + "Trade::PbrSpecularGlossinessMaterialData::diffuseTextureMatrix(): the material doesn't have a diffuse texture\n" + "Trade::PbrSpecularGlossinessMaterialData::diffuseTextureCoordinates(): the material doesn't have a diffuse texture\n" + "Trade::MaterialData::attribute(): attribute SpecularTexture not found in layer 0\n" + "Trade::PbrSpecularGlossinessMaterialData::specularTextureSwizzle(): the material doesn't have a specular texture\n" + "Trade::PbrSpecularGlossinessMaterialData::specularTextureMatrix(): the material doesn't have a specular texture\n" + "Trade::PbrSpecularGlossinessMaterialData::specularTextureCoordinates(): the material doesn't have a specular texture\n" + "Trade::MaterialData::attribute(): attribute GlossinessTexture not found in layer 0\n" + "Trade::PbrSpecularGlossinessMaterialData::glossinessTextureSwizzle(): the material doesn't have a glossiness texture\n" + "Trade::PbrSpecularGlossinessMaterialData::glossinessTextureMatrix(): the material doesn't have a glossiness texture\n" + "Trade::PbrSpecularGlossinessMaterialData::glossinessTextureCoordinates(): the material doesn't have a glossiness texture\n" + "Trade::MaterialData::attribute(): attribute NormalTexture not found in layer 0\n" + "Trade::PbrSpecularGlossinessMaterialData::normalTextureSwizzle(): the material doesn't have a normal texture\n" + "Trade::PbrSpecularGlossinessMaterialData::normalTextureMatrix(): the material doesn't have a normal texture\n" + "Trade::PbrSpecularGlossinessMaterialData::normalTextureCoordinates(): the material doesn't have a normal texture\n" + "Trade::MaterialData::attribute(): attribute OcclusionTexture not found in layer 0\n" + "Trade::PbrSpecularGlossinessMaterialData::occlusionTextureSwizzle(): the material doesn't have an occlusion texture\n" + "Trade::PbrSpecularGlossinessMaterialData::occlusionTextureMatrix(): the material doesn't have an occlusion texture\n" + "Trade::PbrSpecularGlossinessMaterialData::occlusionTextureCoordinates(): the material doesn't have an occlusion texture\n" + "Trade::MaterialData::attribute(): attribute EmissiveTexture not found in layer 0\n" + "Trade::PbrSpecularGlossinessMaterialData::emissiveTextureMatrix(): the material doesn't have an emissive texture\n" + "Trade::PbrSpecularGlossinessMaterialData::emissiveTextureCoordinates(): the material doesn't have an emissive texture\n"); +} + void MaterialDataTest::phongAccess() { MaterialData base{MaterialType::Phong, { {MaterialAttribute::AmbientColor, 0xccffbbff_rgbaf}, @@ -2097,6 +3140,7 @@ void MaterialDataTest::phongAccess() { CORRADE_COMPARE(base.types(), MaterialType::Phong); const PhongMaterialData& data = static_cast(base); + CORRADE_VERIFY(!data.hasSpecularTexture()); CORRADE_VERIFY(!data.hasTextureTransformation()); CORRADE_VERIFY(!data.hasTextureCoordinates()); CORRADE_COMPARE(data.ambientColor(), 0xccffbb_rgbf); @@ -2116,7 +3160,7 @@ void MaterialDataTest::phongAccessDefaults() { CORRADE_VERIFY(!data.hasTextureCoordinates()); CORRADE_COMPARE(data.ambientColor(), 0x000000_rgbf); CORRADE_COMPARE(data.diffuseColor(), 0xffffff_rgbf); - CORRADE_COMPARE(data.specularColor(), 0xffffff_rgbf); + CORRADE_COMPARE(data.specularColor(), 0xffffff00_rgbaf); CORRADE_COMPARE(data.textureMatrix(), Matrix3{}); CORRADE_COMPARE(data.textureCoordinates(), 0); CORRADE_COMPARE(data.shininess(), 80.0f); @@ -2134,13 +3178,16 @@ void MaterialDataTest::phongAccessTextured() { {MaterialAttribute::DiffuseTextureCoordinates, 3u}, {MaterialAttribute::SpecularColor, 0xacabadff_rgbaf}, {MaterialAttribute::SpecularTexture, 17u}, + {MaterialAttribute::SpecularTextureSwizzle, MaterialTextureSwizzle::RGBA}, {MaterialAttribute::SpecularTextureMatrix, Matrix3::scaling({1.0f, 1.0f})}, {MaterialAttribute::SpecularTextureCoordinates, 4u}, {MaterialAttribute::NormalTexture, 0u}, + {MaterialAttribute::NormalTextureSwizzle, MaterialTextureSwizzle::GB}, {MaterialAttribute::NormalTextureMatrix, Matrix3::scaling({1.0f, 0.5f})}, {MaterialAttribute::NormalTextureCoordinates, 5u} }}; + CORRADE_VERIFY(data.hasSpecularTexture()); CORRADE_VERIFY(data.hasTextureTransformation()); CORRADE_VERIFY(data.hasTextureCoordinates()); CORRADE_COMPARE(data.ambientColor(), 0x111111_rgbf); @@ -2153,9 +3200,11 @@ void MaterialDataTest::phongAccessTextured() { CORRADE_COMPARE(data.diffuseTextureCoordinates(), 3); CORRADE_COMPARE(data.specularColor(), 0xacabad_rgbf); CORRADE_COMPARE(data.specularTexture(), 17); + CORRADE_COMPARE(data.specularTextureSwizzle(), MaterialTextureSwizzle::RGBA); CORRADE_COMPARE(data.specularTextureMatrix(), Matrix3::scaling({1.0f, 1.0f})); CORRADE_COMPARE(data.specularTextureCoordinates(), 4); CORRADE_COMPARE(data.normalTexture(), 0); + CORRADE_COMPARE(data.normalTextureSwizzle(), MaterialTextureSwizzle::GB); CORRADE_COMPARE(data.normalTextureMatrix(), Matrix3::scaling({1.0f, 0.5f})); CORRADE_COMPARE(data.normalTextureCoordinates(), 5); @@ -2171,6 +3220,7 @@ void MaterialDataTest::phongAccessTexturedDefaults() { {MaterialAttribute::NormalTexture, 1u} }}; + CORRADE_VERIFY(data.hasSpecularTexture()); CORRADE_VERIFY(!data.hasTextureTransformation()); CORRADE_VERIFY(!data.hasTextureCoordinates()); CORRADE_COMPARE(data.ambientColor(), 0xffffffff_rgbaf); @@ -2181,11 +3231,13 @@ void MaterialDataTest::phongAccessTexturedDefaults() { CORRADE_COMPARE(data.diffuseTexture(), 33); CORRADE_COMPARE(data.diffuseTextureMatrix(), Matrix3{}); CORRADE_COMPARE(data.diffuseTextureCoordinates(), 0); - CORRADE_COMPARE(data.specularColor(), 0xffffffff_rgbaf); + CORRADE_COMPARE(data.specularColor(), 0xffffff00_rgbaf); CORRADE_COMPARE(data.specularTexture(), 17); + CORRADE_COMPARE(data.specularTextureSwizzle(), MaterialTextureSwizzle::RGB); CORRADE_COMPARE(data.specularTextureMatrix(), Matrix3{}); CORRADE_COMPARE(data.specularTextureCoordinates(), 0); CORRADE_COMPARE(data.normalTexture(), 1); + CORRADE_COMPARE(data.normalTextureSwizzle(), MaterialTextureSwizzle::RGB); CORRADE_COMPARE(data.normalTextureMatrix(), Matrix3{}); CORRADE_COMPARE(data.normalTextureCoordinates(), 0); @@ -2218,6 +3270,29 @@ void MaterialDataTest::phongAccessTexturedSingleMatrixCoordinates() { CORRADE_COMPARE(data.textureCoordinates(), 2); } +void MaterialDataTest::phongAccessTexturedImplicitPackedSpecularGlossiness() { + PhongMaterialData data{{}, { + {MaterialAttribute::SpecularColor, 0xacabadff_rgbaf}, + {MaterialAttribute::SpecularGlossinessTexture, 17u}, + {MaterialAttribute::SpecularTextureMatrix, Matrix3::scaling({1.0f, 1.0f})}, + {MaterialAttribute::SpecularTextureCoordinates, 4u}, + }}; + + #ifdef MAGNUM_BUILD_DEPRECATED + CORRADE_IGNORE_DEPRECATED_PUSH + CORRADE_COMPARE(data.flags(), PhongMaterialData::Flag::SpecularTexture|PhongMaterialData::Flag::TextureCoordinates|PhongMaterialData::Flag::TextureTransformation); + CORRADE_IGNORE_DEPRECATED_POP + #endif + CORRADE_VERIFY(data.hasSpecularTexture()); + CORRADE_VERIFY(data.hasTextureTransformation()); + CORRADE_VERIFY(data.hasTextureCoordinates()); + CORRADE_COMPARE(data.specularColor(), 0xacabad_rgbf); + CORRADE_COMPARE(data.specularTexture(), 17); + CORRADE_COMPARE(data.specularTextureSwizzle(), MaterialTextureSwizzle::RGB); + CORRADE_COMPARE(data.specularTextureMatrix(), Matrix3::scaling({1.0f, 1.0f})); + CORRADE_COMPARE(data.specularTextureCoordinates(), 4); +} + void MaterialDataTest::phongAccessInvalidTextures() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); @@ -2234,9 +3309,11 @@ void MaterialDataTest::phongAccessInvalidTextures() { data.diffuseTextureMatrix(); data.diffuseTextureCoordinates(); data.specularTexture(); + data.specularTextureSwizzle(); data.specularTextureMatrix(); data.specularTextureCoordinates(); data.normalTexture(); + data.normalTextureSwizzle(); data.normalTextureMatrix(); data.normalTextureCoordinates(); CORRADE_COMPARE(out.str(), @@ -2247,9 +3324,11 @@ void MaterialDataTest::phongAccessInvalidTextures() { "Trade::PhongMaterialData::diffuseTextureMatrix(): the material doesn't have a diffuse texture\n" "Trade::PhongMaterialData::diffuseTextureCoordinates(): the material doesn't have a diffuse texture\n" "Trade::MaterialData::attribute(): attribute SpecularTexture not found in layer 0\n" + "Trade::PhongMaterialData::specularTextureSwizzle(): the material doesn't have a specular texture\n" "Trade::PhongMaterialData::specularTextureMatrix(): the material doesn't have a specular texture\n" "Trade::PhongMaterialData::specularTextureCoordinates(): the material doesn't have a specular texture\n" "Trade::MaterialData::attribute(): attribute NormalTexture not found in layer 0\n" + "Trade::PhongMaterialData::normalTextureSwizzle(): the material doesn't have a normal texture\n" "Trade::PhongMaterialData::normalTextureMatrix(): the material doesn't have a normal texture\n" "Trade::PhongMaterialData::normalTextureCoordinates(): the material doesn't have a normal texture\n"); } diff --git a/src/Magnum/Trade/Trade.h b/src/Magnum/Trade/Trade.h index ac59202b5d..9d7fc246b4 100644 --- a/src/Magnum/Trade/Trade.h +++ b/src/Magnum/Trade/Trade.h @@ -90,6 +90,8 @@ class MeshObjectData2D; class MeshObjectData3D; class ObjectData2D; class ObjectData3D; +class PbrMetallicRoughnessMaterialData; +class PbrSpecularGlossinessMaterialData; class PhongMaterialData; class TextureData; class SceneData; From 841d28b506de26675aeb2daee14469ce8c930609 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Thu, 6 Aug 2020 13:05:01 +0200 Subject: [PATCH 16/36] Trade: add MaterialData::as(). For convenient reinterpretation as a concrete material type. Also I don't care if some language lawyers think this is an UB. --- src/Magnum/Trade/MaterialData.h | 26 +++++++++ src/Magnum/Trade/Test/MaterialDataTest.cpp | 68 ++++++++++++++++++++-- 2 files changed, 88 insertions(+), 6 deletions(-) diff --git a/src/Magnum/Trade/MaterialData.h b/src/Magnum/Trade/MaterialData.h index 51b36b7655..ba560ed560 100644 --- a/src/Magnum/Trade/MaterialData.h +++ b/src/Magnum/Trade/MaterialData.h @@ -1373,6 +1373,32 @@ class MAGNUM_TRADE_EXPORT MaterialData { */ MaterialTypes types() const { return _types; } + /** + * @brief Interpret as a material data of concrete type + * + * Returns a reference to @cpp *this @ce cast to given type. @p T is + * expected to be a subclass of the same size such as + * @ref PbrMetallicRoughnessMaterialData, + * @ref PbrSpecularGlossinessMaterialData or @ref PhongMaterialData. + */ + /* MSVC needs the & here, otherwise it complains that "cannot overload + a member function with ref-qualifier with a member function without + ref-qualifier". Clang or GCC doesn't. */ + template const T& as() const & { + static_assert(std::is_base_of::value && sizeof(T) == sizeof(MaterialData), "expected a trivial subclass of MaterialData"); + return static_cast(*this); + } + + /** + * @brief Interpret a rvalue as a material data of concrete type + * + * Compared to the above, returns a value and not a reference. The + * original instance then behaves the same as a moved-from instance. + */ + template T as() && { + return T{std::move(const_cast(as()))}; + } + #ifdef MAGNUM_BUILD_DEPRECATED /** * @brief Material type diff --git a/src/Magnum/Trade/Test/MaterialDataTest.cpp b/src/Magnum/Trade/Test/MaterialDataTest.cpp index 783c589aad..ed0f089e15 100644 --- a/src/Magnum/Trade/Test/MaterialDataTest.cpp +++ b/src/Magnum/Trade/Test/MaterialDataTest.cpp @@ -93,6 +93,9 @@ class MaterialDataTest: public TestSuite::Tester { void constructCopy(); void constructMove(); + void as(); + void asRvalue(); + void access(); void accessPointer(); void accessString(); @@ -252,6 +255,9 @@ MaterialDataTest::MaterialDataTest() { &MaterialDataTest::constructCopy, &MaterialDataTest::constructMove, + &MaterialDataTest::as, + &MaterialDataTest::asRvalue, + &MaterialDataTest::access, &MaterialDataTest::accessPointer, &MaterialDataTest::accessString, @@ -1340,6 +1346,56 @@ void MaterialDataTest::constructMove() { CORRADE_VERIFY(std::is_nothrow_move_assignable::value); } +void MaterialDataTest::as() { + int state; + MaterialData data{MaterialType::Phong|MaterialType::PbrSpecularGlossiness, { + {MaterialAttribute::DiffuseColor, 0xccffbbff_rgbaf}, + {MaterialAttribute::SpecularColor, 0x33556600_rgbaf}, + + {MaterialAttribute::LayerName, "ClearCoat"}, + {"highlightColor", 0x335566ff_rgbaf} + }, { + 2, 4 + }, &state}; + + auto& phong = data.as(); + CORRADE_COMPARE(phong.importerState(), &state); + CORRADE_COMPARE(phong.layerCount(), 2); + CORRADE_COMPARE(phong.diffuseColor(), 0xccffbbff_rgbaf); + CORRADE_COMPARE(phong.attribute("ClearCoat", "highlightColor"), 0x335566ff_rgbaf); + + auto& specularGlossiness = data.as(); + CORRADE_COMPARE(specularGlossiness.importerState(), &state); + CORRADE_COMPARE(specularGlossiness.layerCount(), 2); + CORRADE_COMPARE(specularGlossiness.diffuseColor(), 0xccffbbff_rgbaf); + CORRADE_COMPARE(specularGlossiness.attribute("ClearCoat", "highlightColor"), 0x335566ff_rgbaf); +} + +void MaterialDataTest::asRvalue() { + int state; + MaterialData data{MaterialType::Phong|MaterialType::PbrSpecularGlossiness, { + {MaterialAttribute::DiffuseColor, 0xccffbbff_rgbaf}, + {MaterialAttribute::SpecularColor, 0x33556600_rgbaf}, + + {MaterialAttribute::LayerName, "ClearCoat"}, + {"highlightColor", 0x335566ff_rgbaf} + }, { + 2, 4 + }, &state}; + + auto phong = std::move(data).as(); + CORRADE_COMPARE(data.layerCount(), 1); + CORRADE_COMPARE(phong.layerCount(), 2); + CORRADE_COMPARE(phong.diffuseColor(), 0xccffbbff_rgbaf); + CORRADE_COMPARE(phong.attribute("ClearCoat", "highlightColor"), 0x335566ff_rgbaf); + + auto specularGlossiness = std::move(phong).as(); + CORRADE_COMPARE(phong.layerCount(), 1); + CORRADE_COMPARE(specularGlossiness.layerCount(), 2); + CORRADE_COMPARE(specularGlossiness.diffuseColor(), 0xccffbbff_rgbaf); + CORRADE_COMPARE(specularGlossiness.attribute("ClearCoat", "highlightColor"), 0x335566ff_rgbaf); +} + void MaterialDataTest::access() { MaterialData a{{}, { {MaterialAttribute::DoubleSided, false}, @@ -2141,7 +2197,7 @@ void MaterialDataTest::pbrMetallicRoughnessAccess() { }}; CORRADE_COMPARE(base.types(), MaterialType::PbrMetallicRoughness); - const PbrMetallicRoughnessMaterialData& data = static_cast(base); + const auto& data = base.as(); CORRADE_VERIFY(!data.hasMetalnessTexture()); CORRADE_VERIFY(!data.hasRoughnessTexture()); @@ -2162,7 +2218,7 @@ void MaterialDataTest::pbrMetallicRoughnessAccessDefaults() { CORRADE_COMPARE(base.types(), MaterialTypes{}); /* Casting is fine even if the type doesn't include PbrMetallicRoughness */ - const PbrMetallicRoughnessMaterialData& data = static_cast(base); + const auto& data = base.as(); CORRADE_VERIFY(!data.hasMetalnessTexture()); CORRADE_VERIFY(!data.hasRoughnessTexture()); @@ -2756,7 +2812,7 @@ void MaterialDataTest::pbrSpecularGlossinessAccess() { }}; CORRADE_COMPARE(base.types(), MaterialType::PbrSpecularGlossiness); - const PbrSpecularGlossinessMaterialData& data = static_cast(base); + const auto& data = base.as(); CORRADE_VERIFY(!data.hasSpecularTexture()); CORRADE_VERIFY(!data.hasGlossinessTexture()); @@ -2773,7 +2829,7 @@ void MaterialDataTest::pbrSpecularGlossinessAccessDefaults() { CORRADE_COMPARE(base.types(), MaterialTypes{}); /* Casting is fine even if the type doesn't include PbrMetallicRoughness */ - const PbrSpecularGlossinessMaterialData& data = static_cast(base); + const auto& data = base.as(); CORRADE_VERIFY(!data.hasSpecularTexture()); CORRADE_VERIFY(!data.hasGlossinessTexture()); @@ -3138,7 +3194,7 @@ void MaterialDataTest::phongAccess() { }}; CORRADE_COMPARE(base.types(), MaterialType::Phong); - const PhongMaterialData& data = static_cast(base); + const auto& data = base.as(); CORRADE_VERIFY(!data.hasSpecularTexture()); CORRADE_VERIFY(!data.hasTextureTransformation()); @@ -3154,7 +3210,7 @@ void MaterialDataTest::phongAccessDefaults() { CORRADE_COMPARE(base.types(), MaterialTypes{}); /* Casting is fine even if the type doesn't include Phong */ - const PhongMaterialData& data = static_cast(base); + const auto& data = base.as(); CORRADE_VERIFY(!data.hasTextureTransformation()); CORRADE_VERIFY(!data.hasTextureCoordinates()); From a84721d4b7a22186a08f87815001caeb0406a40b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 8 Aug 2020 18:43:24 +0200 Subject: [PATCH 17/36] Trade: clearer asserts for "packable" textures. In these cases there can be more than one attribute, so complaining that just one particular attribute is not found is misleading. --- src/Magnum/Trade/PbrMetallicRoughnessMaterialData.cpp | 8 ++++++++ src/Magnum/Trade/PbrSpecularGlossinessMaterialData.cpp | 8 ++++++++ src/Magnum/Trade/PhongMaterialData.cpp | 4 ++++ src/Magnum/Trade/Test/MaterialDataTest.cpp | 10 +++++----- 4 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/Magnum/Trade/PbrMetallicRoughnessMaterialData.cpp b/src/Magnum/Trade/PbrMetallicRoughnessMaterialData.cpp index 951fd0f954..1070211c70 100644 --- a/src/Magnum/Trade/PbrMetallicRoughnessMaterialData.cpp +++ b/src/Magnum/Trade/PbrMetallicRoughnessMaterialData.cpp @@ -152,6 +152,10 @@ Float PbrMetallicRoughnessMaterialData::metalness() const { } UnsignedInt PbrMetallicRoughnessMaterialData::metalnessTexture() const { + /* Explicit assertion because printing that MetalnessTexture isn't found + would be misleading as it can be also MetallicRoughnessTexture */ + CORRADE_ASSERT(hasMetalnessTexture(), + "Trade::PbrMetallicRoughnessMaterialData::metalnessTexture(): the material doesn't have a metalness texture", {}); if(Containers::Optional value = tryAttribute(MaterialAttribute::MetallicRoughnessTexture)) return *value; return attribute(MaterialAttribute::MetalnessTexture); @@ -186,6 +190,10 @@ Float PbrMetallicRoughnessMaterialData::roughness() const { } UnsignedInt PbrMetallicRoughnessMaterialData::roughnessTexture() const { + /* Explicit assertion because printing that RoughnessTexture isn't found + would be misleading as it can be also MetallicRoughnessTexture */ + CORRADE_ASSERT(hasRoughnessTexture(), + "Trade::PbrMetallicRoughnessMaterialData::roughnessTexture(): the material doesn't have a roughness texture", {}); if(Containers::Optional value = tryAttribute(MaterialAttribute::MetallicRoughnessTexture)) return *value; return attribute(MaterialAttribute::RoughnessTexture); diff --git a/src/Magnum/Trade/PbrSpecularGlossinessMaterialData.cpp b/src/Magnum/Trade/PbrSpecularGlossinessMaterialData.cpp index d38cebaeef..16a6e608e1 100644 --- a/src/Magnum/Trade/PbrSpecularGlossinessMaterialData.cpp +++ b/src/Magnum/Trade/PbrSpecularGlossinessMaterialData.cpp @@ -101,6 +101,10 @@ Color4 PbrSpecularGlossinessMaterialData::specularColor() const { } UnsignedInt PbrSpecularGlossinessMaterialData::specularTexture() const { + /* Explicit assertion because printing that SpecularTexture isn't found + would be misleading as it can be also SpecularGlossinessTexture */ + CORRADE_ASSERT(hasSpecularTexture(), + "Trade::PbrSpecularGlossinessMaterialData::specularTexture(): the material doesn't have a specular texture", {}); if(Containers::Optional value = tryAttribute(MaterialAttribute::SpecularGlossinessTexture)) return *value; return attribute(MaterialAttribute::SpecularTexture); @@ -135,6 +139,10 @@ Float PbrSpecularGlossinessMaterialData::glossiness() const { } UnsignedInt PbrSpecularGlossinessMaterialData::glossinessTexture() const { + /* Explicit assertion because printing that GlossinessTexture isn't found + would be misleading as it can be also SpecularGlossinessTexture */ + CORRADE_ASSERT(hasGlossinessTexture(), + "Trade::PbrSpecularGlossinessMaterialData::glossinessTexture(): the material doesn't have a glossiness texture", {}); if(Containers::Optional value = tryAttribute(MaterialAttribute::SpecularGlossinessTexture)) return *value; return attribute(MaterialAttribute::GlossinessTexture); diff --git a/src/Magnum/Trade/PhongMaterialData.cpp b/src/Magnum/Trade/PhongMaterialData.cpp index a251a7546b..4f1dc3d5c9 100644 --- a/src/Magnum/Trade/PhongMaterialData.cpp +++ b/src/Magnum/Trade/PhongMaterialData.cpp @@ -204,6 +204,10 @@ Color4 PhongMaterialData::specularColor() const { } UnsignedInt PhongMaterialData::specularTexture() const { + /* Explicit assertion because printing that SpecularTexture isn't found + would be misleading as it can be also SpecularGlossinessTexture */ + CORRADE_ASSERT(hasSpecularTexture(), + "Trade::PhongMaterialData::specularTexture(): the material doesn't have a specular texture", {}); if(Containers::Optional value = tryAttribute(MaterialAttribute::SpecularGlossinessTexture)) return *value; return attribute(MaterialAttribute::SpecularTexture); diff --git a/src/Magnum/Trade/Test/MaterialDataTest.cpp b/src/Magnum/Trade/Test/MaterialDataTest.cpp index ed0f089e15..05e614cc75 100644 --- a/src/Magnum/Trade/Test/MaterialDataTest.cpp +++ b/src/Magnum/Trade/Test/MaterialDataTest.cpp @@ -2782,11 +2782,11 @@ void MaterialDataTest::pbrMetallicRoughnessAccessInvalidTextures() { "Trade::MaterialData::attribute(): attribute BaseColorTexture not found in layer 0\n" "Trade::PbrMetallicRoughnessMaterialData::baseColorTextureMatrix(): the material doesn't have a base color texture\n" "Trade::PbrMetallicRoughnessMaterialData::baseColorTextureCoordinates(): the material doesn't have a base color texture\n" - "Trade::MaterialData::attribute(): attribute MetalnessTexture not found in layer 0\n" + "Trade::PbrMetallicRoughnessMaterialData::metalnessTexture(): the material doesn't have a metalness texture\n" "Trade::PbrMetallicRoughnessMaterialData::metalnessTextureSwizzle(): the material doesn't have a metalness texture\n" "Trade::PbrMetallicRoughnessMaterialData::metalnessTextureMatrix(): the material doesn't have a metalness texture\n" "Trade::PbrMetallicRoughnessMaterialData::metalnessTextureCoordinates(): the material doesn't have a metalness texture\n" - "Trade::MaterialData::attribute(): attribute RoughnessTexture not found in layer 0\n" + "Trade::PbrMetallicRoughnessMaterialData::roughnessTexture(): the material doesn't have a roughness texture\n" "Trade::PbrMetallicRoughnessMaterialData::roughnessTextureSwizzle(): the material doesn't have a roughness texture\n" "Trade::PbrMetallicRoughnessMaterialData::roughnessTextureMatrix(): the material doesn't have a roughness texture\n" "Trade::PbrMetallicRoughnessMaterialData::roughnessTextureCoordinates(): the material doesn't have a roughness texture\n" @@ -3164,11 +3164,11 @@ void MaterialDataTest::pbrSpecularGlossinessAccessInvalidTextures() { "Trade::MaterialData::attribute(): attribute DiffuseTexture not found in layer 0\n" "Trade::PbrSpecularGlossinessMaterialData::diffuseTextureMatrix(): the material doesn't have a diffuse texture\n" "Trade::PbrSpecularGlossinessMaterialData::diffuseTextureCoordinates(): the material doesn't have a diffuse texture\n" - "Trade::MaterialData::attribute(): attribute SpecularTexture not found in layer 0\n" + "Trade::PbrSpecularGlossinessMaterialData::specularTexture(): the material doesn't have a specular texture\n" "Trade::PbrSpecularGlossinessMaterialData::specularTextureSwizzle(): the material doesn't have a specular texture\n" "Trade::PbrSpecularGlossinessMaterialData::specularTextureMatrix(): the material doesn't have a specular texture\n" "Trade::PbrSpecularGlossinessMaterialData::specularTextureCoordinates(): the material doesn't have a specular texture\n" - "Trade::MaterialData::attribute(): attribute GlossinessTexture not found in layer 0\n" + "Trade::PbrSpecularGlossinessMaterialData::glossinessTexture(): the material doesn't have a glossiness texture\n" "Trade::PbrSpecularGlossinessMaterialData::glossinessTextureSwizzle(): the material doesn't have a glossiness texture\n" "Trade::PbrSpecularGlossinessMaterialData::glossinessTextureMatrix(): the material doesn't have a glossiness texture\n" "Trade::PbrSpecularGlossinessMaterialData::glossinessTextureCoordinates(): the material doesn't have a glossiness texture\n" @@ -3379,7 +3379,7 @@ void MaterialDataTest::phongAccessInvalidTextures() { "Trade::MaterialData::attribute(): attribute DiffuseTexture not found in layer 0\n" "Trade::PhongMaterialData::diffuseTextureMatrix(): the material doesn't have a diffuse texture\n" "Trade::PhongMaterialData::diffuseTextureCoordinates(): the material doesn't have a diffuse texture\n" - "Trade::MaterialData::attribute(): attribute SpecularTexture not found in layer 0\n" + "Trade::PhongMaterialData::specularTexture(): the material doesn't have a specular texture\n" "Trade::PhongMaterialData::specularTextureSwizzle(): the material doesn't have a specular texture\n" "Trade::PhongMaterialData::specularTextureMatrix(): the material doesn't have a specular texture\n" "Trade::PhongMaterialData::specularTextureCoordinates(): the material doesn't have a specular texture\n" From 417471b83c001825424987772bb69b550ae69949 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 8 Aug 2020 18:49:45 +0200 Subject: [PATCH 18/36] Trade: add FlatMaterialData. --- doc/changelog.dox | 3 +- src/Magnum/Trade/CMakeLists.txt | 2 + src/Magnum/Trade/FlatMaterialData.cpp | 107 +++++++++ src/Magnum/Trade/FlatMaterialData.h | 152 ++++++++++++ src/Magnum/Trade/MaterialData.cpp | 2 + src/Magnum/Trade/MaterialData.h | 36 ++- .../Trade/PbrMetallicRoughnessMaterialData.h | 2 +- .../Trade/PbrSpecularGlossinessMaterialData.h | 2 +- src/Magnum/Trade/PhongMaterialData.h | 2 +- src/Magnum/Trade/Test/MaterialDataTest.cpp | 220 ++++++++++++++++++ src/Magnum/Trade/Trade.h | 2 + 11 files changed, 515 insertions(+), 15 deletions(-) create mode 100644 src/Magnum/Trade/FlatMaterialData.cpp create mode 100644 src/Magnum/Trade/FlatMaterialData.h diff --git a/doc/changelog.dox b/doc/changelog.dox index f84a8d5c9e..412efeb4f2 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -76,7 +76,8 @@ See also: - A new, redesigned @ref Trade::MaterialData class allowing to store custom material attributes as well as more material types together in a single - instance; plus new @ref Trade::PbrMetallicRoughnessMaterialData and + instance; plus new @ref Trade::FlatMaterialData, + @ref Trade::PbrMetallicRoughnessMaterialData and @ref Trade::PbrSpecularGlossinessMaterialData convenience accessor APIs similar to @ref Trade::PhongMaterialData. See [mosra/magnum#459](https://github.com/mosra/magnum/pull/459). diff --git a/src/Magnum/Trade/CMakeLists.txt b/src/Magnum/Trade/CMakeLists.txt index aa9a2a4a1b..699bdaad8f 100644 --- a/src/Magnum/Trade/CMakeLists.txt +++ b/src/Magnum/Trade/CMakeLists.txt @@ -40,6 +40,7 @@ set(MagnumTrade_GracefulAssert_SRCS AbstractSceneConverter.cpp AnimationData.cpp CameraData.cpp + FlatMaterialData.cpp ImageData.cpp MaterialData.cpp MeshData.cpp @@ -57,6 +58,7 @@ set(MagnumTrade_HEADERS ArrayAllocator.h CameraData.h Data.h + FlatMaterialData.h ImageData.h LightData.h MaterialData.h diff --git a/src/Magnum/Trade/FlatMaterialData.cpp b/src/Magnum/Trade/FlatMaterialData.cpp new file mode 100644 index 0000000000..b405f5e68e --- /dev/null +++ b/src/Magnum/Trade/FlatMaterialData.cpp @@ -0,0 +1,107 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + 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 "FlatMaterialData.h" + +#include "Magnum/Math/Color.h" +#include "Magnum/Math/Matrix3.h" + +namespace Magnum { namespace Trade { + +using namespace Math::Literals; + +bool FlatMaterialData::hasTexture() const { + return hasAttribute(MaterialAttribute::BaseColorTexture) || + hasAttribute(MaterialAttribute::DiffuseTexture); +} + +bool FlatMaterialData::hasTextureTransformation() const { + return (hasAttribute(MaterialAttribute::BaseColorTexture) && hasAttribute(MaterialAttribute::BaseColorTextureMatrix)) || + (hasAttribute(MaterialAttribute::DiffuseTexture) && hasAttribute(MaterialAttribute::DiffuseTextureMatrix)) || + hasAttribute(MaterialAttribute::TextureMatrix); +} + +bool FlatMaterialData::hasTextureCoordinates() const { + return (hasAttribute(MaterialAttribute::BaseColorTexture) && hasAttribute(MaterialAttribute::BaseColorTextureCoordinates)) || + (hasAttribute(MaterialAttribute::DiffuseTexture) && hasAttribute(MaterialAttribute::DiffuseTextureCoordinates)) || + hasAttribute(MaterialAttribute::TextureCoordinates); +} + +Color4 FlatMaterialData::color() const { + /* If the material has a texture, return the color that matches it */ + if(hasAttribute(MaterialAttribute::BaseColorTexture)) + return attributeOr(MaterialAttribute::BaseColor, 0xffffffff_srgbaf); + if(hasAttribute(MaterialAttribute::DiffuseTexture)) + return attributeOr(MaterialAttribute::DiffuseColor, 0xffffffff_srgbaf); + + /* If there's no texture, return whatever is present */ + if(Containers::Optional value = tryAttribute(MaterialAttribute::BaseColor)) + return *value; + return attributeOr(MaterialAttribute::DiffuseColor, 0xffffffff_srgbaf); +} + +UnsignedInt FlatMaterialData::texture() const { + /* Explicit assertion because printing that DiffuseTexture isn't found + would be misleading as it can be also BaseColorTexture */ + CORRADE_ASSERT(hasTexture(), + "Trade::FlatMaterialData::texture(): the material doesn't have a texture", {}); + if(Containers::Optional value = tryAttribute(MaterialAttribute::BaseColorTexture)) + return *value; + return attribute(MaterialAttribute::DiffuseTexture); +} + +Matrix3 FlatMaterialData::textureMatrix() const { + if(hasAttribute(MaterialAttribute::BaseColorTexture)) { + if(Containers::Optional value = tryAttribute(MaterialAttribute::BaseColorTextureMatrix)) + return *value; + return attributeOr(MaterialAttribute::TextureMatrix, Matrix3{}); + } + + if(hasAttribute(MaterialAttribute::DiffuseTexture)) { + if(Containers::Optional value = tryAttribute(MaterialAttribute::DiffuseTextureMatrix)) + return *value; + return attributeOr(MaterialAttribute::TextureMatrix, Matrix3{}); + } + + CORRADE_ASSERT_UNREACHABLE("Trade::FlatMaterialData::textureMatrix(): the material doesn't have a texture", {}); +} + +UnsignedInt FlatMaterialData::textureCoordinates() const { + if(hasAttribute(MaterialAttribute::BaseColorTexture)) { + if(Containers::Optional value = tryAttribute(MaterialAttribute::BaseColorTextureCoordinates)) + return *value; + return attributeOr(MaterialAttribute::TextureCoordinates, 0u); + } + + if(hasAttribute(MaterialAttribute::DiffuseTexture)) { + if(Containers::Optional value = tryAttribute(MaterialAttribute::DiffuseTextureCoordinates)) + return *value; + return attributeOr(MaterialAttribute::TextureCoordinates, 0u); + } + + CORRADE_ASSERT_UNREACHABLE("Trade::FlatMaterialData::textureCoordinates(): the material doesn't have a texture", {}); +} + +}} diff --git a/src/Magnum/Trade/FlatMaterialData.h b/src/Magnum/Trade/FlatMaterialData.h new file mode 100644 index 0000000000..88be295352 --- /dev/null +++ b/src/Magnum/Trade/FlatMaterialData.h @@ -0,0 +1,152 @@ +#ifndef Magnum_Trade_FlatMaterialData_h +#define Magnum_Trade_FlatMaterialData_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + 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::FlatMaterialData + * @m_since_latest + */ + +#include "Magnum/Trade/MaterialData.h" + +namespace Magnum { namespace Trade { + +/** +@brief Flat material data +@m_since_latest + +@see @ref AbstractImporter::material(), @ref PbrMetallicRoughnessMaterialData, + @ref PbrSpecularGlossinessMaterialData, @ref PhongMaterialData +*/ +class MAGNUM_TRADE_EXPORT FlatMaterialData: public MaterialData { + public: + #ifndef DOXYGEN_GENERATING_OUTPUT + /* Allow constructing subclasses directly. While not used in the + general Importer workflow, it allows users to create instances with + desired convenience APIs easier (and simplifies testing). It's + however hidden from the docs as constructing instances this way + isn't really common and it would add a lot of noise. */ + using MaterialData::MaterialData; + #endif + + /** + * @brief Whether the material has a specular texture + * + * Returns @cpp true @ce if any of the + * @ref MaterialAttribute::BaseColorTexture or + * @ref MaterialAttribute::DiffuseTexture attributes is present, + * @cpp false @ce otherwise. + */ + bool hasTexture() const; + + /** + * @brief Whether the material has texture transformation + * + * Returns @cpp true @ce if the material is textured and a + * @ref MaterialAttribute::BaseColorTextureMatrix, + * @ref MaterialAttribute::DiffuseTextureMatrix or + * @ref MaterialAttribute::TextureMatrix attribute matching the texture + * is present, @cpp false @ce otherwise. In particular, if there's for + * example a @ref MaterialAttribute::BaseColorTexture but only a + * @ref MaterialAttribute::DiffuseTextureMatrix, + * returns @cpp false @ce. + */ + bool hasTextureTransformation() const; + + /** + * @brief Whether the material uses extra texture coordinate sets + * + * Returns @cpp true @ce if the material is textured an a + * @ref MaterialAttribute::BaseColorTextureCoordinates, + * @ref MaterialAttribute::DiffuseTextureCoordinates or + * @ref MaterialAttribute::TextureCoordinates attribute matching the + * texture is present and has a non-zero value, @cpp false @ce + * otherwise. In particular, if there's for example a + * @ref MaterialAttribute::BaseColorTexture but + * only a @ref MaterialAttribute::DiffuseTextureCoordinates, returns + * @cpp false @ce. + */ + bool hasTextureCoordinates() const; + + /** + * @brief Color + * + * Convenience access to the @ref MaterialAttribute::BaseColor / + * @ref MaterialAttribute::DiffuseColor attributes. If neither of them + * is present, the default is @cpp 0xffffffff_srgbaf @ce. + * + * If the material has a texture, the color attribute matching the + * texture is picked (instead of combining e.g. a + * @ref MaterialAttribute::BaseColor with @ref MaterialAttribute::DiffuseTexture). The color and texture is meant to + * be multiplied together. + * @see @ref hasAttribute(), @ref hasTexture() + */ + Color4 color() const; + + /** + * @brief Texture ID + * + * Available only if either @ref MaterialAttribute::BaseColorTexture or + * @ref MaterialAttribute::DiffuseTexture is present. Meant to be + * multiplied with @ref color(). + * @see @ref hasTexture(), @ref AbstractImporter::texture() + */ + UnsignedInt texture() const; + + /** + * @brief Texture coordinate transformation matrix + * + * Convenience access to the @ref MaterialAttribute::DiffuseTextureMatrix + * / @ref MaterialAttribute::BaseColorTextureMatrix / + * @ref MaterialAttribute::TextureMatrix attributes, picking the one + * matching the texture (instead of combining e.g. a + * @ref MaterialAttribute::BaseColorTexture with + * @ref MaterialAttribute::DiffuseTextureMatrix). If no matching + * attribute is present, the default is an identity matrix. Available + * only if the material has a texture. + * @see @ref hasTexture() + */ + Matrix3 textureMatrix() const; + + /** + * @brief Texture coordinate set + * + * Convenience access to the @ref MaterialAttribute::DiffuseTextureCoordinates + * / @ref MaterialAttribute::BaseColorTextureCoordinates / + * @ref MaterialAttribute::TextureCoordinates attributes, picking the + * one matching the texture (instead of combining e.g. a + * @ref MaterialAttribute::BaseColorTexture with + * @ref MaterialAttribute::DiffuseTextureCoordinates). If no matching + * attribute is present, the default is @cpp 0 @ce. Available only if + * the material has a texture. + * @see @ref hasTexture() + */ + UnsignedInt textureCoordinates() const; +}; + +}} + +#endif diff --git a/src/Magnum/Trade/MaterialData.cpp b/src/Magnum/Trade/MaterialData.cpp index 5fc4ed97db..919a68e764 100644 --- a/src/Magnum/Trade/MaterialData.cpp +++ b/src/Magnum/Trade/MaterialData.cpp @@ -648,6 +648,7 @@ Debug& operator<<(Debug& debug, const MaterialType value) { switch(value) { /* LCOV_EXCL_START */ #define _c(value) case MaterialType::value: return debug << "::" #value; + _c(Flat) _c(Phong) _c(PbrMetallicRoughness) _c(PbrSpecularGlossiness) @@ -660,6 +661,7 @@ Debug& operator<<(Debug& debug, const MaterialType value) { Debug& operator<<(Debug& debug, const MaterialTypes value) { return Containers::enumSetDebugOutput(debug, value, "Trade::MaterialTypes{}", { + MaterialType::Flat, MaterialType::Phong, MaterialType::PbrMetallicRoughness, MaterialType::PbrSpecularGlossiness diff --git a/src/Magnum/Trade/MaterialData.h b/src/Magnum/Trade/MaterialData.h index ba560ed560..14376cbe1c 100644 --- a/src/Magnum/Trade/MaterialData.h +++ b/src/Magnum/Trade/MaterialData.h @@ -149,7 +149,8 @@ enum class MaterialAttribute: UnsignedInt { * * If @ref MaterialAttribute::DiffuseTexture is present as well, these two * are multiplied together. - * @see @ref PhongMaterialData::diffuseColor(), + * @see @ref FlatMaterialData::color(), + * @ref PhongMaterialData::diffuseColor(), * @ref PbrSpecularGlossinessMaterialData::diffuseColor() */ DiffuseColor, @@ -160,7 +161,8 @@ enum class MaterialAttribute: UnsignedInt { * * If @ref MaterialAttribute::DiffuseColor is present as well, these two * are multiplied together. - * @see @ref PhongMaterialData::diffuseTexture(), + * @see @ref FlatMaterialData::texture(), + * @ref PhongMaterialData::diffuseTexture(), * @ref PbrSpecularGlossinessMaterialData::diffuseTexture() */ DiffuseTexture, @@ -171,7 +173,8 @@ enum class MaterialAttribute: UnsignedInt { * * Has a precedence over @ref MaterialAttribute::TextureMatrix if both are * present. - * @see @ref PhongMaterialData::diffuseTextureMatrix(), + * @see @ref FlatMaterialData::textureMatrix(), + * @ref PhongMaterialData::diffuseTextureMatrix(), * @ref PbrSpecularGlossinessMaterialData::diffuseTextureMatrix() */ DiffuseTextureMatrix, @@ -182,7 +185,8 @@ enum class MaterialAttribute: UnsignedInt { * * Has a precedence over @ref MaterialAttribute::TextureCoordinates if both * are present. - * @see @ref PhongMaterialData::diffuseTextureCoordinates(), + * @see @ref FlatMaterialData::textureCoordinates(), + * @ref PhongMaterialData::diffuseTextureCoordinates(), * @ref PbrSpecularGlossinessMaterialData::diffuseTextureCoordinates() */ DiffuseTextureCoordinates, @@ -267,7 +271,8 @@ enum class MaterialAttribute: UnsignedInt { * * If @ref MaterialAttribute::BaseColorTexture is present as well, these * two are multiplied together. - * @see @ref PbrMetallicRoughnessMaterialData::baseColor() + * @see @ref FlatMaterialData::color(), + * @ref PbrMetallicRoughnessMaterialData::baseColor() */ BaseColor, @@ -277,7 +282,8 @@ enum class MaterialAttribute: UnsignedInt { * * If @ref MaterialAttribute::BaseColor is present as well, these two are * multiplied together. - * @see @ref PbrMetallicRoughnessMaterialData::baseColorTexture() + * @see @ref FlatMaterialData::texture(), + * @ref PbrMetallicRoughnessMaterialData::baseColorTexture() */ BaseColorTexture, @@ -287,7 +293,8 @@ enum class MaterialAttribute: UnsignedInt { * * Has a precedence over @ref MaterialAttribute::TextureMatrix if both are * present. - * @see @ref PbrMetallicRoughnessMaterialData::baseColorTextureMatrix() + * @see @ref FlatMaterialData::textureMatrix(), + * @ref PbrMetallicRoughnessMaterialData::baseColorTextureMatrix() */ BaseColorTextureMatrix, @@ -297,7 +304,8 @@ enum class MaterialAttribute: UnsignedInt { * * Has a precedence over @ref MaterialAttribute::TextureCoordinates if both * are present. - * @see @ref PbrMetallicRoughnessMaterialData::baseColorTextureCoordinates() + * @see @ref FlatMaterialData::textureCoordinates(), + * @ref PbrMetallicRoughnessMaterialData::baseColorTextureCoordinates() */ BaseColorTextureCoordinates, @@ -1147,22 +1155,28 @@ class MAGNUM_TRADE_EXPORT MaterialAttributeData { @see @ref MaterialTypes, @ref MaterialData::types() */ enum class MaterialType: UnsignedInt { + /** + * Flat. Use @ref FlatMaterialData for convenience attribute access. + * Materials of this type are generally not combined with any other types. + */ + Flat = 1 << 0, + /** * Phong. Use @ref PhongMaterialData for convenience attribute access. */ - Phong = 1 << 0, + Phong = 1 << 1, /** * PBR metallic/roughness. Use @ref PbrMetallicRoughnessMaterialData for * convenience attribute access. */ - PbrMetallicRoughness = 1 << 1, + PbrMetallicRoughness = 1 << 2, /** * PBR specular/glossiness. Use @ref PbrSpecularGlossinessMaterialData for * convenience attribute access. */ - PbrSpecularGlossiness = 1 << 2 + PbrSpecularGlossiness = 1 << 3 }; /** @debugoperatorenum{MaterialType} */ diff --git a/src/Magnum/Trade/PbrMetallicRoughnessMaterialData.h b/src/Magnum/Trade/PbrMetallicRoughnessMaterialData.h index dcfb9f2deb..97e7b33bfb 100644 --- a/src/Magnum/Trade/PbrMetallicRoughnessMaterialData.h +++ b/src/Magnum/Trade/PbrMetallicRoughnessMaterialData.h @@ -39,7 +39,7 @@ namespace Magnum { namespace Trade { @m_since_latest @see @ref AbstractImporter::material(), @ref PbrSpecularGlossinessMaterialData, - @ref PhongMaterialData + @ref PhongMaterialData, @ref FlatMaterialData */ class MAGNUM_TRADE_EXPORT PbrMetallicRoughnessMaterialData: public MaterialData { public: diff --git a/src/Magnum/Trade/PbrSpecularGlossinessMaterialData.h b/src/Magnum/Trade/PbrSpecularGlossinessMaterialData.h index 719d87f94f..420c02e4b1 100644 --- a/src/Magnum/Trade/PbrSpecularGlossinessMaterialData.h +++ b/src/Magnum/Trade/PbrSpecularGlossinessMaterialData.h @@ -39,7 +39,7 @@ namespace Magnum { namespace Trade { @m_since_latest @see @ref AbstractImporter::material(), @ref PbrMetallicRoughnessMaterialData, - @ref PhongMaterialData + @ref PhongMaterialData, @ref FlatMaterialData */ class MAGNUM_TRADE_EXPORT PbrSpecularGlossinessMaterialData: public MaterialData { public: diff --git a/src/Magnum/Trade/PhongMaterialData.h b/src/Magnum/Trade/PhongMaterialData.h index 6460475562..c436b88a27 100644 --- a/src/Magnum/Trade/PhongMaterialData.h +++ b/src/Magnum/Trade/PhongMaterialData.h @@ -38,7 +38,7 @@ namespace Magnum { namespace Trade { @brief Phong material data @see @ref AbstractImporter::material(), @ref PbrMetallicRoughnessMaterialData, - @ref PbrSpecularGlossinessMaterialData + @ref PbrSpecularGlossinessMaterialData, @ref FlatMaterialData */ class MAGNUM_TRADE_EXPORT PhongMaterialData: public MaterialData { public: diff --git a/src/Magnum/Trade/Test/MaterialDataTest.cpp b/src/Magnum/Trade/Test/MaterialDataTest.cpp index 05e614cc75..47cb7b585d 100644 --- a/src/Magnum/Trade/Test/MaterialDataTest.cpp +++ b/src/Magnum/Trade/Test/MaterialDataTest.cpp @@ -33,6 +33,7 @@ #include "Magnum/Math/Color.h" #include "Magnum/Math/Matrix3.h" +#include "Magnum/Trade/FlatMaterialData.h" #include "Magnum/Trade/MaterialData.h" #include "Magnum/Trade/PbrMetallicRoughnessMaterialData.h" #include "Magnum/Trade/PbrSpecularGlossinessMaterialData.h" @@ -161,6 +162,17 @@ class MaterialDataTest: public TestSuite::Tester { void phongAccessTexturedImplicitPackedSpecularGlossiness(); void phongAccessInvalidTextures(); + void flatAccessBaseColor(); + void flatAccessDiffuseColor(); + void flatAccessDefaults(); + void flatAccessTexturedBaseColor(); + void flatAccessTexturedDiffuseColor(); + void flatAccessTexturedDefaults(); + void flatAccessTexturedBaseColorSingleMatrixCoordinates(); + void flatAccessTexturedDiffuseColorSingleMatrixCoordinates(); + void flatAccessTexturedMismatchedMatrixCoordinates(); + void flatAccessInvalidTextures(); + void debugAttribute(); void debugTextureSwizzle(); void debugAttributeType(); @@ -323,6 +335,17 @@ MaterialDataTest::MaterialDataTest() { &MaterialDataTest::phongAccessTexturedImplicitPackedSpecularGlossiness, &MaterialDataTest::phongAccessInvalidTextures, + &MaterialDataTest::flatAccessBaseColor, + &MaterialDataTest::flatAccessDiffuseColor, + &MaterialDataTest::flatAccessDefaults, + &MaterialDataTest::flatAccessTexturedBaseColor, + &MaterialDataTest::flatAccessTexturedDiffuseColor, + &MaterialDataTest::flatAccessTexturedDefaults, + &MaterialDataTest::flatAccessTexturedBaseColorSingleMatrixCoordinates, + &MaterialDataTest::flatAccessTexturedDiffuseColorSingleMatrixCoordinates, + &MaterialDataTest::flatAccessTexturedMismatchedMatrixCoordinates, + &MaterialDataTest::flatAccessInvalidTextures, + &MaterialDataTest::debugAttribute, &MaterialDataTest::debugTextureSwizzle, &MaterialDataTest::debugAttributeType, @@ -3389,6 +3412,203 @@ void MaterialDataTest::phongAccessInvalidTextures() { "Trade::PhongMaterialData::normalTextureCoordinates(): the material doesn't have a normal texture\n"); } +void MaterialDataTest::flatAccessBaseColor() { + MaterialData base{MaterialType::Flat, { + {MaterialAttribute::BaseColor, 0xccffbbff_rgbaf}, + {MaterialAttribute::DiffuseColor, 0x33556600_rgbaf}, /* Ignored */ + }}; + + CORRADE_COMPARE(base.types(), MaterialType::Flat); + const auto& data = base.as(); + + CORRADE_VERIFY(!data.hasTexture()); + CORRADE_VERIFY(!data.hasTextureTransformation()); + CORRADE_VERIFY(!data.hasTextureCoordinates()); + CORRADE_COMPARE(data.color(), 0xccffbb_rgbf); +} + +void MaterialDataTest::flatAccessDiffuseColor() { + MaterialData base{MaterialType::Flat, { + {MaterialAttribute::DiffuseColor, 0xccffbbff_rgbaf}, + }}; + + CORRADE_COMPARE(base.types(), MaterialType::Flat); + const auto& data = base.as(); + + CORRADE_VERIFY(!data.hasTexture()); + CORRADE_VERIFY(!data.hasTextureTransformation()); + CORRADE_VERIFY(!data.hasTextureCoordinates()); + CORRADE_COMPARE(data.color(), 0xccffbb_rgbf); +} + +void MaterialDataTest::flatAccessDefaults() { + MaterialData base{{}, {}}; + + CORRADE_COMPARE(base.types(), MaterialTypes{}); + /* Casting is fine even if the type doesn't include Flat */ + const auto& data = base.as(); + + CORRADE_VERIFY(!data.hasTexture()); + CORRADE_VERIFY(!data.hasTextureTransformation()); + CORRADE_VERIFY(!data.hasTextureCoordinates()); + CORRADE_COMPARE(data.color(), 0xffffff_rgbf); +} + +void MaterialDataTest::flatAccessTexturedBaseColor() { + FlatMaterialData data{{}, { + {MaterialAttribute::BaseColor, 0xccffbbff_rgbaf}, + {MaterialAttribute::BaseColorTexture, 5u}, + {MaterialAttribute::BaseColorTextureMatrix, Matrix3::scaling({0.5f, 1.0f})}, + {MaterialAttribute::BaseColorTextureCoordinates, 2u}, + + /* All this is ignored */ + {MaterialAttribute::DiffuseColor, 0x33556600_rgbaf}, + {MaterialAttribute::DiffuseTexture, 6u}, + {MaterialAttribute::DiffuseTextureMatrix, Matrix3::translation({0.5f, 1.0f})}, + {MaterialAttribute::DiffuseTextureCoordinates, 3u} + }}; + + CORRADE_VERIFY(data.hasTexture()); + CORRADE_VERIFY(data.hasTextureTransformation()); + CORRADE_VERIFY(data.hasTextureCoordinates()); + CORRADE_COMPARE(data.color(), 0xccffbb_rgbf); + CORRADE_COMPARE(data.texture(), 5); + CORRADE_COMPARE(data.textureMatrix(), Matrix3::scaling({0.5f, 1.0f})); + CORRADE_COMPARE(data.textureCoordinates(), 2); +} + +void MaterialDataTest::flatAccessTexturedDiffuseColor() { + FlatMaterialData data{{}, { + {MaterialAttribute::DiffuseColor, 0xccffbbff_rgbaf}, + {MaterialAttribute::DiffuseTexture, 5u}, + {MaterialAttribute::DiffuseTextureMatrix, Matrix3::scaling({0.5f, 1.0f})}, + {MaterialAttribute::DiffuseTextureCoordinates, 2u}, + + /* Ignored, as we have a diffuse texture */ + {MaterialAttribute::BaseColor, 0x33556600_rgbaf} + }}; + + CORRADE_VERIFY(data.hasTexture()); + CORRADE_VERIFY(data.hasTextureTransformation()); + CORRADE_VERIFY(data.hasTextureCoordinates()); + CORRADE_COMPARE(data.color(), 0xccffbb_rgbf); + CORRADE_COMPARE(data.texture(), 5); + CORRADE_COMPARE(data.textureMatrix(), Matrix3::scaling({0.5f, 1.0f})); + CORRADE_COMPARE(data.textureCoordinates(), 2); +} + +void MaterialDataTest::flatAccessTexturedDefaults() { + FlatMaterialData data{{}, { + {MaterialAttribute::DiffuseTexture, 5u} + }}; + + CORRADE_VERIFY(data.hasTexture()); + CORRADE_VERIFY(!data.hasTextureTransformation()); + CORRADE_VERIFY(!data.hasTextureCoordinates()); + CORRADE_COMPARE(data.color(), 0xffffff_rgbf); + CORRADE_COMPARE(data.texture(), 5); + CORRADE_COMPARE(data.textureMatrix(), Matrix3{}); + CORRADE_COMPARE(data.textureCoordinates(), 0); +} + +void MaterialDataTest::flatAccessTexturedBaseColorSingleMatrixCoordinates() { + FlatMaterialData data{{}, { + {MaterialAttribute::BaseColor, 0xccffbbff_rgbaf}, + {MaterialAttribute::BaseColorTexture, 5u}, + {MaterialAttribute::TextureMatrix, Matrix3::scaling({0.5f, 1.0f})}, + {MaterialAttribute::TextureCoordinates, 2u}, + + /* This is ignored because it doesn't match the texture */ + {MaterialAttribute::DiffuseTextureMatrix, Matrix3::translation({0.5f, 1.0f})}, + {MaterialAttribute::DiffuseTextureCoordinates, 3u} + }}; + + CORRADE_VERIFY(data.hasTexture()); + CORRADE_VERIFY(data.hasTextureTransformation()); + CORRADE_VERIFY(data.hasTextureCoordinates()); + CORRADE_COMPARE(data.color(), 0xccffbb_rgbf); + CORRADE_COMPARE(data.texture(), 5); + CORRADE_COMPARE(data.textureMatrix(), Matrix3::scaling({0.5f, 1.0f})); + CORRADE_COMPARE(data.textureCoordinates(), 2); +} + +void MaterialDataTest::flatAccessTexturedDiffuseColorSingleMatrixCoordinates() { + FlatMaterialData data{{}, { + {MaterialAttribute::DiffuseColor, 0xccffbbff_rgbaf}, + {MaterialAttribute::DiffuseTexture, 5u}, + {MaterialAttribute::TextureMatrix, Matrix3::scaling({0.5f, 1.0f})}, + {MaterialAttribute::TextureCoordinates, 2u}, + + /* This is ignored because it doesn't match the texture */ + {MaterialAttribute::BaseColorTextureMatrix, Matrix3::translation({0.5f, 1.0f})}, + {MaterialAttribute::BaseColorTextureCoordinates, 3u} + }}; + + CORRADE_VERIFY(data.hasTexture()); + CORRADE_VERIFY(data.hasTextureTransformation()); + CORRADE_VERIFY(data.hasTextureCoordinates()); + CORRADE_COMPARE(data.color(), 0xccffbb_rgbf); + CORRADE_COMPARE(data.texture(), 5); + CORRADE_COMPARE(data.textureMatrix(), Matrix3::scaling({0.5f, 1.0f})); + CORRADE_COMPARE(data.textureCoordinates(), 2); +} + +void MaterialDataTest::flatAccessTexturedMismatchedMatrixCoordinates() { + { + FlatMaterialData data{{}, { + {MaterialAttribute::BaseColorTexture, 5u}, + + /* This is ignored because it doesn't match the texture */ + {MaterialAttribute::DiffuseColor, 0x33556600_rgbaf}, + {MaterialAttribute::DiffuseTextureMatrix, Matrix3::scaling({0.5f, 1.0f})}, + {MaterialAttribute::DiffuseTextureCoordinates, 2u}, + }}; + + CORRADE_VERIFY(data.hasTexture()); + CORRADE_VERIFY(!data.hasTextureTransformation()); + CORRADE_VERIFY(!data.hasTextureCoordinates()); + CORRADE_COMPARE(data.color(), 0xffffff_rgbf); + CORRADE_COMPARE(data.texture(), 5); + CORRADE_COMPARE(data.textureMatrix(), Matrix3{}); + CORRADE_COMPARE(data.textureCoordinates(), 0); + } { + FlatMaterialData data{{}, { + {MaterialAttribute::DiffuseTexture, 5u}, + + /* This is ignored because it doesn't match the texture */ + {MaterialAttribute::BaseColor, 0x33556600_rgbaf}, + {MaterialAttribute::BaseColorTextureMatrix, Matrix3::scaling({0.5f, 1.0f})}, + {MaterialAttribute::BaseColorTextureCoordinates, 2u}, + }}; + + CORRADE_VERIFY(data.hasTexture()); + CORRADE_VERIFY(!data.hasTextureTransformation()); + CORRADE_VERIFY(!data.hasTextureCoordinates()); + CORRADE_COMPARE(data.color(), 0xffffff_rgbf); + CORRADE_COMPARE(data.texture(), 5); + CORRADE_COMPARE(data.textureMatrix(), Matrix3{}); + CORRADE_COMPARE(data.textureCoordinates(), 0); + } +} + +void MaterialDataTest::flatAccessInvalidTextures() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + FlatMaterialData data{{}, {}}; + + std::ostringstream out; + Error redirectError{&out}; + data.texture(); + data.textureMatrix(); + data.textureCoordinates(); + CORRADE_COMPARE(out.str(), + "Trade::FlatMaterialData::texture(): the material doesn't have a texture\n" + "Trade::FlatMaterialData::textureMatrix(): the material doesn't have a texture\n" + "Trade::FlatMaterialData::textureCoordinates(): the material doesn't have a texture\n"); +} + void MaterialDataTest::debugAttribute() { std::ostringstream out; diff --git a/src/Magnum/Trade/Trade.h b/src/Magnum/Trade/Trade.h index 9d7fc246b4..0b0e07cdde 100644 --- a/src/Magnum/Trade/Trade.h +++ b/src/Magnum/Trade/Trade.h @@ -70,6 +70,8 @@ class CameraData; enum class DataFlag: UnsignedByte; typedef Containers::EnumSet DataFlags; +class FlatMaterialData; + template class ImageData; typedef ImageData<1> ImageData1D; typedef ImageData<2> ImageData2D; From 0654fe2cb04fbda4dfaa49c5265652cd21a128f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 8 Aug 2020 20:05:49 +0200 Subject: [PATCH 19/36] Trade: add builtin material layer factor + texture attributes. And convenience getters as well. --- .../materialAttributeProperties.hpp | 5 + src/Magnum/Trade/MaterialData.cpp | 103 +++++++++- src/Magnum/Trade/MaterialData.h | 182 ++++++++++++++++- src/Magnum/Trade/Test/MaterialDataTest.cpp | 192 ++++++++++++++++++ 4 files changed, 479 insertions(+), 3 deletions(-) diff --git a/src/Magnum/Trade/Implementation/materialAttributeProperties.hpp b/src/Magnum/Trade/Implementation/materialAttributeProperties.hpp index c7c73e283f..2b81b7725e 100644 --- a/src/Magnum/Trade/Implementation/materialAttributeProperties.hpp +++ b/src/Magnum/Trade/Implementation/materialAttributeProperties.hpp @@ -76,6 +76,11 @@ _c(EmissiveColor,Vector3) _c(EmissiveTexture,UnsignedInt) _c(EmissiveTextureMatrix,Matrix3x3) _c(EmissiveTextureCoordinates,UnsignedInt) +_c(LayerFactor,Float) +_c(LayerFactorTexture,UnsignedInt) +_ct(LayerFactorTextureSwizzle,TextureSwizzle,MaterialTextureSwizzle) +_c(LayerFactorTextureMatrix,Matrix3x3) +_c(LayerFactorTextureCoordinates,UnsignedInt) _c(TextureMatrix,Matrix3x3) _c(TextureCoordinates,UnsignedInt) #endif diff --git a/src/Magnum/Trade/MaterialData.cpp b/src/Magnum/Trade/MaterialData.cpp index 919a68e764..fe7e21ea9e 100644 --- a/src/Magnum/Trade/MaterialData.cpp +++ b/src/Magnum/Trade/MaterialData.cpp @@ -31,7 +31,7 @@ #include #include "Magnum/Math/Vector4.h" -#include "Magnum/Math/Matrix.h" +#include "Magnum/Math/Matrix3.h" #include "Magnum/Trade/Data.h" #include "Magnum/Trade/Implementation/arrayUtilities.h" @@ -301,6 +301,107 @@ Containers::StringView MaterialData::layerName(const UnsignedInt layer) const { return {}; } +Float MaterialData::layerFactor(const UnsignedInt layer) const { + CORRADE_ASSERT(layer < layerCount(), + "Trade::MaterialData::layerFactor(): index" << layer << "out of range for" << layerCount() << "layers", {}); + return attributeOr(layer, MaterialAttribute::LayerFactor, 1.0f); +} + +Float MaterialData::layerFactor(const Containers::StringView layer) const { + const UnsignedInt layerId = layerFor(layer); + CORRADE_ASSERT(layerId != ~UnsignedInt{}, + "Trade::MaterialData::layerFactor(): layer" << layer << "not found", {}); + return layerFactor(layerId); +} + +UnsignedInt MaterialData::layerFactorTexture(const UnsignedInt layer) const { + CORRADE_ASSERT(layer < layerCount(), + "Trade::MaterialData::layerFactorTexture(): index" << layer << "out of range for" << layerCount() << "layers", {}); + return attribute(layer, MaterialAttribute::LayerFactorTexture); +} + +UnsignedInt MaterialData::layerFactorTexture(const Containers::StringView layer) const { + const UnsignedInt layerId = layerFor(layer); + CORRADE_ASSERT(layerId != ~UnsignedInt{}, + "Trade::MaterialData::layerFactorTexture(): layer" << layer << "not found", {}); + /* Not delegating into layerFactorTexture() in order to have layer name + caught in the assert */ + return attribute(layer, MaterialAttribute::LayerFactorTexture); +} + +MaterialTextureSwizzle MaterialData::layerFactorTextureSwizzle(const UnsignedInt layer) const { + CORRADE_ASSERT(layer < layerCount(), + "Trade::MaterialData::layerFactorTextureSwizzle(): index" << layer << "out of range for" << layerCount() << "layers", {}); + CORRADE_ASSERT(hasAttribute(layer, MaterialAttribute::LayerFactorTexture), + "Trade::MaterialData::layerFactorTextureSwizzle(): layer" << layer << "doesn't have a factor texture", {}); + return attributeOr(layer, MaterialAttribute::LayerFactorTextureSwizzle, MaterialTextureSwizzle::R); +} + +MaterialTextureSwizzle MaterialData::layerFactorTextureSwizzle(const Containers::StringView layer) const { + const UnsignedInt layerId = layerFor(layer); + CORRADE_ASSERT(layerId != ~UnsignedInt{}, + "Trade::MaterialData::layerFactorTextureSwizzle(): layer" << layer << "not found", {}); + CORRADE_ASSERT(hasAttribute(layerId, MaterialAttribute::LayerFactorTexture), + "Trade::MaterialData::layerFactorTextureSwizzle(): layer" << layer << "doesn't have a factor texture", {}); + /* Not delegating into layerFactorTextureSwizzle() because we have a + different variant of the assert here */ + return attributeOr(layer, MaterialAttribute::LayerFactorTextureSwizzle, MaterialTextureSwizzle::R); +} + +Matrix3 MaterialData::layerFactorTextureMatrix(const UnsignedInt layer) const { + CORRADE_ASSERT(layer < layerCount(), + "Trade::MaterialData::layerFactorTextureMatrix(): index" << layer << "out of range for" << layerCount() << "layers", {}); + CORRADE_ASSERT(hasAttribute(layer, MaterialAttribute::LayerFactorTexture), + "Trade::MaterialData::layerFactorTextureMatrix(): layer" << layer << "doesn't have a factor texture", {}); + if(Containers::Optional value = tryAttribute(layer, MaterialAttribute::LayerFactorTextureMatrix)) + return *value; + if(Containers::Optional value = tryAttribute(layer, MaterialAttribute::TextureMatrix)) + return *value; + return attributeOr(0, MaterialAttribute::TextureMatrix, Matrix3{}); +} + +Matrix3 MaterialData::layerFactorTextureMatrix(const Containers::StringView layer) const { + const UnsignedInt layerId = layerFor(layer); + CORRADE_ASSERT(layerId != ~UnsignedInt{}, + "Trade::MaterialData::layerFactorTextureMatrix(): layer" << layer << "not found", {}); + CORRADE_ASSERT(hasAttribute(layerId, MaterialAttribute::LayerFactorTexture), + "Trade::MaterialData::layerFactorTextureMatrix(): layer" << layer << "doesn't have a factor texture", {}); + /* Not delegating into layerFactorTextureMatrix() because we have a + different variant of the assert here */ + if(Containers::Optional value = tryAttribute(layerId, MaterialAttribute::LayerFactorTextureMatrix)) + return *value; + if(Containers::Optional value = tryAttribute(layerId, MaterialAttribute::TextureMatrix)) + return *value; + return attributeOr(0, MaterialAttribute::TextureMatrix, Matrix3{}); +} + +UnsignedInt MaterialData::layerFactorTextureCoordinates(const UnsignedInt layer) const { + CORRADE_ASSERT(layer < layerCount(), + "Trade::MaterialData::layerFactorTextureCoordinates(): index" << layer << "out of range for" << layerCount() << "layers", {}); + CORRADE_ASSERT(hasAttribute(layer, MaterialAttribute::LayerFactorTexture), + "Trade::MaterialData::layerFactorTextureCoordinates(): layer" << layer << "doesn't have a factor texture", {}); + if(Containers::Optional value = tryAttribute(layer, MaterialAttribute::LayerFactorTextureCoordinates)) + return *value; + if(Containers::Optional value = tryAttribute(layer, MaterialAttribute::TextureCoordinates)) + return *value; + return attributeOr(0, MaterialAttribute::TextureCoordinates, 0u); +} + +UnsignedInt MaterialData::layerFactorTextureCoordinates(const Containers::StringView layer) const { + const UnsignedInt layerId = layerFor(layer); + CORRADE_ASSERT(layerId != ~UnsignedInt{}, + "Trade::MaterialData::layerFactorTextureCoordinates(): layer" << layer << "not found", {}); + CORRADE_ASSERT(hasAttribute(layerId, MaterialAttribute::LayerFactorTexture), + "Trade::MaterialData::layerFactorTextureCoordinates(): layer" << layer << "doesn't have a factor texture", {}); + /* Not delegating into layerFactorTextureCoordinates() because we have a + different variant of the assert here */ + if(Containers::Optional value = tryAttribute(layerId, MaterialAttribute::LayerFactorTextureCoordinates)) + return *value; + if(Containers::Optional value = tryAttribute(layerId, MaterialAttribute::TextureCoordinates)) + return *value; + return attributeOr(0, MaterialAttribute::TextureCoordinates, 0u); +} + UnsignedInt MaterialData::attributeCount(const UnsignedInt layer) const { CORRADE_ASSERT(layer < layerCount(), "Trade::MaterialData::attributeCount(): index" << layer << "out of range for" << layerCount() << "layers", {}); diff --git a/src/Magnum/Trade/MaterialData.h b/src/Magnum/Trade/MaterialData.h index 14376cbe1c..3df96010dc 100644 --- a/src/Magnum/Trade/MaterialData.h +++ b/src/Magnum/Trade/MaterialData.h @@ -692,6 +692,60 @@ enum class MaterialAttribute: UnsignedInt { */ EmissiveTextureCoordinates, + /** + * Layer intensity. @ref MaterialAttributeType::Float. + * + * Expected to be contained in additional layers, not the base material. + * The exact semantic of this attribute is layer-specific. If + * @ref MaterialAttribute::LayerFactorTexture is present as well, these two + * are multiplied together. + * @see @ref MaterialData::layerFactor() + */ + LayerFactor, + + /** + * Layer intensity texture, @ref MaterialAttributeType::UnsignedInt. + * + * Expected to be contained in additional layers, not the base material. + * The exact semantic of this attribute is layer-specific. If + * @ref MaterialAttribute::LayerFactor is present as well, these two are + * multiplied together. + * @see @ref MaterialData::layerFactorTexture() + */ + LayerFactorTexture, + + /** + * Layer intensity texture swizzle, @ref MaterialAttributeType::TextureSwizzle. + * + * Can be used to express arbitrary packing together with other maps in a + * single texture. A single-channel swizzle value is expected. If not + * present, @ref MaterialTextureSwizzle::R is assumed. + * @see @ref MaterialData::layerFactorTextureSwizzle() + */ + LayerFactorTextureSwizzle, + + /** + * Layer intensity texture transformation matrix, + * @ref MaterialAttributeType::Matrix3x3. + * + * Expected to be contained in additional layers, not the base material. + * Has a precedence over @ref MaterialAttribute::TextureMatrix if both are + * present. + * @see @ref MaterialData::layerFactorTextureMatrix() + */ + LayerFactorTextureMatrix, + + /** + * Layer intensity texture coordinate set index, + * @ref MaterialAttributeType::UnsignedInt. + * + * Expected to be contained in additional layers, not the base material. + * Has a precedence over @ref MaterialAttribute::TextureCoordinates if both + * are present. + * @see @ref MaterialData::layerFactorTextureCoordinates() + */ + LayerFactorTextureCoordinates, + /** * Common texture transformation matrix for all textures, * @ref MaterialAttributeType::Matrix3x3. @@ -704,7 +758,8 @@ enum class MaterialAttribute: UnsignedInt { * @ref MaterialAttribute::GlossinessTextureMatrix / * @ref MaterialAttribute::NormalTextureMatrix / * @ref MaterialAttribute::OcclusionTextureMatrix / - * @ref MaterialAttribute::EmissiveTextureMatrix have a precedence over + * @ref MaterialAttribute::EmissiveTextureMatrix / + * @ref MaterialAttribute::LayerFactorTextureMatrix have a precedence over * this attribute for given texture, if present. * @see @ref PhongMaterialData::textureMatrix(), * @ref PbrMetallicRoughnessMaterialData::textureMatrix(), @@ -724,7 +779,8 @@ enum class MaterialAttribute: UnsignedInt { * @ref MaterialAttribute::GlossinessTextureCoordinates / * @ref MaterialAttribute::NormalTextureCoordinates / * @ref MaterialAttribute::OcclusionTextureCoordinates / - * @ref MaterialAttribute::EmissiveTextureCoordinates have a precedence + * @ref MaterialAttribute::EmissiveTextureCoordinates / + * @ref MaterialAttribute::LayerFactorTextureCoordinates have a precedence * over this attribute for given texture, if present. * @see @ref PhongMaterialData::textureCoordinates(), * @ref PbrMetallicRoughnessMaterialData::textureCoordinates(), @@ -1484,6 +1540,128 @@ class MAGNUM_TRADE_EXPORT MaterialData { */ Containers::StringView layerName(UnsignedInt layer) const; + /** + * @brief Factor of given layer + * + * Convenience access to the @ref MaterialAttribute::LayerFactor + * attribute. If not present, the default is @cpp 1.0f @ce. The + * @p layer is expected to be smaller than @ref layerCount() const. + * + * If the layer has @ref MaterialAttribute::LayerFactorTexture, the + * factor and texture is meant to be multiplied together. + */ + Float layerFactor(UnsignedInt layer) const; + + /** + * @brief Factor of a named layer + * + * Convenience access to the @ref MaterialAttribute::LayerFactor + * attribute. If not present, the default is @cpp 1.0f @ce. The + * @p layer is expected to exist. + * + * If the layer has @ref MaterialAttribute::LayerFactorTexture, the + * factor and texture is meant to be multiplied together. + * @see @ref hasLayer() + */ + Float layerFactor(Containers::StringView layer) const; + + /** + * @brief Factor texture ID for given layer + * + * Available only if the @ref MaterialAttribute::LayerFactorTexture + * attribute is present. Meant to be multiplied with @ref layerFactor(). + * The @p layer is expected to be smaller than @ref layerCount(). + * @see @ref hasAttribute() + */ + UnsignedInt layerFactorTexture(UnsignedInt layer) const; + + /** + * @brief Factor texture ID for a named layer + * + * Available only if the @ref MaterialAttribute::LayerFactorTexture + * attribute is present. Meant to be multiplied with @ref layerFactor(). + * The @p layer is expected to exist. + * @see @ref hasLayer(), @ref hasAttribute() + */ + UnsignedInt layerFactorTexture(Containers::StringView layer) const; + + /** + * @brief Factor texture swizzle for given layer + * + * Convenience access to the @ref MaterialAttribute::LayerFactorTextureSwizzle + * attribute. If not present, the default is @ref MaterialTextureSwizzle::R. + * Available only if the @ref MaterialAttribute::LayerFactorTexture + * attribute is present. The @p layer is expected to be smaller than + * @ref layerCount(). + * @see @ref hasAttribute() + */ + MaterialTextureSwizzle layerFactorTextureSwizzle(UnsignedInt layer) const; + + /** + * @brief Factor texture swizzle for a named layer + * + * Convenience access to the @ref MaterialAttribute::LayerFactorTextureSwizzle + * attribute. If not present, the default is @ref MaterialTextureSwizzle::R. + * Available only if the @ref MaterialAttribute::LayerFactorTexture + * attribute is present. The @p layer is expected to exist. + * @see @ref hasLayer(), @ref hasAttribute() + */ + MaterialTextureSwizzle layerFactorTextureSwizzle(Containers::StringView layer) const; + + /** + * @brief Factor texture coordinate transformation matrix for given layer + * + * Convenience access to the @ref MaterialAttribute::LayerFactorTextureMatrix + * / @ref MaterialAttribute::TextureMatrix attributes in given layer or + * a @ref MaterialAttribute::TextureMatrix attribute in the base + * material. If neither is present, the default is an identity matrix. + * Available only if the @ref MaterialAttribute::LayerFactorTexture + * attribute is present. The @p layer is expected to be smaller than + * @ref layerCount(). + * @see @ref hasAttribute() + */ + Matrix3 layerFactorTextureMatrix(UnsignedInt layer) const; + + /** + * @brief Factor texture coordinate transformation matrix for a named layer + * + * Convenience access to the @ref MaterialAttribute::LayerFactorTextureMatrix + * / @ref MaterialAttribute::TextureMatrix attributes in given layer or + * a @ref MaterialAttribute::TextureMatrix attribute in the base + * material. If neither is present, the default is an identity matrix. + * Available only if the @ref MaterialAttribute::LayerFactorTexture + * attribute is present. The @p layer is expected to exist. + * @see @ref hasLayer(), @ref hasAttribute() + */ + Matrix3 layerFactorTextureMatrix(Containers::StringView layer) const; + + /** + * @brief Factor texture coordinate set for given layer + * + * Convenience access to the @ref MaterialAttribute::LayerFactorTextureCoordinates + * / @ref MaterialAttribute::TextureCoordinates attributes in given + * layer or a @ref MaterialAttribute::TextureCoordinates attribute in + * the base material. If neither is present, the default is @cpp 0 @ce. + * Available only if the @ref MaterialAttribute::LayerFactorTexture + * attribute is present. The @p layer is expected to be smaller than + * @ref layerCount(). + * @see @ref hasAttribute() + */ + UnsignedInt layerFactorTextureCoordinates(UnsignedInt layer) const; + + /** + * @brief Factor texture coordinate set for a named layer + * + * Convenience access to the @ref MaterialAttribute::LayerFactorTextureCoordinates + * / @ref MaterialAttribute::TextureCoordinates attributes in given + * layer or a @ref MaterialAttribute::TextureCoordinates attribute in + * the base material. If not present, the default is @cpp 0 @ce. + * Available only if the @ref MaterialAttribute::LayerFactorTexture + * attribute is present. The @p layer is expected to exist. + * @see @ref hasLayer(), @ref hasAttribute() + */ + UnsignedInt layerFactorTextureCoordinates(Containers::StringView layer) const; + /** * @brief Attribute count in given layer * diff --git a/src/Magnum/Trade/Test/MaterialDataTest.cpp b/src/Magnum/Trade/Test/MaterialDataTest.cpp index 47cb7b585d..2864bc0929 100644 --- a/src/Magnum/Trade/Test/MaterialDataTest.cpp +++ b/src/Magnum/Trade/Test/MaterialDataTest.cpp @@ -109,6 +109,14 @@ class MaterialDataTest: public TestSuite::Tester { void accessWrongPointerType(); void accessWrongTypeString(); + void accessLayers(); + void accessLayersDefaults(); + void accessLayersTextured(); + void accessLayersTexturedDefault(); + void accessLayersTexturedSingleMatrixCoordinates(); + void accessLayersTexturedBaseMaterialMatrixCoordinates(); + void accessLayersInvalidTextures(); + void accessLayerLayerNameInBaseMaterial(); void accessLayerEmptyLayer(); void accessLayerIndexOptional(); @@ -282,6 +290,14 @@ MaterialDataTest::MaterialDataTest() { &MaterialDataTest::accessWrongPointerType, &MaterialDataTest::accessWrongTypeString, + &MaterialDataTest::accessLayers, + &MaterialDataTest::accessLayersDefaults, + &MaterialDataTest::accessLayersTextured, + &MaterialDataTest::accessLayersTexturedDefault, + &MaterialDataTest::accessLayersTexturedSingleMatrixCoordinates, + &MaterialDataTest::accessLayersTexturedBaseMaterialMatrixCoordinates, + &MaterialDataTest::accessLayersInvalidTextures, + &MaterialDataTest::accessLayerLayerNameInBaseMaterial, &MaterialDataTest::accessLayerEmptyLayer, &MaterialDataTest::accessLayerIndexOptional, @@ -1648,6 +1664,160 @@ void MaterialDataTest::accessWrongTypeString() { "Trade::MaterialData::attribute(): Shininess of Trade::MaterialAttributeType::Float can't be retrieved as a string\n"); } +void MaterialDataTest::accessLayers() { + MaterialData data{{}, { + {MaterialAttribute::LayerName, "decals"}, + + {MaterialAttribute::LayerName, "ClearCoat"}, + {MaterialAttribute::LayerFactor, 0.5f}, + }, { + 0, 1, 3 + }}; + + CORRADE_COMPARE(data.layerCount(), 3); + + CORRADE_COMPARE(data.layerFactor(2), 0.5f); + CORRADE_COMPARE(data.layerFactor("ClearCoat"), 0.5f); +} + +void MaterialDataTest::accessLayersDefaults() { + MaterialData data{{}, { + {MaterialAttribute::LayerName, "ClearCoat"}, + }, { + 0, 1 + }}; + + CORRADE_COMPARE(data.layerCount(), 2); + + CORRADE_COMPARE(data.layerFactor(1), 1.0f); + CORRADE_COMPARE(data.layerFactor("ClearCoat"), 1.0f); +} + +void MaterialDataTest::accessLayersTextured() { + MaterialData data{{}, { + {MaterialAttribute::LayerName, "decals"}, + + {MaterialAttribute::LayerName, "ClearCoat"}, + {MaterialAttribute::LayerFactor, 0.5f}, + {MaterialAttribute::LayerFactorTexture, 4u}, + {MaterialAttribute::LayerFactorTextureSwizzle, MaterialTextureSwizzle::A}, + {MaterialAttribute::LayerFactorTextureMatrix, Matrix3::scaling({0.5f, 1.0f})}, + {MaterialAttribute::LayerFactorTextureCoordinates, 2u}, + }, { + 0, 1, 7 + }}; + + CORRADE_COMPARE(data.layerCount(), 3); + + CORRADE_COMPARE(data.layerFactor(2), 0.5f); + CORRADE_COMPARE(data.layerFactor("ClearCoat"), 0.5f); + + CORRADE_COMPARE(data.layerFactorTexture(2), 4u); + CORRADE_COMPARE(data.layerFactorTexture("ClearCoat"), 4u); + + CORRADE_COMPARE(data.layerFactorTextureSwizzle(2), MaterialTextureSwizzle::A); + CORRADE_COMPARE(data.layerFactorTextureSwizzle("ClearCoat"), MaterialTextureSwizzle::A); + + CORRADE_COMPARE(data.layerFactorTextureMatrix(2), Matrix3::scaling({0.5f, 1.0f})); + CORRADE_COMPARE(data.layerFactorTextureMatrix("ClearCoat"), Matrix3::scaling({0.5f, 1.0f})); + + CORRADE_COMPARE(data.layerFactorTextureCoordinates(2), 2u); + CORRADE_COMPARE(data.layerFactorTextureCoordinates("ClearCoat"), 2u); +} + +void MaterialDataTest::accessLayersTexturedDefault() { + MaterialData data{{}, { + {MaterialAttribute::LayerName, "decals"}, + + {MaterialAttribute::LayerName, "ClearCoat"}, + {MaterialAttribute::LayerFactorTexture, 3u}, + }, { + 0, 1, 3 + }}; + + CORRADE_COMPARE(data.layerCount(), 3); + + CORRADE_COMPARE(data.layerFactor(2), 1.0f); + CORRADE_COMPARE(data.layerFactor("ClearCoat"), 1.0f); + + CORRADE_COMPARE(data.layerFactorTexture(2), 3u); + CORRADE_COMPARE(data.layerFactorTexture("ClearCoat"), 3u); + + CORRADE_COMPARE(data.layerFactorTextureSwizzle(2), MaterialTextureSwizzle::R); + CORRADE_COMPARE(data.layerFactorTextureSwizzle("ClearCoat"), MaterialTextureSwizzle::R); + + CORRADE_COMPARE(data.layerFactorTextureMatrix(2), Matrix3{}); + CORRADE_COMPARE(data.layerFactorTextureMatrix("ClearCoat"), Matrix3{}); + + CORRADE_COMPARE(data.layerFactorTextureCoordinates(2), 0); + CORRADE_COMPARE(data.layerFactorTextureCoordinates("ClearCoat"), 0); +} + +void MaterialDataTest::accessLayersTexturedSingleMatrixCoordinates() { + MaterialData data{{}, { + {MaterialAttribute::LayerName, "ClearCoat"}, + {MaterialAttribute::LayerFactorTexture, 4u}, + {MaterialAttribute::TextureMatrix, Matrix3::scaling({0.5f, 1.0f})}, + {MaterialAttribute::TextureCoordinates, 2u}, + }, { + 0, 4 + }}; + + CORRADE_COMPARE(data.layerFactorTextureMatrix(1), Matrix3::scaling({0.5f, 1.0f})); + CORRADE_COMPARE(data.layerFactorTextureMatrix("ClearCoat"), Matrix3::scaling({0.5f, 1.0f})); + + CORRADE_COMPARE(data.layerFactorTextureCoordinates(1), 2u); + CORRADE_COMPARE(data.layerFactorTextureCoordinates("ClearCoat"), 2u); +} + +void MaterialDataTest::accessLayersTexturedBaseMaterialMatrixCoordinates() { + MaterialData data{{}, { + {MaterialAttribute::TextureMatrix, Matrix3::scaling({0.5f, 1.0f})}, + {MaterialAttribute::TextureCoordinates, 2u}, + + {MaterialAttribute::LayerName, "ClearCoat"}, + {MaterialAttribute::LayerFactorTexture, 4u}, + }, { + 2, 4 + }}; + + CORRADE_COMPARE(data.layerFactorTextureMatrix(1), Matrix3::scaling({0.5f, 1.0f})); + CORRADE_COMPARE(data.layerFactorTextureMatrix("ClearCoat"), Matrix3::scaling({0.5f, 1.0f})); + + CORRADE_COMPARE(data.layerFactorTextureCoordinates(1), 2u); + CORRADE_COMPARE(data.layerFactorTextureCoordinates("ClearCoat"), 2u); +} + +void MaterialDataTest::accessLayersInvalidTextures() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + MaterialData data{{}, { + {MaterialAttribute::LayerName, "ClearCoat"}, + }, {0, 1}}; + + std::ostringstream out; + Error redirectError{&out}; + data.layerFactorTexture(1); + data.layerFactorTexture("ClearCoat"); + data.layerFactorTextureSwizzle(1); + data.layerFactorTextureSwizzle("ClearCoat"); + data.layerFactorTextureMatrix(1); + data.layerFactorTextureMatrix("ClearCoat"); + data.layerFactorTextureCoordinates(1); + data.layerFactorTextureCoordinates("ClearCoat"); + CORRADE_COMPARE(out.str(), + "Trade::MaterialData::attribute(): attribute LayerFactorTexture not found in layer 1\n" + "Trade::MaterialData::attribute(): attribute LayerFactorTexture not found in layer ClearCoat\n" + "Trade::MaterialData::layerFactorTextureSwizzle(): layer 1 doesn't have a factor texture\n" + "Trade::MaterialData::layerFactorTextureSwizzle(): layer ClearCoat doesn't have a factor texture\n" + "Trade::MaterialData::layerFactorTextureMatrix(): layer 1 doesn't have a factor texture\n" + "Trade::MaterialData::layerFactorTextureMatrix(): layer ClearCoat doesn't have a factor texture\n" + "Trade::MaterialData::layerFactorTextureCoordinates(): layer 1 doesn't have a factor texture\n" + "Trade::MaterialData::layerFactorTextureCoordinates(): layer ClearCoat doesn't have a factor texture\n"); +} + void MaterialDataTest::accessLayerLayerNameInBaseMaterial() { MaterialData data{{}, { {MaterialAttribute::Shininess, 50.0f}, @@ -1743,6 +1913,11 @@ void MaterialDataTest::accessLayerOutOfBounds() { std::ostringstream out; Error redirectError{&out}; data.layerName(2); + data.layerFactor(2); + data.layerFactorTexture(2); + data.layerFactorTextureSwizzle(2); + data.layerFactorTextureMatrix(2); + data.layerFactorTextureCoordinates(2); data.attributeCount(2); data.hasAttribute(2, "AlphaMask"); data.hasAttribute(2, MaterialAttribute::AlphaMask); @@ -1767,6 +1942,11 @@ void MaterialDataTest::accessLayerOutOfBounds() { data.attributeOr(2, MaterialAttribute::AlphaMask, false); CORRADE_COMPARE(out.str(), "Trade::MaterialData::layerName(): index 2 out of range for 2 layers\n" + "Trade::MaterialData::layerFactor(): index 2 out of range for 2 layers\n" + "Trade::MaterialData::layerFactorTexture(): index 2 out of range for 2 layers\n" + "Trade::MaterialData::layerFactorTextureSwizzle(): index 2 out of range for 2 layers\n" + "Trade::MaterialData::layerFactorTextureMatrix(): index 2 out of range for 2 layers\n" + "Trade::MaterialData::layerFactorTextureCoordinates(): index 2 out of range for 2 layers\n" "Trade::MaterialData::attributeCount(): index 2 out of range for 2 layers\n" "Trade::MaterialData::hasAttribute(): index 2 out of range for 2 layers\n" "Trade::MaterialData::hasAttribute(): index 2 out of range for 2 layers\n" @@ -1803,6 +1983,12 @@ void MaterialDataTest::accessLayerNotFound() { std::ostringstream out; Error redirectError{&out}; + data.layerId("ClearCoat"); + data.layerFactor("ClearCoat"); + data.layerFactorTexture("ClearCoat"); + data.layerFactorTextureSwizzle("ClearCoat"); + data.layerFactorTextureMatrix("ClearCoat"); + data.layerFactorTextureCoordinates("ClearCoat"); data.attributeCount("ClearCoat"); data.hasAttribute("ClearCoat", "AlphaMask"); data.hasAttribute("ClearCoat", MaterialAttribute::AlphaMask); @@ -1826,6 +2012,12 @@ void MaterialDataTest::accessLayerNotFound() { data.attributeOr("ClearCoat", "AlphaMask", false); data.attributeOr("ClearCoat", MaterialAttribute::AlphaMask, false); CORRADE_COMPARE(out.str(), + "Trade::MaterialData::layerId(): layer ClearCoat not found\n" + "Trade::MaterialData::layerFactor(): layer ClearCoat not found\n" + "Trade::MaterialData::layerFactorTexture(): layer ClearCoat not found\n" + "Trade::MaterialData::layerFactorTextureSwizzle(): layer ClearCoat not found\n" + "Trade::MaterialData::layerFactorTextureMatrix(): layer ClearCoat not found\n" + "Trade::MaterialData::layerFactorTextureCoordinates(): layer ClearCoat not found\n" "Trade::MaterialData::attributeCount(): layer ClearCoat not found\n" "Trade::MaterialData::hasAttribute(): layer ClearCoat not found\n" "Trade::MaterialData::hasAttribute(): layer ClearCoat not found\n" From 106de5fa154541227e05f3144b52b0f7e5e397bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 9 Aug 2020 01:03:19 +0200 Subject: [PATCH 20/36] Trade: add named material layer support and a first layer. This is a bit huge because of all the new overloads that take a MaterialLayer instead of a string, but all that is just boring boilerplate. Additionally this: * exposes glTF clear coat parameters (which, interestingly enough, reuse existing attributes and don't introduce any new) * provides a convenience wrapper in PbrClearCoatMaterialData * and a convenience base for material layer wrappers that redirect all APIs with implicit layer argument to desired layer instead of the base material --- doc/changelog.dox | 8 +- src/Magnum/Trade/CMakeLists.txt | 3 + .../materialLayerProperties.hpp | 29 ++ src/Magnum/Trade/MaterialData.cpp | 159 +++++- src/Magnum/Trade/MaterialData.h | 133 ++++- src/Magnum/Trade/MaterialLayerData.h | 255 ++++++++++ src/Magnum/Trade/PbrClearCoatMaterialData.cpp | 110 +++++ src/Magnum/Trade/PbrClearCoatMaterialData.h | 193 ++++++++ src/Magnum/Trade/Test/MaterialDataTest.cpp | 457 +++++++++++++++++- src/Magnum/Trade/Trade.h | 2 + 10 files changed, 1341 insertions(+), 8 deletions(-) create mode 100644 src/Magnum/Trade/Implementation/materialLayerProperties.hpp create mode 100644 src/Magnum/Trade/MaterialLayerData.h create mode 100644 src/Magnum/Trade/PbrClearCoatMaterialData.cpp create mode 100644 src/Magnum/Trade/PbrClearCoatMaterialData.h diff --git a/doc/changelog.dox b/doc/changelog.dox index 412efeb4f2..5ebad732ed 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -77,10 +77,10 @@ See also: - A new, redesigned @ref Trade::MaterialData class allowing to store custom material attributes as well as more material types together in a single instance; plus new @ref Trade::FlatMaterialData, - @ref Trade::PbrMetallicRoughnessMaterialData and - @ref Trade::PbrSpecularGlossinessMaterialData convenience accessor APIs - similar to @ref Trade::PhongMaterialData. See - [mosra/magnum#459](https://github.com/mosra/magnum/pull/459). + @ref Trade::PbrMetallicRoughnessMaterialData, + @ref Trade::PbrSpecularGlossinessMaterialData and + @ref Trade::PbrClearCoatMaterialData convenience accessor APIs similar to + @ref Trade::PhongMaterialData. See [mosra/magnum#459](https://github.com/mosra/magnum/pull/459). - Added @ref Trade::PhongMaterialData::hasSpecularTexture(), @ref Trade::PhongMaterialData::specularTextureSwizzle() and @ref Trade::PhongMaterialData::normalTextureSwizzle() to make new features diff --git a/src/Magnum/Trade/CMakeLists.txt b/src/Magnum/Trade/CMakeLists.txt index 699bdaad8f..d15b58eb86 100644 --- a/src/Magnum/Trade/CMakeLists.txt +++ b/src/Magnum/Trade/CMakeLists.txt @@ -46,6 +46,7 @@ set(MagnumTrade_GracefulAssert_SRCS MeshData.cpp ObjectData2D.cpp ObjectData3D.cpp + PbrClearCoatMaterialData.cpp PbrMetallicRoughnessMaterialData.cpp PbrSpecularGlossinessMaterialData.cpp PhongMaterialData.cpp) @@ -62,11 +63,13 @@ set(MagnumTrade_HEADERS ImageData.h LightData.h MaterialData.h + MaterialLayerData.h MeshData.h MeshObjectData2D.h MeshObjectData3D.h ObjectData2D.h ObjectData3D.h + PbrClearCoatMaterialData.h PbrMetallicRoughnessMaterialData.h PbrSpecularGlossinessMaterialData.h PhongMaterialData.h diff --git a/src/Magnum/Trade/Implementation/materialLayerProperties.hpp b/src/Magnum/Trade/Implementation/materialLayerProperties.hpp new file mode 100644 index 0000000000..2541a090e9 --- /dev/null +++ b/src/Magnum/Trade/Implementation/materialLayerProperties.hpp @@ -0,0 +1,29 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + 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(ClearCoat) +#endif diff --git a/src/Magnum/Trade/MaterialData.cpp b/src/Magnum/Trade/MaterialData.cpp index fe7e21ea9e..88f08df4f5 100644 --- a/src/Magnum/Trade/MaterialData.cpp +++ b/src/Magnum/Trade/MaterialData.cpp @@ -42,6 +42,12 @@ namespace { using namespace Containers::Literals; #ifndef DOXYGEN_GENERATING_OUTPUT /* It gets *really* confused */ +constexpr Containers::StringView LayerMap[]{ + #define _c(name) #name ## _s, + #include "Magnum/Trade/Implementation/materialLayerProperties.hpp" + #undef _c +}; + constexpr struct { Containers::StringView name; MaterialAttributeType type; @@ -168,6 +174,12 @@ MaterialAttributeData::MaterialAttributeData(const MaterialAttribute name, const } } +/* Interestingly enough, [[ is an invalid syntax in C++11? */ +MaterialAttributeData::MaterialAttributeData(const MaterialLayer layerName) noexcept: MaterialAttributeData{MaterialAttribute::LayerName, LayerMap[([](const MaterialLayer layerName){ + CORRADE_ASSERT(UnsignedInt(layerName) - 1 < Containers::arraySize(LayerMap), "Trade::MaterialAttributeData: invalid name" << layerName, UnsignedInt{}); + return UnsignedInt(layerName) - 1; +}(layerName))]} {} + const void* MaterialAttributeData::value() const { if(_data.type == MaterialAttributeType::String) return _data.s.nameValue + Implementation::MaterialAttributeDataSize - _data.s.size - 3; @@ -263,6 +275,14 @@ MaterialData::~MaterialData() = default; MaterialData& MaterialData::operator=(MaterialData&&) noexcept = default; +Containers::StringView MaterialData::layerString(const MaterialLayer name) { + #ifndef CORRADE_NO_ASSERT + if(UnsignedInt(name) - 1 >= Containers::arraySize(LayerMap)) + return nullptr; + #endif + return LayerMap[UnsignedInt(name) - 1]; +} + Containers::StringView MaterialData::attributeString(const MaterialAttribute name) { #ifndef CORRADE_NO_ASSERT if(UnsignedInt(name) - 1 >= Containers::arraySize(AttributeMap)) @@ -285,6 +305,12 @@ bool MaterialData::hasLayer(const Containers::StringView layer) const { return layerFor(layer) != ~UnsignedInt{}; } +bool MaterialData::hasLayer(const MaterialLayer layer) const { + const Containers::StringView string = layerString(layer); + CORRADE_ASSERT(string.data(), "Trade::MaterialData::hasLayer(): invalid name" << layer, {}); + return hasLayer(string); +} + UnsignedInt MaterialData::layerId(const Containers::StringView layer) const { const UnsignedInt id = layerFor(layer); CORRADE_ASSERT(id != ~UnsignedInt{}, @@ -292,6 +318,12 @@ UnsignedInt MaterialData::layerId(const Containers::StringView layer) const { return id; } +UnsignedInt MaterialData::layerId(const MaterialLayer layer) const { + const Containers::StringView string = layerString(layer); + CORRADE_ASSERT(string.data(), "Trade::MaterialData::layerId(): invalid name" << layer, {}); + return layerId(string); +} + Containers::StringView MaterialData::layerName(const UnsignedInt layer) const { CORRADE_ASSERT(layer < layerCount(), "Trade::MaterialData::layerName(): index" << layer << "out of range for" << layerCount() << "layers", {}); @@ -314,6 +346,12 @@ Float MaterialData::layerFactor(const Containers::StringView layer) const { return layerFactor(layerId); } +Float MaterialData::layerFactor(const MaterialLayer layer) const { + const Containers::StringView string = layerString(layer); + CORRADE_ASSERT(string.data(), "Trade::MaterialData::layerFactor(): invalid name" << layer, {}); + return layerFactor(string); +} + UnsignedInt MaterialData::layerFactorTexture(const UnsignedInt layer) const { CORRADE_ASSERT(layer < layerCount(), "Trade::MaterialData::layerFactorTexture(): index" << layer << "out of range for" << layerCount() << "layers", {}); @@ -329,6 +367,12 @@ UnsignedInt MaterialData::layerFactorTexture(const Containers::StringView layer) return attribute(layer, MaterialAttribute::LayerFactorTexture); } +UnsignedInt MaterialData::layerFactorTexture(const MaterialLayer layer) const { + const Containers::StringView string = layerString(layer); + CORRADE_ASSERT(string.data(), "Trade::MaterialData::layerFactorTexture(): invalid name" << layer, {}); + return layerFactorTexture(string); +} + MaterialTextureSwizzle MaterialData::layerFactorTextureSwizzle(const UnsignedInt layer) const { CORRADE_ASSERT(layer < layerCount(), "Trade::MaterialData::layerFactorTextureSwizzle(): index" << layer << "out of range for" << layerCount() << "layers", {}); @@ -348,6 +392,12 @@ MaterialTextureSwizzle MaterialData::layerFactorTextureSwizzle(const Containers: return attributeOr(layer, MaterialAttribute::LayerFactorTextureSwizzle, MaterialTextureSwizzle::R); } +MaterialTextureSwizzle MaterialData::layerFactorTextureSwizzle(const MaterialLayer layer) const { + const Containers::StringView string = layerString(layer); + CORRADE_ASSERT(string.data(), "Trade::MaterialData::layerFactorTextureSwizzle(): invalid name" << layer, {}); + return layerFactorTextureSwizzle(string); +} + Matrix3 MaterialData::layerFactorTextureMatrix(const UnsignedInt layer) const { CORRADE_ASSERT(layer < layerCount(), "Trade::MaterialData::layerFactorTextureMatrix(): index" << layer << "out of range for" << layerCount() << "layers", {}); @@ -375,6 +425,12 @@ Matrix3 MaterialData::layerFactorTextureMatrix(const Containers::StringView laye return attributeOr(0, MaterialAttribute::TextureMatrix, Matrix3{}); } +Matrix3 MaterialData::layerFactorTextureMatrix(const MaterialLayer layer) const { + const Containers::StringView string = layerString(layer); + CORRADE_ASSERT(string.data(), "Trade::MaterialData::layerFactorTextureMatrix(): invalid name" << layer, {}); + return layerFactorTextureMatrix(string); +} + UnsignedInt MaterialData::layerFactorTextureCoordinates(const UnsignedInt layer) const { CORRADE_ASSERT(layer < layerCount(), "Trade::MaterialData::layerFactorTextureCoordinates(): index" << layer << "out of range for" << layerCount() << "layers", {}); @@ -402,6 +458,12 @@ UnsignedInt MaterialData::layerFactorTextureCoordinates(const Containers::String return attributeOr(0, MaterialAttribute::TextureCoordinates, 0u); } +UnsignedInt MaterialData::layerFactorTextureCoordinates(const MaterialLayer layer) const { + const Containers::StringView string = layerString(layer); + CORRADE_ASSERT(string.data(), "Trade::MaterialData::layerFactorTextureCoordinates(): invalid name" << layer, {}); + return layerFactorTextureCoordinates(string); +} + UnsignedInt MaterialData::attributeCount(const UnsignedInt layer) const { CORRADE_ASSERT(layer < layerCount(), "Trade::MaterialData::attributeCount(): index" << layer << "out of range for" << layerCount() << "layers", {}); @@ -417,6 +479,12 @@ UnsignedInt MaterialData::attributeCount(const Containers::StringView layer) con return attributeCount(layerId); } +UnsignedInt MaterialData::attributeCount(const MaterialLayer layer) const { + const Containers::StringView string = layerString(layer); + CORRADE_ASSERT(string.data(), "Trade::MaterialData::attributeCount(): invalid name" << layer, {}); + return attributeCount(string); +} + UnsignedInt MaterialData::attributeFor(const UnsignedInt layer, const Containers::StringView name) const { const MaterialAttributeData* begin = _data.begin() + (layer && _layerOffsets ? _layerOffsets[layer - 1] : 0); @@ -454,6 +522,18 @@ bool MaterialData::hasAttribute(const Containers::StringView layer, const Materi return hasAttribute(layer, string); } +bool MaterialData::hasAttribute(const MaterialLayer layer, const Containers::StringView name) const { + const Containers::StringView string = layerString(layer); + CORRADE_ASSERT(string.data(), "Trade::MaterialData::hasAttribute(): invalid name" << layer, {}); + return hasAttribute(string, name); +} + +bool MaterialData::hasAttribute(const MaterialLayer layer, const MaterialAttribute name) const { + const Containers::StringView string = layerString(layer); + CORRADE_ASSERT(string.data(), "Trade::MaterialData::hasAttribute(): invalid name" << layer, {}); + return hasAttribute(string, name); +} + UnsignedInt MaterialData::attributeId(const UnsignedInt layer, const Containers::StringView name) const { CORRADE_ASSERT(layer < layerCount(), "Trade::MaterialData::attributeId(): index" << layer << "out of range for" << layerCount() << "layers", {}); @@ -485,6 +565,18 @@ UnsignedInt MaterialData::attributeId(const Containers::StringView layer, const return attributeId(layer, string); } +UnsignedInt MaterialData::attributeId(const MaterialLayer layer, const Containers::StringView name) const { + const Containers::StringView string = layerString(layer); + CORRADE_ASSERT(string.data(), "Trade::MaterialData::attributeId(): invalid name" << layer, {}); + return attributeId(string, name); +} + +UnsignedInt MaterialData::attributeId(const MaterialLayer layer, const MaterialAttribute name) const { + const Containers::StringView string = layerString(layer); + CORRADE_ASSERT(string.data(), "Trade::MaterialData::attributeId(): invalid name" << layer, {}); + return attributeId(string, name); +} + Containers::StringView MaterialData::attributeName(const UnsignedInt layer, const UnsignedInt id) const { CORRADE_ASSERT(layer < layerCount(), "Trade::MaterialData::attributeName(): index" << layer << "out of range for" << layerCount() << "layers", {}); @@ -502,6 +594,12 @@ Containers::StringView MaterialData::attributeName(const Containers::StringView return _data[layerOffset(layerId) + id]._data.data + 1; } +Containers::StringView MaterialData::attributeName(const MaterialLayer layer, const UnsignedInt id) const { + const Containers::StringView string = layerString(layer); + CORRADE_ASSERT(string.data(), "Trade::MaterialData::attributeName(): invalid name" << layer, {}); + return attributeName(string, id); +} + MaterialAttributeType MaterialData::attributeType(const UnsignedInt layer, const UnsignedInt id) const { CORRADE_ASSERT(layer < layerCount(), "Trade::MaterialData::attributeType(): index" << layer << "out of range for" << layerCount() << "layers", {}); @@ -550,6 +648,24 @@ MaterialAttributeType MaterialData::attributeType(const Containers::StringView l return attributeType(layer, string); } +MaterialAttributeType MaterialData::attributeType(const MaterialLayer layer, const UnsignedInt id) const { + const Containers::StringView string = layerString(layer); + CORRADE_ASSERT(string.data(), "Trade::MaterialData::attributeType(): invalid name" << layer, {}); + return attributeType(string, id); +} + +MaterialAttributeType MaterialData::attributeType(const MaterialLayer layer, const Containers::StringView name) const { + const Containers::StringView string = layerString(layer); + CORRADE_ASSERT(string.data(), "Trade::MaterialData::attributeType(): invalid name" << layer, {}); + return attributeType(string, name); +} + +MaterialAttributeType MaterialData::attributeType(const MaterialLayer layer, const MaterialAttribute name) const { + const Containers::StringView string = layerString(layer); + CORRADE_ASSERT(string.data(), "Trade::MaterialData::attributeType(): invalid name" << layer, {}); + return attributeType(string, name); +} + const void* MaterialData::attribute(const UnsignedInt layer, const UnsignedInt id) const { CORRADE_ASSERT(layer < layerCount(), "Trade::MaterialData::attribute(): index" << layer << "out of range for" << layerCount() << "layers", {}); @@ -598,6 +714,24 @@ const void* MaterialData::attribute(const Containers::StringView layer, const Ma return attribute(layer, string); } +const void* MaterialData::attribute(const MaterialLayer layer, const UnsignedInt id) const { + const Containers::StringView string = layerString(layer); + CORRADE_ASSERT(string.data(), "Trade::MaterialData::attribute(): invalid name" << layer, {}); + return attribute(string, id); +} + +const void* MaterialData::attribute(const MaterialLayer layer, const Containers::StringView name) const { + const Containers::StringView string = layerString(layer); + CORRADE_ASSERT(string.data(), "Trade::MaterialData::attribute(): invalid name" << layer, {}); + return attribute(string, name); +} + +const void* MaterialData::attribute(const MaterialLayer layer, const MaterialAttribute name) const { + const Containers::StringView string = layerString(layer); + CORRADE_ASSERT(string.data(), "Trade::MaterialData::attribute(): invalid name" << layer, {}); + return attribute(string, name); +} + #ifndef DOXYGEN_GENERATING_OUTPUT /* On Windows (MSVC, clang-cl and MinGw) it needs an explicit export otherwise the symbol doesn't get exported. */ @@ -644,6 +778,18 @@ const void* MaterialData::tryAttribute(const Containers::StringView layer, const return tryAttribute(layer, string); } +const void* MaterialData::tryAttribute(const MaterialLayer layer, const Containers::StringView name) const { + const Containers::StringView string = layerString(layer); + CORRADE_ASSERT(string.data(), "Trade::MaterialData::tryAttribute(): invalid name" << layer, {}); + return tryAttribute(string, name); +} + +const void* MaterialData::tryAttribute(const MaterialLayer layer, const MaterialAttribute name) const { + const Containers::StringView string = layerString(layer); + CORRADE_ASSERT(string.data(), "Trade::MaterialData::tryAttribute(): invalid name" << layer, {}); + return tryAttribute(string, name); +} + #ifdef MAGNUM_BUILD_DEPRECATED CORRADE_IGNORE_DEPRECATED_PUSH MaterialData::Flags MaterialData::flags() const { @@ -679,6 +825,15 @@ Containers::Array MaterialData::releaseAttributeData() { return std::move(_data); } +Debug& operator<<(Debug& debug, const MaterialLayer value) { + debug << "Trade::MaterialLayer" << Debug::nospace; + + if(UnsignedInt(value) - 1 >= Containers::arraySize(LayerMap)) + return debug << "(" << Debug::nospace << reinterpret_cast(UnsignedInt(value)) << Debug::nospace << ")"; + + return debug << "::" << Debug::nospace << LayerMap[UnsignedInt(value) - 1]; +} + Debug& operator<<(Debug& debug, const MaterialAttribute value) { debug << "Trade::MaterialAttribute" << Debug::nospace; @@ -753,6 +908,7 @@ Debug& operator<<(Debug& debug, const MaterialType value) { _c(Phong) _c(PbrMetallicRoughness) _c(PbrSpecularGlossiness) + _c(PbrClearCoat) #undef _c /* LCOV_EXCL_STOP */ } @@ -765,7 +921,8 @@ Debug& operator<<(Debug& debug, const MaterialTypes value) { MaterialType::Flat, MaterialType::Phong, MaterialType::PbrMetallicRoughness, - MaterialType::PbrSpecularGlossiness + MaterialType::PbrSpecularGlossiness, + MaterialType::PbrClearCoat }); } diff --git a/src/Magnum/Trade/MaterialData.h b/src/Magnum/Trade/MaterialData.h index 3df96010dc..9b5dd48d0a 100644 --- a/src/Magnum/Trade/MaterialData.h +++ b/src/Magnum/Trade/MaterialData.h @@ -26,7 +26,7 @@ */ /** @file - * @brief Class @ref Magnum::Trade::MaterialData, @ref Magnum::Trade::MaterialAttributeData, enum @ref Magnum::Trade::MaterialAttribute, @ref Magnum::Trade::MaterialTextureSwizzle, @ref Magnum::Trade::MaterialAttributeType + * @brief Class @ref Magnum::Trade::MaterialData, @ref Magnum::Trade::MaterialAttributeData, enum @ref Magnum::Trade::MaterialLayer, @ref Magnum::Trade::MaterialAttribute, @ref Magnum::Trade::MaterialTextureSwizzle, @ref Magnum::Trade::MaterialAttributeType * @m_since_latest */ @@ -43,6 +43,48 @@ namespace Magnum { namespace Trade { +/** +@brief Material layer name +@m_since_latest + +Convenience aliases to actual layer name strings. The alias is in the same form +and capitalization --- so for example @ref MaterialLayer::ClearCoat is an alias +for @cpp "ClearCoat" @ce. Each layer is expected to contain (a subset of) the +@ref MaterialAttribute::LayerName, @ref MaterialAttribute::LayerFactor, +@ref MaterialAttribute::LayerFactorTexture, +@ref MaterialAttribute::LayerFactorTextureSwizzle, +@ref MaterialAttribute::LayerFactorTextureMatrix, +@ref MaterialAttribute::LayerFactorTextureCoordinates attributes in addition to +what's specified for a particular named layer. +@see @ref MaterialData, @ref MaterialData::layerName(), @ref MaterialLayerData +*/ +enum class MaterialLayer: UnsignedInt { + /* Zero used for an invalid value */ + + /** + * Clear coat material layer. + * + * Expected to contain (a subset of) the + * @ref MaterialAttribute::Roughness, + * @ref MaterialAttribute::RoughnessTexture, + * @ref MaterialAttribute::RoughnessTextureSwizzle, + * @ref MaterialAttribute::RoughnessTextureMatrix, + * @ref MaterialAttribute::RoughnessTextureCoordinates, + * @ref MaterialAttribute::NormalTexture, + * @ref MaterialAttribute::NormalTextureSwizzle, + * @ref MaterialAttribute::NormalTextureMatrix and + * @ref MaterialAttribute::NormalTextureCoordinates attributes. + * @see @ref PbrClearCoatMaterialData + */ + ClearCoat = 1, +}; + +/** +@debugoperatorenum{MaterialLayer} +@m_since_latest +*/ +MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, MaterialLayer value); + /** @brief Material attribute name @m_since_latest @@ -1055,6 +1097,16 @@ class MAGNUM_TRADE_EXPORT MaterialAttributeData { */ /*implicit*/ MaterialAttributeData(MaterialAttribute name, MaterialAttributeType type, const void* value) noexcept; + /** + * @brief Construct a layer name attribute + * @param layerName Material layer name + * + * Equivalent to calling @ref MaterialAttributeData(MaterialAttribute, Containers::StringView) + * with @ref MaterialAttribute::LayerName and a string corresponding to + * @p layerName. + */ + /*implicit*/ MaterialAttributeData(MaterialLayer layerName) noexcept; + /** @brief Attribute type */ MaterialAttributeType type() const { return _data.type; } @@ -1232,7 +1284,13 @@ enum class MaterialType: UnsignedInt { * PBR specular/glossiness. Use @ref PbrSpecularGlossinessMaterialData for * convenience attribute access. */ - PbrSpecularGlossiness = 1 << 3 + PbrSpecularGlossiness = 1 << 3, + + /** + * PBR clear coat layer. Use @ref PbrClearCoatMaterialData for convenience + * attribute access. + */ + PbrClearCoat = 1 << 4 }; /** @debugoperatorenum{MaterialType} */ @@ -1516,6 +1574,7 @@ class MAGNUM_TRADE_EXPORT MaterialData { * @see @ref hasAttribute() */ bool hasLayer(Containers::StringView layer) const; + bool hasLayer(MaterialLayer layer) const; /**< @overload */ /** * @brief ID of a named layer @@ -1524,6 +1583,7 @@ class MAGNUM_TRADE_EXPORT MaterialData { * @see @ref hasLayer() */ UnsignedInt layerId(Containers::StringView layer) const; + UnsignedInt layerId(MaterialLayer layer) const; /**< @overload */ /** * @brief Layer name @@ -1564,6 +1624,7 @@ class MAGNUM_TRADE_EXPORT MaterialData { * @see @ref hasLayer() */ Float layerFactor(Containers::StringView layer) const; + Float layerFactor(MaterialLayer layer) const; /**< @overload */ /** * @brief Factor texture ID for given layer @@ -1584,6 +1645,7 @@ class MAGNUM_TRADE_EXPORT MaterialData { * @see @ref hasLayer(), @ref hasAttribute() */ UnsignedInt layerFactorTexture(Containers::StringView layer) const; + UnsignedInt layerFactorTexture(MaterialLayer layer) const; /**< @overload */ /** * @brief Factor texture swizzle for given layer @@ -1607,6 +1669,7 @@ class MAGNUM_TRADE_EXPORT MaterialData { * @see @ref hasLayer(), @ref hasAttribute() */ MaterialTextureSwizzle layerFactorTextureSwizzle(Containers::StringView layer) const; + MaterialTextureSwizzle layerFactorTextureSwizzle(MaterialLayer layer) const; /**< @overload */ /** * @brief Factor texture coordinate transformation matrix for given layer @@ -1634,6 +1697,7 @@ class MAGNUM_TRADE_EXPORT MaterialData { * @see @ref hasLayer(), @ref hasAttribute() */ Matrix3 layerFactorTextureMatrix(Containers::StringView layer) const; + Matrix3 layerFactorTextureMatrix(MaterialLayer layer) const; /**< @overload */ /** * @brief Factor texture coordinate set for given layer @@ -1661,6 +1725,7 @@ class MAGNUM_TRADE_EXPORT MaterialData { * @see @ref hasLayer(), @ref hasAttribute() */ UnsignedInt layerFactorTextureCoordinates(Containers::StringView layer) const; + UnsignedInt layerFactorTextureCoordinates(MaterialLayer layer) const; /**< @overload */ /** * @brief Attribute count in given layer @@ -1676,6 +1741,7 @@ class MAGNUM_TRADE_EXPORT MaterialData { * @see @ref hasLayer() */ UnsignedInt attributeCount(Containers::StringView layer) const; + UnsignedInt attributeCount(MaterialLayer layer) const; /**< @overload */ /** * @brief Attribute count in the base material @@ -1702,6 +1768,8 @@ class MAGNUM_TRADE_EXPORT MaterialData { */ bool hasAttribute(Containers::StringView layer, Containers::StringView name) const; bool hasAttribute(Containers::StringView layer, MaterialAttribute name) const; /**< @overload */ + bool hasAttribute(MaterialLayer layer, Containers::StringView name) const; /**< @overload */ + bool hasAttribute(MaterialLayer layer, MaterialAttribute name) const; /**< @overload */ /** * @brief Whether the base material has given attribute @@ -1736,6 +1804,8 @@ class MAGNUM_TRADE_EXPORT MaterialData { */ UnsignedInt attributeId(Containers::StringView layer, Containers::StringView name) const; UnsignedInt attributeId(Containers::StringView layer, MaterialAttribute name) const; /**< @overload */ + UnsignedInt attributeId(MaterialLayer layer, Containers::StringView name) const; /**< @overload */ + UnsignedInt attributeId(MaterialLayer layer, MaterialAttribute name) const; /**< @overload */ /** * @brief ID of a named attribute in the base material @@ -1769,6 +1839,7 @@ class MAGNUM_TRADE_EXPORT MaterialData { * @see @ref hasLayer() */ Containers::StringView attributeName(Containers::StringView layer, UnsignedInt id) const; + Containers::StringView attributeName(MaterialLayer layer, UnsignedInt id) const; /**< @overload */ /** * @brief Name of an attribute in the base material @@ -1809,6 +1880,7 @@ class MAGNUM_TRADE_EXPORT MaterialData { * @see @ref hasLayer() */ MaterialAttributeType attributeType(Containers::StringView layer, UnsignedInt id) const; + MaterialAttributeType attributeType(MaterialLayer layer, UnsignedInt id) const; /**< @overload */ /** * @brief Type of a named attribute in a named material layer @@ -1819,6 +1891,8 @@ class MAGNUM_TRADE_EXPORT MaterialData { */ MaterialAttributeType attributeType(Containers::StringView layer, Containers::StringView name) const; MaterialAttributeType attributeType(Containers::StringView layer, MaterialAttribute name) const; /**< @overload */ + MaterialAttributeType attributeType(MaterialLayer layer, Containers::StringView name) const; /**< @overload */ + MaterialAttributeType attributeType(MaterialLayer layer, MaterialAttribute name) const; /**< @overload */ /** * @brief Type of an attribute in the base material @@ -1900,6 +1974,7 @@ class MAGNUM_TRADE_EXPORT MaterialData { * @see @ref hasLayer() */ const void* attribute(Containers::StringView layer, UnsignedInt id) const; + const void* attribute(MaterialLayer layer, UnsignedInt id) const; /**< @overload */ /** * @brief Type-erased value of a named attribute in a named material layer @@ -1920,6 +1995,8 @@ class MAGNUM_TRADE_EXPORT MaterialData { */ const void* attribute(Containers::StringView layer, Containers::StringView name) const; const void* attribute(Containers::StringView layer, MaterialAttribute name) const; /**< @overload */ + const void* attribute(MaterialLayer layer, Containers::StringView name) const; /**< @overload */ + const void* attribute(MaterialLayer layer, MaterialAttribute name) const; /**< @overload */ /** * @brief Type-erased value of an attribute in the base material @@ -1984,6 +2061,7 @@ class MAGNUM_TRADE_EXPORT MaterialData { * @see @ref hasLayer() */ template T attribute(Containers::StringView layer, UnsignedInt id) const; + template T attribute(MaterialLayer layer, UnsignedInt id) const; /**< @overload */ /** * @brief Value of a named attribute in a named material layer @@ -1998,6 +2076,8 @@ class MAGNUM_TRADE_EXPORT MaterialData { */ template T attribute(Containers::StringView layer, Containers::StringView name) const; template T attribute(Containers::StringView layer, MaterialAttribute name) const; /**< @overload */ + template T attribute(MaterialLayer layer, Containers::StringView name) const; /**< @overload */ + template T attribute(MaterialLayer layer, MaterialAttribute name) const; /**< @overload */ /** * @brief Value of an attribute in the base material @@ -2045,6 +2125,8 @@ class MAGNUM_TRADE_EXPORT MaterialData { */ const void* tryAttribute(Containers::StringView layer, Containers::StringView name) const; const void* tryAttribute(Containers::StringView layer, MaterialAttribute name) const; /**< @overload */ + const void* tryAttribute(MaterialLayer layer, Containers::StringView name) const; /**< @overload */ + const void* tryAttribute(MaterialLayer layer, MaterialAttribute name) const; /**< @overload */ /** * @brief Value of a named attribute in given material layer, if exists @@ -2071,6 +2153,8 @@ class MAGNUM_TRADE_EXPORT MaterialData { */ template Containers::Optional tryAttribute(Containers::StringView layer, Containers::StringView name) const; template Containers::Optional tryAttribute(Containers::StringView layer, MaterialAttribute name) const; /**< @overload */ + template Containers::Optional tryAttribute(MaterialLayer layer, Containers::StringView name) const; /**< @overload */ + template Containers::Optional tryAttribute(MaterialLayer layer, MaterialAttribute name) const; /**< @overload */ /** * @brief Type-erased attribute value in the base material, if exists @@ -2123,6 +2207,8 @@ class MAGNUM_TRADE_EXPORT MaterialData { */ template T attributeOr(Containers::StringView layer, Containers::StringView name, const T& defaultValue) const; template T attributeOr(Containers::StringView layer, MaterialAttribute name, const T& defaultValue) const; /**< @overload */ + template T attributeOr(MaterialLayer layer, Containers::StringView name, const T& defaultValue) const; /**< @overload */ + template T attributeOr(MaterialLayer layer, MaterialAttribute name, const T& defaultValue) const; /**< @overload */ /** * @brief Value of a named attribute in the base material or a default @@ -2225,6 +2311,7 @@ class MAGNUM_TRADE_EXPORT MaterialData { implementations. */ friend AbstractImporter; + static Containers::StringView layerString(MaterialLayer name); static Containers::StringView attributeString(MaterialAttribute name); /* Internal helpers that don't assert, unlike layerId() / attributeId() */ UnsignedInt layerFor(Containers::StringView layer) const; @@ -2407,6 +2494,24 @@ template T MaterialData::attribute(const Containers::StringView layer, return attribute(layer, string); } +template T MaterialData::attribute(const MaterialLayer layer, const UnsignedInt id) const { + const Containers::StringView string = layerString(layer); + CORRADE_ASSERT(string.data(), "Trade::MaterialData::attribute(): invalid name" << layer, {}); + return attribute(string, id); +} + +template T MaterialData::attribute(const MaterialLayer layer, const Containers::StringView name) const { + const Containers::StringView string = layerString(layer); + CORRADE_ASSERT(string.data(), "Trade::MaterialData::attribute(): invalid name" << layer, {}); + return attribute(string, name); +} + +template T MaterialData::attribute(const MaterialLayer layer, const MaterialAttribute name) const { + const Containers::StringView string = layerString(layer); + CORRADE_ASSERT(string.data(), "Trade::MaterialData::attribute(): invalid name" << layer, {}); + return attribute(string, name); +} + template Containers::Optional MaterialData::tryAttribute(const UnsignedInt layer, const Containers::StringView name) const { CORRADE_ASSERT(layer < layerCount(), "Trade::MaterialData::tryAttribute(): index" << layer << "out of range for" << layerCount() << "layers", {}); @@ -2434,6 +2539,18 @@ template Containers::Optional MaterialData::tryAttribute(const Conta return tryAttribute(layer, string); } +template Containers::Optional MaterialData::tryAttribute(const MaterialLayer layer, const Containers::StringView name) const { + const Containers::StringView string = layerString(layer); + CORRADE_ASSERT(string.data(), "Trade::MaterialData::tryAttribute(): invalid name" << layer, {}); + return tryAttribute(string, name); +} + +template Containers::Optional MaterialData::tryAttribute(const MaterialLayer layer, const MaterialAttribute name) const { + const Containers::StringView string = layerString(layer); + CORRADE_ASSERT(string.data(), "Trade::MaterialData::tryAttribute(): invalid name" << layer, {}); + return tryAttribute(string, name); +} + template T MaterialData::attributeOr(const UnsignedInt layer, const Containers::StringView name, const T& defaultValue) const { CORRADE_ASSERT(layer < layerCount(), "Trade::MaterialData::attributeOr(): index" << layer << "out of range for" << layerCount() << "layers", {}); @@ -2461,6 +2578,18 @@ template T MaterialData::attributeOr(const Containers::StringView layer return attributeOr(layer, string, defaultValue); } +template T MaterialData::attributeOr(const MaterialLayer layer, const Containers::StringView name, const T& defaultValue) const { + const Containers::StringView string = layerString(layer); + CORRADE_ASSERT(string.data(), "Trade::MaterialData::attributeOr(): invalid name" << layer, {}); + return attributeOr(string, name, defaultValue); +} + +template T MaterialData::attributeOr(const MaterialLayer layer, const MaterialAttribute name, const T& defaultValue) const { + const Containers::StringView string = layerString(layer); + CORRADE_ASSERT(string.data(), "Trade::MaterialData::attributeOr(): invalid name" << layer, {}); + return attributeOr(string, name, defaultValue); +} + }} #endif diff --git a/src/Magnum/Trade/MaterialLayerData.h b/src/Magnum/Trade/MaterialLayerData.h new file mode 100644 index 0000000000..b0dad741fe --- /dev/null +++ b/src/Magnum/Trade/MaterialLayerData.h @@ -0,0 +1,255 @@ +#ifndef Magnum_Trade_MaterialLayerData_h +#define Magnum_Trade_MaterialLayerData_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + 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::MaterialLayerData + * @m_since_latest + */ + +#include "Magnum/Math/Matrix3.h" +#include "Magnum/Trade/MaterialData.h" + +namespace Magnum { namespace Trade { + +/** +@brief Material layer data +@m_since_latest + +Convenience wrapper that re-routes all @ref MaterialData base material layer +and attribute accessors APIS from to a layer specified in the @p layer template +parameter. All APIs expect that given layer exists. +*/ +template class MaterialLayerData: public MaterialData { + public: + /* Allow constructing subclasses directly. While not used in the + general Importer workflow, it allows users to create instances with + desired convenience APIs easier (and simplifies testing) */ + using MaterialData::MaterialData; + + /* Bring in all other overloads as well and override just the ones + with implicit layers */ + using MaterialData::layerName; + using MaterialData::layerFactor; + using MaterialData::layerFactorTexture; + using MaterialData::layerFactorTextureSwizzle; + using MaterialData::layerFactorTextureMatrix; + using MaterialData::layerFactorTextureCoordinates; + using MaterialData::attributeCount; + using MaterialData::hasAttribute; + using MaterialData::attributeId; + using MaterialData::attributeName; + using MaterialData::attributeType; + using MaterialData::attribute; + using MaterialData::tryAttribute; + using MaterialData::attributeOr; + + /** + * @brief Layer name + * + * Same as calling @ref MaterialData::layerName() with @ref layerId() + * for @p layer. + */ + Containers::StringView layerName() const { + return MaterialData::layerName(layerId(layer)); + } + + /** + * @brief Layer factor + * + * Same as calling @ref MaterialData::layerFactor() with @p layer. + */ + Float layerFactor() const { + return MaterialData::layerFactor(layer); + } + + /** + * @brief Layer factor texture ID + * + * Same as calling @ref MaterialData::layerFactorTexture() with + * @p layer. + */ + UnsignedInt layerFactorTexture() const { + return MaterialData::layerFactorTexture(layer); + } + + /** + * @brief Layer factor texture swizzle + * + * Same as calling @ref MaterialData::layerFactorTextureSwizzle() with + * @p layer. + */ + MaterialTextureSwizzle layerFactorTextureSwizzle() const { + return MaterialData::layerFactorTextureSwizzle(layer); + } + + /** + * @brief Layer factor texture coordinate transformation matrix + * + * Same as calling @ref MaterialData::layerFactorTextureMatrix() with + * @p layer. + */ + Matrix3 layerFactorTextureMatrix() const { + return MaterialData::layerFactorTextureMatrix(layer); + } + + /** + * @brief Layer factor texture coordinate set + * + * Same as calling @ref MaterialData::layerFactorTextureCoordinates() + * with @p layer. + */ + UnsignedInt layerFactorTextureCoordinates() const { + return MaterialData::layerFactorTextureCoordinates(layer); + } + + /** + * @brief Attribute count in this layer + * + * Same as calling @ref MaterialData::attributeCount() with @p layer. + */ + UnsignedInt attributeCount() const { + return MaterialData::attributeCount(layer); + } + + /** + * @brief Whether this layer has given attribute + * + * Same as calling @ref MaterialData::hasAttribute() with @p layer. + */ + bool hasAttribute(Containers::StringView name) const { + return MaterialData::hasAttribute(layer, name); + } + bool hasAttribute(MaterialAttribute name) const { + return MaterialData::hasAttribute(layer, name); + } /**< @overload */ + + /** + * @brief ID of a named attribute in this layer + * + * Same as calling @ref MaterialData::attributeId() with @p layer. + */ + UnsignedInt attributeId(Containers::StringView name) const { + return MaterialData::attributeId(layer, name); + } + UnsignedInt attributeId(MaterialAttribute name) const { + return MaterialData::attributeId(layer, name); + } /**< @overload */ + + /** + * @brief Name of an attribute in this layer + * + * Same as calling @ref MaterialData::attributeName() with @p layer. + */ + Containers::StringView attributeName(UnsignedInt id) const { + return MaterialData::attributeName(layer, id); + } + + /** + * @brief Type of an attribute in this layer + * + * Same as calling @ref MaterialData::attributeType() with @p layer. + */ + MaterialAttributeType attributeType(UnsignedInt id) const { + return MaterialData::attributeType(layer, id); + } + MaterialAttributeType attributeType(Containers::StringView name) const { + return MaterialData::attributeType(layer, name); + } /**< @overload */ + MaterialAttributeType attributeType(MaterialAttribute name) const { + return MaterialData::attributeType(layer, name); + } /**< @overload */ + + /** + * @brief Type-erased value of an attribute in this layer + * + * Same as calling @ref MaterialData::attribute() with @p layer. + */ + const void* attribute(UnsignedInt id) const { + return MaterialData::attribute(layer, id); + } + const void* attribute(Containers::StringView name) const { + return MaterialData::attribute(layer, name); + } /**< @overload */ + const void* attribute(MaterialAttribute name) const { + return MaterialData::attribute(layer, name); + } /**< @overload */ + + /** + * @brief Value of an attribute in this layer + * + * Same as calling @ref MaterialData::attribute() with @p layer. + */ + template T attribute(UnsignedInt id) const { + return MaterialData::attribute(layer, id); + } + template T attribute(Containers::StringView name) const { + return MaterialData::attribute(layer, name); + } /**< @overload */ + template T attribute(MaterialAttribute name) const { + return MaterialData::attribute(layer, name); + } /**< @overload */ + + /** + * @brief Type-erased attribute value in this layer, if exists + * + * Same as calling @ref MaterialData::tryAttribute() with @p layer. + */ + const void* tryAttribute(Containers::StringView name) const { + return MaterialData::tryAttribute(layer, name); + } + const void* tryAttribute(MaterialAttribute name) const { + return MaterialData::tryAttribute(layer, name); + } /**< @overload */ + + /** + * @brief Value of a named attribute in this layer, if exists + * + * Same as calling @ref MaterialData::tryAttribute() with @p layer. + */ + template Containers::Optional tryAttribute(Containers::StringView name) const { + return MaterialData::tryAttribute(layer, name); + } + template Containers::Optional tryAttribute(MaterialAttribute name) const { + return MaterialData::tryAttribute(layer, name); + } /**< @overload */ + + /** + * @brief Value of a named attribute in this layer or a default + * + * Same as calling @ref MaterialData::attributeOr() with @p layer. + */ + template T attributeOr(Containers::StringView name, const T& defaultValue) const { + return MaterialData::attributeOr(layer, name, defaultValue); + } + template T attributeOr(MaterialAttribute name, const T& defaultValue) const { + return MaterialData::attributeOr(layer, name, defaultValue); + } /**< @overload */ +}; + +}} + +#endif diff --git a/src/Magnum/Trade/PbrClearCoatMaterialData.cpp b/src/Magnum/Trade/PbrClearCoatMaterialData.cpp new file mode 100644 index 0000000000..0ff45bbd3b --- /dev/null +++ b/src/Magnum/Trade/PbrClearCoatMaterialData.cpp @@ -0,0 +1,110 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + 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 "PbrClearCoatMaterialData.h" + +namespace Magnum { namespace Trade { + +bool PbrClearCoatMaterialData::hasTextureTransformation() const { + return hasAttribute(MaterialAttribute::LayerFactorTextureMatrix) || + hasAttribute(MaterialAttribute::RoughnessTextureMatrix) || + hasAttribute(MaterialAttribute::NormalTextureMatrix) || + hasAttribute(MaterialAttribute::TextureMatrix) || + hasAttribute(0, MaterialAttribute::TextureMatrix); +} + +bool PbrClearCoatMaterialData::hasTextureCoordinates() const { + return hasAttribute(MaterialAttribute::LayerFactorTextureCoordinates) || + hasAttribute(MaterialAttribute::RoughnessTextureCoordinates) || + hasAttribute(MaterialAttribute::NormalTextureCoordinates) || + hasAttribute(MaterialAttribute::TextureCoordinates) || + hasAttribute(0, MaterialAttribute::TextureCoordinates); +} + +Float PbrClearCoatMaterialData::roughness() const { + return attributeOr(MaterialAttribute::Roughness, 0.0f); +} + +UnsignedInt PbrClearCoatMaterialData::roughnessTexture() const { + return attribute(MaterialAttribute::RoughnessTexture); +} + +MaterialTextureSwizzle PbrClearCoatMaterialData::roughnessTextureSwizzle() const { + CORRADE_ASSERT(hasAttribute(MaterialAttribute::RoughnessTexture), + "Trade::PbrClearCoatMaterialData::roughnessTextureSwizzle(): the layer doesn't have a roughness texture", {}); + return attributeOr(MaterialAttribute::RoughnessTextureSwizzle, MaterialTextureSwizzle::R); +} + +Matrix3 PbrClearCoatMaterialData::roughnessTextureMatrix() const { + CORRADE_ASSERT(hasAttribute(MaterialAttribute::RoughnessTexture), + "Trade::PbrClearCoatMaterialData::roughnessTextureMatrix(): the layer doesn't have a roughness texture", {}); + if(Containers::Optional value = tryAttribute(MaterialAttribute::RoughnessTextureMatrix)) + return *value; + if(Containers::Optional value = tryAttribute(MaterialAttribute::TextureMatrix)) + return *value; + return attributeOr(0, MaterialAttribute::TextureMatrix, Matrix3{}); +} + +UnsignedInt PbrClearCoatMaterialData::roughnessTextureCoordinates() const { + CORRADE_ASSERT(hasAttribute(MaterialAttribute::RoughnessTexture), + "Trade::PbrClearCoatMaterialData::roughnessTextureCoordinates(): the layer doesn't have a roughness texture", {}); + if(Containers::Optional value = tryAttribute(MaterialAttribute::RoughnessTextureCoordinates)) + return *value; + if(Containers::Optional value = tryAttribute(MaterialAttribute::TextureCoordinates)) + return *value; + return attributeOr(0, MaterialAttribute::TextureCoordinates, 0u); +} + +UnsignedInt PbrClearCoatMaterialData::normalTexture() const { + return attribute(MaterialAttribute::NormalTexture); +} + +MaterialTextureSwizzle PbrClearCoatMaterialData::normalTextureSwizzle() const { + CORRADE_ASSERT(hasAttribute(MaterialAttribute::NormalTexture), + "Trade::PbrClearCoatMaterialData::normalTextureSwizzle(): the layer doesn't have a normal texture", {}); + return attributeOr(MaterialAttribute::NormalTextureSwizzle, MaterialTextureSwizzle::RGB); +} + +Matrix3 PbrClearCoatMaterialData::normalTextureMatrix() const { + CORRADE_ASSERT(hasAttribute(MaterialAttribute::NormalTexture), + "Trade::PbrClearCoatMaterialData::normalTextureMatrix(): the layer doesn't have a normal texture", {}); + if(Containers::Optional value = tryAttribute(MaterialAttribute::NormalTextureMatrix)) + return *value; + if(Containers::Optional value = tryAttribute(MaterialAttribute::TextureMatrix)) + return *value; + return attributeOr(0, MaterialAttribute::TextureMatrix, Matrix3{}); +} + +UnsignedInt PbrClearCoatMaterialData::normalTextureCoordinates() const { + CORRADE_ASSERT(hasAttribute(MaterialAttribute::NormalTexture), + "Trade::PbrClearCoatMaterialData::normalTextureCoordinates(): the layer doesn't have a normal texture", {}); + if(Containers::Optional value = tryAttribute(MaterialAttribute::NormalTextureCoordinates)) + return *value; + if(Containers::Optional value = tryAttribute(MaterialAttribute::TextureCoordinates)) + return *value; + return attributeOr(0, MaterialAttribute::TextureCoordinates, 0u); +} + +}} diff --git a/src/Magnum/Trade/PbrClearCoatMaterialData.h b/src/Magnum/Trade/PbrClearCoatMaterialData.h new file mode 100644 index 0000000000..be3a0c1452 --- /dev/null +++ b/src/Magnum/Trade/PbrClearCoatMaterialData.h @@ -0,0 +1,193 @@ +#ifndef Magnum_Trade_PbrClearCoatMaterialData_h +#define Magnum_Trade_PbrClearCoatMaterialData_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + 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::PbrClearCoatMaterialData + * @m_since_latest + */ + +#include "Magnum/Trade/MaterialLayerData.h" + +namespace Magnum { namespace Trade { + +/** +@brief Clear coat material layer data +@m_since_latest + +Exposes properties of a @ref MaterialLayer::ClearCoat layer. All APIs expect +that the layer is present in the material. +@see @ref AbstractImporter::material(), @ref MaterialType::PbrClearCoat +*/ +class MAGNUM_TRADE_EXPORT PbrClearCoatMaterialData: public MaterialLayerData { + public: + #ifndef DOXYGEN_GENERATING_OUTPUT + /* Allow constructing subclasses directly. While not used in the + general Importer workflow, it allows users to create instances with + desired convenience APIs easier (and simplifies testing). It's + however hidden from the docs as constructing instances this way + isn't really common and it would add a lot of noise. */ + using MaterialLayerData::MaterialLayerData; + #endif + + /** + * @brief Whether the material has texture transformation + * + * Returns @cpp true @ce if any of the + * @ref MaterialAttribute::LayerFactorTextureMatrix, + * @ref MaterialAttribute::RoughnessTextureMatrix, + * @ref MaterialAttribute::NormalTextureMatrix or + * @ref MaterialAttribute::TextureMatrix attributes are present in this + * layer or if @ref MaterialAttribute::TextureMatrix is present in the + * base material, @cpp false @ce otherwise + */ + bool hasTextureTransformation() const; + + /** + * @brief Whether the material uses extra texture coordinate sets + * + * Returns @cpp true @ce if any of the + * @ref MaterialAttribute::LayerFactorTextureCoordinates, + * @ref MaterialAttribute::RoughnessTextureCoordinates, + * @ref MaterialAttribute::NormalTextureCoordinates or + * @ref MaterialAttribute::TextureCoordinates attributes are present in + * this material or if @ref MaterialAttribute::TextureCoordinates is + * present in the base material, @cpp false @ce otherwise. + */ + bool hasTextureCoordinates() const; + + /** + * @brief Roughness factor + * + * Convenience access to the @ref MaterialAttribute::Roughness + * attribute in this layer. If not present, the default is @cpp 1.0f @ce. + * + * If the layer has a @ref MaterialAttribute::RoughnessTexture, the + * factor and texture is meant to be multiplied together. + */ + Float roughness() const; + + /** + * @brief Roughness texture ID + * + * Available only if @ref MaterialAttribute::RoughnessTexture is + * present in this layer. Meant to be multiplied with @ref roughness(). + * @see @ref AbstractImporter::texture() + */ + UnsignedInt roughnessTexture() const; + + /** + * @brief Roughness texture swizzle + * + * Convenience access to the @ref MaterialAttribute::RoughnessTextureSwizzle + * attribute in this layer. If not present, the default is @cpp 1.0f @ce. + * Available only if @ref MaterialAttribute::RoughnessTexture is + * present in this layer. + * @see @ref hasAttribute() + */ + MaterialTextureSwizzle roughnessTextureSwizzle() const; + + /** + * @brief Roughness texture coordinate transformation matrix + * + * Convenience access to the @ref MaterialAttribute::RoughnessTextureMatrix + * / @ref MaterialAttribute::TextureMatrix attributes in this layer or + * a @ref MaterialAttribute::TextureMatrix attribute in the base + * material. If neither is present, the default is an identity matrix. + * Available only if @ref MaterialAttribute::RoughnessTexture is + * present in this layer. + * @see @ref hasAttribute() + */ + Matrix3 roughnessTextureMatrix() const; + + /** + * @brief Roughness texture coordinate set + * + * Convenience access to the @ref MaterialAttribute::RoughnessTextureCoordinates + * / @ref MaterialAttribute::TextureCoordinates attributes in this + * layer or a @ref MaterialAttribute::TextureCoordinates attribute in + * the base material. If neither is present, the default is @cpp 0 @ce. + * Available only if @ref MaterialAttribute::RoughnessTexture is + * present in this layer. + * @see @ref hasAttribute() + */ + UnsignedInt roughnessTextureCoordinates() const; + + /** + * @brief Normal texture ID + * + * Available only if @ref MaterialAttribute::NormalTexture is present + * in this layer. + * @see @ref hasAttribute(), @ref AbstractImporter::texture() + */ + UnsignedInt normalTexture() const; + + /** + * @brief Normal texture swizzle + * + * Convenience access to the + * @ref MaterialAttribute::NormalTextureSwizzle attribute in this + * layer. If not present, the default is + * @ref MaterialTextureSwizzle::RGB. Available only if + * @ref MaterialAttribute::NormalTexture is present in this layer. + * + * The texture can be also just two-component, in which case the + * remaining component is implicit and calculated as + * @f$ z = \sqrt{1 - x^2 - y^2} @f$. + * @see @ref hasAttribute() + */ + MaterialTextureSwizzle normalTextureSwizzle() const; + + /** + * @brief Normal texture coordinate transformation matrix + * + * Convenience access to the @ref MaterialAttribute::NormalTextureMatrix + * / @ref MaterialAttribute::TextureMatrix attributes in this layer or + * a @ref MaterialAttribute::TextureMatrix attribute in the base + * material. If neither is present, the default is an identity matrix. + * Available only if @ref MaterialAttribute::NormalTexture is present + * in this layer. + * @see @ref hasAttribute() + */ + Matrix3 normalTextureMatrix() const; + + /** + * @brief Normal texture coordinate set + * + * Convenience access to the @ref MaterialAttribute::NormalTextureCoordinates + * / @ref MaterialAttribute::TextureCoordinates attributes in this + * layer or a @ref MaterialAttribute::TextureCoordinates attribute in + * the base material. If neither is present, the default is @cpp 0 @ce. + * Available only if @ref MaterialAttribute::NormalTexture is present + * in this layer. + * @see @ref hasAttribute() + */ + UnsignedInt normalTextureCoordinates() const; +}; + +}} + +#endif diff --git a/src/Magnum/Trade/Test/MaterialDataTest.cpp b/src/Magnum/Trade/Test/MaterialDataTest.cpp index 2864bc0929..ee5703c71f 100644 --- a/src/Magnum/Trade/Test/MaterialDataTest.cpp +++ b/src/Magnum/Trade/Test/MaterialDataTest.cpp @@ -35,6 +35,7 @@ #include "Magnum/Math/Matrix3.h" #include "Magnum/Trade/FlatMaterialData.h" #include "Magnum/Trade/MaterialData.h" +#include "Magnum/Trade/PbrClearCoatMaterialData.h" #include "Magnum/Trade/PbrMetallicRoughnessMaterialData.h" #include "Magnum/Trade/PbrSpecularGlossinessMaterialData.h" #include "Magnum/Trade/PhongMaterialData.h" @@ -49,6 +50,7 @@ class MaterialDataTest: public TestSuite::Tester { void attributeTypeSizeInvalid(); void attributeMap(); + void layerMap(); void constructAttributeDefault(); void constructAttributeString(); @@ -63,8 +65,10 @@ class MaterialDataTest: public TestSuite::Tester { void constructAttributeStringNameStringValue(); void constructAttributeNameStringValue(); void constructAttributeTextureSwizzle(); + void constructAttributeLayer(); void constructAttributeInvalidName(); + void constructAttributeInvalidLayerName(); void constructAttributeWrongTypeForName(); void constructAttributeInvalidType(); void constructAttributeTooLarge(); @@ -120,9 +124,11 @@ class MaterialDataTest: public TestSuite::Tester { void accessLayerLayerNameInBaseMaterial(); void accessLayerEmptyLayer(); void accessLayerIndexOptional(); + void accessLayerNameOptional(); void accessLayerStringOptional(); void accessLayerOutOfBounds(); void accessLayerNotFound(); + void accessInvalidLayerName(); void accessOutOfBoundsInLayerIndex(); void accessOutOfBoundsInLayerString(); void accessNotFoundInLayerIndex(); @@ -181,6 +187,17 @@ class MaterialDataTest: public TestSuite::Tester { void flatAccessTexturedMismatchedMatrixCoordinates(); void flatAccessInvalidTextures(); + void templateLayerAccess(); + + void pbrClearCoatAccess(); + void pbrClearCoatAccessDefaults(); + void pbrClearCoatAccessTextured(); + void pbrClearCoatAccessTexturedDefaults(); + void pbrClearCoatAccessTexturedSingleMatrixCoordinates(); + void pbrClearCoatAccessTexturedBaseMaterialMatrixCoordinates(); + void pbrClearCoatAccessInvalidTextures(); + + void debugLayer(); void debugAttribute(); void debugTextureSwizzle(); void debugAttributeType(); @@ -203,6 +220,7 @@ MaterialDataTest::MaterialDataTest() { addTests({&MaterialDataTest::attributeTypeSize, &MaterialDataTest::attributeTypeSizeInvalid, &MaterialDataTest::attributeMap, + &MaterialDataTest::layerMap, &MaterialDataTest::constructAttributeDefault, &MaterialDataTest::constructAttributeString, @@ -241,8 +259,10 @@ MaterialDataTest::MaterialDataTest() { &MaterialDataTest::constructAttributeStringNameStringValue, &MaterialDataTest::constructAttributeNameStringValue, &MaterialDataTest::constructAttributeTextureSwizzle, + &MaterialDataTest::constructAttributeLayer, &MaterialDataTest::constructAttributeInvalidName, + &MaterialDataTest::constructAttributeInvalidLayerName, &MaterialDataTest::constructAttributeWrongTypeForName, &MaterialDataTest::constructAttributeInvalidType, &MaterialDataTest::constructAttributeTooLarge, @@ -301,9 +321,11 @@ MaterialDataTest::MaterialDataTest() { &MaterialDataTest::accessLayerLayerNameInBaseMaterial, &MaterialDataTest::accessLayerEmptyLayer, &MaterialDataTest::accessLayerIndexOptional, + &MaterialDataTest::accessLayerNameOptional, &MaterialDataTest::accessLayerStringOptional, &MaterialDataTest::accessLayerOutOfBounds, &MaterialDataTest::accessLayerNotFound, + &MaterialDataTest::accessInvalidLayerName, &MaterialDataTest::accessOutOfBoundsInLayerIndex, &MaterialDataTest::accessOutOfBoundsInLayerString, &MaterialDataTest::accessNotFoundInLayerIndex, @@ -362,6 +384,17 @@ MaterialDataTest::MaterialDataTest() { &MaterialDataTest::flatAccessTexturedMismatchedMatrixCoordinates, &MaterialDataTest::flatAccessInvalidTextures, + &MaterialDataTest::templateLayerAccess, + + &MaterialDataTest::pbrClearCoatAccess, + &MaterialDataTest::pbrClearCoatAccessDefaults, + &MaterialDataTest::pbrClearCoatAccessTextured, + &MaterialDataTest::pbrClearCoatAccessTexturedDefaults, + &MaterialDataTest::pbrClearCoatAccessTexturedSingleMatrixCoordinates, + &MaterialDataTest::pbrClearCoatAccessTexturedBaseMaterialMatrixCoordinates, + &MaterialDataTest::pbrClearCoatAccessInvalidTextures, + + &MaterialDataTest::debugLayer, &MaterialDataTest::debugAttribute, &MaterialDataTest::debugTextureSwizzle, &MaterialDataTest::debugAttributeType, @@ -456,6 +489,35 @@ void MaterialDataTest::attributeMap() { } } +void MaterialDataTest::layerMap() { + /* Ensure all layer names are: + - present in the map, + - and that their translated string name corresponds to the enum value + name + This goes through the first 16 bits, which should be enough. Going + through 32 bits takes 8 seconds, too much. */ + for(UnsignedInt i = 1; i <= 0xffff; ++i) { + /* Attribute 0 reserved for an invalid value */ + + const auto attribute = MaterialLayer(i); + #ifdef __GNUC__ + #pragma GCC diagnostic push + #pragma GCC diagnostic error "-Wswitch" + #endif + switch(attribute) { + #define _c(name_) \ + case MaterialLayer::name_: \ + CORRADE_COMPARE((MaterialAttributeData{MaterialLayer::name_}.value()), #name_); \ + break; + #include "Magnum/Trade/Implementation/materialLayerProperties.hpp" + #undef _c + } + #ifdef __GNUC__ + #pragma GCC diagnostic pop + #endif + } +} + void MaterialDataTest::constructAttributeDefault() { MaterialAttributeData attribute; CORRADE_COMPARE(attribute.name(), ""); @@ -678,6 +740,13 @@ void MaterialDataTest::constructAttributeTextureSwizzle() { CORRADE_COMPARE(typeErased.value(), MaterialTextureSwizzle::GBA); } +void MaterialDataTest::constructAttributeLayer() { + MaterialAttributeData attribute{MaterialLayer::ClearCoat}; + CORRADE_COMPARE(attribute.name(), "$LayerName"); + CORRADE_COMPARE(attribute.type(), MaterialAttributeType::String); + CORRADE_COMPARE(attribute.value(), "ClearCoat"); +} + void MaterialDataTest::constructAttributeInvalidName() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); @@ -692,6 +761,20 @@ void MaterialDataTest::constructAttributeInvalidName() { "Trade::MaterialAttributeData: invalid name Trade::MaterialAttribute(0xfefe)\n"); } +void MaterialDataTest::constructAttributeInvalidLayerName() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + std::ostringstream out; + Error redirectError{&out}; + MaterialAttributeData{MaterialLayer(0x0)}; + MaterialAttributeData{MaterialLayer(0xfefe)}; + CORRADE_COMPARE(out.str(), + "Trade::MaterialAttributeData: invalid name Trade::MaterialLayer(0x0)\n" + "Trade::MaterialAttributeData: invalid name Trade::MaterialLayer(0xfefe)\n"); +} + void MaterialDataTest::constructAttributeWrongTypeForName() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); @@ -952,7 +1035,7 @@ void MaterialDataTest::constructLayers() { /* Layer name gets sorted first by the constructor */ {"highlightColor", 0x335566ff_rgbaf}, {MaterialAttribute::AlphaBlend, true}, - {MaterialAttribute::LayerName, "ClearCoat"}, + {MaterialLayer::ClearCoat}, /* Empty layer here */ @@ -975,6 +1058,7 @@ void MaterialDataTest::constructLayers() { CORRADE_COMPARE(data.attributeCount(2), 0); CORRADE_COMPARE(data.attributeCount(3), 2); CORRADE_COMPARE(data.attributeCount("ClearCoat"), 3); + CORRADE_COMPARE(data.attributeCount(MaterialLayer::ClearCoat), 3); /* Layer access */ CORRADE_COMPARE(data.layerName(0), ""); @@ -983,10 +1067,12 @@ void MaterialDataTest::constructLayers() { CORRADE_COMPARE(data.layerName(3), ""); CORRADE_VERIFY(data.hasLayer("ClearCoat")); + CORRADE_VERIFY(data.hasLayer(MaterialLayer::ClearCoat)); CORRADE_VERIFY(!data.hasLayer("")); CORRADE_VERIFY(!data.hasLayer("DoubleSided")); CORRADE_COMPARE(data.layerId("ClearCoat"), 1); + CORRADE_COMPARE(data.layerId(MaterialLayer::ClearCoat), 1); /* Verify sorting in each layer */ CORRADE_COMPARE(data.attributeName(0, 0), "DiffuseTextureCoordinates"); @@ -1070,6 +1156,51 @@ void MaterialDataTest::constructLayers() { CORRADE_COMPARE(static_cast(data.attribute(1, "$LayerName")), "ClearCoat"_s); CORRADE_COMPARE(*static_cast(data.attribute(3, "NormalTexture")), 3); + /* Access by layer name and attribute ID */ + CORRADE_COMPARE(data.attributeName(MaterialLayer::ClearCoat, 1), "AlphaBlend"); + CORRADE_COMPARE(data.attributeName(MaterialLayer::ClearCoat, 2), "highlightColor"); + + CORRADE_COMPARE(data.attributeType(MaterialLayer::ClearCoat, 1), MaterialAttributeType::Bool); + CORRADE_COMPARE(data.attributeType(MaterialLayer::ClearCoat, 2), MaterialAttributeType::Vector4); + + CORRADE_COMPARE(data.attribute(MaterialLayer::ClearCoat, 1), true); + CORRADE_COMPARE(data.attribute(MaterialLayer::ClearCoat, 2), 0x335566ff_rgbaf); + + CORRADE_COMPARE(*static_cast(data.attribute(MaterialLayer::ClearCoat, 1)), true); + CORRADE_COMPARE(*static_cast(data.attribute(MaterialLayer::ClearCoat, 2)), 0x335566ff_rgbaf); + + /* Access by layer name and attribute name */ + CORRADE_VERIFY(data.hasAttribute(MaterialLayer::ClearCoat, MaterialAttribute::AlphaBlend)); + CORRADE_VERIFY(data.hasAttribute(MaterialLayer::ClearCoat, MaterialAttribute::LayerName)); + + CORRADE_COMPARE(data.attributeId(MaterialLayer::ClearCoat, MaterialAttribute::AlphaBlend), 1); + CORRADE_COMPARE(data.attributeId(MaterialLayer::ClearCoat, MaterialAttribute::LayerName), 0); + + CORRADE_COMPARE(data.attributeType(MaterialLayer::ClearCoat, MaterialAttribute::AlphaBlend), MaterialAttributeType::Bool); + CORRADE_COMPARE(data.attributeType(MaterialLayer::ClearCoat, MaterialAttribute::LayerName), MaterialAttributeType::String); + + CORRADE_COMPARE(data.attribute(MaterialLayer::ClearCoat, MaterialAttribute::AlphaBlend), true); + CORRADE_COMPARE(data.attribute(MaterialLayer::ClearCoat, MaterialAttribute::LayerName), "ClearCoat"); + + CORRADE_COMPARE(*static_cast(data.attribute(MaterialLayer::ClearCoat, MaterialAttribute::AlphaBlend)), true); + CORRADE_COMPARE(static_cast(data.attribute(MaterialLayer::ClearCoat, MaterialAttribute::LayerName)), "ClearCoat"_s); + + /* Access by layer name and attribute string */ + CORRADE_VERIFY(data.hasAttribute(MaterialLayer::ClearCoat, "highlightColor")); + CORRADE_VERIFY(data.hasAttribute(MaterialLayer::ClearCoat, "$LayerName")); + + CORRADE_COMPARE(data.attributeId(MaterialLayer::ClearCoat, "highlightColor"), 2); + CORRADE_COMPARE(data.attributeId(MaterialLayer::ClearCoat, "$LayerName"), 0); + + CORRADE_COMPARE(data.attributeType(MaterialLayer::ClearCoat, "highlightColor"), MaterialAttributeType::Vector4); + CORRADE_COMPARE(data.attributeType(MaterialLayer::ClearCoat, "$LayerName"), MaterialAttributeType::String); + + CORRADE_COMPARE(data.attribute(MaterialLayer::ClearCoat, "highlightColor"), 0x335566ff_rgbaf); + CORRADE_COMPARE(data.attribute(MaterialLayer::ClearCoat, "$LayerName"), "ClearCoat"); + + CORRADE_COMPARE(*static_cast(data.attribute(MaterialLayer::ClearCoat, "highlightColor")), 0x335566ff_rgbaf); + CORRADE_COMPARE(static_cast(data.attribute(MaterialLayer::ClearCoat, "$LayerName")), "ClearCoat"_s); + /* Access by layer string and attribute ID */ CORRADE_COMPARE(data.attributeName("ClearCoat", 1), "AlphaBlend"); CORRADE_COMPARE(data.attributeName("ClearCoat", 2), "highlightColor"); @@ -1678,6 +1809,7 @@ void MaterialDataTest::accessLayers() { CORRADE_COMPARE(data.layerFactor(2), 0.5f); CORRADE_COMPARE(data.layerFactor("ClearCoat"), 0.5f); + CORRADE_COMPARE(data.layerFactor(MaterialLayer::ClearCoat), 0.5f); } void MaterialDataTest::accessLayersDefaults() { @@ -1691,6 +1823,7 @@ void MaterialDataTest::accessLayersDefaults() { CORRADE_COMPARE(data.layerFactor(1), 1.0f); CORRADE_COMPARE(data.layerFactor("ClearCoat"), 1.0f); + CORRADE_COMPARE(data.layerFactor(MaterialLayer::ClearCoat), 1.0f); } void MaterialDataTest::accessLayersTextured() { @@ -1711,18 +1844,23 @@ void MaterialDataTest::accessLayersTextured() { CORRADE_COMPARE(data.layerFactor(2), 0.5f); CORRADE_COMPARE(data.layerFactor("ClearCoat"), 0.5f); + CORRADE_COMPARE(data.layerFactor(MaterialLayer::ClearCoat), 0.5f); CORRADE_COMPARE(data.layerFactorTexture(2), 4u); CORRADE_COMPARE(data.layerFactorTexture("ClearCoat"), 4u); + CORRADE_COMPARE(data.layerFactorTexture(MaterialLayer::ClearCoat), 4u); CORRADE_COMPARE(data.layerFactorTextureSwizzle(2), MaterialTextureSwizzle::A); CORRADE_COMPARE(data.layerFactorTextureSwizzle("ClearCoat"), MaterialTextureSwizzle::A); + CORRADE_COMPARE(data.layerFactorTextureSwizzle(MaterialLayer::ClearCoat), MaterialTextureSwizzle::A); CORRADE_COMPARE(data.layerFactorTextureMatrix(2), Matrix3::scaling({0.5f, 1.0f})); CORRADE_COMPARE(data.layerFactorTextureMatrix("ClearCoat"), Matrix3::scaling({0.5f, 1.0f})); + CORRADE_COMPARE(data.layerFactorTextureMatrix(MaterialLayer::ClearCoat), Matrix3::scaling({0.5f, 1.0f})); CORRADE_COMPARE(data.layerFactorTextureCoordinates(2), 2u); CORRADE_COMPARE(data.layerFactorTextureCoordinates("ClearCoat"), 2u); + CORRADE_COMPARE(data.layerFactorTextureCoordinates(MaterialLayer::ClearCoat), 2u); } void MaterialDataTest::accessLayersTexturedDefault() { @@ -1739,18 +1877,23 @@ void MaterialDataTest::accessLayersTexturedDefault() { CORRADE_COMPARE(data.layerFactor(2), 1.0f); CORRADE_COMPARE(data.layerFactor("ClearCoat"), 1.0f); + CORRADE_COMPARE(data.layerFactor(MaterialLayer::ClearCoat), 1.0f); CORRADE_COMPARE(data.layerFactorTexture(2), 3u); CORRADE_COMPARE(data.layerFactorTexture("ClearCoat"), 3u); + CORRADE_COMPARE(data.layerFactorTexture(MaterialLayer::ClearCoat), 3u); CORRADE_COMPARE(data.layerFactorTextureSwizzle(2), MaterialTextureSwizzle::R); CORRADE_COMPARE(data.layerFactorTextureSwizzle("ClearCoat"), MaterialTextureSwizzle::R); + CORRADE_COMPARE(data.layerFactorTextureSwizzle(MaterialLayer::ClearCoat), MaterialTextureSwizzle::R); CORRADE_COMPARE(data.layerFactorTextureMatrix(2), Matrix3{}); CORRADE_COMPARE(data.layerFactorTextureMatrix("ClearCoat"), Matrix3{}); + CORRADE_COMPARE(data.layerFactorTextureMatrix(MaterialLayer::ClearCoat), Matrix3{}); CORRADE_COMPARE(data.layerFactorTextureCoordinates(2), 0); CORRADE_COMPARE(data.layerFactorTextureCoordinates("ClearCoat"), 0); + CORRADE_COMPARE(data.layerFactorTextureCoordinates(MaterialLayer::ClearCoat), 0); } void MaterialDataTest::accessLayersTexturedSingleMatrixCoordinates() { @@ -1765,9 +1908,11 @@ void MaterialDataTest::accessLayersTexturedSingleMatrixCoordinates() { CORRADE_COMPARE(data.layerFactorTextureMatrix(1), Matrix3::scaling({0.5f, 1.0f})); CORRADE_COMPARE(data.layerFactorTextureMatrix("ClearCoat"), Matrix3::scaling({0.5f, 1.0f})); + CORRADE_COMPARE(data.layerFactorTextureMatrix(MaterialLayer::ClearCoat), Matrix3::scaling({0.5f, 1.0f})); CORRADE_COMPARE(data.layerFactorTextureCoordinates(1), 2u); CORRADE_COMPARE(data.layerFactorTextureCoordinates("ClearCoat"), 2u); + CORRADE_COMPARE(data.layerFactorTextureCoordinates(MaterialLayer::ClearCoat), 2u); } void MaterialDataTest::accessLayersTexturedBaseMaterialMatrixCoordinates() { @@ -1783,9 +1928,11 @@ void MaterialDataTest::accessLayersTexturedBaseMaterialMatrixCoordinates() { CORRADE_COMPARE(data.layerFactorTextureMatrix(1), Matrix3::scaling({0.5f, 1.0f})); CORRADE_COMPARE(data.layerFactorTextureMatrix("ClearCoat"), Matrix3::scaling({0.5f, 1.0f})); + CORRADE_COMPARE(data.layerFactorTextureMatrix(MaterialLayer::ClearCoat), Matrix3::scaling({0.5f, 1.0f})); CORRADE_COMPARE(data.layerFactorTextureCoordinates(1), 2u); CORRADE_COMPARE(data.layerFactorTextureCoordinates("ClearCoat"), 2u); + CORRADE_COMPARE(data.layerFactorTextureCoordinates(MaterialLayer::ClearCoat), 2u); } void MaterialDataTest::accessLayersInvalidTextures() { @@ -1801,20 +1948,28 @@ void MaterialDataTest::accessLayersInvalidTextures() { Error redirectError{&out}; data.layerFactorTexture(1); data.layerFactorTexture("ClearCoat"); + data.layerFactorTexture(MaterialLayer::ClearCoat); data.layerFactorTextureSwizzle(1); data.layerFactorTextureSwizzle("ClearCoat"); + data.layerFactorTextureSwizzle(MaterialLayer::ClearCoat); data.layerFactorTextureMatrix(1); data.layerFactorTextureMatrix("ClearCoat"); + data.layerFactorTextureMatrix(MaterialLayer::ClearCoat); data.layerFactorTextureCoordinates(1); data.layerFactorTextureCoordinates("ClearCoat"); + data.layerFactorTextureCoordinates(MaterialLayer::ClearCoat); CORRADE_COMPARE(out.str(), "Trade::MaterialData::attribute(): attribute LayerFactorTexture not found in layer 1\n" "Trade::MaterialData::attribute(): attribute LayerFactorTexture not found in layer ClearCoat\n" + "Trade::MaterialData::attribute(): attribute LayerFactorTexture not found in layer ClearCoat\n" "Trade::MaterialData::layerFactorTextureSwizzle(): layer 1 doesn't have a factor texture\n" "Trade::MaterialData::layerFactorTextureSwizzle(): layer ClearCoat doesn't have a factor texture\n" + "Trade::MaterialData::layerFactorTextureSwizzle(): layer ClearCoat doesn't have a factor texture\n" "Trade::MaterialData::layerFactorTextureMatrix(): layer 1 doesn't have a factor texture\n" "Trade::MaterialData::layerFactorTextureMatrix(): layer ClearCoat doesn't have a factor texture\n" + "Trade::MaterialData::layerFactorTextureMatrix(): layer ClearCoat doesn't have a factor texture\n" "Trade::MaterialData::layerFactorTextureCoordinates(): layer 1 doesn't have a factor texture\n" + "Trade::MaterialData::layerFactorTextureCoordinates(): layer ClearCoat doesn't have a factor texture\n" "Trade::MaterialData::layerFactorTextureCoordinates(): layer ClearCoat doesn't have a factor texture\n"); } @@ -1873,6 +2028,33 @@ void MaterialDataTest::accessLayerIndexOptional() { CORRADE_COMPARE(data.attributeOr(1, MaterialAttribute::DiffuseTexture, 5u), 5); } +void MaterialDataTest::accessLayerNameOptional() { + MaterialData data{{}, { + {MaterialAttribute::DiffuseColor, 0x335566ff_rgbaf}, + {MaterialLayer::ClearCoat}, + {MaterialAttribute::AlphaMask, 0.5f}, + {MaterialAttribute::SpecularTexture, 3u} + }, {1, 4}}; + + /* This exists */ + CORRADE_VERIFY(data.tryAttribute(MaterialLayer::ClearCoat, "SpecularTexture")); + CORRADE_VERIFY(data.tryAttribute(MaterialLayer::ClearCoat, MaterialAttribute::SpecularTexture)); + CORRADE_COMPARE(*static_cast(data.tryAttribute(MaterialLayer::ClearCoat, "SpecularTexture")), 3); + CORRADE_COMPARE(*static_cast(data.tryAttribute(MaterialLayer::ClearCoat, MaterialAttribute::SpecularTexture)), 3); + CORRADE_COMPARE(data.tryAttribute(MaterialLayer::ClearCoat, "SpecularTexture"), 3); + CORRADE_COMPARE(data.tryAttribute(MaterialLayer::ClearCoat, MaterialAttribute::SpecularTexture), 3); + CORRADE_COMPARE(data.attributeOr(MaterialLayer::ClearCoat, "SpecularTexture", 5u), 3); + CORRADE_COMPARE(data.attributeOr(MaterialLayer::ClearCoat, MaterialAttribute::SpecularTexture, 5u), 3); + + /* This doesn't */ + CORRADE_VERIFY(!data.tryAttribute(MaterialLayer::ClearCoat, "DiffuseTexture")); + CORRADE_VERIFY(!data.tryAttribute(MaterialLayer::ClearCoat, MaterialAttribute::DiffuseTexture)); + CORRADE_VERIFY(!data.tryAttribute(MaterialLayer::ClearCoat, "DiffuseTexture")); + CORRADE_VERIFY(!data.tryAttribute(MaterialLayer::ClearCoat, MaterialAttribute::DiffuseTexture)); + CORRADE_COMPARE(data.attributeOr(MaterialLayer::ClearCoat, "DiffuseTexture", 5u), 5); + CORRADE_COMPARE(data.attributeOr(MaterialLayer::ClearCoat, MaterialAttribute::DiffuseTexture, 5u), 5); +} + void MaterialDataTest::accessLayerStringOptional() { MaterialData data{{}, { {MaterialAttribute::DiffuseColor, 0x335566ff_rgbaf}, @@ -2042,6 +2224,76 @@ void MaterialDataTest::accessLayerNotFound() { "Trade::MaterialData::attributeOr(): layer ClearCoat not found\n"); } +void MaterialDataTest::accessInvalidLayerName() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + MaterialData data{{}, {}}; + + std::ostringstream out; + Error redirectError{&out}; + data.layerId(MaterialLayer(0x0)); + data.layerId(MaterialLayer(0xfefe)); + data.layerFactor(MaterialLayer(0xfefe)); + data.layerFactorTexture(MaterialLayer(0xfefe)); + data.layerFactorTextureSwizzle(MaterialLayer(0xfefe)); + data.layerFactorTextureMatrix(MaterialLayer(0xfefe)); + data.layerFactorTextureCoordinates(MaterialLayer(0xfefe)); + data.attributeCount(MaterialLayer(0xfefe)); + data.hasAttribute(MaterialLayer(0xfefe), "AlphaMask"); + data.hasAttribute(MaterialLayer(0xfefe), MaterialAttribute::AlphaMask); + data.attributeId(MaterialLayer(0xfefe), "AlphaMask"); + data.attributeId(MaterialLayer(0xfefe), MaterialAttribute::AlphaMask); + data.attributeName(MaterialLayer(0xfefe), 0); + data.attributeType(MaterialLayer(0xfefe), 0); + data.attributeType(MaterialLayer(0xfefe), "AlphaMask"); + data.attributeType(MaterialLayer(0xfefe), MaterialAttribute::AlphaMask); + data.attribute(MaterialLayer(0xfefe), 0); + data.attribute(MaterialLayer(0xfefe), "AlphaMask"); + data.attribute(MaterialLayer(0xfefe), MaterialAttribute::AlphaMask); + data.attribute(MaterialLayer(0xfefe), 0); + data.attribute(MaterialLayer(0xfefe), "AlphaMask"); + data.attribute(MaterialLayer(0xfefe), MaterialAttribute::AlphaMask); + data.attribute(MaterialLayer(0xfefe), 0); + data.tryAttribute(MaterialLayer(0xfefe), "AlphaMask"); + data.tryAttribute(MaterialLayer(0xfefe), MaterialAttribute::AlphaMask); + data.tryAttribute(MaterialLayer(0xfefe), "AlphaMask"); + data.tryAttribute(MaterialLayer(0xfefe), MaterialAttribute::AlphaMask); + data.attributeOr(MaterialLayer(0xfefe), "AlphaMask", false); + data.attributeOr(MaterialLayer(0xfefe), MaterialAttribute::AlphaMask, false); + CORRADE_COMPARE(out.str(), + "Trade::MaterialData::layerId(): invalid name Trade::MaterialLayer(0x0)\n" + "Trade::MaterialData::layerId(): invalid name Trade::MaterialLayer(0xfefe)\n" + "Trade::MaterialData::layerFactor(): invalid name Trade::MaterialLayer(0xfefe)\n" + "Trade::MaterialData::layerFactorTexture(): invalid name Trade::MaterialLayer(0xfefe)\n" + "Trade::MaterialData::layerFactorTextureSwizzle(): invalid name Trade::MaterialLayer(0xfefe)\n" + "Trade::MaterialData::layerFactorTextureMatrix(): invalid name Trade::MaterialLayer(0xfefe)\n" + "Trade::MaterialData::layerFactorTextureCoordinates(): invalid name Trade::MaterialLayer(0xfefe)\n" + "Trade::MaterialData::attributeCount(): invalid name Trade::MaterialLayer(0xfefe)\n" + "Trade::MaterialData::hasAttribute(): invalid name Trade::MaterialLayer(0xfefe)\n" + "Trade::MaterialData::hasAttribute(): invalid name Trade::MaterialLayer(0xfefe)\n" + "Trade::MaterialData::attributeId(): invalid name Trade::MaterialLayer(0xfefe)\n" + "Trade::MaterialData::attributeId(): invalid name Trade::MaterialLayer(0xfefe)\n" + "Trade::MaterialData::attributeName(): invalid name Trade::MaterialLayer(0xfefe)\n" + "Trade::MaterialData::attributeType(): invalid name Trade::MaterialLayer(0xfefe)\n" + "Trade::MaterialData::attributeType(): invalid name Trade::MaterialLayer(0xfefe)\n" + "Trade::MaterialData::attributeType(): invalid name Trade::MaterialLayer(0xfefe)\n" + "Trade::MaterialData::attribute(): invalid name Trade::MaterialLayer(0xfefe)\n" + "Trade::MaterialData::attribute(): invalid name Trade::MaterialLayer(0xfefe)\n" + "Trade::MaterialData::attribute(): invalid name Trade::MaterialLayer(0xfefe)\n" + "Trade::MaterialData::attribute(): invalid name Trade::MaterialLayer(0xfefe)\n" + "Trade::MaterialData::attribute(): invalid name Trade::MaterialLayer(0xfefe)\n" + "Trade::MaterialData::attribute(): invalid name Trade::MaterialLayer(0xfefe)\n" + "Trade::MaterialData::attribute(): invalid name Trade::MaterialLayer(0xfefe)\n" + "Trade::MaterialData::tryAttribute(): invalid name Trade::MaterialLayer(0xfefe)\n" + "Trade::MaterialData::tryAttribute(): invalid name Trade::MaterialLayer(0xfefe)\n" + "Trade::MaterialData::tryAttribute(): invalid name Trade::MaterialLayer(0xfefe)\n" + "Trade::MaterialData::tryAttribute(): invalid name Trade::MaterialLayer(0xfefe)\n" + "Trade::MaterialData::attributeOr(): invalid name Trade::MaterialLayer(0xfefe)\n" + "Trade::MaterialData::attributeOr(): invalid name Trade::MaterialLayer(0xfefe)\n"); +} + void MaterialDataTest::accessOutOfBoundsInLayerIndex() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); @@ -3801,6 +4053,209 @@ void MaterialDataTest::flatAccessInvalidTextures() { "Trade::FlatMaterialData::textureCoordinates(): the material doesn't have a texture\n"); } +void MaterialDataTest::templateLayerAccess() { + MaterialLayerData data{{}, { + {MaterialAttribute::BaseColor, 0x335566ff_rgbaf}, + + {MaterialLayer::ClearCoat}, + {MaterialAttribute::LayerFactor, 0.35f}, + {MaterialAttribute::LayerFactorTexture, 3u}, + {MaterialAttribute::LayerFactorTextureSwizzle, MaterialTextureSwizzle::B}, + {MaterialAttribute::LayerFactorTextureMatrix, Matrix3::scaling({0.5f, 1.0f})}, + {MaterialAttribute::LayerFactorTextureCoordinates, 4u}, + }, {1, 7}}; + + CORRADE_COMPARE(data.layerName(), "ClearCoat"); + CORRADE_COMPARE(data.layerFactor(), 0.35f); + CORRADE_COMPARE(data.layerFactorTexture(), 3u); + CORRADE_COMPARE(data.layerFactorTextureSwizzle(), MaterialTextureSwizzle::B); + CORRADE_COMPARE(data.layerFactorTextureMatrix(), Matrix3::scaling({0.5f, 1.0f})); + CORRADE_COMPARE(data.layerFactorTextureCoordinates(), 4u); + + CORRADE_COMPARE(data.attributeCount(), 6); + CORRADE_VERIFY(data.hasAttribute(MaterialAttribute::LayerFactor)); + CORRADE_VERIFY(data.hasAttribute("LayerFactorTexture")); + CORRADE_VERIFY(!data.hasAttribute(MaterialAttribute::BaseColor)); + CORRADE_VERIFY(!data.hasAttribute("BaseColor")); + CORRADE_VERIFY(data.hasAttribute(0, MaterialAttribute::BaseColor)); + CORRADE_VERIFY(data.hasAttribute(0, "BaseColor")); + + CORRADE_COMPARE(data.attributeId(MaterialAttribute::LayerFactorTexture), 2); + CORRADE_COMPARE(data.attributeId("LayerFactorTexture"), 2); + + CORRADE_COMPARE(data.attributeName(2), "LayerFactorTexture"); + + CORRADE_COMPARE(data.attributeType(2), MaterialAttributeType::UnsignedInt); + CORRADE_COMPARE(data.attributeType(MaterialAttribute::LayerFactorTexture), MaterialAttributeType::UnsignedInt); + CORRADE_COMPARE(data.attributeType("LayerFactorTexture"), MaterialAttributeType::UnsignedInt); + + CORRADE_COMPARE(*static_cast(data.attribute(2)), 3); + CORRADE_COMPARE(*static_cast(data.attribute(MaterialAttribute::LayerFactorTexture)), 3); + CORRADE_COMPARE(*static_cast(data.attribute("LayerFactorTexture")), 3); + + CORRADE_COMPARE(data.attribute(2), 3); + CORRADE_COMPARE(data.attribute(MaterialAttribute::LayerFactorTexture), 3); + CORRADE_COMPARE(data.attribute("LayerFactorTexture"), 3); + + CORRADE_COMPARE(*static_cast(data.tryAttribute(MaterialAttribute::LayerFactorTexture)), 3); + CORRADE_COMPARE(*static_cast(data.tryAttribute("LayerFactorTexture")), 3); + + CORRADE_COMPARE(data.tryAttribute(MaterialAttribute::LayerFactorTexture), 3); + CORRADE_COMPARE(data.tryAttribute("LayerFactorTexture"), 3); + + CORRADE_COMPARE(data.attributeOr(MaterialAttribute::LayerFactorTexture, 5u), 3); + CORRADE_COMPARE(data.attributeOr("LayerFactorTexture", 5u), 3); +} + +void MaterialDataTest::pbrClearCoatAccess() { + MaterialData base{MaterialType::PbrClearCoat, { + {MaterialLayer::ClearCoat}, + {MaterialAttribute::Roughness, 0.7f} + }, {0, 2}}; + + CORRADE_COMPARE(base.types(), MaterialType::PbrClearCoat); + const auto& data = base.as(); + + CORRADE_VERIFY(!data.hasTextureTransformation()); + CORRADE_VERIFY(!data.hasTextureCoordinates()); + CORRADE_COMPARE(data.roughness(), 0.7f); +} + +void MaterialDataTest::pbrClearCoatAccessDefaults() { + MaterialData base{{}, { + /* Needs to have at least the layer name, otherwise the queries will + blow up */ + {MaterialLayer::ClearCoat} + }, {0, 1}}; + + CORRADE_COMPARE(base.types(), MaterialTypes{}); + const auto& data = base.as(); + + CORRADE_VERIFY(!data.hasTextureTransformation()); + CORRADE_VERIFY(!data.hasTextureCoordinates()); + CORRADE_COMPARE(data.layerFactor(), 1.0f); + CORRADE_COMPARE(data.roughness(), 0.0f); +} + +void MaterialDataTest::pbrClearCoatAccessTextured() { + PbrClearCoatMaterialData data{{}, { + {MaterialLayer::ClearCoat}, + {MaterialAttribute::Roughness, 0.7f}, + {MaterialAttribute::RoughnessTexture, 2u}, + {MaterialAttribute::RoughnessTextureSwizzle, MaterialTextureSwizzle::A}, + {MaterialAttribute::RoughnessTextureMatrix, Matrix3::translation({2.0f, 1.5f})}, + {MaterialAttribute::RoughnessTextureCoordinates, 6u}, + {MaterialAttribute::NormalTexture, 3u}, + {MaterialAttribute::NormalTextureSwizzle, MaterialTextureSwizzle::B}, + {MaterialAttribute::NormalTextureMatrix, Matrix3::translation({0.0f, 0.5f})}, + {MaterialAttribute::NormalTextureCoordinates, 7u}, + }, {0, 10}}; + + CORRADE_VERIFY(data.hasTextureTransformation()); + CORRADE_VERIFY(data.hasTextureCoordinates()); + CORRADE_COMPARE(data.roughness(), 0.7f); + CORRADE_COMPARE(data.roughnessTexture(), 2u); + CORRADE_COMPARE(data.roughnessTextureSwizzle(), MaterialTextureSwizzle::A); + CORRADE_COMPARE(data.roughnessTextureMatrix(), Matrix3::translation({2.0f, 1.5f})); + CORRADE_COMPARE(data.roughnessTextureCoordinates(), 6u); + CORRADE_COMPARE(data.normalTexture(), 3u); + CORRADE_COMPARE(data.normalTextureSwizzle(), MaterialTextureSwizzle::B); + CORRADE_COMPARE(data.normalTextureMatrix(), Matrix3::translation({0.0f, 0.5f})); + CORRADE_COMPARE(data.normalTextureCoordinates(), 7u); +} + +void MaterialDataTest::pbrClearCoatAccessTexturedDefaults() { + PbrClearCoatMaterialData data{{}, { + {MaterialLayer::ClearCoat}, + {MaterialAttribute::RoughnessTexture, 2u}, + {MaterialAttribute::NormalTexture, 3u}, + }, {0, 3}}; + + CORRADE_VERIFY(!data.hasTextureTransformation()); + CORRADE_VERIFY(!data.hasTextureCoordinates()); + CORRADE_COMPARE(data.roughness(), 0.0f); + CORRADE_COMPARE(data.roughnessTexture(), 2u); + CORRADE_COMPARE(data.roughnessTextureSwizzle(), MaterialTextureSwizzle::R); + CORRADE_COMPARE(data.roughnessTextureMatrix(), Matrix3{}); + CORRADE_COMPARE(data.roughnessTextureCoordinates(), 0u); + CORRADE_COMPARE(data.normalTexture(), 3u); + CORRADE_COMPARE(data.normalTextureSwizzle(), MaterialTextureSwizzle::RGB); + CORRADE_COMPARE(data.normalTextureMatrix(), Matrix3{}); + CORRADE_COMPARE(data.normalTextureCoordinates(), 0u); +} + +void MaterialDataTest::pbrClearCoatAccessTexturedSingleMatrixCoordinates() { + PbrClearCoatMaterialData data{{}, { + {MaterialLayer::ClearCoat}, + {MaterialAttribute::RoughnessTexture, 2u}, + {MaterialAttribute::NormalTexture, 3u}, + {MaterialAttribute::TextureMatrix, Matrix3::translation({0.0f, 0.5f})}, + {MaterialAttribute::TextureCoordinates, 7u}, + }, {0, 5}}; + + CORRADE_VERIFY(data.hasTextureTransformation()); + CORRADE_VERIFY(data.hasTextureCoordinates()); + CORRADE_COMPARE(data.roughnessTextureMatrix(), Matrix3::translation({0.0f, 0.5f})); + CORRADE_COMPARE(data.roughnessTextureCoordinates(), 7u); + CORRADE_COMPARE(data.normalTextureMatrix(), Matrix3::translation({0.0f, 0.5f})); + CORRADE_COMPARE(data.normalTextureCoordinates(), 7u); +} + +void MaterialDataTest::pbrClearCoatAccessTexturedBaseMaterialMatrixCoordinates() { + PbrClearCoatMaterialData data{{}, { + {MaterialAttribute::TextureMatrix, Matrix3::translation({0.0f, 0.5f})}, + {MaterialAttribute::TextureCoordinates, 7u}, + + {MaterialLayer::ClearCoat}, + {MaterialAttribute::RoughnessTexture, 2u}, + {MaterialAttribute::NormalTexture, 3u}, + }, {2, 5}}; + + CORRADE_VERIFY(data.hasTextureTransformation()); + CORRADE_VERIFY(data.hasTextureCoordinates()); + CORRADE_COMPARE(data.roughnessTextureMatrix(), Matrix3::translation({0.0f, 0.5f})); + CORRADE_COMPARE(data.roughnessTextureCoordinates(), 7u); + CORRADE_COMPARE(data.normalTextureMatrix(), Matrix3::translation({0.0f, 0.5f})); + CORRADE_COMPARE(data.normalTextureCoordinates(), 7u); +} + +void MaterialDataTest::pbrClearCoatAccessInvalidTextures() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + PbrClearCoatMaterialData data{{}, { + {MaterialLayer::ClearCoat}, + }, {0, 1}}; + + std::ostringstream out; + Error redirectError{&out}; + data.roughnessTexture(); + data.roughnessTextureSwizzle(); + data.roughnessTextureMatrix(); + data.roughnessTextureCoordinates(); + data.normalTexture(); + data.normalTextureSwizzle(); + data.normalTextureMatrix(); + data.normalTextureCoordinates(); + CORRADE_COMPARE(out.str(), + "Trade::MaterialData::attribute(): attribute RoughnessTexture not found in layer ClearCoat\n" + "Trade::PbrClearCoatMaterialData::roughnessTextureSwizzle(): the layer doesn't have a roughness texture\n" + "Trade::PbrClearCoatMaterialData::roughnessTextureMatrix(): the layer doesn't have a roughness texture\n" + "Trade::PbrClearCoatMaterialData::roughnessTextureCoordinates(): the layer doesn't have a roughness texture\n" + "Trade::MaterialData::attribute(): attribute NormalTexture not found in layer ClearCoat\n" + "Trade::PbrClearCoatMaterialData::normalTextureSwizzle(): the layer doesn't have a normal texture\n" + "Trade::PbrClearCoatMaterialData::normalTextureMatrix(): the layer doesn't have a normal texture\n" + "Trade::PbrClearCoatMaterialData::normalTextureCoordinates(): the layer doesn't have a normal texture\n"); +} + +void MaterialDataTest::debugLayer() { + std::ostringstream out; + + Debug{&out} << MaterialLayer::ClearCoat << MaterialLayer(0xfefe) << MaterialLayer{}; + CORRADE_COMPARE(out.str(), "Trade::MaterialLayer::ClearCoat Trade::MaterialLayer(0xfefe) Trade::MaterialLayer(0x0)\n"); +} + void MaterialDataTest::debugAttribute() { std::ostringstream out; diff --git a/src/Magnum/Trade/Trade.h b/src/Magnum/Trade/Trade.h index 0b0e07cdde..dac3bb7389 100644 --- a/src/Magnum/Trade/Trade.h +++ b/src/Magnum/Trade/Trade.h @@ -51,6 +51,7 @@ typedef CORRADE_DEPRECATED("use InputFileCallbackPolicy instead") InputFileCallb enum class MaterialAttribute: UnsignedInt; enum class MaterialTextureSwizzle: UnsignedInt; enum class MaterialAttributeType: UnsignedByte; +enum class MaterialLayer: UnsignedInt; enum class MaterialType: UnsignedInt; enum class MaterialAlphaMode: UnsignedByte; class MaterialAttributeData; @@ -92,6 +93,7 @@ class MeshObjectData2D; class MeshObjectData3D; class ObjectData2D; class ObjectData3D; +class PbrClearCoatMaterialData; class PbrMetallicRoughnessMaterialData; class PbrSpecularGlossinessMaterialData; class PhongMaterialData; From 271b7a76295ab7fee7522506a1bc7dbf62d250f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 4 Aug 2020 17:40:18 +0200 Subject: [PATCH 21/36] Trade: usage docs for MaterialData. --- doc/snippets/MagnumTrade.cpp | 207 +++++++++++++++ src/Magnum/Trade/FlatMaterialData.h | 8 +- src/Magnum/Trade/MaterialData.h | 239 +++++++++++++++--- src/Magnum/Trade/MaterialLayerData.h | 3 +- src/Magnum/Trade/PbrClearCoatMaterialData.h | 7 +- .../Trade/PbrMetallicRoughnessMaterialData.h | 7 +- .../Trade/PbrSpecularGlossinessMaterialData.h | 7 +- src/Magnum/Trade/PhongMaterialData.h | 7 +- 8 files changed, 436 insertions(+), 49 deletions(-) diff --git a/doc/snippets/MagnumTrade.cpp b/doc/snippets/MagnumTrade.cpp index 2e524f8de2..5306b5bb52 100644 --- a/doc/snippets/MagnumTrade.cpp +++ b/doc/snippets/MagnumTrade.cpp @@ -40,9 +40,13 @@ #include "Magnum/Trade/AbstractImporter.h" #include "Magnum/Trade/AnimationData.h" #include "Magnum/Trade/ImageData.h" +#include "Magnum/Trade/MaterialData.h" #include "Magnum/Trade/MeshData.h" #include "Magnum/Trade/ObjectData2D.h" #include "Magnum/Trade/MeshObjectData3D.h" +#include "Magnum/Trade/PbrClearCoatMaterialData.h" +#include "Magnum/Trade/PbrSpecularGlossinessMaterialData.h" +#include "Magnum/Trade/PbrMetallicRoughnessMaterialData.h" #include "Magnum/Trade/PhongMaterialData.h" #ifdef MAGNUM_TARGET_GL #include "Magnum/GL/Texture.h" @@ -61,6 +65,8 @@ #include "Magnum/Trade/MeshData3D.h" #endif +#define DOXYGEN_IGNORE(...) __VA_ARGS__ + using namespace Magnum; using namespace Magnum::Math::Literals; @@ -248,6 +254,207 @@ for(auto&& row: data.mutablePixels()) /* [ImageData-usage-mutable] */ } +{ +/* [MaterialAttributeData-name] */ +Trade::MaterialAttributeData a{ + Trade::MaterialAttribute::DiffuseColor, 0x3bd267ff_rgbaf}; +Trade::MaterialAttributeData b{"DiffuseColor", 0x3bd267ff_rgbaf}; +/* [MaterialAttributeData-name] */ +} + +{ +/* [MaterialData-usage] */ +Trade::MaterialData data = DOXYGEN_IGNORE(Trade::MaterialData{{}, {}}); + +// Assumes the attribute exists +Float roughness = data.attribute(Trade::MaterialAttribute::Roughness); + +// Optional access +Color4 color = data.attributeOr(Trade::MaterialAttribute::BaseColor, + 0x3bd267ff_rgbaf); +if(Containers::Optional texture = + data.tryAttribute(Trade::MaterialAttribute::BaseColorTexture)) +{ + // ... +} +/* [MaterialData-usage] */ +static_cast(roughness); +static_cast(color); +} + +{ +Trade::MaterialData data{{}, {}}; +/* [MaterialData-usage-types] */ +/* Prefer a specular/roughness workflow, if present */ +if(data.types() & Trade::MaterialType::PbrSpecularGlossiness) { + const auto& pbr = data.as(); + + Color4 diffuse = pbr.diffuseColor(); + Color4 specular = pbr.specularColor(); + Float glossiness = pbr.glossiness(); + + DOXYGEN_IGNORE(static_cast(diffuse), static_cast(specular), static_cast(glossiness);) + +/* Otherwise use metallic/roughness (or defaults if no attributes present) */ +} else { + const auto& pbr = data.as(); + + Color4 base = pbr.baseColor(); + Float metalness = pbr.metalness(); + Float roughness = pbr.roughness(); + + DOXYGEN_IGNORE(static_cast(base), static_cast(metalness), static_cast(roughness);) +} +/* [MaterialData-usage-types] */ +} + +{ +/* [MaterialData-usage-packing] */ +Trade::PbrMetallicRoughnessMaterialData data = DOXYGEN_IGNORE(Trade::PbrMetallicRoughnessMaterialData{{}, {}}); + +/* Use a shader that accepts a single packed metallic/roughness texture. + Querying any texture attributes will give the same values for both metalness + and roughness. */ +if(data.hasMetallicRoughnessTexture()) { + UnsignedInt metallicRoughness = data.metalnessTexture(); + + DOXYGEN_IGNORE(static_cast(metallicRoughness);) + +/* Supply texture channels separately */ +} else { + UnsignedInt metalness = data.metalnessTexture(); + UnsignedInt roughness = data.roughnessTexture(); + Trade::MaterialTextureSwizzle metalnessSwizzle = data.metalnessTextureSwizzle(); + Trade::MaterialTextureSwizzle roughnessSwizzle = data.roughnessTextureSwizzle(); + + DOXYGEN_IGNORE(static_cast(metalness), static_cast(roughness), static_cast(metalnessSwizzle), static_cast(roughnessSwizzle);) +} +/* [MaterialData-usage-packing] */ +} + +{ +Trade::MaterialData data{{}, {}}; +/* [MaterialData-usage-layers] */ +if(data.hasLayer(Trade::MaterialLayer::ClearCoat)) { + Float clearCoatFactor = data.attributeOr(Trade::MaterialLayer::ClearCoat, + Trade::MaterialAttribute::LayerFactor, 1.0f); + Float clearCoatRoughness = data.attributeOr(Trade::MaterialLayer::ClearCoat, + Trade::MaterialAttribute::Roughness, 0.0f); + + DOXYGEN_IGNORE(static_cast(clearCoatFactor), static_cast(clearCoatRoughness);) +} +/* [MaterialData-usage-layers] */ +} + +{ +Trade::MaterialData data{{}, {}}; +/* [MaterialData-usage-layers-types] */ +if(data.types() & Trade::MaterialType::PbrClearCoat) { + const auto& clearCoat = data.as(); + + Float clearCoatFactor = clearCoat.layerFactor(); + Float clearCoatRoughness = clearCoat.roughness(); + + DOXYGEN_IGNORE(static_cast(clearCoatFactor), static_cast(clearCoatRoughness);) +} +/* [MaterialData-usage-layers-types] */ +} + +{ +/* [MaterialData-populating] */ +Trade::MaterialData data{Trade::MaterialType::PbrMetallicRoughness, { + {Trade::MaterialAttribute::DoubleSided, true}, + {Trade::MaterialAttribute::BaseColor, 0x3bd267ff_srgbaf}, + {Trade::MaterialAttribute::BaseColorTexture, 17u}, + {Trade::MaterialAttribute::TextureMatrix, Matrix3::scaling({0.5f, 1.0f})} +}}; +/* [MaterialData-populating] */ +} + +{ +/* [MaterialData-populating-non-owned] */ +using namespace Containers::Literals; + +constexpr Trade::MaterialAttributeData attributes[]{ + {"DoubleSided"_s, true}, + {"BaseColor"_s, Color4{0.043735f, 0.64448f, 0.135633f, 1.0f}}, + {"BaseColorTexture"_s, 5u}, + {"TextureMatrix"_s, Matrix3{{0.5f, 0.0f, 0.0f}, + {0.0f, 1.0f, 0.0f}, + {0.0f, 0.0f, 1.0f}}} +}; + +Trade::MaterialData data{Trade::MaterialType::Phong, {}, attributes}; +/* [MaterialData-populating-non-owned] */ +} + +#ifdef MAGNUM_TARGET_GL +{ +GL::Texture2D baseColorTexture; +/* [MaterialData-populating-custom] */ +Trade::MaterialData data{Trade::MaterialType::PbrMetallicRoughness, { + {Trade::MaterialAttribute::BaseColor, 0x3bd267ff_srgbaf}, + {Trade::MaterialAttribute::TextureMatrix, Matrix3::scaling({0.5f, 1.0f})}, + {"baseColorTexturePointer", &baseColorTexture}, + {"highlightColor", 0x00ffff_srgbf}, + {"name", "Canary Green Plastic, really ugly"} +}}; + +// Retrieving the texture pointer +GL::Texture2D* texture = data.attribute("baseColorTexturePointer"); +/* [MaterialData-populating-custom] */ +static_cast(texture); +} +#endif + +{ +/* [MaterialData-populating-layers] */ +Trade::MaterialData data{ + Trade::MaterialType::PbrMetallicRoughness|Trade::MaterialType::PbrClearCoat, + { + {Trade::MaterialAttribute::BaseColor, 0xffcc33_srgbf}, + {Trade::MaterialAttribute::MetallicRoughnessTexture, 0u}, + + {Trade::MaterialLayer::ClearCoat}, + {Trade::MaterialAttribute::LayerFactorTexture, 1u}, + {Trade::MaterialAttribute::RoughnessTexture, 1u}, + {Trade::MaterialAttribute::RoughnessTextureSwizzle, + Trade::MaterialTextureSwizzle::G} + }, + + {2, 6} +}; +/* [MaterialData-populating-layers] */ +} + +{ +UnsignedInt a, b, c, d, sandTile, grassTile, rockTile; +/* [MaterialData-populating-layers-custom] */ +Trade::MaterialData proceduralLandscape{ + Trade::MaterialTypes{}, // Doesn't match any builtin material type + { + // Rock layer + {Trade::MaterialAttribute::LayerFactorTexture, a}, + {Trade::MaterialAttribute::BaseColorTexture, rockTile}, + + // Sand layer + {Trade::MaterialAttribute::LayerFactorTexture, b}, + {"blendType", "mix"}, + {Trade::MaterialAttribute::BaseColorTexture, sandTile}, + + // Grass layer + {Trade::MaterialAttribute::LayerFactorTexture, c}, + {"blendType", "overlay"}, + {"strandLengthTexture", d}, + {Trade::MaterialAttribute::BaseColorTexture, grassTile}, + }, + + // There's no base material, everything is in layers + {0, 2, 5, 9} +}; +/* [MaterialData-populating-layers-custom] */ +} + { /* [MeshIndexData-usage] */ Containers::ArrayView indices; diff --git a/src/Magnum/Trade/FlatMaterialData.h b/src/Magnum/Trade/FlatMaterialData.h index 88be295352..eb5b4ff18c 100644 --- a/src/Magnum/Trade/FlatMaterialData.h +++ b/src/Magnum/Trade/FlatMaterialData.h @@ -38,8 +38,12 @@ namespace Magnum { namespace Trade { @brief Flat material data @m_since_latest -@see @ref AbstractImporter::material(), @ref PbrMetallicRoughnessMaterialData, - @ref PbrSpecularGlossinessMaterialData, @ref PhongMaterialData +See @ref Trade-MaterialData-usage-types for more information about how to use +this class. +@see @ref AbstractImporter::material(), @ref PhongMaterialData, + @ref PbrMetallicRoughnessMaterialData, + @ref PbrSpecularGlossinessMaterialData, @ref PbrClearCoatMaterialData, + @ref MaterialType::Flat */ class MAGNUM_TRADE_EXPORT FlatMaterialData: public MaterialData { public: diff --git a/src/Magnum/Trade/MaterialData.h b/src/Magnum/Trade/MaterialData.h index 9b5dd48d0a..5f6e991485 100644 --- a/src/Magnum/Trade/MaterialData.h +++ b/src/Magnum/Trade/MaterialData.h @@ -1341,8 +1341,168 @@ MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, MaterialAlphaMode value); @brief Material data @m_since_latest -Key-value store for material attributes in one of the types defined by -@ref MaterialAttributeType. +Key-value store for builtin as well as custom material attributes, with an +ability to define additional layers further affecting the base material. +Populated instances of this class are returned from +@ref AbstractImporter::material(). + +@section Trade-MaterialData-usage Usage + +The simplest usage is through templated @ref attribute() functions, which take +a string attribute name or one of the pre-defined @ref MaterialAttribute +values. You're expected to check for attribute presence first with +@ref hasAttribute(), and the requested type has to match @ref attributeType(). +To make things easier, each of the attributes defined in @ref MaterialAttribute +has a strictly defined type, so you can safely assume the type when requesting +those. In addition there's @ref tryAttribute() and @ref attributeOr() that +return a @ref Containers::NullOpt or a default value in case given attribute is +not found. + +@snippet MagnumTrade.cpp MaterialData-usage + +It's also possible to iterate through all attributes using @ref attributeName(), +@ref attributeType() and @ref attribute() taking indices instead of names, with +@ref attributeCount() returning the total attribute count. + +@subsection Trade-MaterialData-usage-types Material types and cnvenience accessors + +A material usually consists of a set of attributes for a particular rendering +workflow --- PBR metallic/roughness, Phong or for example flat-shaded +materials. To hint what the material contains, @ref types() returns a set of +@ref MaterialType values. It's not just a single value as the data can define +attributes for more than one material type (for example both metallic/roughness +and specular/glossiness PBR workflow), allowing the application to pick the +best type for a particular use case. + +Because retrieving everything through the @ref attribute() APIs can get verbose +and complex, the @ref Trade library provides a set of accessor APIs for common +material types such as @ref FlatMaterialData, @ref PhongMaterialData, +@ref PbrMetallicRoughnessMaterialData or @ref PbrSpecularGlossinessMaterialData. +Using @ref as() you can convert any @ref MaterialData instance to a reference +to one of those. These convenience APIs then take care of default values when +an attribute isn't present or handle fallbacks when an attribute can be defined +in multiple ways: + +@snippet MagnumTrade.cpp MaterialData-usage-types + +Each @ref MaterialAttribute is exposed through one or more of those convenience +APIs, see the documentation of of a particular enum value for more information. + +@subsection Trade-MaterialData-usage-packing Texture packing + +Especially for single- and two-channel texture maps it's common to have more +than one map packed into a single texture. Such packing is expressed through +various @ref MaterialTextureSwizzle attributes such as +@ref MaterialAttribute::MetalnessTextureSwizzle. While this provides an almost +endless variability, real-world textures are in just a few common packing +schemes. For convenience these have dedicated attributes such as +@ref MaterialAttribute::MetallicRoughnessTexture and can also be checked for +with for example @ref PbrMetallicRoughnessMaterialData::hasMetallicRoughnessTexture(): + +@snippet MagnumTrade.cpp MaterialData-usage-packing + +@subsection Trade-MaterialData-usage-layers Material layers + +In addition to the base material, there can be material layers. While a +material attribute can be specified only once for a particular layer, multiple +layers can use the same attribute name for different purpose. Layers are +commonly used in PBR workflows to describe lacquered wood, metallic paint or +for example a thin film on leather surfaces. You can enumerate and query layers +using @ref layerCount(), @ref layerName() and @ref hasLayer(), layer-specific +attributes are retrieved by passing layer ID or name as the first parameter to +the @ref attribute() family of APIs. + +For each layer there can be predefined @ref layerFactor(), +@ref layerFactorTexture() and other texture-related attributes which define how +much the layer affects the underlying material, but the exact semantics of how +the factor is applied is left to the layer implementation. + +Here's an example showing retrieval of a clear coat layer parameters, if +present: + +@snippet MagnumTrade.cpp MaterialData-usage-layers + +Like with base material attributes, builtin layers also have convenience +accessor APIs. The above can be written in a more compact way using +@link PbrClearCoatMaterialData @endlink: + +@snippet MagnumTrade.cpp MaterialData-usage-layers-types + +@section Trade-MaterialData-populating Populating an instance + +A @ref MaterialData instance by default takes over ownership of an +@ref Corrade::Containers::Array containing @ref MaterialAttributeData +instances, together with @ref MaterialTypes suggesting available material types +(or an empty set, in case of a fully custom material). Attribute values can be +in one of the types from @ref MaterialAttributeType, and the type is in most +cases inferred implicitly. The class internally uses strings for attribute +names, but you're encouraged to use the predefined names from +@ref MaterialAttribute --- with those, the attribute gets checked additionally +that it's in an expected type. Attribute order doesn't matter, the array gets +internally sorted by name to allow a @f$ \mathcal{O}(\log n) @f$ lookup. + +@snippet MagnumTrade.cpp MaterialData-populating + +In addition to passing ownership of an array it's also possible to have the +@ref MaterialData instance refer to external data (for example in a +memory-mapped file, constant memory etc.). The additional second argument is +@ref DataFlags that's used only for safely disambiguating from the owning +constructor, and you'll pass a @ref Corrade::Containers::ArrayView instead of +an @ref Corrade::Containers::Array. Note that in this case, since the attribute +data is treated as immutable, you *have to* ensure the list is sorted by name. + +@snippet MagnumTrade.cpp MaterialData-populating-non-owned + + + +@m_class{m-note m-info} + +@par + Additionally, as shown above, in order to create a @cpp constexpr @ce + @ref MaterialAttributeData array, you need to use + @ref Corrade::Containers::StringView literals instead of plain C strings + or the @ref MaterialAttribute enum, and be sure to call only + @cpp constexpr @ce-enabled constructors of stored data types. + +@subsection Trade-MaterialData-populating-custom Custom material attributes + +While attribute names beginning with uppercase letters are reserved for builtin +Magnum attributes, anything beginning with a lowercase letter can be a custom +attribute. For greater flexibility, custom attributes can be also strings or +pointers, allowing you to store arbitrary properties or direct texture pointers +instead of IDs: + +@snippet MagnumTrade.cpp MaterialData-populating-custom + +@subsection Trade-MaterialData-populating-layers Adding material layers + +Material layers are internally represented as ranges of the attribute array and +by default the whole attribute array is treated as a base material. The actual +split into layers can be described using an additionaly offset array passed to +@ref MaterialData(MaterialTypes, Containers::Array&&, Containers::Array&&, const void*), +where entry *i* specifies the end offset of layer *i* --- in the following +snippet we have two layers (one base material and one clear coat layer), the +base material being the first two attributes and the clear coat layer being +attributes in range @cpp 2 @ce to @cpp 6 @ce (thus four attributes): + +@snippet MagnumTrade.cpp MaterialData-populating-layers + +Like with just a base material, the attributes get sorted for a +@f$ \mathcal{O}(\log n) @f$ lookup --- but not as a whole, each layer +separately. In contrary, because layer order matters, those are not reordered +(and thus their lookup is @f$ \mathcal{O}(n) @f$, however it isn't expected to +have that many layers for this to matter). The layers can be named by supplying +a @ref MaterialAttribute::LayerName attribute (or, like shown above, by using +the convenience @ref MaterialAttributeData::MaterialAttributeData(MaterialLayer) +constructor) but don't have to --- if a layer doesn't have a name, it can be +only looked up by its index, not by a name. + +Apart from builtin layers, there's no limit in what the layers can be used for +--- the data can for example describe a completely custom landscape from a set +of authored `rockTile`, `sandTile`, `grassTile` textures and procedurally +generated blend factors `a`, `b`, `c`, `d`: + +@snippet MagnumTrade.cpp MaterialData-populating-layers-custom @section Trade-MaterialData-representation Internal representation @@ -1923,16 +2083,16 @@ class MAGNUM_TRADE_EXPORT MaterialData { * The @p layer is expected to be smaller than @ref layerCount() const * and @p id is expected to be smaller than @ref attributeCount(UnsignedInt) const * in that layer. Cast the pointer to a concrete type based on - * @ref type(). Note that in case of a - * @ref MaterialAttributeType::Pointer or a - * @ref MaterialAttributeType::MutablePointer, returns a - * *pointer to a pointer*, not the pointer value itself. + * @ref type(). * - * In case of a @ref MaterialAttributeType::String returns a - * null-terminated @cpp const char* @ce (not a pointer to - * @ref Containers::StringView). This doesn't preserve the actual - * string size in case the string data contain zero bytes, thus prefer - * to use typed access in that case. + * - In case of a @ref MaterialAttributeType::Pointer or a + * @ref MaterialAttributeType::MutablePointer, returns a + * *pointer to a pointer*, not the pointer value itself. + * - In case of a @ref MaterialAttributeType::String returns a + * null-terminated @cpp const char* @ce (not a pointer to + * @ref Containers::StringView). This doesn't preserve the actual + * string size in case the string data contain zero bytes, thus + * prefer to use typed access in that case. */ const void* attribute(UnsignedInt layer, UnsignedInt id) const; @@ -1941,16 +2101,17 @@ class MAGNUM_TRADE_EXPORT MaterialData { * * The @p layer is expected to be smaller than @ref layerCount() const * and @p name is expected to exist in that layer. Cast the pointer to - * a concrete type based on @ref attributeType(). Note that - * in case of a @ref MaterialAttributeType::Pointer or a - * @ref MaterialAttributeType::MutablePointer, returns a - * *pointer to a pointer*, not the pointer value itself. + * a concrete type based on @ref attributeType(). + * + * - In case of a @ref MaterialAttributeType::Pointer or a + * @ref MaterialAttributeType::MutablePointer, returns a + * *pointer to a pointer*, not the pointer value itself. + * - In case of a @ref MaterialAttributeType::String returns a + * null-terminated @cpp const char* @ce (not a pointer to + * @ref Containers::StringView). This doesn't preserve the actual + * string size in case the string data contain zero bytes, thus prefer + * to use typed access in that case. * - * In case of a @ref MaterialAttributeType::String returns a - * null-terminated @cpp const char* @ce (not a pointer to - * @ref Containers::StringView). This doesn't preserve the actual - * string size in case the string data contain zero bytes, thus prefer - * to use typed access in that case. * @see @ref hasAttribute(), @ref tryAttribute(), @ref attributeOr() */ const void* attribute(UnsignedInt layer, Containers::StringView name) const; @@ -1961,16 +2122,17 @@ class MAGNUM_TRADE_EXPORT MaterialData { * * The @p layer is expected to exist and the @p id is expected to be smaller than @ref attributeCount(UnsignedInt) const * in that layer. Cast the pointer to a concrete type based on - * @ref attributeType(). Note that in case of a - * @ref MaterialAttributeType::Pointer or a - * @ref MaterialAttributeType::MutablePointer, returns a - * *pointer to a pointer*, not the pointer value itself. + * @ref attributeType(). + * + * - In case of a @ref MaterialAttributeType::Pointer or a + * @ref MaterialAttributeType::MutablePointer, returns a + * *pointer to a pointer*, not the pointer value itself. + * - In case of a @ref MaterialAttributeType::String returns a + * null-terminated @cpp const char* @ce (not a pointer to + * @ref Containers::StringView). This doesn't preserve the actual + * string size in case the string data contain zero bytes, thus prefer + * to use typed access in that case. * - * In case of a @ref MaterialAttributeType::String returns a - * null-terminated @cpp const char* @ce (not a pointer to - * @ref Containers::StringView). This doesn't preserve the actual - * string size in case the string data contain zero bytes, thus prefer - * to use typed access in that case. * @see @ref hasLayer() */ const void* attribute(Containers::StringView layer, UnsignedInt id) const; @@ -1981,16 +2143,17 @@ class MAGNUM_TRADE_EXPORT MaterialData { * * The @p layer is expected to exist and @p name is expected to exist * in that layer. Cast the pointer to a concrete type based on - * @ref attributeType(). Note that in case of a - * @ref MaterialAttributeType::Pointer or a - * @ref MaterialAttributeType::MutablePointer, returns a - * *pointer to a pointer*, not the pointer value itself. + * @ref attributeType(). + * + * - In case of a @ref MaterialAttributeType::Pointer or a + * @ref MaterialAttributeType::MutablePointer, returns a + * *pointer to a pointer*, not the pointer value itself. + * - In case of a @ref MaterialAttributeType::String returns a + * null-terminated @cpp const char* @ce (not a pointer to + * @ref Containers::StringView). This doesn't preserve the actual + * string size in case the string data contain zero bytes, thus + * prefer to use typed access in that case. * - * In case of a @ref MaterialAttributeType::String returns a - * null-terminated @cpp const char* @ce (not a pointer to - * @ref Containers::StringView). This doesn't preserve the actual - * string size in case the string data contain zero bytes, thus prefer - * to use typed access in that case. * @see @ref hasLayer(), @ref hasAttribute() */ const void* attribute(Containers::StringView layer, Containers::StringView name) const; diff --git a/src/Magnum/Trade/MaterialLayerData.h b/src/Magnum/Trade/MaterialLayerData.h index b0dad741fe..d2ef55b72b 100644 --- a/src/Magnum/Trade/MaterialLayerData.h +++ b/src/Magnum/Trade/MaterialLayerData.h @@ -41,7 +41,8 @@ namespace Magnum { namespace Trade { Convenience wrapper that re-routes all @ref MaterialData base material layer and attribute accessors APIS from to a layer specified in the @p layer template -parameter. All APIs expect that given layer exists. +parameter. All APIs expect that given layer exists. See +@ref Trade-MaterialData-usage-layers for more information. */ template class MaterialLayerData: public MaterialData { public: diff --git a/src/Magnum/Trade/PbrClearCoatMaterialData.h b/src/Magnum/Trade/PbrClearCoatMaterialData.h index be3a0c1452..0a43f179f0 100644 --- a/src/Magnum/Trade/PbrClearCoatMaterialData.h +++ b/src/Magnum/Trade/PbrClearCoatMaterialData.h @@ -39,8 +39,11 @@ namespace Magnum { namespace Trade { @m_since_latest Exposes properties of a @ref MaterialLayer::ClearCoat layer. All APIs expect -that the layer is present in the material. -@see @ref AbstractImporter::material(), @ref MaterialType::PbrClearCoat +that the layer is present in the material. See @ref Trade-MaterialData-usage-types +for more information about how to use this class. +@see @ref AbstractImporter::material(), @ref FlatMaterialData, + @ref PhongMaterialData, @ref PbrMetallicRoughnessMaterialData, + @ref PbrSpecularGlossinessMaterialData, @ref MaterialType::PbrClearCoat */ class MAGNUM_TRADE_EXPORT PbrClearCoatMaterialData: public MaterialLayerData { public: diff --git a/src/Magnum/Trade/PbrMetallicRoughnessMaterialData.h b/src/Magnum/Trade/PbrMetallicRoughnessMaterialData.h index 97e7b33bfb..3e271a08ac 100644 --- a/src/Magnum/Trade/PbrMetallicRoughnessMaterialData.h +++ b/src/Magnum/Trade/PbrMetallicRoughnessMaterialData.h @@ -38,8 +38,11 @@ namespace Magnum { namespace Trade { @brief PBR metallic/roughness material data @m_since_latest -@see @ref AbstractImporter::material(), @ref PbrSpecularGlossinessMaterialData, - @ref PhongMaterialData, @ref FlatMaterialData +See @ref Trade-MaterialData-usage-types for more information about how to use +this class. +@see @ref AbstractImporter::material(), @ref FlatMaterialData, + @ref PhongMaterialData, @ref PbrSpecularGlossinessMaterialData, + @ref PbrClearCoatMaterialData, @ref MaterialType::PbrMetallicRoughness */ class MAGNUM_TRADE_EXPORT PbrMetallicRoughnessMaterialData: public MaterialData { public: diff --git a/src/Magnum/Trade/PbrSpecularGlossinessMaterialData.h b/src/Magnum/Trade/PbrSpecularGlossinessMaterialData.h index 420c02e4b1..edc5c4bade 100644 --- a/src/Magnum/Trade/PbrSpecularGlossinessMaterialData.h +++ b/src/Magnum/Trade/PbrSpecularGlossinessMaterialData.h @@ -38,8 +38,11 @@ namespace Magnum { namespace Trade { @brief PBR specular/glossiness material data @m_since_latest -@see @ref AbstractImporter::material(), @ref PbrMetallicRoughnessMaterialData, - @ref PhongMaterialData, @ref FlatMaterialData +See @ref Trade-MaterialData-usage-types for more information about how to use +this class. +@see @ref AbstractImporter::material(), @ref PhongMaterialData, + @ref FlatMaterialData, @ref PbrMetallicRoughnessMaterialData, + @ref PbrClearCoatMaterialData, @ref MaterialType::PbrSpecularGlossiness */ class MAGNUM_TRADE_EXPORT PbrSpecularGlossinessMaterialData: public MaterialData { public: diff --git a/src/Magnum/Trade/PhongMaterialData.h b/src/Magnum/Trade/PhongMaterialData.h index c436b88a27..fe24f7530f 100644 --- a/src/Magnum/Trade/PhongMaterialData.h +++ b/src/Magnum/Trade/PhongMaterialData.h @@ -37,8 +37,11 @@ namespace Magnum { namespace Trade { /** @brief Phong material data -@see @ref AbstractImporter::material(), @ref PbrMetallicRoughnessMaterialData, - @ref PbrSpecularGlossinessMaterialData, @ref FlatMaterialData +See @ref Trade-MaterialData-usage-types for more information about how to use +this class. +@see @ref AbstractImporter::material(), @ref FlatMaterialData, + @ref PbrMetallicRoughnessMaterialData, + @ref PbrSpecularGlossinessMaterialData, @ref PbrClearCoatMaterialData */ class MAGNUM_TRADE_EXPORT PhongMaterialData: public MaterialData { public: From 9c00efa7e01a77a9e4133134dca75d7e5f702965 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 11 Aug 2020 12:33:22 +0200 Subject: [PATCH 22/36] Trade: MaterialTextureSwizzle::GA for normal maps and related utils. Things are getting complicated, heh. --- doc/snippets/MagnumTrade.glsl | 38 ++++++++++++++++++++++ src/Magnum/Trade/MaterialData.cpp | 7 ++++ src/Magnum/Trade/MaterialData.h | 35 ++++++++++++++++++-- src/Magnum/Trade/Test/MaterialDataTest.cpp | 13 +++++++- 4 files changed, 90 insertions(+), 3 deletions(-) create mode 100644 doc/snippets/MagnumTrade.glsl diff --git a/doc/snippets/MagnumTrade.glsl b/doc/snippets/MagnumTrade.glsl new file mode 100644 index 0000000000..d1cef7a37a --- /dev/null +++ b/doc/snippets/MagnumTrade.glsl @@ -0,0 +1,38 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + 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. +*/ + +/* [unpackTwoChannelNormal] */ +/* Assumes (1, y, 1, x) or (x, y, , 1) */ +vec3 unpackTwoChannelNormal(vec4 packed) { + packed.x *= packed.w; + + /* reconstruct Z */ + vec3 normal; + normal.xy = packed.xy*2.0 - 1.0; + normal.z = sqrt(max(0.0, 1.0 - dot(normal.xy, + normal.xy))); + return normal; +} +/* [unpackTwoChannelNormal] */ diff --git a/src/Magnum/Trade/MaterialData.cpp b/src/Magnum/Trade/MaterialData.cpp index 88f08df4f5..a888e2d27e 100644 --- a/src/Magnum/Trade/MaterialData.cpp +++ b/src/Magnum/Trade/MaterialData.cpp @@ -65,6 +65,13 @@ constexpr struct { } +UnsignedInt materialTextureSwizzleComponentCount(const MaterialTextureSwizzle swizzle) { + return (UnsignedInt(swizzle) & 0xff000000u ? 1 : 0) + + (UnsignedInt(swizzle) & 0x00ff0000u ? 1 : 0) + + (UnsignedInt(swizzle) & 0x0000ff00u ? 1 : 0) + + (UnsignedInt(swizzle) & 0x000000ffu ? 1 : 0); +} + std::size_t materialAttributeTypeSize(const MaterialAttributeType type) { switch(type) { case MaterialAttributeType::Bool: diff --git a/src/Magnum/Trade/MaterialData.h b/src/Magnum/Trade/MaterialData.h index 5f6e991485..990cae23e9 100644 --- a/src/Magnum/Trade/MaterialData.h +++ b/src/Magnum/Trade/MaterialData.h @@ -613,10 +613,22 @@ enum class MaterialAttribute: UnsignedInt { * not present, @ref MaterialTextureSwizzle::RGB is assumed. * * If the texture is just two-component, the remaining component is - * implicit and calculated as @f$ z = \sqrt{1 - x^2 - y^2} @f$. + * implicit and calculated as @f$ z = \sqrt{1 - x^2 - y^2} @f$. In order to + * account for numeric issues and avoid negative values under the square + * root, it's commonly done as @f$ z = \sqrt{\max(0, 1 - x^2 - y^2)} @f$. + * Additionally, to mitigate artifacts when storing normal texture in a + * compressed format, @ref MaterialTextureSwizzle::GA may get used instead + * of @ref MaterialTextureSwizzle::RG. + * + * Shader code that is able to reconstruct a XYZ normal from both RG and GA + * variants assuming constant values in other channels ([source](https://github.com/KhronosGroup/glTF/issues/1682#issuecomment-557880407)): + * + * @snippet MagnumTrade.glsl unpackTwoChannelNormal + * * @see @ref PbrMetallicRoughnessMaterialData::hasNormalRoughnessMetallicTexture(), * @ref PbrMetallicRoughnessMaterialData::normalTextureSwizzle(), - * @ref PbrSpecularGlossinessMaterialData::normalTextureSwizzle() + * @ref PbrSpecularGlossinessMaterialData::normalTextureSwizzle(), + * @ref materialTextureSwizzleComponentCount() */ NormalTextureSwizzle, @@ -842,6 +854,7 @@ MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, MaterialAttribute value); @m_since_latest See @ref MaterialData for more information. +@see @ref materialTextureSwizzleComponentCount() */ enum class MaterialTextureSwizzle: UnsignedInt { /** Red component */ @@ -862,6 +875,16 @@ enum class MaterialTextureSwizzle: UnsignedInt { /** Green and blue component */ GB = Utility::Endianness::fourCC('G', 'B', '\0', '\0'), + /** + * Green and alpha component. May get used to mitigate artifacts when + * storing two independent channels (such as two-channel normal maps) in + * compressed texture formats --- these commonly have separately compressed + * RGB and alpha, with green channel having the most precision of the RGB + * triplet. + * @see @ref MaterialAttribute::NormalTextureSwizzle + */ + GA = Utility::Endianness::fourCC('G', 'A', '\0', '\0'), + /** Blue and alpha component */ BA = Utility::Endianness::fourCC('B', 'A', '\0', '\0'), @@ -875,6 +898,14 @@ enum class MaterialTextureSwizzle: UnsignedInt { RGBA = Utility::Endianness::fourCC('R', 'G', 'B', 'A'), }; +/** +@brief Component count in a material texture swizzle +@m_since_latest + +Returns for example @cpp 2 @ce for @ref MaterialTextureSwizzle::GA. +*/ +MAGNUM_TRADE_EXPORT UnsignedInt materialTextureSwizzleComponentCount(MaterialTextureSwizzle swizzle); + /** @debugoperatorenum{MaterialTextureSwizzle} @m_since_latest diff --git a/src/Magnum/Trade/Test/MaterialDataTest.cpp b/src/Magnum/Trade/Test/MaterialDataTest.cpp index ee5703c71f..3a50546b4a 100644 --- a/src/Magnum/Trade/Test/MaterialDataTest.cpp +++ b/src/Magnum/Trade/Test/MaterialDataTest.cpp @@ -46,6 +46,7 @@ class MaterialDataTest: public TestSuite::Tester { public: explicit MaterialDataTest(); + void textureSwizzleComponentCount(); void attributeTypeSize(); void attributeTypeSizeInvalid(); @@ -217,7 +218,9 @@ class MaterialDataTest: public TestSuite::Tester { }; MaterialDataTest::MaterialDataTest() { - addTests({&MaterialDataTest::attributeTypeSize, + addTests({&MaterialDataTest::textureSwizzleComponentCount, + + &MaterialDataTest::attributeTypeSize, &MaterialDataTest::attributeTypeSizeInvalid, &MaterialDataTest::attributeMap, &MaterialDataTest::layerMap, @@ -417,6 +420,14 @@ MaterialDataTest::MaterialDataTest() { using namespace Containers::Literals; using namespace Math::Literals; +void MaterialDataTest::textureSwizzleComponentCount() { + CORRADE_COMPARE(materialTextureSwizzleComponentCount(MaterialTextureSwizzle::B), 1); + CORRADE_COMPARE(materialTextureSwizzleComponentCount(MaterialTextureSwizzle::RG), 2); + CORRADE_COMPARE(materialTextureSwizzleComponentCount(MaterialTextureSwizzle::GA), 2); + CORRADE_COMPARE(materialTextureSwizzleComponentCount(MaterialTextureSwizzle::GBA), 3); + CORRADE_COMPARE(materialTextureSwizzleComponentCount(MaterialTextureSwizzle::RGBA), 4); +} + void MaterialDataTest::attributeTypeSize() { CORRADE_COMPARE(materialAttributeTypeSize(MaterialAttributeType::Bool), 1); CORRADE_COMPARE(materialAttributeTypeSize(MaterialAttributeType::Deg), 4); From ef17497801ab0181c059c18e149118931f483e71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 11 Aug 2020 12:59:42 +0200 Subject: [PATCH 23/36] Trade: added MaterialAttribute::NormalTextureScale. For some reason I forgot this one. --- doc/changelog.dox | 3 ++- .../materialAttributeProperties.hpp | 1 + src/Magnum/Trade/MaterialData.h | 14 ++++++++++++ src/Magnum/Trade/PbrClearCoatMaterialData.cpp | 6 +++++ src/Magnum/Trade/PbrClearCoatMaterialData.h | 10 +++++++++ .../PbrMetallicRoughnessMaterialData.cpp | 6 +++++ .../Trade/PbrMetallicRoughnessMaterialData.h | 10 +++++++++ .../PbrSpecularGlossinessMaterialData.cpp | 6 +++++ .../Trade/PbrSpecularGlossinessMaterialData.h | 10 +++++++++ src/Magnum/Trade/PhongMaterialData.cpp | 6 +++++ src/Magnum/Trade/PhongMaterialData.h | 11 ++++++++++ src/Magnum/Trade/Test/MaterialDataTest.cpp | 22 ++++++++++++++++++- 12 files changed, 103 insertions(+), 2 deletions(-) diff --git a/doc/changelog.dox b/doc/changelog.dox index 5ebad732ed..760bf2ada3 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -82,7 +82,8 @@ See also: @ref Trade::PbrClearCoatMaterialData convenience accessor APIs similar to @ref Trade::PhongMaterialData. See [mosra/magnum#459](https://github.com/mosra/magnum/pull/459). - Added @ref Trade::PhongMaterialData::hasSpecularTexture(), - @ref Trade::PhongMaterialData::specularTextureSwizzle() and + @ref Trade::PhongMaterialData::specularTextureSwizzle(), + @ref Trade::PhongMaterialData::normalTextureScale() and @ref Trade::PhongMaterialData::normalTextureSwizzle() to make new features added for PBR materials recognizable also in classic Phong workflows. diff --git a/src/Magnum/Trade/Implementation/materialAttributeProperties.hpp b/src/Magnum/Trade/Implementation/materialAttributeProperties.hpp index 2b81b7725e..9e42b57257 100644 --- a/src/Magnum/Trade/Implementation/materialAttributeProperties.hpp +++ b/src/Magnum/Trade/Implementation/materialAttributeProperties.hpp @@ -65,6 +65,7 @@ _c(GlossinessTextureMatrix,Matrix3x3) _c(GlossinessTextureCoordinates,UnsignedInt) _c(SpecularGlossinessTexture,UnsignedInt) _c(NormalTexture,UnsignedInt) +_c(NormalTextureScale,Float) _ct(NormalTextureSwizzle,TextureSwizzle,MaterialTextureSwizzle) _c(NormalTextureMatrix,Matrix3x3) _c(NormalTextureCoordinates,UnsignedInt) diff --git a/src/Magnum/Trade/MaterialData.h b/src/Magnum/Trade/MaterialData.h index 990cae23e9..1928e31f18 100644 --- a/src/Magnum/Trade/MaterialData.h +++ b/src/Magnum/Trade/MaterialData.h @@ -598,6 +598,9 @@ enum class MaterialAttribute: UnsignedInt { /** * Tangent-space normal map texture index, * @ref MaterialAttributeType::UnsignedInt. + * + * If @ref MaterialAttribute::NormalTextureScale is present as well, these + * two are multiplied together. * @see @ref PhongMaterialData::normalTexture(), * @ref PbrMetallicRoughnessMaterialData::hasNormalRoughnessMetallicTexture(), * @ref PbrMetallicRoughnessMaterialData::normalTexture(), @@ -605,6 +608,17 @@ enum class MaterialAttribute: UnsignedInt { */ NormalTexture, + /** + * Normal texture scale, @ref MaterialAttributeType::Float. + * + * Scales the texture defined by @ref MaterialAttribute::NormalTexture. + * @see @ref PhongMaterialData::normalTextureScale(), + * @ref PbrMetallicRoughnessMaterialData::normalTextureScale(), + * @ref PbrSpecularGlossinessMaterialData::normalTextureScale(), + * @ref PbrClearCoatMaterialData::normalTextureScale() + */ + NormalTextureScale, + /** * Normal texture swizzle, @ref MaterialAttributeType::TextureSwizzle. * diff --git a/src/Magnum/Trade/PbrClearCoatMaterialData.cpp b/src/Magnum/Trade/PbrClearCoatMaterialData.cpp index 0ff45bbd3b..ec9a6bf090 100644 --- a/src/Magnum/Trade/PbrClearCoatMaterialData.cpp +++ b/src/Magnum/Trade/PbrClearCoatMaterialData.cpp @@ -81,6 +81,12 @@ UnsignedInt PbrClearCoatMaterialData::normalTexture() const { return attribute(MaterialAttribute::NormalTexture); } +Float PbrClearCoatMaterialData::normalTextureScale() const { + CORRADE_ASSERT(hasAttribute(MaterialAttribute::NormalTexture), + "Trade::PbrClearCoatMaterialData::normalTextureScale(): the layer doesn't have a normal texture", {}); + return attributeOr(MaterialAttribute::NormalTextureScale, 1.0f); +} + MaterialTextureSwizzle PbrClearCoatMaterialData::normalTextureSwizzle() const { CORRADE_ASSERT(hasAttribute(MaterialAttribute::NormalTexture), "Trade::PbrClearCoatMaterialData::normalTextureSwizzle(): the layer doesn't have a normal texture", {}); diff --git a/src/Magnum/Trade/PbrClearCoatMaterialData.h b/src/Magnum/Trade/PbrClearCoatMaterialData.h index 0a43f179f0..454d27488b 100644 --- a/src/Magnum/Trade/PbrClearCoatMaterialData.h +++ b/src/Magnum/Trade/PbrClearCoatMaterialData.h @@ -148,6 +148,16 @@ class MAGNUM_TRADE_EXPORT PbrClearCoatMaterialData: public MaterialLayerData(MaterialAttribute::NormalTexture); } +Float PbrMetallicRoughnessMaterialData::normalTextureScale() const { + CORRADE_ASSERT(hasAttribute(MaterialAttribute::NormalTexture), + "Trade::PbrMetallicRoughnessMaterialData::normalTextureScale(): the material doesn't have a normal texture", {}); + return attributeOr(MaterialAttribute::NormalTextureScale, 1.0f); +} + MaterialTextureSwizzle PbrMetallicRoughnessMaterialData::normalTextureSwizzle() const { CORRADE_ASSERT(hasAttribute(MaterialAttribute::NormalTexture), "Trade::PbrMetallicRoughnessMaterialData::normalTextureSwizzle(): the material doesn't have a normal texture", {}); diff --git a/src/Magnum/Trade/PbrMetallicRoughnessMaterialData.h b/src/Magnum/Trade/PbrMetallicRoughnessMaterialData.h index 3e271a08ac..def41bfaf7 100644 --- a/src/Magnum/Trade/PbrMetallicRoughnessMaterialData.h +++ b/src/Magnum/Trade/PbrMetallicRoughnessMaterialData.h @@ -401,6 +401,16 @@ class MAGNUM_TRADE_EXPORT PbrMetallicRoughnessMaterialData: public MaterialData */ UnsignedInt normalTexture() const; + /** + * @brief Normal texture scale + * + * Convenience access to the @ref MaterialAttribute::NormalTextureScale + * attribute. If not present, the default is @cpp 1.0f @ce. + * Available only if @ref MaterialAttribute::NormalTexture is present. + * @see @ref hasAttribute() + */ + Float normalTextureScale() const; + /** * @brief Normal texture coordinate transformation matrix * diff --git a/src/Magnum/Trade/PbrSpecularGlossinessMaterialData.cpp b/src/Magnum/Trade/PbrSpecularGlossinessMaterialData.cpp index 16a6e608e1..238b01843a 100644 --- a/src/Magnum/Trade/PbrSpecularGlossinessMaterialData.cpp +++ b/src/Magnum/Trade/PbrSpecularGlossinessMaterialData.cpp @@ -176,6 +176,12 @@ UnsignedInt PbrSpecularGlossinessMaterialData::normalTexture() const { return attribute(MaterialAttribute::NormalTexture); } +Float PbrSpecularGlossinessMaterialData::normalTextureScale() const { + CORRADE_ASSERT(hasAttribute(MaterialAttribute::NormalTexture), + "Trade::PbrSpecularGlossinessMaterialData::normalTextureScale(): the material doesn't have a normal texture", {}); + return attributeOr(MaterialAttribute::NormalTextureScale, 1.0f); +} + MaterialTextureSwizzle PbrSpecularGlossinessMaterialData::normalTextureSwizzle() const { CORRADE_ASSERT(hasAttribute(MaterialAttribute::NormalTexture), "Trade::PbrSpecularGlossinessMaterialData::normalTextureSwizzle(): the material doesn't have a normal texture", {}); diff --git a/src/Magnum/Trade/PbrSpecularGlossinessMaterialData.h b/src/Magnum/Trade/PbrSpecularGlossinessMaterialData.h index edc5c4bade..dd21276a4c 100644 --- a/src/Magnum/Trade/PbrSpecularGlossinessMaterialData.h +++ b/src/Magnum/Trade/PbrSpecularGlossinessMaterialData.h @@ -296,6 +296,16 @@ class MAGNUM_TRADE_EXPORT PbrSpecularGlossinessMaterialData: public MaterialData */ UnsignedInt normalTexture() const; + /** + * @brief Normal texture scale + * + * Convenience access to the @ref MaterialAttribute::NormalTextureScale + * attribute. If not present, the default is @cpp 1.0f @ce. + * Available only if @ref MaterialAttribute::NormalTexture is present. + * @see @ref hasAttribute() + */ + Float normalTextureScale() const; + /** * @brief Normal texture swizzle * diff --git a/src/Magnum/Trade/PhongMaterialData.cpp b/src/Magnum/Trade/PhongMaterialData.cpp index 4f1dc3d5c9..8e8a5f929f 100644 --- a/src/Magnum/Trade/PhongMaterialData.cpp +++ b/src/Magnum/Trade/PhongMaterialData.cpp @@ -241,6 +241,12 @@ UnsignedInt PhongMaterialData::normalTexture() const { return attribute(MaterialAttribute::NormalTexture); } +Float PhongMaterialData::normalTextureScale() const { + CORRADE_ASSERT(hasAttribute(MaterialAttribute::NormalTexture), + "Trade::PhongMaterialData::normalTextureScale(): the material doesn't have a normal texture", {}); + return attributeOr(MaterialAttribute::NormalTextureScale, 1.0f); +} + MaterialTextureSwizzle PhongMaterialData::normalTextureSwizzle() const { CORRADE_ASSERT(hasAttribute(MaterialAttribute::NormalTexture), "Trade::PhongMaterialData::normalTextureSwizzle(): the material doesn't have a normal texture", {}); diff --git a/src/Magnum/Trade/PhongMaterialData.h b/src/Magnum/Trade/PhongMaterialData.h index fe24f7530f..695f6daa02 100644 --- a/src/Magnum/Trade/PhongMaterialData.h +++ b/src/Magnum/Trade/PhongMaterialData.h @@ -480,6 +480,17 @@ class MAGNUM_TRADE_EXPORT PhongMaterialData: public MaterialData { */ UnsignedInt normalTexture() const; + /** + * @brief Normal texture scale + * @m_since_latest + * + * Convenience access to the @ref MaterialAttribute::NormalTextureScale + * attribute. If not present, the default is @cpp 1.0f @ce. + * Available only if @ref MaterialAttribute::NormalTexture is present. + * @see @ref hasAttribute() + */ + Float normalTextureScale() const; + /** * @brief Normal texture swizzle * @m_since_latest diff --git a/src/Magnum/Trade/Test/MaterialDataTest.cpp b/src/Magnum/Trade/Test/MaterialDataTest.cpp index 3a50546b4a..2dff7a542e 100644 --- a/src/Magnum/Trade/Test/MaterialDataTest.cpp +++ b/src/Magnum/Trade/Test/MaterialDataTest.cpp @@ -2731,6 +2731,7 @@ void MaterialDataTest::pbrMetallicRoughnessAccessTextured() { {MaterialAttribute::RoughnessTextureMatrix, Matrix3::scaling({1.0f, 1.0f})}, {MaterialAttribute::RoughnessTextureCoordinates, 4u}, {MaterialAttribute::NormalTexture, 3u}, + {MaterialAttribute::NormalTextureScale, 0.35f}, {MaterialAttribute::NormalTextureSwizzle, MaterialTextureSwizzle::BA}, {MaterialAttribute::NormalTextureMatrix, Matrix3::scaling({1.0f, 0.5f})}, {MaterialAttribute::NormalTextureCoordinates, 5u}, @@ -2767,6 +2768,7 @@ void MaterialDataTest::pbrMetallicRoughnessAccessTextured() { CORRADE_COMPARE(data.roughnessTextureMatrix(), Matrix3::scaling({1.0f, 1.0f})); CORRADE_COMPARE(data.roughnessTextureCoordinates(), 4); CORRADE_COMPARE(data.normalTexture(), 3); + CORRADE_COMPARE(data.normalTextureScale(), 0.35f); CORRADE_COMPARE(data.normalTextureSwizzle(), MaterialTextureSwizzle::BA); CORRADE_COMPARE(data.normalTextureMatrix(), Matrix3::scaling({1.0f, 0.5f})); CORRADE_COMPARE(data.normalTextureCoordinates(), 5); @@ -2816,6 +2818,7 @@ void MaterialDataTest::pbrMetallicRoughnessAccessTexturedDefaults() { CORRADE_COMPARE(data.roughnessTextureMatrix(), Matrix3{}); CORRADE_COMPARE(data.roughnessTextureCoordinates(), 0); CORRADE_COMPARE(data.normalTexture(), 4); + CORRADE_COMPARE(data.normalTextureScale(), 1.0f); CORRADE_COMPARE(data.normalTextureSwizzle(), MaterialTextureSwizzle::RGB); CORRADE_COMPARE(data.normalTextureMatrix(), Matrix3{}); CORRADE_COMPARE(data.normalTextureCoordinates(), 0); @@ -3246,6 +3249,7 @@ void MaterialDataTest::pbrMetallicRoughnessAccessInvalidTextures() { data.roughnessTextureMatrix(); data.roughnessTextureCoordinates(); data.normalTexture(); + data.normalTextureScale(); data.normalTextureSwizzle(); data.normalTextureMatrix(); data.normalTextureCoordinates(); @@ -3269,6 +3273,7 @@ void MaterialDataTest::pbrMetallicRoughnessAccessInvalidTextures() { "Trade::PbrMetallicRoughnessMaterialData::roughnessTextureMatrix(): the material doesn't have a roughness texture\n" "Trade::PbrMetallicRoughnessMaterialData::roughnessTextureCoordinates(): the material doesn't have a roughness texture\n" "Trade::MaterialData::attribute(): attribute NormalTexture not found in layer 0\n" + "Trade::PbrMetallicRoughnessMaterialData::normalTextureScale(): the material doesn't have a normal texture\n" "Trade::PbrMetallicRoughnessMaterialData::normalTextureSwizzle(): the material doesn't have a normal texture\n" "Trade::PbrMetallicRoughnessMaterialData::normalTextureMatrix(): the material doesn't have a normal texture\n" "Trade::PbrMetallicRoughnessMaterialData::normalTextureCoordinates(): the material doesn't have a normal texture\n" @@ -3336,6 +3341,7 @@ void MaterialDataTest::pbrSpecularGlossinessAccessTextured() { {MaterialAttribute::GlossinessTextureMatrix, Matrix3::scaling({1.0f, 1.0f})}, {MaterialAttribute::GlossinessTextureCoordinates, 4u}, {MaterialAttribute::NormalTexture, 3u}, + {MaterialAttribute::NormalTextureScale, 0.35f}, {MaterialAttribute::NormalTextureSwizzle, MaterialTextureSwizzle::BA}, {MaterialAttribute::NormalTextureMatrix, Matrix3::scaling({1.0f, 0.5f})}, {MaterialAttribute::NormalTextureCoordinates, 5u}, @@ -3369,6 +3375,7 @@ void MaterialDataTest::pbrSpecularGlossinessAccessTextured() { CORRADE_COMPARE(data.glossinessTextureMatrix(), Matrix3::scaling({1.0f, 1.0f})); CORRADE_COMPARE(data.glossinessTextureCoordinates(), 4); CORRADE_COMPARE(data.normalTexture(), 3); + CORRADE_COMPARE(data.normalTextureScale(), 0.35f); CORRADE_COMPARE(data.normalTextureSwizzle(), MaterialTextureSwizzle::BA); CORRADE_COMPARE(data.normalTextureMatrix(), Matrix3::scaling({1.0f, 0.5f})); CORRADE_COMPARE(data.normalTextureCoordinates(), 5); @@ -3415,6 +3422,7 @@ void MaterialDataTest::pbrSpecularGlossinessAccessTexturedDefaults() { CORRADE_COMPARE(data.glossinessTextureMatrix(), Matrix3{}); CORRADE_COMPARE(data.glossinessTextureCoordinates(), 0); CORRADE_COMPARE(data.normalTexture(), 4); + CORRADE_COMPARE(data.normalTextureScale(), 1.0f); CORRADE_COMPARE(data.normalTextureSwizzle(), MaterialTextureSwizzle::RGB); CORRADE_COMPARE(data.normalTextureMatrix(), Matrix3{}); CORRADE_COMPARE(data.normalTextureCoordinates(), 0); @@ -3628,6 +3636,7 @@ void MaterialDataTest::pbrSpecularGlossinessAccessInvalidTextures() { data.glossinessTextureMatrix(); data.glossinessTextureCoordinates(); data.normalTexture(); + data.normalTextureScale(); data.normalTextureSwizzle(); data.normalTextureMatrix(); data.normalTextureCoordinates(); @@ -3651,6 +3660,7 @@ void MaterialDataTest::pbrSpecularGlossinessAccessInvalidTextures() { "Trade::PbrSpecularGlossinessMaterialData::glossinessTextureMatrix(): the material doesn't have a glossiness texture\n" "Trade::PbrSpecularGlossinessMaterialData::glossinessTextureCoordinates(): the material doesn't have a glossiness texture\n" "Trade::MaterialData::attribute(): attribute NormalTexture not found in layer 0\n" + "Trade::PbrSpecularGlossinessMaterialData::normalTextureScale(): the material doesn't have a normal texture\n" "Trade::PbrSpecularGlossinessMaterialData::normalTextureSwizzle(): the material doesn't have a normal texture\n" "Trade::PbrSpecularGlossinessMaterialData::normalTextureMatrix(): the material doesn't have a normal texture\n" "Trade::PbrSpecularGlossinessMaterialData::normalTextureCoordinates(): the material doesn't have a normal texture\n" @@ -3716,6 +3726,7 @@ void MaterialDataTest::phongAccessTextured() { {MaterialAttribute::SpecularTextureMatrix, Matrix3::scaling({1.0f, 1.0f})}, {MaterialAttribute::SpecularTextureCoordinates, 4u}, {MaterialAttribute::NormalTexture, 0u}, + {MaterialAttribute::NormalTextureScale, 0.5f}, {MaterialAttribute::NormalTextureSwizzle, MaterialTextureSwizzle::GB}, {MaterialAttribute::NormalTextureMatrix, Matrix3::scaling({1.0f, 0.5f})}, {MaterialAttribute::NormalTextureCoordinates, 5u} @@ -3738,6 +3749,7 @@ void MaterialDataTest::phongAccessTextured() { CORRADE_COMPARE(data.specularTextureMatrix(), Matrix3::scaling({1.0f, 1.0f})); CORRADE_COMPARE(data.specularTextureCoordinates(), 4); CORRADE_COMPARE(data.normalTexture(), 0); + CORRADE_COMPARE(data.normalTextureScale(), 0.5f); CORRADE_COMPARE(data.normalTextureSwizzle(), MaterialTextureSwizzle::GB); CORRADE_COMPARE(data.normalTextureMatrix(), Matrix3::scaling({1.0f, 0.5f})); CORRADE_COMPARE(data.normalTextureCoordinates(), 5); @@ -3771,6 +3783,7 @@ void MaterialDataTest::phongAccessTexturedDefaults() { CORRADE_COMPARE(data.specularTextureMatrix(), Matrix3{}); CORRADE_COMPARE(data.specularTextureCoordinates(), 0); CORRADE_COMPARE(data.normalTexture(), 1); + CORRADE_COMPARE(data.normalTextureScale(), 1.0f); CORRADE_COMPARE(data.normalTextureSwizzle(), MaterialTextureSwizzle::RGB); CORRADE_COMPARE(data.normalTextureMatrix(), Matrix3{}); CORRADE_COMPARE(data.normalTextureCoordinates(), 0); @@ -3847,6 +3860,7 @@ void MaterialDataTest::phongAccessInvalidTextures() { data.specularTextureMatrix(); data.specularTextureCoordinates(); data.normalTexture(); + data.normalTextureScale(); data.normalTextureSwizzle(); data.normalTextureMatrix(); data.normalTextureCoordinates(); @@ -3862,6 +3876,7 @@ void MaterialDataTest::phongAccessInvalidTextures() { "Trade::PhongMaterialData::specularTextureMatrix(): the material doesn't have a specular texture\n" "Trade::PhongMaterialData::specularTextureCoordinates(): the material doesn't have a specular texture\n" "Trade::MaterialData::attribute(): attribute NormalTexture not found in layer 0\n" + "Trade::PhongMaterialData::normalTextureScale(): the material doesn't have a normal texture\n" "Trade::PhongMaterialData::normalTextureSwizzle(): the material doesn't have a normal texture\n" "Trade::PhongMaterialData::normalTextureMatrix(): the material doesn't have a normal texture\n" "Trade::PhongMaterialData::normalTextureCoordinates(): the material doesn't have a normal texture\n"); @@ -4157,10 +4172,11 @@ void MaterialDataTest::pbrClearCoatAccessTextured() { {MaterialAttribute::RoughnessTextureMatrix, Matrix3::translation({2.0f, 1.5f})}, {MaterialAttribute::RoughnessTextureCoordinates, 6u}, {MaterialAttribute::NormalTexture, 3u}, + {MaterialAttribute::NormalTextureScale, 0.5f}, {MaterialAttribute::NormalTextureSwizzle, MaterialTextureSwizzle::B}, {MaterialAttribute::NormalTextureMatrix, Matrix3::translation({0.0f, 0.5f})}, {MaterialAttribute::NormalTextureCoordinates, 7u}, - }, {0, 10}}; + }, {0, 11}}; CORRADE_VERIFY(data.hasTextureTransformation()); CORRADE_VERIFY(data.hasTextureCoordinates()); @@ -4170,6 +4186,7 @@ void MaterialDataTest::pbrClearCoatAccessTextured() { CORRADE_COMPARE(data.roughnessTextureMatrix(), Matrix3::translation({2.0f, 1.5f})); CORRADE_COMPARE(data.roughnessTextureCoordinates(), 6u); CORRADE_COMPARE(data.normalTexture(), 3u); + CORRADE_COMPARE(data.normalTextureScale(), 0.5f); CORRADE_COMPARE(data.normalTextureSwizzle(), MaterialTextureSwizzle::B); CORRADE_COMPARE(data.normalTextureMatrix(), Matrix3::translation({0.0f, 0.5f})); CORRADE_COMPARE(data.normalTextureCoordinates(), 7u); @@ -4190,6 +4207,7 @@ void MaterialDataTest::pbrClearCoatAccessTexturedDefaults() { CORRADE_COMPARE(data.roughnessTextureMatrix(), Matrix3{}); CORRADE_COMPARE(data.roughnessTextureCoordinates(), 0u); CORRADE_COMPARE(data.normalTexture(), 3u); + CORRADE_COMPARE(data.normalTextureScale(), 1.0f); CORRADE_COMPARE(data.normalTextureSwizzle(), MaterialTextureSwizzle::RGB); CORRADE_COMPARE(data.normalTextureMatrix(), Matrix3{}); CORRADE_COMPARE(data.normalTextureCoordinates(), 0u); @@ -4246,6 +4264,7 @@ void MaterialDataTest::pbrClearCoatAccessInvalidTextures() { data.roughnessTextureMatrix(); data.roughnessTextureCoordinates(); data.normalTexture(); + data.normalTextureScale(); data.normalTextureSwizzle(); data.normalTextureMatrix(); data.normalTextureCoordinates(); @@ -4255,6 +4274,7 @@ void MaterialDataTest::pbrClearCoatAccessInvalidTextures() { "Trade::PbrClearCoatMaterialData::roughnessTextureMatrix(): the layer doesn't have a roughness texture\n" "Trade::PbrClearCoatMaterialData::roughnessTextureCoordinates(): the layer doesn't have a roughness texture\n" "Trade::MaterialData::attribute(): attribute NormalTexture not found in layer ClearCoat\n" + "Trade::PbrClearCoatMaterialData::normalTextureScale(): the layer doesn't have a normal texture\n" "Trade::PbrClearCoatMaterialData::normalTextureSwizzle(): the layer doesn't have a normal texture\n" "Trade::PbrClearCoatMaterialData::normalTextureMatrix(): the layer doesn't have a normal texture\n" "Trade::PbrClearCoatMaterialData::normalTextureCoordinates(): the layer doesn't have a normal texture\n"); From f9edbcdd2453fc223af29e66b8af97d20f138668 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 11 Aug 2020 17:22:00 +0200 Subject: [PATCH 24/36] Trade: rework metallic/roughness packing. Turns out glTF doesn't actually put metalness into R and roughness into G, even though the naming suggests that. This was done originally, but then they changed that in order to be compatible with UE4 and allow for a more efficient storage of an occlusion map. Because this feels extremely arbitrary, the docs have added rationale for each of the packing variants, and I'm also renaming the packed attribute and checks to imply the red channel isn't used. --- doc/snippets/MagnumTrade.cpp | 22 ++-- .../materialAttributeProperties.hpp | 2 +- src/Magnum/Trade/MaterialData.h | 61 +++++----- .../PbrMetallicRoughnessMaterialData.cpp | 26 ++--- .../Trade/PbrMetallicRoughnessMaterialData.h | 91 +++++++++------ src/Magnum/Trade/Test/MaterialDataTest.cpp | 104 ++++++++++-------- 6 files changed, 175 insertions(+), 131 deletions(-) diff --git a/doc/snippets/MagnumTrade.cpp b/doc/snippets/MagnumTrade.cpp index 5306b5bb52..61eb9b3c5a 100644 --- a/doc/snippets/MagnumTrade.cpp +++ b/doc/snippets/MagnumTrade.cpp @@ -310,24 +310,26 @@ if(data.types() & Trade::MaterialType::PbrSpecularGlossiness) { { /* [MaterialData-usage-packing] */ -Trade::PbrMetallicRoughnessMaterialData data = DOXYGEN_IGNORE(Trade::PbrMetallicRoughnessMaterialData{{}, {}}); +Trade::PbrSpecularGlossinessMaterialData data = DOXYGEN_IGNORE(Trade::PbrSpecularGlossinessMaterialData{{}, {}}); /* Use a shader that accepts a single packed metallic/roughness texture. Querying any texture attributes will give the same values for both metalness and roughness. */ -if(data.hasMetallicRoughnessTexture()) { - UnsignedInt metallicRoughness = data.metalnessTexture(); +if(data.hasSpecularGlossinessTexture()) { + UnsignedInt specularGlossiness = data.specularTexture(); - DOXYGEN_IGNORE(static_cast(metallicRoughness);) + DOXYGEN_IGNORE(static_cast(specularGlossiness);) /* Supply texture channels separately */ } else { - UnsignedInt metalness = data.metalnessTexture(); - UnsignedInt roughness = data.roughnessTexture(); - Trade::MaterialTextureSwizzle metalnessSwizzle = data.metalnessTextureSwizzle(); - Trade::MaterialTextureSwizzle roughnessSwizzle = data.roughnessTextureSwizzle(); + UnsignedInt specular = data.specularTexture(); + UnsignedInt glossiness = data.glossinessTexture(); + Trade::MaterialTextureSwizzle specularSwizzle = + data.specularTextureSwizzle(); + Trade::MaterialTextureSwizzle glossinessSwizzle = + data.glossinessTextureSwizzle(); - DOXYGEN_IGNORE(static_cast(metalness), static_cast(roughness), static_cast(metalnessSwizzle), static_cast(roughnessSwizzle);) + DOXYGEN_IGNORE(static_cast(specular), static_cast(glossiness), static_cast(specularSwizzle), static_cast(glossinessSwizzle);) } /* [MaterialData-usage-packing] */ } @@ -413,7 +415,7 @@ Trade::MaterialData data{ Trade::MaterialType::PbrMetallicRoughness|Trade::MaterialType::PbrClearCoat, { {Trade::MaterialAttribute::BaseColor, 0xffcc33_srgbf}, - {Trade::MaterialAttribute::MetallicRoughnessTexture, 0u}, + {Trade::MaterialAttribute::NoneRoughnessMetallicTexture, 0u}, {Trade::MaterialLayer::ClearCoat}, {Trade::MaterialAttribute::LayerFactorTexture, 1u}, diff --git a/src/Magnum/Trade/Implementation/materialAttributeProperties.hpp b/src/Magnum/Trade/Implementation/materialAttributeProperties.hpp index 9e42b57257..306440a914 100644 --- a/src/Magnum/Trade/Implementation/materialAttributeProperties.hpp +++ b/src/Magnum/Trade/Implementation/materialAttributeProperties.hpp @@ -57,7 +57,7 @@ _c(RoughnessTexture,UnsignedInt) _ct(RoughnessTextureSwizzle,TextureSwizzle,MaterialTextureSwizzle) _c(RoughnessTextureMatrix,Matrix3x3) _c(RoughnessTextureCoordinates,UnsignedInt) -_c(MetallicRoughnessTexture,UnsignedInt) +_c(NoneRoughnessMetallicTexture,UnsignedInt) _c(Glossiness,Float) _c(GlossinessTexture,UnsignedInt) _ct(GlossinessTextureSwizzle,TextureSwizzle,MaterialTextureSwizzle) diff --git a/src/Magnum/Trade/MaterialData.h b/src/Magnum/Trade/MaterialData.h index 1928e31f18..ecfa2701ec 100644 --- a/src/Magnum/Trade/MaterialData.h +++ b/src/Magnum/Trade/MaterialData.h @@ -356,7 +356,7 @@ enum class MaterialAttribute: UnsignedInt { * @ref MaterialAttributeType::Float. * * If @ref MaterialAttribute::MetalnessTexture or - * @ref MaterialAttribute::MetallicRoughnessTexture is present as well, + * @ref MaterialAttribute::NoneRoughnessMetallicTexture is present as well, * these two are multiplied together. * @see @ref PbrMetallicRoughnessMaterialData::metalness() */ @@ -368,9 +368,9 @@ enum class MaterialAttribute: UnsignedInt { * * If @ref MaterialAttribute::Metalness is present as well, these two are * multiplied together. Can be alternatively supplied as a packed - * @ref MaterialAttribute::MetallicRoughnessTexture. + * @ref MaterialAttribute::NoneRoughnessMetallicTexture. * @see @ref PbrMetallicRoughnessMaterialData::hasMetalnessTexture(), - * @ref PbrMetallicRoughnessMaterialData::hasMetallicRoughnessTexture(), + * @ref PbrMetallicRoughnessMaterialData::hasNoneRoughnessMetallicTexture(), * @ref PbrMetallicRoughnessMaterialData::hasOcclusionRoughnessMetallicTexture(), * @ref PbrMetallicRoughnessMaterialData::hasNormalRoughnessMetallicTexture() * @ref PbrMetallicRoughnessMaterialData::metalnessTexture() @@ -385,9 +385,10 @@ enum class MaterialAttribute: UnsignedInt { * @ref MaterialAttribute::MetalnessTexture together with other maps in a * single texture. A single-channel swizzle value is expected. If not * present, @ref MaterialTextureSwizzle::R is assumed. Does not apply to - * @ref MaterialAttribute::MetallicRoughnessTexture --- in that case, the - * metalness is implicitly in the red channel regardless of this attribute. - * @see @ref PbrMetallicRoughnessMaterialData::hasMetallicRoughnessTexture(), + * @ref MaterialAttribute::NoneRoughnessMetallicTexture --- in that case, + * the metalness is implicitly in the red channel regardless of this + * attribute. + * @see @ref PbrMetallicRoughnessMaterialData::hasNoneRoughnessMetallicTexture(), * @ref PbrMetallicRoughnessMaterialData::hasOcclusionRoughnessMetallicTexture(), * @ref PbrMetallicRoughnessMaterialData::hasNormalRoughnessMetallicTexture() * @ref PbrMetallicRoughnessMaterialData::metalnessTextureSwizzle() @@ -419,7 +420,7 @@ enum class MaterialAttribute: UnsignedInt { * @ref MaterialAttributeType::Float. * * If @ref MaterialAttribute::RoughnessTexture or - * @ref MaterialAttribute::MetallicRoughnessTexture is present as well, + * @ref MaterialAttribute::NoneRoughnessMetallicTexture is present as well, * these two are multiplied together. * @see @ref PbrMetallicRoughnessMaterialData::roughness() */ @@ -431,9 +432,9 @@ enum class MaterialAttribute: UnsignedInt { * * If @ref MaterialAttribute::Roughness is present as well, these two are * multiplied together. Can be alternatively supplied as a packed - * @ref MaterialAttribute::MetallicRoughnessTexture. + * @ref MaterialAttribute::NoneRoughnessMetallicTexture. * @see @ref PbrMetallicRoughnessMaterialData::hasRoughnessTexture(), - * @ref PbrMetallicRoughnessMaterialData::hasMetallicRoughnessTexture(), + * @ref PbrMetallicRoughnessMaterialData::hasNoneRoughnessMetallicTexture(), * @ref PbrMetallicRoughnessMaterialData::hasOcclusionRoughnessMetallicTexture(), * @ref PbrMetallicRoughnessMaterialData::hasNormalRoughnessMetallicTexture() * @ref PbrMetallicRoughnessMaterialData::roughnessTexture() @@ -448,10 +449,10 @@ enum class MaterialAttribute: UnsignedInt { * @ref MaterialAttribute::RoughnessTexture together with other maps in a * single texture. A single-channel swizzle value is expected. If not * present, @ref MaterialTextureSwizzle::R is assumed. Does not apply to - * @ref MaterialAttribute::MetallicRoughnessTexture --- in that case, the - * metalness is implicitly in the green channel regardless of this + * @ref MaterialAttribute::NoneRoughnessMetallicTexture --- in that case, + * the metalness is implicitly in the green channel regardless of this * attribute. - * @see @ref PbrMetallicRoughnessMaterialData::hasMetallicRoughnessTexture(), + * @see @ref PbrMetallicRoughnessMaterialData::hasNoneRoughnessMetallicTexture(), * @ref PbrMetallicRoughnessMaterialData::hasOcclusionRoughnessMetallicTexture(), * @ref PbrMetallicRoughnessMaterialData::hasNormalRoughnessMetallicTexture() * @ref PbrMetallicRoughnessMaterialData::roughnessTextureSwizzle() @@ -479,32 +480,36 @@ enum class MaterialAttribute: UnsignedInt { RoughnessTextureCoordinates, /** - * Combined metallic/roughness texture index for PBR metallic/roughness - * materials with metalness in the red channel and roughness in the green + * Combined roughness/metallic texture index for PBR metallic/roughness + * materials with metalness in the blue channel and roughness in the green * channel, @ref MaterialAttributeType::UnsignedInt. * * If @ref MaterialAttribute::Metalness / @ref MaterialAttribute::Roughness - * is present as well, these two are multiplied together. Can be - * alternatively specified as a pair of @ref MaterialAttribute::MetalnessTexture - * / @ref MaterialAttribute::RoughnessTexture attributes together with - * @ref MaterialAttribute::MetalnessTextureSwizzle set to - * @ref MaterialTextureSwizzle::R (or omitted, since that's the default) - * and @ref MaterialAttribute::RoughnessTextureSwizzle set to - * @ref MaterialTextureSwizzle::G. Texture transformation and coordinate + * is present as well, these two are multiplied together. + * + * This is a convenience alias to simplify representation of glTF and UE4 + * materials, which is where this packing is used ([rationale](https://github.com/KhronosGroup/glTF/issues/857)). + * This packing (and other variants) can be alternatively specified as a + * pair of @ref MaterialAttribute::RoughnessTexture / + * @ref MaterialAttribute::MetalnessTexture attributes together with + * @ref MaterialAttribute::RoughnessTextureSwizzle set to + * @ref MaterialTextureSwizzle::G + * and @ref MaterialAttribute::MetalnessTextureSwizzle set to + * @ref MaterialTextureSwizzle::B. Texture transformation and coordinate * set, if needed, have to be specified either using the global * @ref MaterialAttribute::TextureMatrix and * @ref MaterialAttribute::TextureCoordinates attributes or the per-texture - * @ref MaterialAttribute::MetalnessTextureMatrix, * @ref MaterialAttribute::RoughnessTextureMatrix, - * @ref MaterialAttribute::MetalnessTextureCoordinates and - * @ref MaterialAttribute::RoughnessTextureCoordinates variants. - * @see @ref PbrMetallicRoughnessMaterialData::hasMetallicRoughnessTexture(), + * @ref MaterialAttribute::MetalnessTextureMatrix, + * @ref MaterialAttribute::RoughnessTextureCoordinates and + * @ref MaterialAttribute::MetalnessTextureCoordinates variants. + * @see @ref PbrMetallicRoughnessMaterialData::hasNoneRoughnessMetallicTexture(), * @ref PbrMetallicRoughnessMaterialData::hasOcclusionRoughnessMetallicTexture(), * @ref PbrMetallicRoughnessMaterialData::hasNormalRoughnessMetallicTexture() * @ref PbrMetallicRoughnessMaterialData::metalnessTexture(), * @ref PbrMetallicRoughnessMaterialData::roughnessTexture() */ - MetallicRoughnessTexture, + NoneRoughnessMetallicTexture, /* DiffuseColor, DiffuseTexture, DiffuseTextureMatrix, DiffuseTextureCoordinates, SpecularColor, SpecularTexture, @@ -1441,8 +1446,8 @@ various @ref MaterialTextureSwizzle attributes such as @ref MaterialAttribute::MetalnessTextureSwizzle. While this provides an almost endless variability, real-world textures are in just a few common packing schemes. For convenience these have dedicated attributes such as -@ref MaterialAttribute::MetallicRoughnessTexture and can also be checked for -with for example @ref PbrMetallicRoughnessMaterialData::hasMetallicRoughnessTexture(): +@ref MaterialAttribute::SpecularGlossinessTexture and can also be checked +for with for example @ref PbrSpecularGlossinessMaterialData::hasSpecularGlossinessTexture(): @snippet MagnumTrade.cpp MaterialData-usage-packing diff --git a/src/Magnum/Trade/PbrMetallicRoughnessMaterialData.cpp b/src/Magnum/Trade/PbrMetallicRoughnessMaterialData.cpp index 3655b118c2..d22ba4c23f 100644 --- a/src/Magnum/Trade/PbrMetallicRoughnessMaterialData.cpp +++ b/src/Magnum/Trade/PbrMetallicRoughnessMaterialData.cpp @@ -34,22 +34,22 @@ using namespace Math::Literals; bool PbrMetallicRoughnessMaterialData::hasMetalnessTexture() const { return hasAttribute(MaterialAttribute::MetalnessTexture) || - hasAttribute(MaterialAttribute::MetallicRoughnessTexture); + hasAttribute(MaterialAttribute::NoneRoughnessMetallicTexture); } bool PbrMetallicRoughnessMaterialData::hasRoughnessTexture() const { return hasAttribute(MaterialAttribute::RoughnessTexture) || - hasAttribute(MaterialAttribute::MetallicRoughnessTexture); + hasAttribute(MaterialAttribute::NoneRoughnessMetallicTexture); } -bool PbrMetallicRoughnessMaterialData::hasMetallicRoughnessTexture() const { - return (hasAttribute(MaterialAttribute::MetallicRoughnessTexture) || +bool PbrMetallicRoughnessMaterialData::hasNoneRoughnessMetallicTexture() const { + return (hasAttribute(MaterialAttribute::NoneRoughnessMetallicTexture) || (hasAttribute(MaterialAttribute::MetalnessTexture) && hasAttribute(MaterialAttribute::RoughnessTexture) && - metalnessTextureSwizzle() == MaterialTextureSwizzle::R && - roughnessTextureSwizzle() == MaterialTextureSwizzle::G)) && - metalnessTextureMatrix() == roughnessTextureMatrix() && - metalnessTextureCoordinates() == roughnessTextureCoordinates(); + roughnessTextureSwizzle() == MaterialTextureSwizzle::G && + metalnessTextureSwizzle() == MaterialTextureSwizzle::B)) && + roughnessTextureMatrix() == metalnessTextureMatrix() && + roughnessTextureCoordinates() == metalnessTextureCoordinates(); } bool PbrMetallicRoughnessMaterialData::hasRoughnessMetallicOcclusionTexture() const { @@ -156,7 +156,7 @@ UnsignedInt PbrMetallicRoughnessMaterialData::metalnessTexture() const { would be misleading as it can be also MetallicRoughnessTexture */ CORRADE_ASSERT(hasMetalnessTexture(), "Trade::PbrMetallicRoughnessMaterialData::metalnessTexture(): the material doesn't have a metalness texture", {}); - if(Containers::Optional value = tryAttribute(MaterialAttribute::MetallicRoughnessTexture)) + if(Containers::Optional value = tryAttribute(MaterialAttribute::NoneRoughnessMetallicTexture)) return *value; return attribute(MaterialAttribute::MetalnessTexture); } @@ -164,8 +164,8 @@ UnsignedInt PbrMetallicRoughnessMaterialData::metalnessTexture() const { MaterialTextureSwizzle PbrMetallicRoughnessMaterialData::metalnessTextureSwizzle() const { CORRADE_ASSERT(hasMetalnessTexture(), "Trade::PbrMetallicRoughnessMaterialData::metalnessTextureSwizzle(): the material doesn't have a metalness texture", {}); - if(hasAttribute(MaterialAttribute::MetallicRoughnessTexture)) - return MaterialTextureSwizzle::R; + if(hasAttribute(MaterialAttribute::NoneRoughnessMetallicTexture)) + return MaterialTextureSwizzle::B; return attributeOr(MaterialAttribute::MetalnessTextureSwizzle, MaterialTextureSwizzle::R); } @@ -194,7 +194,7 @@ UnsignedInt PbrMetallicRoughnessMaterialData::roughnessTexture() const { would be misleading as it can be also MetallicRoughnessTexture */ CORRADE_ASSERT(hasRoughnessTexture(), "Trade::PbrMetallicRoughnessMaterialData::roughnessTexture(): the material doesn't have a roughness texture", {}); - if(Containers::Optional value = tryAttribute(MaterialAttribute::MetallicRoughnessTexture)) + if(Containers::Optional value = tryAttribute(MaterialAttribute::NoneRoughnessMetallicTexture)) return *value; return attribute(MaterialAttribute::RoughnessTexture); } @@ -202,7 +202,7 @@ UnsignedInt PbrMetallicRoughnessMaterialData::roughnessTexture() const { MaterialTextureSwizzle PbrMetallicRoughnessMaterialData::roughnessTextureSwizzle() const { CORRADE_ASSERT(hasRoughnessTexture(), "Trade::PbrMetallicRoughnessMaterialData::roughnessTextureSwizzle(): the material doesn't have a roughness texture", {}); - if(hasAttribute(MaterialAttribute::MetallicRoughnessTexture)) + if(hasAttribute(MaterialAttribute::NoneRoughnessMetallicTexture)) return MaterialTextureSwizzle::G; return attributeOr(MaterialAttribute::RoughnessTextureSwizzle, MaterialTextureSwizzle::R); } diff --git a/src/Magnum/Trade/PbrMetallicRoughnessMaterialData.h b/src/Magnum/Trade/PbrMetallicRoughnessMaterialData.h index def41bfaf7..6723660e9e 100644 --- a/src/Magnum/Trade/PbrMetallicRoughnessMaterialData.h +++ b/src/Magnum/Trade/PbrMetallicRoughnessMaterialData.h @@ -60,9 +60,9 @@ class MAGNUM_TRADE_EXPORT PbrMetallicRoughnessMaterialData: public MaterialData * * Returns @cpp true @ce if any of the * @ref MaterialAttribute::MetalnessTexture or - * @ref MaterialAttribute::MetallicRoughnessTexture attributes is + * @ref MaterialAttribute::NoneRoughnessMetallicTexture attributes is * present, @cpp false @ce otherwise. - * @see @ref hasRoughnessTexture(), @ref hasMetallicRoughnessTexture() + * @see @ref hasRoughnessTexture(), @ref hasNoneRoughnessMetallicTexture() */ bool hasMetalnessTexture() const; @@ -71,41 +71,48 @@ class MAGNUM_TRADE_EXPORT PbrMetallicRoughnessMaterialData: public MaterialData * * Returns @cpp true @ce if any of the * @ref MaterialAttribute::RoughnessTexture or - * @ref MaterialAttribute::MetallicRoughnessTexture attributes is + * @ref MaterialAttribute::NoneRoughnessMetallicTexture attributes is * present, @cpp false @ce otherwise. - * @see @ref hasMetalnessTexture(), @ref hasMetallicRoughnessTexture() + * @see @ref hasMetalnessTexture(), @ref hasNoneRoughnessMetallicTexture() */ bool hasRoughnessTexture() const; /** - * @brief Whether the material has a combined metallic/roughness texture + * @brief Whether the material has a combined roughness/metallic texture * * Returns @cpp true @ce if either the - * @ref MaterialAttribute::MetallicRoughnessTexture attribute is - * present or both @ref MaterialAttribute::MetalnessTexture and - * @ref MaterialAttribute::RoughnessTexture are present, point to - * the same texture ID, @ref MaterialAttribute::MetalnessTextureSwizzle - * is set to @ref MaterialTextureSwizzle::R (or omitted, in which case - * it's the default) and @ref MaterialAttribute::RoughnessTextureSwizzle - * is set to @ref MaterialTextureSwizzle::G, and ddditionally - * @ref MaterialAttribute::MetalnessTextureMatrix and - * @ref MaterialAttribute::RoughnessTextureMatrix are both either not + * @ref MaterialAttribute::NoneRoughnessMetallicTexture attribute is + * present or both @ref MaterialAttribute::RoughnessTexture and + * @ref MaterialAttribute::MetalnessTexture are present, point to the + * same texture ID, @ref MaterialAttribute::RoughnessTextureSwizzle is + * set to @ref MaterialTextureSwizzle::G and + * @ref MaterialAttribute::MetalnessTextureSwizzle is set to + * @ref MaterialTextureSwizzle::B, and ddditionally + * @ref MaterialAttribute::RoughnessTextureMatrix and + * @ref MaterialAttribute::MetalnessTextureMatrix are both either not * present or have the same value, and - * @ref MaterialAttribute::MetalnessTextureCoordinates and - * @ref MaterialAttribute::RoughnessTextureCoordinates are both either + * @ref MaterialAttribute::RoughnessTextureCoordinates and + * @ref MaterialAttribute::MetalnessTextureCoordinates are both either * not present or have the same value; @cpp false @ce otherwise. * * In other words, if this function returns @cpp true @ce, - * @ref metalnessTexture(), @ref metalnessTextureMatrix() and - * @ref metalnessTextureCoordinates() return values common for both + * @ref roughnessTexture(), @ref roughnessTextureMatrix() and + * @ref roughnessTextureCoordinates() return values common for both * metalness and roughness texture, and the two are packed together - * with metalness occupying the R channel and roughness the G channel. + * with roughness occupying the G channel and metalness the B channel. + * This packing is common in glTF metallic/roughness materials, which + * in turn is compatible with how UE4 assets are usually packed. + * Original rationale for this [can be seen here](https://github.com/KhronosGroup/glTF/issues/857). + * + * The red and alpha channels are ignored and can be repurposed for + * other maps such as occlusion or transmission. This check is a subset + * of @ref hasOcclusionRoughnessMetallicTexture() --- if that function + * returns @cpp true @ce, this will return @cpp true @ce as well. * @see @ref hasMetalnessTexture(), @ref hasRoughnessTexture(), - * @ref hasOcclusionRoughnessMetallicTexture(), * @ref hasRoughnessMetallicOcclusionTexture(), * @ref hasNormalRoughnessMetallicTexture() */ - bool hasMetallicRoughnessTexture() const; + bool hasNoneRoughnessMetallicTexture() const; /** * @brief Whether the material has a combined occlusion/roughness/metallic texture @@ -133,9 +140,19 @@ class MAGNUM_TRADE_EXPORT PbrMetallicRoughnessMaterialData: public MaterialData * @ref occlusionTextureCoordinates() return values common for * occlusion, roughness and metalness textures, and the three are * packed together with occlusion occupying the R channel, roughness - * the G channel and metalness the B channel. + * the G channel and metalness the B channel. This packing is common in + * glTF metallic/roughness materials, which in turn is compatible with + * how UE4 assets are usually packed. Original rationale for this [can + * be seen here](https://github.com/KhronosGroup/glTF/issues/857), + * there's also a [MSFT_packing_occlusionRoughnessMetallic](https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Vendor/MSFT_packing_occlusionRoughnessMetallic/README.md) + * glTF extension using this packing in a more explicit way. + * + * The alpha channel is ignored and can be repurposed for other maps + * such as transmission. This check is a superset of + * @ref hasNoneRoughnessMetallicTexture() --- if this function returns + * @cpp true @ce, that one will return @cpp true @ce as well. * @see @ref hasMetalnessTexture(), @ref hasRoughnessTexture(), - * @ref hasMetallicRoughnessTexture(), + * @ref hasNoneRoughnessMetallicTexture(), * @ref hasRoughnessMetallicOcclusionTexture(), * @ref hasNormalRoughnessMetallicTexture() */ @@ -167,9 +184,14 @@ class MAGNUM_TRADE_EXPORT PbrMetallicRoughnessMaterialData: public MaterialData * @ref roughnessTextureCoordinates() return values common for * roughness, metalness and occlusion textures, and the three are * packed together with roughness occupying the R channel, metalness - * the G channel and occlusion the B channel. + * the G channel and occlusion the B channel. This check is present in + * order to provide support for the [MSFT_packing_occlusionRoughnessMetallic](https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Vendor/MSFT_packing_occlusionRoughnessMetallic/README.md) + * glTF extension. + * + * The alpha channel is ignored and can be repurposed for other maps + * such as transmission. * @see @ref hasMetalnessTexture(), @ref hasRoughnessTexture(), - * @ref hasMetallicRoughnessTexture(), + * @ref hasNoneRoughnessMetallicTexture(), * @ref hasOcclusionRoughnessMetallicTexture(), * @ref hasNormalRoughnessMetallicTexture() */ @@ -201,9 +223,12 @@ class MAGNUM_TRADE_EXPORT PbrMetallicRoughnessMaterialData: public MaterialData * @ref normalTextureCoordinates() return values common for normal, * roughness and metalness textures, and the three are packed together * with normals occupying the RG channel, roughness the B channel and - * metalness the A channel. + * metalness the A channel. This check is present in order to provide + * support for the [MSFT_packing_normalRoughnessMetallic](https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Vendor/MSFT_packing_normalRoughnessMetallic/README.md) + * glTF extension. + * * @see @ref hasMetalnessTexture(), @ref hasRoughnessTexture(), - * @ref hasMetallicRoughnessTexture(), + * @ref hasNoneRoughnessMetallicTexture(), * @ref hasRoughnessMetallicOcclusionTexture(), * @ref hasOcclusionRoughnessMetallicTexture() */ @@ -297,8 +322,8 @@ class MAGNUM_TRADE_EXPORT PbrMetallicRoughnessMaterialData: public MaterialData * @brief Metalness texture ID * * Available only if either @ref MaterialAttribute::MetalnessTexture or - * @ref MaterialAttribute::MetallicRoughnessTexture is present. Meant - * to be multiplied with @ref metalness(). + * @ref MaterialAttribute::NoneRoughnessMetallicTexture is present. + * Meant to be multiplied with @ref metalness(). * @see @ref hasMetalnessTexture(), @ref AbstractImporter::texture() */ UnsignedInt metalnessTexture() const; @@ -306,8 +331,8 @@ class MAGNUM_TRADE_EXPORT PbrMetallicRoughnessMaterialData: public MaterialData /** * @brief Metalness texture swizzle * - * If @ref MaterialAttribute::MetallicRoughnessTexture is present, - * returns always @ref MaterialTextureSwizzle::R. Otherwise returns the + * If @ref MaterialAttribute::NoneRoughnessMetallicTexture is present, + * returns always @ref MaterialTextureSwizzle::B. Otherwise returns the * @ref MaterialAttribute::MetalnessTextureSwizzle attribute, or * @ref MaterialTextureSwizzle::R, if it's not present. Available only * if the material has a metalness texture. @@ -353,7 +378,7 @@ class MAGNUM_TRADE_EXPORT PbrMetallicRoughnessMaterialData: public MaterialData * @brief Roughness texture ID * * Available only if either @ref MaterialAttribute::RoughnessTexture or - * @ref MaterialAttribute::MetallicRoughnessTexture is present. Meant + * @ref MaterialAttribute::NoneRoughnessMetallicTexture is present. Meant * to be multiplied with @ref roughness(). * @see @ref hasRoughnessTexture(), @ref AbstractImporter::texture() */ @@ -362,7 +387,7 @@ class MAGNUM_TRADE_EXPORT PbrMetallicRoughnessMaterialData: public MaterialData /** * @brief Roughness texture swizzle * - * If @ref MaterialAttribute::MetallicRoughnessTexture is present, + * If @ref MaterialAttribute::NoneRoughnessMetallicTexture is present, * returns always @ref MaterialTextureSwizzle::G. Otherwise returns the * @ref MaterialAttribute::RoughnessTextureSwizzle attribute, or * @ref MaterialTextureSwizzle::R, if it's not present. Available only diff --git a/src/Magnum/Trade/Test/MaterialDataTest.cpp b/src/Magnum/Trade/Test/MaterialDataTest.cpp index 2dff7a542e..6c6e2bd45f 100644 --- a/src/Magnum/Trade/Test/MaterialDataTest.cpp +++ b/src/Magnum/Trade/Test/MaterialDataTest.cpp @@ -2679,7 +2679,7 @@ void MaterialDataTest::pbrMetallicRoughnessAccess() { CORRADE_VERIFY(!data.hasMetalnessTexture()); CORRADE_VERIFY(!data.hasRoughnessTexture()); - CORRADE_VERIFY(!data.hasMetallicRoughnessTexture()); + CORRADE_VERIFY(!data.hasNoneRoughnessMetallicTexture()); CORRADE_VERIFY(!data.hasRoughnessMetallicOcclusionTexture()); CORRADE_VERIFY(!data.hasOcclusionRoughnessMetallicTexture()); CORRADE_VERIFY(!data.hasNormalRoughnessMetallicTexture()); @@ -2700,7 +2700,7 @@ void MaterialDataTest::pbrMetallicRoughnessAccessDefaults() { CORRADE_VERIFY(!data.hasMetalnessTexture()); CORRADE_VERIFY(!data.hasRoughnessTexture()); - CORRADE_VERIFY(!data.hasMetallicRoughnessTexture()); + CORRADE_VERIFY(!data.hasNoneRoughnessMetallicTexture()); CORRADE_VERIFY(!data.hasRoughnessMetallicOcclusionTexture()); CORRADE_VERIFY(!data.hasOcclusionRoughnessMetallicTexture()); CORRADE_VERIFY(!data.hasNormalRoughnessMetallicTexture()); @@ -2747,7 +2747,7 @@ void MaterialDataTest::pbrMetallicRoughnessAccessTextured() { CORRADE_VERIFY(data.hasMetalnessTexture()); CORRADE_VERIFY(data.hasRoughnessTexture()); - CORRADE_VERIFY(!data.hasMetallicRoughnessTexture()); + CORRADE_VERIFY(!data.hasNoneRoughnessMetallicTexture()); CORRADE_VERIFY(!data.hasRoughnessMetallicOcclusionTexture()); CORRADE_VERIFY(!data.hasOcclusionRoughnessMetallicTexture()); CORRADE_VERIFY(!data.hasNormalRoughnessMetallicTexture()); @@ -2797,7 +2797,7 @@ void MaterialDataTest::pbrMetallicRoughnessAccessTexturedDefaults() { CORRADE_VERIFY(data.hasMetalnessTexture()); CORRADE_VERIFY(data.hasRoughnessTexture()); - CORRADE_VERIFY(!data.hasMetallicRoughnessTexture()); + CORRADE_VERIFY(!data.hasNoneRoughnessMetallicTexture()); CORRADE_VERIFY(!data.hasRoughnessMetallicOcclusionTexture()); CORRADE_VERIFY(!data.hasOcclusionRoughnessMetallicTexture()); CORRADE_VERIFY(!data.hasNormalRoughnessMetallicTexture()); @@ -2868,63 +2868,63 @@ void MaterialDataTest::pbrMetallicRoughnessAccessTexturedImplicitPackedMetallicR /* Just the texture ID, the rest is implicit */ { PbrMetallicRoughnessMaterialData data{{}, { - {MaterialAttribute::MetallicRoughnessTexture, 2u}, + {MaterialAttribute::NoneRoughnessMetallicTexture, 2u}, }}; - CORRADE_VERIFY(data.hasMetallicRoughnessTexture()); - CORRADE_COMPARE(data.metalnessTexture(), 2); - CORRADE_COMPARE(data.metalnessTextureSwizzle(), MaterialTextureSwizzle::R); - CORRADE_COMPARE(data.metalnessTextureMatrix(), Matrix3{}); - CORRADE_COMPARE(data.metalnessTextureCoordinates(), 0); + CORRADE_VERIFY(data.hasNoneRoughnessMetallicTexture()); CORRADE_COMPARE(data.roughnessTexture(), 2); CORRADE_COMPARE(data.roughnessTextureSwizzle(), MaterialTextureSwizzle::G); CORRADE_COMPARE(data.roughnessTextureMatrix(), Matrix3{}); CORRADE_COMPARE(data.roughnessTextureCoordinates(), 0); + CORRADE_COMPARE(data.metalnessTexture(), 2); + CORRADE_COMPARE(data.metalnessTextureSwizzle(), MaterialTextureSwizzle::B); + CORRADE_COMPARE(data.metalnessTextureMatrix(), Matrix3{}); + CORRADE_COMPARE(data.metalnessTextureCoordinates(), 0); /* Explicit parameters for everything, but all the same */ } { PbrMetallicRoughnessMaterialData data{{}, { - {MaterialAttribute::MetallicRoughnessTexture, 2u}, - {MaterialAttribute::MetalnessTextureMatrix, Matrix3::scaling({0.5f, 0.5f})}, - {MaterialAttribute::MetalnessTextureSwizzle, MaterialTextureSwizzle::R}, - {MaterialAttribute::MetalnessTextureCoordinates, 3u}, + {MaterialAttribute::NoneRoughnessMetallicTexture, 2u}, {MaterialAttribute::RoughnessTextureSwizzle, MaterialTextureSwizzle::G}, {MaterialAttribute::RoughnessTextureMatrix, Matrix3::scaling({0.5f, 0.5f})}, - {MaterialAttribute::RoughnessTextureCoordinates, 3u} + {MaterialAttribute::RoughnessTextureCoordinates, 3u}, + {MaterialAttribute::MetalnessTextureMatrix, Matrix3::scaling({0.5f, 0.5f})}, + {MaterialAttribute::MetalnessTextureSwizzle, MaterialTextureSwizzle::B}, + {MaterialAttribute::MetalnessTextureCoordinates, 3u} }}; - CORRADE_VERIFY(data.hasMetallicRoughnessTexture()); - CORRADE_COMPARE(data.metalnessTexture(), 2); - CORRADE_COMPARE(data.metalnessTextureSwizzle(), MaterialTextureSwizzle::R); - CORRADE_COMPARE(data.metalnessTextureMatrix(), Matrix3::scaling({0.5f, 0.5f})); - CORRADE_COMPARE(data.metalnessTextureCoordinates(), 3); + CORRADE_VERIFY(data.hasNoneRoughnessMetallicTexture()); CORRADE_COMPARE(data.roughnessTexture(), 2); CORRADE_COMPARE(data.roughnessTextureSwizzle(), MaterialTextureSwizzle::G); CORRADE_COMPARE(data.roughnessTextureMatrix(), Matrix3::scaling({0.5f, 0.5f})); CORRADE_COMPARE(data.roughnessTextureCoordinates(), 3); + CORRADE_COMPARE(data.metalnessTexture(), 2); + CORRADE_COMPARE(data.metalnessTextureSwizzle(), MaterialTextureSwizzle::B); + CORRADE_COMPARE(data.metalnessTextureMatrix(), Matrix3::scaling({0.5f, 0.5f})); + CORRADE_COMPARE(data.metalnessTextureCoordinates(), 3); /* Swizzle is ignored when the combined texture is specified, so this is fine */ } { PbrMetallicRoughnessMaterialData data{{}, { - {MaterialAttribute::MetallicRoughnessTexture, 2u}, - {MaterialAttribute::RoughnessTextureSwizzle, MaterialTextureSwizzle::B}, + {MaterialAttribute::NoneRoughnessMetallicTexture, 2u}, + {MaterialAttribute::MetalnessTextureSwizzle, MaterialTextureSwizzle::G}, }}; - CORRADE_VERIFY(data.hasMetallicRoughnessTexture()); + CORRADE_VERIFY(data.hasNoneRoughnessMetallicTexture()); /* Unexpected texture matrix */ } { PbrMetallicRoughnessMaterialData data{{}, { - {MaterialAttribute::MetallicRoughnessTexture, 2u}, + {MaterialAttribute::NoneRoughnessMetallicTexture, 2u}, {MaterialAttribute::MetalnessTextureMatrix, Matrix3::scaling({0.5f, 1.0f})}, }}; - CORRADE_VERIFY(!data.hasMetallicRoughnessTexture()); + CORRADE_VERIFY(!data.hasNoneRoughnessMetallicTexture()); /* Unexpected texture coordinates */ } { PbrMetallicRoughnessMaterialData data{{}, { - {MaterialAttribute::MetallicRoughnessTexture, 2u}, + {MaterialAttribute::NoneRoughnessMetallicTexture, 2u}, {MaterialAttribute::RoughnessTextureCoordinates, 1u}, }}; - CORRADE_VERIFY(!data.hasMetallicRoughnessTexture()); + CORRADE_VERIFY(!data.hasNoneRoughnessMetallicTexture()); } } @@ -2932,62 +2932,66 @@ void MaterialDataTest::pbrMetallicRoughnessAccessTexturedExplicitPackedMetallicR /* Just the texture IDs and swizzles, the rest is implicit */ { PbrMetallicRoughnessMaterialData data{{}, { - {MaterialAttribute::MetalnessTexture, 2u}, {MaterialAttribute::RoughnessTexture, 2u}, - {MaterialAttribute::RoughnessTextureSwizzle, MaterialTextureSwizzle::G} + {MaterialAttribute::MetalnessTexture, 2u}, + {MaterialAttribute::RoughnessTextureSwizzle, MaterialTextureSwizzle::G}, + {MaterialAttribute::MetalnessTextureSwizzle, MaterialTextureSwizzle::B} }}; - CORRADE_VERIFY(data.hasMetallicRoughnessTexture()); + CORRADE_VERIFY(data.hasNoneRoughnessMetallicTexture()); /* Explicit parameters for everything, but all the same */ } { PbrMetallicRoughnessMaterialData data{{}, { - {MaterialAttribute::MetalnessTexture, 2u}, - {MaterialAttribute::MetalnessTextureMatrix, Matrix3::scaling({0.5f, 0.5f})}, - {MaterialAttribute::MetalnessTextureSwizzle, MaterialTextureSwizzle::R}, - {MaterialAttribute::MetalnessTextureCoordinates, 3u}, {MaterialAttribute::RoughnessTexture, 2u}, {MaterialAttribute::RoughnessTextureSwizzle, MaterialTextureSwizzle::G}, {MaterialAttribute::RoughnessTextureMatrix, Matrix3::scaling({0.5f, 0.5f})}, - {MaterialAttribute::RoughnessTextureCoordinates, 3u} + {MaterialAttribute::RoughnessTextureCoordinates, 3u}, + {MaterialAttribute::MetalnessTexture, 2u}, + {MaterialAttribute::MetalnessTextureMatrix, Matrix3::scaling({0.5f, 0.5f})}, + {MaterialAttribute::MetalnessTextureSwizzle, MaterialTextureSwizzle::B}, + {MaterialAttribute::MetalnessTextureCoordinates, 3u} }}; - CORRADE_VERIFY(data.hasMetallicRoughnessTexture()); + CORRADE_VERIFY(data.hasNoneRoughnessMetallicTexture()); /* One texture missing */ } { PbrMetallicRoughnessMaterialData data{{}, { {MaterialAttribute::RoughnessTexture, 2u}, - {MaterialAttribute::RoughnessTextureSwizzle, MaterialTextureSwizzle::B}, + {MaterialAttribute::RoughnessTextureSwizzle, MaterialTextureSwizzle::G}, }}; - CORRADE_VERIFY(!data.hasMetallicRoughnessTexture()); + CORRADE_VERIFY(!data.hasNoneRoughnessMetallicTexture()); /* Unexpected swizzle */ } { PbrMetallicRoughnessMaterialData data{{}, { - {MaterialAttribute::MetalnessTexture, 2u}, {MaterialAttribute::RoughnessTexture, 2u}, - {MaterialAttribute::RoughnessTextureSwizzle, MaterialTextureSwizzle::B}, + {MaterialAttribute::RoughnessTextureSwizzle, MaterialTextureSwizzle::R}, + {MaterialAttribute::MetalnessTexture, 2u}, + {MaterialAttribute::MetalnessTextureSwizzle, MaterialTextureSwizzle::B}, }}; - CORRADE_VERIFY(!data.hasMetallicRoughnessTexture()); + CORRADE_VERIFY(!data.hasNoneRoughnessMetallicTexture()); /* Unexpected texture matrix */ } { PbrMetallicRoughnessMaterialData data{{}, { - {MaterialAttribute::MetalnessTexture, 2u}, - {MaterialAttribute::MetalnessTextureMatrix, Matrix3::scaling({0.5f, 1.0f})}, {MaterialAttribute::RoughnessTexture, 2u}, {MaterialAttribute::RoughnessTextureSwizzle, MaterialTextureSwizzle::G}, + {MaterialAttribute::MetalnessTexture, 2u}, + {MaterialAttribute::MetalnessTextureSwizzle, MaterialTextureSwizzle::B}, + {MaterialAttribute::MetalnessTextureMatrix, Matrix3::scaling({0.5f, 1.0f})}, }}; - CORRADE_VERIFY(!data.hasMetallicRoughnessTexture()); + CORRADE_VERIFY(!data.hasNoneRoughnessMetallicTexture()); /* Unexpected texture coordinates */ } { PbrMetallicRoughnessMaterialData data{{}, { - {MaterialAttribute::MetalnessTexture, 2u}, {MaterialAttribute::RoughnessTexture, 2u}, {MaterialAttribute::RoughnessTextureSwizzle, MaterialTextureSwizzle::G}, {MaterialAttribute::RoughnessTextureCoordinates, 1u}, + {MaterialAttribute::MetalnessTexture, 2u}, + {MaterialAttribute::MetalnessTextureSwizzle, MaterialTextureSwizzle::B}, }}; - CORRADE_VERIFY(!data.hasMetallicRoughnessTexture()); + CORRADE_VERIFY(!data.hasNoneRoughnessMetallicTexture()); } } @@ -3002,6 +3006,8 @@ void MaterialDataTest::pbrMetallicRoughnessAccessTexturedExplicitPackedRoughness {MaterialAttribute::OcclusionTextureSwizzle, MaterialTextureSwizzle::B}, }}; CORRADE_VERIFY(data.hasRoughnessMetallicOcclusionTexture()); + /* This isn't a superset */ + CORRADE_VERIFY(!data.hasNoneRoughnessMetallicTexture()); /* Explicit parameters for everything, but all the same */ } { @@ -3020,6 +3026,8 @@ void MaterialDataTest::pbrMetallicRoughnessAccessTexturedExplicitPackedRoughness {MaterialAttribute::OcclusionTextureCoordinates, 3u} }}; CORRADE_VERIFY(data.hasRoughnessMetallicOcclusionTexture()); + /* This isn't a superset */ + CORRADE_VERIFY(!data.hasNoneRoughnessMetallicTexture()); /* One texture missing */ } { @@ -3080,6 +3088,8 @@ void MaterialDataTest::pbrMetallicRoughnessAccessTexturedExplicitPackedOcclusion {MaterialAttribute::MetalnessTextureSwizzle, MaterialTextureSwizzle::B}, }}; CORRADE_VERIFY(data.hasOcclusionRoughnessMetallicTexture()); + /* This is a superset */ + CORRADE_VERIFY(data.hasNoneRoughnessMetallicTexture()); /* Explicit parameters for everything, but all the same */ } { @@ -3098,6 +3108,8 @@ void MaterialDataTest::pbrMetallicRoughnessAccessTexturedExplicitPackedOcclusion {MaterialAttribute::MetalnessTextureCoordinates, 3u} }}; CORRADE_VERIFY(data.hasOcclusionRoughnessMetallicTexture()); + /* This is a superset */ + CORRADE_VERIFY(data.hasNoneRoughnessMetallicTexture()); /* One texture missing */ } { From 9764672ff8e70fd6656a038d944b7c5fa93b294a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 11 Aug 2020 18:59:17 +0200 Subject: [PATCH 25/36] Trade: MSVC can you just FUCK OFF already. --- src/Magnum/Trade/MaterialLayerData.h | 245 +++++++++++++++++++++++++-- 1 file changed, 228 insertions(+), 17 deletions(-) diff --git a/src/Magnum/Trade/MaterialLayerData.h b/src/Magnum/Trade/MaterialLayerData.h index d2ef55b72b..d1e6db023e 100644 --- a/src/Magnum/Trade/MaterialLayerData.h +++ b/src/Magnum/Trade/MaterialLayerData.h @@ -51,23 +51,6 @@ template class MaterialLayerData: public MaterialData { desired convenience APIs easier (and simplifies testing) */ using MaterialData::MaterialData; - /* Bring in all other overloads as well and override just the ones - with implicit layers */ - using MaterialData::layerName; - using MaterialData::layerFactor; - using MaterialData::layerFactorTexture; - using MaterialData::layerFactorTextureSwizzle; - using MaterialData::layerFactorTextureMatrix; - using MaterialData::layerFactorTextureCoordinates; - using MaterialData::attributeCount; - using MaterialData::hasAttribute; - using MaterialData::attributeId; - using MaterialData::attributeName; - using MaterialData::attributeType; - using MaterialData::attribute; - using MaterialData::tryAttribute; - using MaterialData::attributeOr; - /** * @brief Layer name * @@ -249,6 +232,234 @@ template class MaterialLayerData: public MaterialData { template T attributeOr(MaterialAttribute name, const T& defaultValue) const { return MaterialData::attributeOr(layer, name, defaultValue); } /**< @overload */ + + /* Bring in all other overloads as well and override just the ones + with implicit layers */ + using MaterialData::layerName; + using MaterialData::layerFactor; + using MaterialData::layerFactorTexture; + using MaterialData::layerFactorTextureSwizzle; + using MaterialData::layerFactorTextureMatrix; + using MaterialData::layerFactorTextureCoordinates; + /* MSVC is so damn unbelievably stupid it's setting my ass on fire. + https://en.cppreference.com/w/cpp/language/using_declaration says + that "If the derived class already has a member with the same name, + parameter list, and qualifications, the derived class member hides + or overrides (doesn't conflict with) the member that is introduced + from the base class." and EVERY COMPILER ON EARTH EXCEPT THIS DAMN + POOL OF GOO implements that correctly. So instead of a bunch of sane + using declarations I have to enumerate EACH AND EVERY overload and + explicitly pass them through LIKE A FUCKING SAVAGE. FFS. */ + #if !defined(CORRADE_TARGET_MSVC) || defined(CORRADE_TARGET_CLANG_CL) + using MaterialData::attributeCount; + using MaterialData::hasAttribute; + using MaterialData::attributeId; + using MaterialData::attributeName; + using MaterialData::attributeType; + using MaterialData::attribute; + using MaterialData::tryAttribute; + using MaterialData::attributeOr; + #else + UnsignedInt attributeCount(UnsignedInt layer_) const { + return MaterialData::attributeCount(layer_); + } + UnsignedInt attributeCount(Containers::StringView layer_) const { + return MaterialData::attributeCount(layer_); + } + UnsignedInt attributeCount(MaterialLayer layer_) const { + return MaterialData::attributeCount(layer_); + } + + bool hasAttribute(UnsignedInt layer_, Containers::StringView name) const { + return MaterialData::hasAttribute(layer_, name); + } + bool hasAttribute(UnsignedInt layer_, MaterialAttribute name) const { + return MaterialData::hasAttribute(layer_, name); + } + bool hasAttribute(Containers::StringView layer_, Containers::StringView name) const { + return MaterialData::hasAttribute(layer_, name); + } + bool hasAttribute(Containers::StringView layer_, MaterialAttribute name) const { + return MaterialData::hasAttribute(layer_, name); + } + bool hasAttribute(MaterialLayer layer_, Containers::StringView name) const { + return MaterialData::hasAttribute(layer_, name); + } + bool hasAttribute(MaterialLayer layer_, MaterialAttribute name) const { + return MaterialData::hasAttribute(layer_, name); + } + + UnsignedInt attributeId(UnsignedInt layer_, Containers::StringView name) const { + return MaterialData::attributeId(layer_, name); + } + UnsignedInt attributeId(UnsignedInt layer_, MaterialAttribute name) const { + return MaterialData::attributeId(layer_, name); + } + UnsignedInt attributeId(Containers::StringView layer_, Containers::StringView name) const { + return MaterialData::attributeId(layer_, name); + } + UnsignedInt attributeId(Containers::StringView layer_, MaterialAttribute name) const { + return MaterialData::attributeId(layer_, name); + } + UnsignedInt attributeId(MaterialLayer layer_, Containers::StringView name) const { + return MaterialData::attributeId(layer_, name); + } + UnsignedInt attributeId(MaterialLayer layer_, MaterialAttribute name) const { + return MaterialData::attributeId(layer_, name); + } + + Containers::StringView attributeName(UnsignedInt layer_, UnsignedInt id) const { + return MaterialData::attributeName(layer_, id); + } + Containers::StringView attributeName(Containers::StringView layer_, UnsignedInt id) const { + return MaterialData::attributeName(layer_, id); + } + Containers::StringView attributeName(MaterialLayer layer_, UnsignedInt id) const { + return MaterialData::attributeName(layer_, id); + } + + MaterialAttributeType attributeType(UnsignedInt layer_, UnsignedInt id) const { + return MaterialData::attributeType(layer_, id); + } + MaterialAttributeType attributeType(UnsignedInt layer_, Containers::StringView name) const { + return MaterialData::attributeType(layer_, name); + } + MaterialAttributeType attributeType(UnsignedInt layer_, MaterialAttribute name) const { + return MaterialData::attributeType(layer_, name); + } + MaterialAttributeType attributeType(Containers::StringView layer_, UnsignedInt id) const { + return MaterialData::attributeType(layer_, id); + } + MaterialAttributeType attributeType(MaterialLayer layer_, UnsignedInt id) const { + return MaterialData::attributeType(layer_, id); + } + MaterialAttributeType attributeType(Containers::StringView layer_, Containers::StringView name) const { + return MaterialData::attributeType(layer_, name); + } + MaterialAttributeType attributeType(Containers::StringView layer_, MaterialAttribute name) const { + return MaterialData::attributeType(layer_, name); + } + MaterialAttributeType attributeType(MaterialLayer layer_, Containers::StringView name) const { + return MaterialData::attributeType(layer_, name); + } + MaterialAttributeType attributeType(MaterialLayer layer_, MaterialAttribute name) const { + return MaterialData::attributeType(layer_, name); + } + + const void* attribute(UnsignedInt layer_, UnsignedInt id) const { + return MaterialData::attribute(layer_, id); + } + const void* attribute(Containers::StringView layer_, UnsignedInt id) const { + return MaterialData::attribute(layer_, id); + } + const void* attribute(MaterialLayer layer_, UnsignedInt id) const { + return MaterialData::attribute(layer_, id); + } + const void* attribute(UnsignedInt layer_, Containers::StringView name) const { + return MaterialData::attribute(layer_, name); + } + const void* attribute(UnsignedInt layer_, MaterialAttribute name) const { + return MaterialData::attribute(layer_, name); + } + const void* attribute(Containers::StringView layer_, Containers::StringView name) const { + return MaterialData::attribute(layer_, name); + } + const void* attribute(Containers::StringView layer_, MaterialAttribute name) const { + return MaterialData::attribute(layer_, name); + } + const void* attribute(MaterialLayer layer_, Containers::StringView name) const { + return MaterialData::attribute(layer_, name); + } + const void* attribute(MaterialLayer layer_, MaterialAttribute name) const { + return MaterialData::attribute(layer_, name); + } + + template T attribute(UnsignedInt layer_, UnsignedInt id) const { + return MaterialData::attribute(layer_, id); + } + template T attribute(Containers::StringView layer_, UnsignedInt id) const { + return MaterialData::attribute(layer_, id); + } + template T attribute(MaterialLayer layer_, UnsignedInt id) const { + return MaterialData::attribute(layer_, id); + } + template T attribute(UnsignedInt layer_, Containers::StringView name) const { + return MaterialData::attribute(layer_, name); + } + template T attribute(UnsignedInt layer_, MaterialAttribute name) const { + return MaterialData::attribute(layer_, name); + } + template T attribute(Containers::StringView layer_, Containers::StringView name) const { + return MaterialData::attribute(layer_, name); + } + template T attribute(Containers::StringView layer_, MaterialAttribute name) const { + return MaterialData::attribute(layer_, name); + } + template T attribute(MaterialLayer layer_, Containers::StringView name) const { + return MaterialData::attribute(layer_, name); + } + template T attribute(MaterialLayer layer_, MaterialAttribute name) const { + return MaterialData::attribute(layer_, name); + } + + const void* tryAttribute(UnsignedInt layer_, Containers::StringView name) const { + return MaterialData::tryAttribute(layer_, name); + } + const void* tryAttribute(UnsignedInt layer_, MaterialAttribute name) const { + return MaterialData::tryAttribute(layer_, name); + } + const void* tryAttribute(Containers::StringView layer_, Containers::StringView name) const { + return MaterialData::tryAttribute(layer_, name); + } + const void* tryAttribute(Containers::StringView layer_, MaterialAttribute name) const { + return MaterialData::tryAttribute(layer_, name); + } + const void* tryAttribute(MaterialLayer layer_, Containers::StringView name) const { + return MaterialData::tryAttribute(layer_, name); + } + const void* tryAttribute(MaterialLayer layer_, MaterialAttribute name) const { + return MaterialData::tryAttribute(layer_, name); + } + + template Containers::Optional tryAttribute(UnsignedInt layer_, Containers::StringView name) const { + return MaterialData::tryAttribute(layer_, name); + } + template Containers::Optional tryAttribute(UnsignedInt layer_, MaterialAttribute name) const { + return MaterialData::tryAttribute(layer_, name); + } + + template Containers::Optional tryAttribute(Containers::StringView layer_, Containers::StringView name) const { + return MaterialData::tryAttribute(layer_, name); + } + template Containers::Optional tryAttribute(Containers::StringView layer_, MaterialAttribute name) const { + return MaterialData::tryAttribute(layer_, name); + } + template Containers::Optional tryAttribute(MaterialLayer layer_, Containers::StringView name) const { + return MaterialData::tryAttribute(layer_, name); + } + template Containers::Optional tryAttribute(MaterialLayer layer_, MaterialAttribute name) const { + return MaterialData::tryAttribute(layer_, name); + } + + template T attributeOr(UnsignedInt layer_, Containers::StringView name, const T& defaultValue) const { + return MaterialData::attributeOr(layer_, name, defaultValue); + } + template T attributeOr(UnsignedInt layer_, MaterialAttribute name, const T& defaultValue) const { + return MaterialData::attributeOr(layer_, name, defaultValue); + } + template T attributeOr(Containers::StringView layer_, Containers::StringView name, const T& defaultValue) const { + return MaterialData::attributeOr(layer_, name, defaultValue); + } + template T attributeOr(Containers::StringView layer_, MaterialAttribute name, const T& defaultValue) const { + return MaterialData::attributeOr(layer_, name, defaultValue); + } + template T attributeOr(MaterialLayer layer_, Containers::StringView name, const T& defaultValue) const { + return MaterialData::attributeOr(layer_, name, defaultValue); + } + template T attributeOr(MaterialLayer layer_, MaterialAttribute name, const T& defaultValue) const { + return MaterialData::attributeOr(layer_, name, defaultValue); + } + #endif }; }} From baa23ffd10f0f65728eb9f1f27474057218da2f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 11 Aug 2020 20:01:43 +0200 Subject: [PATCH 26/36] Trade: and you, MSVC 2015, need a special place in hell. --- src/Magnum/Trade/MaterialData.h | 12 +++++++++++- src/Magnum/Trade/Test/MaterialDataTest.cpp | 9 ++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/Magnum/Trade/MaterialData.h b/src/Magnum/Trade/MaterialData.h index ecfa2701ec..61a737b3d2 100644 --- a/src/Magnum/Trade/MaterialData.h +++ b/src/Magnum/Trade/MaterialData.h @@ -2636,7 +2636,17 @@ template constexpr MaterialAttributeData::MaterialAttributeData(const Containers::StringView name, const T& value) noexcept: _data{Implementation::MaterialAttributeTypeFor::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::type() << "but got" << name.size()), name), value} {} +> constexpr MaterialAttributeData::MaterialAttributeData(const Containers::StringView name, const T& value) noexcept: + _data{Implementation::MaterialAttributeTypeFor::type(), + /* MSVC 2015 complains about "error C2065: 'T': undeclared identifier" + in the lambda inside this macro. Sorry, the assert will be less + useful on that stupid thing. */ + #ifndef CORRADE_MSVC2015_COMPATIBILITY + (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::type() << "but got" << name.size()), name) + #else + (CORRADE_CONSTEXPR_ASSERT(name.size() + sizeof(T) + 2 <= Implementation::MaterialAttributeDataSize, "Trade::MaterialAttributeData: name" << name << "too long, got" << name.size() << "bytes"), name) + #endif + , value} {} /* The 4 extra bytes are for a null byte after both the name and value, a type and a string size */ diff --git a/src/Magnum/Trade/Test/MaterialDataTest.cpp b/src/Magnum/Trade/Test/MaterialDataTest.cpp index 6c6e2bd45f..6d9dad15c6 100644 --- a/src/Magnum/Trade/Test/MaterialDataTest.cpp +++ b/src/Magnum/Trade/Test/MaterialDataTest.cpp @@ -821,11 +821,18 @@ void MaterialDataTest::constructAttributeTooLarge() { Error redirectError{&out}; MaterialAttributeData{"attributeIsLong", Matrix3x4{}}; /* Constexpr variant has the same assert, but in the header. It should have - the same output. */ + the same output. Except on MSVC 2015, which is a crap thing and gets + lost when encountering T in there, so the assert is less useful. */ /*constexpr*/ MaterialAttributeData{"attributeIsLong"_s, Matrix3x4{}}; + #ifndef CORRADE_MSVC2015_COMPATIBILITY CORRADE_COMPARE(out.str(), "Trade::MaterialAttributeData: name attributeIsLong too long, expected at most 14 bytes for Trade::MaterialAttributeType::Matrix3x4 but got 15\n" "Trade::MaterialAttributeData: name attributeIsLong too long, expected at most 14 bytes for Trade::MaterialAttributeType::Matrix3x4 but got 15\n"); + #else + CORRADE_COMPARE(out.str(), + "Trade::MaterialAttributeData: name attributeIsLong too long, expected at most 14 bytes for Trade::MaterialAttributeType::Matrix3x4 but got 15\n" + "Trade::MaterialAttributeData: name attributeIsLong too long, got 15 bytes\n"); + #endif } void MaterialDataTest::constructAttributeTooLargeString() { From b3a848c0d682a918c05191329101f74463c4da87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Thu, 13 Aug 2020 08:20:23 +0200 Subject: [PATCH 27/36] Trade: sigh, MSVC 2015, again. --- src/Magnum/Trade/Test/MaterialDataTest.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Magnum/Trade/Test/MaterialDataTest.cpp b/src/Magnum/Trade/Test/MaterialDataTest.cpp index 6d9dad15c6..966509107d 100644 --- a/src/Magnum/Trade/Test/MaterialDataTest.cpp +++ b/src/Magnum/Trade/Test/MaterialDataTest.cpp @@ -613,7 +613,12 @@ template void MaterialDataTest::constructAttributeStringConstexpr() { setTestCaseTemplateName(TypeName::name()); /* "templateAttrib" is 14 chars, which is the maximum for 48-bit types */ - constexpr MaterialAttributeData attribute{"templateAttrib"_s, T(15)}; + #ifndef CORRADE_MSVC2015_COMPATIBILITY + /* Unfortunately this doesn't always work, for example Vector3. Disabling + unconditionally, might revisit later. */ + constexpr + #endif + MaterialAttributeData attribute{"templateAttrib"_s, T(15)}; CORRADE_COMPARE(attribute.name(), "templateAttrib"); CORRADE_COMPARE(attribute.name().flags(), Containers::StringViewFlag::NullTerminated); CORRADE_COMPARE(attribute.name()[attribute.name().size()], '\0'); From 829e9b6d0f9d5d1a4519d16e749b2fcaffc82502 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Thu, 13 Aug 2020 22:24:34 +0200 Subject: [PATCH 28/36] Trade: add forgotten OcclusionTextureStrength attribute. Whoops, again. --- .../materialAttributeProperties.hpp | 1 + src/Magnum/Trade/MaterialData.h | 26 ++++++++++++++++++- .../PbrMetallicRoughnessMaterialData.cpp | 6 +++++ .../Trade/PbrMetallicRoughnessMaterialData.h | 11 ++++++++ .../PbrSpecularGlossinessMaterialData.cpp | 6 +++++ .../Trade/PbrSpecularGlossinessMaterialData.h | 11 ++++++++ src/Magnum/Trade/Test/MaterialDataTest.cpp | 10 +++++++ 7 files changed, 70 insertions(+), 1 deletion(-) diff --git a/src/Magnum/Trade/Implementation/materialAttributeProperties.hpp b/src/Magnum/Trade/Implementation/materialAttributeProperties.hpp index 306440a914..9cfd2b932a 100644 --- a/src/Magnum/Trade/Implementation/materialAttributeProperties.hpp +++ b/src/Magnum/Trade/Implementation/materialAttributeProperties.hpp @@ -70,6 +70,7 @@ _ct(NormalTextureSwizzle,TextureSwizzle,MaterialTextureSwizzle) _c(NormalTextureMatrix,Matrix3x3) _c(NormalTextureCoordinates,UnsignedInt) _c(OcclusionTexture,UnsignedInt) +_c(OcclusionTextureStrength,Float) _ct(OcclusionTextureSwizzle,TextureSwizzle,MaterialTextureSwizzle) _c(OcclusionTextureMatrix,Matrix3x3) _c(OcclusionTextureCoordinates,UnsignedInt) diff --git a/src/Magnum/Trade/MaterialData.h b/src/Magnum/Trade/MaterialData.h index 61a737b3d2..262e5dec18 100644 --- a/src/Magnum/Trade/MaterialData.h +++ b/src/Magnum/Trade/MaterialData.h @@ -678,12 +678,36 @@ enum class MaterialAttribute: UnsignedInt { /** * Occlusion texture index, * @ref MaterialAttributeType::UnsignedInt. + * + * Single-channel texture that multiplies the resulting material color + * @f$ \boldsymbol{c} @f$: @f[ + * \boldsymbol{c}_o = o \boldsymbol{c} + * @f] + * + * If @ref MaterialAttribute::OcclusionTextureStrength is present as well, + * it's used as an interpolation factor @f$ \color{m-success} s @f$ between + * material color @f$ \boldsymbol{c} @f$ and color with occlusion applied + * @f$ o \boldsymbol{c} @f$: @f[ + * \boldsymbol{c}_o = \operatorname{lerp}(\boldsymbol{c}, o \boldsymbol{c}, {\color{m-success} s}) = + * \boldsymbol{c} (1 - {\color{m-success} s}) + o \boldsymbol{c} {\color{m-success} s} + * @f] * @see @ref PbrMetallicRoughnessMaterialData::hasOcclusionRoughnessMetallicTexture(), * @ref PbrMetallicRoughnessMaterialData::occlusionTexture(), - * @ref PbrSpecularGlossinessMaterialData::occlusionTexture() + * @ref PbrSpecularGlossinessMaterialData::occlusionTexture(), + * @ref Math::lerp() */ OcclusionTexture, + /** + * Occlusion texture strength, @ref MaterialAttributeType::Float. + * + * Affects the texture defined by @ref MaterialAttribute::OcclusionTexture, + * see above for details. + * @see @ref PbrMetallicRoughnessMaterialData::occlusionTextureStrength(), + * @ref PbrSpecularGlossinessMaterialData::occlusionTextureStrength() + */ + OcclusionTextureStrength, + /** * Occlusion texture swizzle, @ref MaterialAttributeType::TextureSwizzle. * diff --git a/src/Magnum/Trade/PbrMetallicRoughnessMaterialData.cpp b/src/Magnum/Trade/PbrMetallicRoughnessMaterialData.cpp index d22ba4c23f..a1a06f0abe 100644 --- a/src/Magnum/Trade/PbrMetallicRoughnessMaterialData.cpp +++ b/src/Magnum/Trade/PbrMetallicRoughnessMaterialData.cpp @@ -259,6 +259,12 @@ UnsignedInt PbrMetallicRoughnessMaterialData::occlusionTexture() const { return attribute(MaterialAttribute::OcclusionTexture); } +Float PbrMetallicRoughnessMaterialData::occlusionTextureStrength() const { + CORRADE_ASSERT(hasAttribute(MaterialAttribute::OcclusionTexture), + "Trade::PbrMetallicRoughnessMaterialData::occlusionTextureStrength(): the material doesn't have an occlusion texture", {}); + return attributeOr(MaterialAttribute::OcclusionTextureStrength, 1.0f); +} + MaterialTextureSwizzle PbrMetallicRoughnessMaterialData::occlusionTextureSwizzle() const { CORRADE_ASSERT(hasAttribute(MaterialAttribute::OcclusionTexture), "Trade::PbrMetallicRoughnessMaterialData::occlusionTextureSwizzle(): the material doesn't have an occlusion texture", {}); diff --git a/src/Magnum/Trade/PbrMetallicRoughnessMaterialData.h b/src/Magnum/Trade/PbrMetallicRoughnessMaterialData.h index 6723660e9e..0d5bd67d16 100644 --- a/src/Magnum/Trade/PbrMetallicRoughnessMaterialData.h +++ b/src/Magnum/Trade/PbrMetallicRoughnessMaterialData.h @@ -478,6 +478,17 @@ class MAGNUM_TRADE_EXPORT PbrMetallicRoughnessMaterialData: public MaterialData */ UnsignedInt occlusionTexture() const; + /** + * @brief Occlusion texture strength + * + * Convenience access to the @ref MaterialAttribute::OcclusionTextureStrength + * attribute. If not present, the default is @cpp 1.0f @ce. + * Available only if @ref MaterialAttribute::OcclusionTexture is + * present. + * @see @ref hasAttribute() + */ + Float occlusionTextureStrength() const; + /** * @brief Occlusion texture swizzle * diff --git a/src/Magnum/Trade/PbrSpecularGlossinessMaterialData.cpp b/src/Magnum/Trade/PbrSpecularGlossinessMaterialData.cpp index 238b01843a..d137882c14 100644 --- a/src/Magnum/Trade/PbrSpecularGlossinessMaterialData.cpp +++ b/src/Magnum/Trade/PbrSpecularGlossinessMaterialData.cpp @@ -208,6 +208,12 @@ UnsignedInt PbrSpecularGlossinessMaterialData::occlusionTexture() const { return attribute(MaterialAttribute::OcclusionTexture); } +Float PbrSpecularGlossinessMaterialData::occlusionTextureStrength() const { + CORRADE_ASSERT(hasAttribute(MaterialAttribute::OcclusionTexture), + "Trade::PbrSpecularGlossinessMaterialData::occlusionTextureStrength(): the material doesn't have an occlusion texture", {}); + return attributeOr(MaterialAttribute::OcclusionTextureStrength, 1.0f); +} + MaterialTextureSwizzle PbrSpecularGlossinessMaterialData::occlusionTextureSwizzle() const { CORRADE_ASSERT(hasAttribute(MaterialAttribute::OcclusionTexture), "Trade::PbrSpecularGlossinessMaterialData::occlusionTextureSwizzle(): the material doesn't have an occlusion texture", {}); diff --git a/src/Magnum/Trade/PbrSpecularGlossinessMaterialData.h b/src/Magnum/Trade/PbrSpecularGlossinessMaterialData.h index dd21276a4c..753bae0ae3 100644 --- a/src/Magnum/Trade/PbrSpecularGlossinessMaterialData.h +++ b/src/Magnum/Trade/PbrSpecularGlossinessMaterialData.h @@ -347,6 +347,17 @@ class MAGNUM_TRADE_EXPORT PbrSpecularGlossinessMaterialData: public MaterialData */ UnsignedInt occlusionTexture() const; + /** + * @brief Occlusion texture strength + * + * Convenience access to the @ref MaterialAttribute::OcclusionTextureStrength + * attribute. If not present, the default is @cpp 1.0f @ce. + * Available only if @ref MaterialAttribute::OcclusionTexture is + * present. + * @see @ref hasAttribute() + */ + Float occlusionTextureStrength() const; + /** * @brief Occlusion texture swizzle * diff --git a/src/Magnum/Trade/Test/MaterialDataTest.cpp b/src/Magnum/Trade/Test/MaterialDataTest.cpp index 966509107d..51fa51bd41 100644 --- a/src/Magnum/Trade/Test/MaterialDataTest.cpp +++ b/src/Magnum/Trade/Test/MaterialDataTest.cpp @@ -2748,6 +2748,7 @@ void MaterialDataTest::pbrMetallicRoughnessAccessTextured() { {MaterialAttribute::NormalTextureMatrix, Matrix3::scaling({1.0f, 0.5f})}, {MaterialAttribute::NormalTextureCoordinates, 5u}, {MaterialAttribute::OcclusionTexture, 4u}, + {MaterialAttribute::OcclusionTextureStrength, 0.66f}, {MaterialAttribute::OcclusionTextureSwizzle, MaterialTextureSwizzle::B}, {MaterialAttribute::OcclusionTextureMatrix, Matrix3::scaling({1.0f, 0.75f})}, {MaterialAttribute::OcclusionTextureCoordinates, 6u}, @@ -2785,6 +2786,7 @@ void MaterialDataTest::pbrMetallicRoughnessAccessTextured() { CORRADE_COMPARE(data.normalTextureMatrix(), Matrix3::scaling({1.0f, 0.5f})); CORRADE_COMPARE(data.normalTextureCoordinates(), 5); CORRADE_COMPARE(data.occlusionTexture(), 4); + CORRADE_COMPARE(data.occlusionTextureStrength(), 0.66f); CORRADE_COMPARE(data.occlusionTextureMatrix(), Matrix3::scaling({1.0f, 0.75f})); CORRADE_COMPARE(data.occlusionTextureSwizzle(), MaterialTextureSwizzle::B); CORRADE_COMPARE(data.occlusionTextureCoordinates(), 6); @@ -2835,6 +2837,7 @@ void MaterialDataTest::pbrMetallicRoughnessAccessTexturedDefaults() { CORRADE_COMPARE(data.normalTextureMatrix(), Matrix3{}); CORRADE_COMPARE(data.normalTextureCoordinates(), 0); CORRADE_COMPARE(data.occlusionTexture(), 5); + CORRADE_COMPARE(data.occlusionTextureStrength(), 1.0f); CORRADE_COMPARE(data.occlusionTextureSwizzle(), MaterialTextureSwizzle::R); CORRADE_COMPARE(data.occlusionTextureMatrix(), Matrix3{}); CORRADE_COMPARE(data.occlusionTextureCoordinates(), 0); @@ -3278,6 +3281,7 @@ void MaterialDataTest::pbrMetallicRoughnessAccessInvalidTextures() { data.normalTextureMatrix(); data.normalTextureCoordinates(); data.occlusionTexture(); + data.occlusionTextureStrength(); data.occlusionTextureSwizzle(); data.occlusionTextureMatrix(); data.occlusionTextureCoordinates(); @@ -3302,6 +3306,7 @@ void MaterialDataTest::pbrMetallicRoughnessAccessInvalidTextures() { "Trade::PbrMetallicRoughnessMaterialData::normalTextureMatrix(): the material doesn't have a normal texture\n" "Trade::PbrMetallicRoughnessMaterialData::normalTextureCoordinates(): the material doesn't have a normal texture\n" "Trade::MaterialData::attribute(): attribute OcclusionTexture not found in layer 0\n" + "Trade::PbrMetallicRoughnessMaterialData::occlusionTextureStrength(): the material doesn't have an occlusion texture\n" "Trade::PbrMetallicRoughnessMaterialData::occlusionTextureSwizzle(): the material doesn't have an occlusion texture\n" "Trade::PbrMetallicRoughnessMaterialData::occlusionTextureMatrix(): the material doesn't have an occlusion texture\n" "Trade::PbrMetallicRoughnessMaterialData::occlusionTextureCoordinates(): the material doesn't have an occlusion texture\n" @@ -3370,6 +3375,7 @@ void MaterialDataTest::pbrSpecularGlossinessAccessTextured() { {MaterialAttribute::NormalTextureMatrix, Matrix3::scaling({1.0f, 0.5f})}, {MaterialAttribute::NormalTextureCoordinates, 5u}, {MaterialAttribute::OcclusionTexture, 4u}, + {MaterialAttribute::OcclusionTextureStrength, 0.66f}, {MaterialAttribute::OcclusionTextureSwizzle, MaterialTextureSwizzle::B}, {MaterialAttribute::OcclusionTextureMatrix, Matrix3::scaling({1.0f, 0.75f})}, {MaterialAttribute::OcclusionTextureCoordinates, 6u}, @@ -3404,6 +3410,7 @@ void MaterialDataTest::pbrSpecularGlossinessAccessTextured() { CORRADE_COMPARE(data.normalTextureMatrix(), Matrix3::scaling({1.0f, 0.5f})); CORRADE_COMPARE(data.normalTextureCoordinates(), 5); CORRADE_COMPARE(data.occlusionTexture(), 4); + CORRADE_COMPARE(data.occlusionTextureStrength(), 0.66f); CORRADE_COMPARE(data.occlusionTextureMatrix(), Matrix3::scaling({1.0f, 0.75f})); CORRADE_COMPARE(data.occlusionTextureSwizzle(), MaterialTextureSwizzle::B); CORRADE_COMPARE(data.occlusionTextureCoordinates(), 6); @@ -3451,6 +3458,7 @@ void MaterialDataTest::pbrSpecularGlossinessAccessTexturedDefaults() { CORRADE_COMPARE(data.normalTextureMatrix(), Matrix3{}); CORRADE_COMPARE(data.normalTextureCoordinates(), 0); CORRADE_COMPARE(data.occlusionTexture(), 5); + CORRADE_COMPARE(data.occlusionTextureStrength(), 1.0f); CORRADE_COMPARE(data.occlusionTextureMatrix(), Matrix3{}); CORRADE_COMPARE(data.occlusionTextureSwizzle(), MaterialTextureSwizzle::R); CORRADE_COMPARE(data.occlusionTextureCoordinates(), 0); @@ -3665,6 +3673,7 @@ void MaterialDataTest::pbrSpecularGlossinessAccessInvalidTextures() { data.normalTextureMatrix(); data.normalTextureCoordinates(); data.occlusionTexture(); + data.occlusionTextureStrength(); data.occlusionTextureSwizzle(); data.occlusionTextureMatrix(); data.occlusionTextureCoordinates(); @@ -3689,6 +3698,7 @@ void MaterialDataTest::pbrSpecularGlossinessAccessInvalidTextures() { "Trade::PbrSpecularGlossinessMaterialData::normalTextureMatrix(): the material doesn't have a normal texture\n" "Trade::PbrSpecularGlossinessMaterialData::normalTextureCoordinates(): the material doesn't have a normal texture\n" "Trade::MaterialData::attribute(): attribute OcclusionTexture not found in layer 0\n" + "Trade::PbrSpecularGlossinessMaterialData::occlusionTextureStrength(): the material doesn't have an occlusion texture\n" "Trade::PbrSpecularGlossinessMaterialData::occlusionTextureSwizzle(): the material doesn't have an occlusion texture\n" "Trade::PbrSpecularGlossinessMaterialData::occlusionTextureMatrix(): the material doesn't have an occlusion texture\n" "Trade::PbrSpecularGlossinessMaterialData::occlusionTextureCoordinates(): the material doesn't have an occlusion texture\n" From 81f5468b6551d1c71b08a6959fc08237805b5500 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Thu, 13 Aug 2020 22:25:14 +0200 Subject: [PATCH 29/36] Trade: custom material attribs can start with non-letters as well. Becase $assimp ?does +that. --- src/Magnum/Trade/MaterialData.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Magnum/Trade/MaterialData.h b/src/Magnum/Trade/MaterialData.h index 262e5dec18..8bd106397e 100644 --- a/src/Magnum/Trade/MaterialData.h +++ b/src/Magnum/Trade/MaterialData.h @@ -1541,10 +1541,10 @@ data is treated as immutable, you *have to* ensure the list is sorted by name. @subsection Trade-MaterialData-populating-custom Custom material attributes While attribute names beginning with uppercase letters are reserved for builtin -Magnum attributes, anything beginning with a lowercase letter can be a custom -attribute. For greater flexibility, custom attributes can be also strings or -pointers, allowing you to store arbitrary properties or direct texture pointers -instead of IDs: +Magnum attributes, anything beginning with a lowercase letter or a non-letter +can be a custom attribute. For greater flexibility, custom attributes can be +also strings or pointers, allowing you to store arbitrary properties or direct +texture pointers instead of IDs: @snippet MagnumTrade.cpp MaterialData-populating-custom From b1a719d67a06d1a4d6bc8070a0b287d8c5645fb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Thu, 13 Aug 2020 22:26:10 +0200 Subject: [PATCH 30/36] sceneconverter: list textures in --info. --- doc/changelog.dox | 2 + src/Magnum/MeshTools/sceneconverter.cpp | 64 ++++++++++++++++++++++++- 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/doc/changelog.dox b/doc/changelog.dox index 760bf2ada3..1c672a5b93 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -99,6 +99,8 @@ See also: - Added a `--bounds` option to @ref magnum-sceneconverter "magnum-sceneconverter", showing data ranges of known attributes +- @ref magnum-sceneconverter "magnum-sceneconverter" now lists also materials + and textures in `--info` @subsubsection changelog-latest-changes-trade Trade library diff --git a/src/Magnum/MeshTools/sceneconverter.cpp b/src/Magnum/MeshTools/sceneconverter.cpp index 4c94391a07..aa25e19b25 100644 --- a/src/Magnum/MeshTools/sceneconverter.cpp +++ b/src/Magnum/MeshTools/sceneconverter.cpp @@ -42,6 +42,7 @@ #include "Magnum/Trade/MaterialData.h" #include "Magnum/Trade/MeshData.h" #include "Magnum/Trade/MeshObjectData3D.h" +#include "Magnum/Trade/TextureData.h" #include "Magnum/Trade/AbstractSceneConverter.h" #include "Magnum/Trade/Implementation/converterUtilities.h" @@ -276,6 +277,13 @@ save its output; if no --converter is specified, AnySceneConverter is used.)") std::string name; }; + struct TextureInfo { + UnsignedInt texture; + UnsignedInt references; + Trade::TextureData data{{}, {}, {}, {}, {}, {}}; + std::string name; + }; + struct MeshAttributeInfo { std::size_t offset; UnsignedInt stride, arraySize; @@ -299,7 +307,8 @@ save its output; if no --converter is specified, AnySceneConverter is used.)") /* Parse everything first to avoid errors interleaved with output */ /* Scene properties. Currently just counting how much is each mesh / - material shared. */ + material shared. Texture reference count is calculated when parsing + materials. */ Containers::Array materialReferenceCount{importer->materialCount()}; Containers::Array meshReferenceCount{importer->meshCount()}; for(UnsignedInt i = 0; i != importer->object3DCount(); ++i) { @@ -316,6 +325,7 @@ save its output; if no --converter is specified, AnySceneConverter is used.)") /* Material properties */ bool error = false; Containers::Array materialInfos; + Containers::Array textureReferenceCount{importer->textureCount()}; for(UnsignedInt i = 0; i != importer->materialCount(); ++i) { Containers::Optional material; { @@ -326,6 +336,19 @@ save its output; if no --converter is specified, AnySceneConverter is used.)") } } + /* Calculate texture reference count for all properties that + look like a texture */ + for(UnsignedInt j = 0; j != material->layerCount(); ++j) { + for(UnsignedInt k = 0; k != material->attributeCount(j); ++k) { + if(material->attributeType(j, k) != Trade::MaterialAttributeType::UnsignedInt || !Utility::String::endsWith(material->attributeName(j, k), "Texture")) + continue; + + const UnsignedInt texture = material->attribute(j, k); + if(texture < textureReferenceCount.size()) + ++textureReferenceCount[texture]; + } + } + MaterialInfo info{}; info.material = i; info.name = importer->materialName(i); @@ -419,6 +442,27 @@ save its output; if no --converter is specified, AnySceneConverter is used.)") } } + /* Texture properties */ + Containers::Array textureInfos; + for(UnsignedInt i = 0; i != importer->textureCount(); ++i) { + Containers::Optional texture; + { + Duration d{importTime}; + if(!(texture = importer->texture(i))) { + error = true; + continue; + } + } + + TextureInfo info{}; + info.texture = i; + info.name = importer->textureName(i); + info.references = textureReferenceCount[i]; + info.data = *std::move(texture); + + arrayAppend(textureInfos, std::move(info)); + } + /* In case the images have all just a single level and no names, write them in a compact way without listing levels. */ bool compactImages = false; @@ -544,6 +588,24 @@ save its output; if no --converter is specified, AnySceneConverter is used.)") d << Debug::newline << " bounds:" << attribute.bounds; } } + + for(const TextureInfo& info: textureInfos) { + Debug d; + d << "Texture" << info.texture; + /* Print reference count only if there actually are some + materials, otherwise this information is useless */ + if(importer->materialCount()) + d << Utility::formatString("(referenced by {} materials)", info.references); + d << Debug::nospace << ":"; + if(!info.name.empty()) d << info.name; + d << Debug::newline; + d << " Type:" << info.data.type(); + d << "\n Minification:" << info.data.minificationFilter() << info.data.mipmapFilter(); + d << "\n Magnification:" << info.data.magnificationFilter(); + d << "\n Wrapping:" << info.data.wrapping(); + d << "\n Image:" << info.data.image(); + } + for(const Trade::Implementation::ImageInfo& info: imageInfos) { Debug d; if(info.level == 0) { From 44f67e77488d77fe7b21496a68e4fee10ad55d50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 14 Aug 2020 19:41:07 +0200 Subject: [PATCH 31/36] Trade: rethink common material texture coordinate transform/sets. There's actually a lot of code involved in checking if all textures use the same transform or coordinate set, especially when considering all fallback variants and potential future expansion with separate texture offset/scale/rotation attributes. A lot of the complexity was thus hidden in plugin implementations, which were each trying to find a common value for all textures to save the user from doing the same. All that code can now be removed and left up to the material APIs themselves -- now it's just about checking hasCommonTextureTransformation() and then retrieving that one common transformation, independently on how the material actually defines it. --- doc/changelog.dox | 14 +- doc/snippets/MagnumTrade.cpp | 33 +- src/Magnum/Trade/FlatMaterialData.h | 4 + src/Magnum/Trade/MaterialData.h | 47 +- src/Magnum/Trade/PbrClearCoatMaterialData.cpp | 70 +++ src/Magnum/Trade/PbrClearCoatMaterialData.h | 47 ++ .../PbrMetallicRoughnessMaterialData.cpp | 86 ++- .../Trade/PbrMetallicRoughnessMaterialData.h | 79 +-- .../PbrSpecularGlossinessMaterialData.cpp | 86 ++- .../Trade/PbrSpecularGlossinessMaterialData.h | 78 +-- src/Magnum/Trade/PhongMaterialData.cpp | 74 ++- src/Magnum/Trade/PhongMaterialData.h | 86 ++- src/Magnum/Trade/Test/MaterialDataTest.cpp | 488 ++++++++++++++++-- 13 files changed, 1026 insertions(+), 166 deletions(-) diff --git a/doc/changelog.dox b/doc/changelog.dox index 1c672a5b93..f86b8a614f 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -105,15 +105,17 @@ See also: @subsubsection changelog-latest-changes-trade Trade library - Recognizing TIFF file header magic in @ref Trade::AnyImageImporter "AnyImageImporter" -- Added @ref Trade::PhongMaterialData::ambientTextureMatrix(), +- Added @ref Trade::PhongMaterialData::hasCommonTextureTransformation(), + @ref Trade::PhongMaterialData::ambientTextureMatrix(), @ref Trade::PhongMaterialData::diffuseTextureMatrix(), @ref Trade::PhongMaterialData::specularTextureMatrix() and @ref Trade::PhongMaterialData::normalTextureMatrix() exposing also per-texture coordinate transformation, similarly to per-texture coordinate sets added in 2020.06 -- Added @ref Trade::PhongMaterialData::textureCoordinates() exposing a common - texture coordinate set as a complement to a per-texture property added in - 2020.06 +- Added @ref Trade::PhongMaterialData::hasCommonTextureCoordinates() and + @ref Trade::PhongMaterialData::commonTextureCoordinates() exposing a + common texture coordinate set as a complement to a per-texture property + added in 2020.06 @subsection changelog-latest-buildsystem Build system @@ -151,6 +153,10 @@ See also: and @ref Trade::PhongMaterialData::normalTextureCoordinates() "normalTextureCoordinates()" - @ref Trade::PhongMaterialData constructor is deprecated as the designated way is to populate the @ref Trade::MaterialData class directly instead. +- @cpp Trade::PhongMaterialData::textureMatrix() @ce is deprecated in favor + of @ref Trade::PhongMaterialData::hasCommonTextureTransformation() and + @ref Trade::PhongMaterialData::commonTextureMatrix(), which do proper + checking and fallback in case a per-texture transformation is present - @cpp Trade::MaterialData::type() @ce (coming from the original `AbstractMaterialData` class) is deprecated in favor of @ref Trade::MaterialData::types(), as a material data can now contain diff --git a/doc/snippets/MagnumTrade.cpp b/doc/snippets/MagnumTrade.cpp index 61eb9b3c5a..4baf08cd9c 100644 --- a/doc/snippets/MagnumTrade.cpp +++ b/doc/snippets/MagnumTrade.cpp @@ -309,29 +309,28 @@ if(data.types() & Trade::MaterialType::PbrSpecularGlossiness) { } { -/* [MaterialData-usage-packing] */ +/* [MaterialData-usage-texture-complexity] */ Trade::PbrSpecularGlossinessMaterialData data = DOXYGEN_IGNORE(Trade::PbrSpecularGlossinessMaterialData{{}, {}}); -/* Use a shader that accepts a single packed metallic/roughness texture. - Querying any texture attributes will give the same values for both metalness - and roughness. */ -if(data.hasSpecularGlossinessTexture()) { +/* Simple case for diffuse + packed specular/glossiness texture, the default + coordinate set and a common coordinate transformation for all textures */ +if(data.hasAttribute(Trade::MaterialAttribute::DiffuseTexture) && + data.hasSpecularGlossinessTexture() && + data.hasCommonTextureTransformation() && !data.hasTextureCoordinates()) +{ + UnsignedInt diffuse = data.diffuseTexture(); UnsignedInt specularGlossiness = data.specularTexture(); + Matrix3 textureMatrix = data.commonTextureMatrix(); - DOXYGEN_IGNORE(static_cast(specularGlossiness);) + DOXYGEN_IGNORE(static_cast(diffuse), static_cast(specularGlossiness), static_cast(textureMatrix);) -/* Supply texture channels separately */ -} else { - UnsignedInt specular = data.specularTexture(); - UnsignedInt glossiness = data.glossinessTexture(); - Trade::MaterialTextureSwizzle specularSwizzle = - data.specularTextureSwizzle(); - Trade::MaterialTextureSwizzle glossinessSwizzle = - data.glossinessTextureSwizzle(); +/* Extra work needed when using a non-default texture coordinate set */ +} else if(data.hasTextureCoordinates() && data.hasCommonTextureCoordinates()) { + DOXYGEN_IGNORE() - DOXYGEN_IGNORE(static_cast(specular), static_cast(glossiness), static_cast(specularSwizzle), static_cast(glossinessSwizzle);) -} -/* [MaterialData-usage-packing] */ +/* Etc... */ +} else Fatal{} << "Material too complex, giving up"; +/* [MaterialData-usage-texture-complexity] */ } { diff --git a/src/Magnum/Trade/FlatMaterialData.h b/src/Magnum/Trade/FlatMaterialData.h index eb5b4ff18c..9e52a41291 100644 --- a/src/Magnum/Trade/FlatMaterialData.h +++ b/src/Magnum/Trade/FlatMaterialData.h @@ -80,6 +80,10 @@ class MAGNUM_TRADE_EXPORT FlatMaterialData: public MaterialData { */ bool hasTextureTransformation() const; + /* Since there's just one texture, no need for any + hasCommonTextureTransformation(), hasCommonTextureCoordinates(), + commonTextureMatrix() or commonTextureCoordinates() APIs */ + /** * @brief Whether the material uses extra texture coordinate sets * diff --git a/src/Magnum/Trade/MaterialData.h b/src/Magnum/Trade/MaterialData.h index 8bd106397e..bdc1873e65 100644 --- a/src/Magnum/Trade/MaterialData.h +++ b/src/Magnum/Trade/MaterialData.h @@ -858,9 +858,15 @@ enum class MaterialAttribute: UnsignedInt { * @ref MaterialAttribute::EmissiveTextureMatrix / * @ref MaterialAttribute::LayerFactorTextureMatrix have a precedence over * this attribute for given texture, if present. - * @see @ref PhongMaterialData::textureMatrix(), - * @ref PbrMetallicRoughnessMaterialData::textureMatrix(), - * @ref PbrSpecularGlossinessMaterialData::textureMatrix() + * @see @ref PhongMaterialData::hasCommonTextureTransformation(), + * @ref PbrMetallicRoughnessMaterialData::hasCommonTextureTransformation(), + * @ref PbrSpecularGlossinessMaterialData::hasCommonTextureTransformation(), + * @ref PbrClearCoatMaterialData::hasCommonTextureTransformation(), + * @ref PhongMaterialData::commonTextureMatrix(), + * @ref PbrMetallicRoughnessMaterialData::commonTextureMatrix(), + * @ref PbrSpecularGlossinessMaterialData::commonTextureMatrix(), + * @ref PbrClearCoatMaterialData::commonTextureMatrix(), + * @ref FlatMaterialData::textureMatrix() */ TextureMatrix, @@ -879,9 +885,15 @@ enum class MaterialAttribute: UnsignedInt { * @ref MaterialAttribute::EmissiveTextureCoordinates / * @ref MaterialAttribute::LayerFactorTextureCoordinates have a precedence * over this attribute for given texture, if present. - * @see @ref PhongMaterialData::textureCoordinates(), - * @ref PbrMetallicRoughnessMaterialData::textureCoordinates(), - * @ref PbrSpecularGlossinessMaterialData::textureCoordinates() + * @see @ref PhongMaterialData::hasCommonTextureCoordinates(), + * @ref PbrMetallicRoughnessMaterialData::hasCommonTextureCoordinates(), + * @ref PbrSpecularGlossinessMaterialData::hasCommonTextureCoordinates(), + * @ref PbrClearCoatMaterialData::hasCommonTextureCoordinates(), + * @ref PhongMaterialData::commonTextureCoordinates(), + * @ref PbrMetallicRoughnessMaterialData::commonTextureCoordinates(), + * @ref PbrSpecularGlossinessMaterialData::commonTextureCoordinates(), + * @ref PbrClearCoatMaterialData::commonTextureCoordinates(), + * @ref FlatMaterialData::textureCoordinates() */ TextureCoordinates, }; @@ -1462,18 +1474,21 @@ in multiple ways: Each @ref MaterialAttribute is exposed through one or more of those convenience APIs, see the documentation of of a particular enum value for more information. -@subsection Trade-MaterialData-usage-packing Texture packing +@subsection Trade-MaterialData-usage-texture-complexity Texture packing, coordinate transformation and coordinate sets -Especially for single- and two-channel texture maps it's common to have more -than one map packed into a single texture. Such packing is expressed through -various @ref MaterialTextureSwizzle attributes such as -@ref MaterialAttribute::MetalnessTextureSwizzle. While this provides an almost -endless variability, real-world textures are in just a few common packing -schemes. For convenience these have dedicated attributes such as -@ref MaterialAttribute::SpecularGlossinessTexture and can also be checked -for with for example @ref PbrSpecularGlossinessMaterialData::hasSpecularGlossinessTexture(): +The material APIs allow for a lot of flexibility regarding --- texture maps may +be arbitrarily packed together to efficiently use all four channels, each +texture can be using a different set of texture coordinates and there can be +a different coordinate transformation for each texture. -@snippet MagnumTrade.cpp MaterialData-usage-packing +In most cases, however, real-world textures fit into a few well-known packing +schemes and usually have a common transformation and coordinate sets for all. +Checking for all corner cases on the application side would be a headache, so +there are queries like @ref PbrSpecularGlossinessMaterialData::hasSpecularGlossinessTexture() or +@ref PbrSpecularGlossinessMaterialData::hasCommonTextureTransformation() to +help narrowing the options down: + +@snippet MagnumTrade.cpp MaterialData-usage-texture-complexity @subsection Trade-MaterialData-usage-layers Material layers diff --git a/src/Magnum/Trade/PbrClearCoatMaterialData.cpp b/src/Magnum/Trade/PbrClearCoatMaterialData.cpp index ec9a6bf090..87761a2470 100644 --- a/src/Magnum/Trade/PbrClearCoatMaterialData.cpp +++ b/src/Magnum/Trade/PbrClearCoatMaterialData.cpp @@ -35,6 +35,27 @@ bool PbrClearCoatMaterialData::hasTextureTransformation() const { hasAttribute(0, MaterialAttribute::TextureMatrix); } +bool PbrClearCoatMaterialData::hasCommonTextureTransformation() const { + auto check = [](Containers::Optional& transformation, Matrix3 current) { + if(!transformation) { + transformation = current; + return true; + } + return transformation == current; + }; + + Containers::Optional transformation; + /* First one can't fail */ + if(hasAttribute(MaterialAttribute::LayerFactorTexture) && !check(transformation, layerFactorTextureMatrix())) + CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ + if(hasAttribute(MaterialAttribute::RoughnessTexture) && !check(transformation, roughnessTextureMatrix())) + return false; + if(hasAttribute(MaterialAttribute::NormalTexture) && !check(transformation, normalTextureMatrix())) + return false; + + return true; +} + bool PbrClearCoatMaterialData::hasTextureCoordinates() const { return hasAttribute(MaterialAttribute::LayerFactorTextureCoordinates) || hasAttribute(MaterialAttribute::RoughnessTextureCoordinates) || @@ -43,6 +64,27 @@ bool PbrClearCoatMaterialData::hasTextureCoordinates() const { hasAttribute(0, MaterialAttribute::TextureCoordinates); } +bool PbrClearCoatMaterialData::hasCommonTextureCoordinates() const { + auto check = [](Containers::Optional& coordinates, UnsignedInt current) { + if(!coordinates) { + coordinates = current; + return true; + } + return coordinates == current; + }; + + Containers::Optional coordinates; + /* First one can't fail */ + if(hasAttribute(MaterialAttribute::LayerFactorTexture) && !check(coordinates, layerFactorTextureCoordinates())) + CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ + if(hasAttribute(MaterialAttribute::RoughnessTexture) && !check(coordinates, roughnessTextureCoordinates())) + return false; + if(hasAttribute(MaterialAttribute::NormalTexture) && !check(coordinates, normalTextureCoordinates())) + return false; + + return true; +} + Float PbrClearCoatMaterialData::roughness() const { return attributeOr(MaterialAttribute::Roughness, 0.0f); } @@ -113,4 +155,32 @@ UnsignedInt PbrClearCoatMaterialData::normalTextureCoordinates() const { return attributeOr(0, MaterialAttribute::TextureCoordinates, 0u); } +Matrix3 PbrClearCoatMaterialData::commonTextureMatrix() const { + CORRADE_ASSERT(hasCommonTextureTransformation(), + "Trade::PbrClearCoatMaterialData::commonTextureMatrix(): the layer doesn't have a common texture coordinate transformation", {}); + if(hasAttribute(MaterialAttribute::LayerFactorTexture)) + return layerFactorTextureMatrix(); + if(hasAttribute(MaterialAttribute::RoughnessTexture)) + return roughnessTextureMatrix(); + if(hasAttribute(MaterialAttribute::NormalTexture)) + return normalTextureMatrix(); + if(Containers::Optional value = tryAttribute(MaterialAttribute::TextureMatrix)) + return *value; + return attributeOr(0, MaterialAttribute::TextureMatrix, Matrix3{}); +} + +UnsignedInt PbrClearCoatMaterialData::commonTextureCoordinates() const { + CORRADE_ASSERT(hasCommonTextureCoordinates(), + "Trade::PbrClearCoatMaterialData::commonTextureCoordinates(): the layer doesn't have a common texture coordinate set", {}); + if(hasAttribute(MaterialAttribute::LayerFactorTexture)) + return layerFactorTextureCoordinates(); + if(hasAttribute(MaterialAttribute::RoughnessTexture)) + return roughnessTextureCoordinates(); + if(hasAttribute(MaterialAttribute::NormalTexture)) + return normalTextureCoordinates(); + if(Containers::Optional value = tryAttribute(MaterialAttribute::TextureCoordinates)) + return *value; + return attributeOr(0, MaterialAttribute::TextureCoordinates, 0u); +} + }} diff --git a/src/Magnum/Trade/PbrClearCoatMaterialData.h b/src/Magnum/Trade/PbrClearCoatMaterialData.h index 454d27488b..9323581e8f 100644 --- a/src/Magnum/Trade/PbrClearCoatMaterialData.h +++ b/src/Magnum/Trade/PbrClearCoatMaterialData.h @@ -69,6 +69,18 @@ class MAGNUM_TRADE_EXPORT PbrClearCoatMaterialData: public MaterialLayerData& transformation, Matrix3 current) { + if(!transformation) { + transformation = current; + return true; + } + return transformation == current; + }; + + Containers::Optional transformation; + /* First one can't fail */ + if(hasAttribute(MaterialAttribute::BaseColorTexture) && !check(transformation, baseColorTextureMatrix())) + CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ + if(hasMetalnessTexture() && !check(transformation, metalnessTextureMatrix())) + return false; + if(hasRoughnessTexture() && !check(transformation, roughnessTextureMatrix())) + return false; + if(hasAttribute(MaterialAttribute::NormalTexture) && !check(transformation, normalTextureMatrix())) + return false; + if(hasAttribute(MaterialAttribute::OcclusionTexture) && !check(transformation, occlusionTextureMatrix())) + return false; + if(hasAttribute(MaterialAttribute::EmissiveTexture) && !check(transformation, emissiveTextureMatrix())) + return false; + + return true; +} + bool PbrMetallicRoughnessMaterialData::hasTextureCoordinates() const { return hasAttribute(MaterialAttribute::TextureCoordinates) || hasAttribute(MaterialAttribute::BaseColorTextureCoordinates) || @@ -123,6 +150,33 @@ bool PbrMetallicRoughnessMaterialData::hasTextureCoordinates() const { hasAttribute(MaterialAttribute::EmissiveTextureCoordinates); } +bool PbrMetallicRoughnessMaterialData::hasCommonTextureCoordinates() const { + auto check = [](Containers::Optional& coordinates, UnsignedInt current) { + if(!coordinates) { + coordinates = current; + return true; + } + return coordinates == current; + }; + + Containers::Optional coordinates; + /* First one can't fail */ + if(hasAttribute(MaterialAttribute::BaseColorTexture) && !check(coordinates, baseColorTextureCoordinates())) + CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ + if(hasMetalnessTexture() && !check(coordinates, metalnessTextureCoordinates())) + return false; + if(hasRoughnessTexture() && !check(coordinates, roughnessTextureCoordinates())) + return false; + if(hasAttribute(MaterialAttribute::NormalTexture) && !check(coordinates, normalTextureCoordinates())) + return false; + if(hasAttribute(MaterialAttribute::OcclusionTexture) && !check(coordinates, occlusionTextureCoordinates())) + return false; + if(hasAttribute(MaterialAttribute::EmissiveTexture) && !check(coordinates, emissiveTextureCoordinates())) + return false; + + return true; +} + Color4 PbrMetallicRoughnessMaterialData::baseColor() const { return attributeOr(MaterialAttribute::BaseColor, 0xffffffff_rgbaf); } @@ -311,11 +365,39 @@ UnsignedInt PbrMetallicRoughnessMaterialData::emissiveTextureCoordinates() const return attributeOr(MaterialAttribute::TextureCoordinates, 0u); } -Matrix3 PbrMetallicRoughnessMaterialData::textureMatrix() const { +Matrix3 PbrMetallicRoughnessMaterialData::commonTextureMatrix() const { + CORRADE_ASSERT(hasCommonTextureTransformation(), + "Trade::PbrMetallicRoughnessMaterialData::commonTextureMatrix(): the material doesn't have a common texture coordinate transformation", {}); + if(hasAttribute(MaterialAttribute::BaseColorTexture)) + return baseColorTextureMatrix(); + if(hasMetalnessTexture()) + return metalnessTextureMatrix(); + if(hasRoughnessTexture()) + return roughnessTextureMatrix(); + if(hasAttribute(MaterialAttribute::NormalTexture)) + return normalTextureMatrix(); + if(hasAttribute(MaterialAttribute::OcclusionTexture)) + return occlusionTextureMatrix(); + if(hasAttribute(MaterialAttribute::EmissiveTexture)) + return emissiveTextureMatrix(); return attributeOr(MaterialAttribute::TextureMatrix, Matrix3{}); } -UnsignedInt PbrMetallicRoughnessMaterialData::textureCoordinates() const { +UnsignedInt PbrMetallicRoughnessMaterialData::commonTextureCoordinates() const { + CORRADE_ASSERT(hasCommonTextureCoordinates(), + "Trade::PbrMetallicRoughnessMaterialData::commonTextureCoordinates(): the material doesn't have a common texture coordinate set", {}); + if(hasAttribute(MaterialAttribute::BaseColorTexture)) + return baseColorTextureCoordinates(); + if(hasMetalnessTexture()) + return metalnessTextureCoordinates(); + if(hasRoughnessTexture()) + return roughnessTextureCoordinates(); + if(hasAttribute(MaterialAttribute::NormalTexture)) + return normalTextureCoordinates(); + if(hasAttribute(MaterialAttribute::OcclusionTexture)) + return occlusionTextureCoordinates(); + if(hasAttribute(MaterialAttribute::EmissiveTexture)) + return emissiveTextureCoordinates(); return attributeOr(MaterialAttribute::TextureCoordinates, 0u); } diff --git a/src/Magnum/Trade/PbrMetallicRoughnessMaterialData.h b/src/Magnum/Trade/PbrMetallicRoughnessMaterialData.h index 0d5bd67d16..328212ed32 100644 --- a/src/Magnum/Trade/PbrMetallicRoughnessMaterialData.h +++ b/src/Magnum/Trade/PbrMetallicRoughnessMaterialData.h @@ -246,9 +246,23 @@ class MAGNUM_TRADE_EXPORT PbrMetallicRoughnessMaterialData: public MaterialData * @ref MaterialAttribute::EmissiveTextureMatrix or * @ref MaterialAttribute::TextureMatrix attributes is present, * @cpp false @ce otherwise. + * @see @ref hasCommonTextureTransformation() */ bool hasTextureTransformation() const; + /** + * @brief Whether the material has a common transformation for all textures + * + * Returns @cpp true @ce if, for each texture that is present, + * @ref baseColorTextureMatrix(), @ref metalnessTextureMatrix(), + * @ref roughnessTextureMatrix(), @ref normalTextureMatrix(), + * @ref occlusionTextureMatrix() and @ref emissiveTextureMatrix() have + * the same value, @cpp false @ce otherwise. In particular, returns + * @cpp true @ce also if there's no texture transformation at all. Use + * @ref hasTextureTransformation() to distinguish that case. + */ + bool hasCommonTextureTransformation() const; + /** * @brief Whether the material uses extra texture coordinate sets * @@ -261,9 +275,24 @@ class MAGNUM_TRADE_EXPORT PbrMetallicRoughnessMaterialData: public MaterialData * @ref MaterialAttribute::EmissiveTextureCoordinates or * @ref MaterialAttribute::TextureCoordinates attributes is present and * has a non-zero value, @cpp false @ce otherwise. + * @see @ref hasCommonTextureCoordinates() */ bool hasTextureCoordinates() const; + /** + * @brief Whether the material has a common coordinate set for all textures + * + * Returns @cpp true @ce if, for each texture that is present, + * @ref baseColorTextureCoordinates(), @ref metalnessTextureCoordinates(), + * @ref roughnessTextureCoordinates(), @ref normalTextureCoordinates(), + * @ref occlusionTextureCoordinates() and @ref emissiveTextureCoordinates() + * have the same value, @cpp false @ce otherwise. In particular, + * returns @cpp true @ce also if there's no extra texture coordinate + * set used at all. Use @ref hasTextureCoordinates() to distinguish + * that case. + */ + bool hasCommonTextureCoordinates() const; + /** * @brief Base color * @@ -570,44 +599,28 @@ class MAGNUM_TRADE_EXPORT PbrMetallicRoughnessMaterialData: public MaterialData /** * @brief Common texture coordinate transformation matrix for all textures * - * Convenience access to the @ref MaterialAttribute::TextureMatrix - * attribute. If not present, the default is an identity matrix. Note - * that the material may also define per-texture transformation using - * the @ref MaterialAttribute::BaseColorTextureMatrix, - * @ref MaterialAttribute::MetalnessTextureMatrix, - * @ref MaterialAttribute::RoughnessTextureMatrix, - * @ref MaterialAttribute::NormalTextureMatrix, - * @ref MaterialAttribute::OcclusionTextureMatrix and - * @ref MaterialAttribute::EmissiveTextureMatrix attributes, which then - * take precedence over the common one. - * @see @ref hasAttribute(), @ref baseColorTextureMatrix(), - * @ref metalnessTextureMatrix(), @ref roughnessTextureMatrix(), - * @ref normalTextureMatrix(), @ref occlusionTextureMatrix(), - * @ref emissiveTextureMatrix() + * Expects that @ref hasCommonTextureTransformation() is @cpp true @ce; + * returns a coordinate set index that's the same for all of + * @ref baseColorTextureMatrix(), @ref metalnessTextureMatrix(), + * @ref roughnessTextureMatrix(), @ref normalTextureMatrix(), + * @ref occlusionTextureMatrix() and @ref emissiveTextureMatrix() where + * a texture is present. If no texture is present, returns an identity + * matrix. */ - Matrix3 textureMatrix() const; + Matrix3 commonTextureMatrix() const; /** * @brief Common texture coordinate set index for all textures * - * Convenience access to the @ref MaterialAttribute::TextureCoordinates - * attribute. If not present, the default is @cpp 0 @ce. Note that the - * material may also define per-texture coordinate set using the - * @ref MaterialAttribute::BaseColorTextureCoordinates, - * @ref MaterialAttribute::MetalnessTextureCoordinates, - * @ref MaterialAttribute::RoughnessTextureCoordinates, - * @ref MaterialAttribute::NormalTextureCoordinates, - * @ref MaterialAttribute::OcclusionTextureCoordinates and - * @ref MaterialAttribute::EmissiveTextureCoordinates attributes, which - * then take precedence over the common one. - * @see @ref hasAttribute(), @ref baseColorTextureCoordinates(), - * @ref metalnessTextureCoordinates(), - * @ref roughnessTextureCoordinates(), - * @ref normalTextureCoordinates(), - * @ref occlusionTextureCoordinates(), - * @ref emissiveTextureCoordinates() - */ - UnsignedInt textureCoordinates() const; + * Expects that @ref hasCommonTextureCoordinates() is @cpp true @ce; + * returns a coordinate set index that's the same for all of + * @ref baseColorTextureCoordinates(), @ref metalnessTextureCoordinates(), + * @ref roughnessTextureCoordinates(), @ref normalTextureCoordinates(), + * @ref occlusionTextureCoordinates() and @ref emissiveTextureCoordinates() + * where a texture is present. If no texture is present, returns + * @cpp 0 @ce. + */ + UnsignedInt commonTextureCoordinates() const; }; }} diff --git a/src/Magnum/Trade/PbrSpecularGlossinessMaterialData.cpp b/src/Magnum/Trade/PbrSpecularGlossinessMaterialData.cpp index d137882c14..083cdc1715 100644 --- a/src/Magnum/Trade/PbrSpecularGlossinessMaterialData.cpp +++ b/src/Magnum/Trade/PbrSpecularGlossinessMaterialData.cpp @@ -62,6 +62,33 @@ bool PbrSpecularGlossinessMaterialData::hasTextureTransformation() const { hasAttribute(MaterialAttribute::EmissiveTextureMatrix); } +bool PbrSpecularGlossinessMaterialData::hasCommonTextureTransformation() const { + auto check = [](Containers::Optional& transformation, Matrix3 current) { + if(!transformation) { + transformation = current; + return true; + } + return transformation == current; + }; + + Containers::Optional transformation; + /* First one can't fail */ + if(hasAttribute(MaterialAttribute::DiffuseTexture) && !check(transformation, diffuseTextureMatrix())) + CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ + if(hasSpecularTexture() && !check(transformation, specularTextureMatrix())) + return false; + if(hasGlossinessTexture() && !check(transformation, glossinessTextureMatrix())) + return false; + if(hasAttribute(MaterialAttribute::NormalTexture) && !check(transformation, normalTextureMatrix())) + return false; + if(hasAttribute(MaterialAttribute::OcclusionTexture) && !check(transformation, occlusionTextureMatrix())) + return false; + if(hasAttribute(MaterialAttribute::EmissiveTexture) && !check(transformation, emissiveTextureMatrix())) + return false; + + return true; +} + bool PbrSpecularGlossinessMaterialData::hasTextureCoordinates() const { return hasAttribute(MaterialAttribute::TextureCoordinates) || hasAttribute(MaterialAttribute::DiffuseTextureCoordinates) || @@ -72,6 +99,33 @@ bool PbrSpecularGlossinessMaterialData::hasTextureCoordinates() const { hasAttribute(MaterialAttribute::EmissiveTextureCoordinates); } +bool PbrSpecularGlossinessMaterialData::hasCommonTextureCoordinates() const { + auto check = [](Containers::Optional& coordinates, UnsignedInt current) { + if(!coordinates) { + coordinates = current; + return true; + } + return coordinates == current; + }; + + Containers::Optional coordinates; + /* First one can't fail */ + if(hasAttribute(MaterialAttribute::DiffuseTexture) && !check(coordinates, diffuseTextureCoordinates())) + CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ + if(hasSpecularTexture() && !check(coordinates, specularTextureCoordinates())) + return false; + if(hasGlossinessTexture() && !check(coordinates, glossinessTextureCoordinates())) + return false; + if(hasAttribute(MaterialAttribute::NormalTexture) && !check(coordinates, normalTextureCoordinates())) + return false; + if(hasAttribute(MaterialAttribute::OcclusionTexture) && !check(coordinates, occlusionTextureCoordinates())) + return false; + if(hasAttribute(MaterialAttribute::EmissiveTexture) && !check(coordinates, emissiveTextureCoordinates())) + return false; + + return true; +} + Color4 PbrSpecularGlossinessMaterialData::diffuseColor() const { return attributeOr(MaterialAttribute::DiffuseColor, 0xffffffff_srgbaf); } @@ -260,11 +314,39 @@ UnsignedInt PbrSpecularGlossinessMaterialData::emissiveTextureCoordinates() cons return attributeOr(MaterialAttribute::TextureCoordinates, 0u); } -Matrix3 PbrSpecularGlossinessMaterialData::textureMatrix() const { +Matrix3 PbrSpecularGlossinessMaterialData::commonTextureMatrix() const { + CORRADE_ASSERT(hasCommonTextureTransformation(), + "Trade::PbrSpecularGlossinessMaterialData::commonTextureMatrix(): the material doesn't have a common texture coordinate transformation", {}); + if(hasAttribute(MaterialAttribute::DiffuseTexture)) + return diffuseTextureMatrix(); + if(hasSpecularTexture()) + return specularTextureMatrix(); + if(hasGlossinessTexture()) + return glossinessTextureMatrix(); + if(hasAttribute(MaterialAttribute::NormalTexture)) + return normalTextureMatrix(); + if(hasAttribute(MaterialAttribute::OcclusionTexture)) + return occlusionTextureMatrix(); + if(hasAttribute(MaterialAttribute::EmissiveTexture)) + return emissiveTextureMatrix(); return attributeOr(MaterialAttribute::TextureMatrix, Matrix3{}); } -UnsignedInt PbrSpecularGlossinessMaterialData::textureCoordinates() const { +UnsignedInt PbrSpecularGlossinessMaterialData::commonTextureCoordinates() const { + CORRADE_ASSERT(hasCommonTextureCoordinates(), + "Trade::PbrSpecularGlossinessMaterialData::commonTextureCoordinates(): the material doesn't have a common texture coordinate set", {}); + if(hasAttribute(MaterialAttribute::DiffuseTexture)) + return diffuseTextureCoordinates(); + if(hasSpecularTexture()) + return specularTextureCoordinates(); + if(hasGlossinessTexture()) + return glossinessTextureCoordinates(); + if(hasAttribute(MaterialAttribute::NormalTexture)) + return normalTextureCoordinates(); + if(hasAttribute(MaterialAttribute::OcclusionTexture)) + return occlusionTextureCoordinates(); + if(hasAttribute(MaterialAttribute::EmissiveTexture)) + return emissiveTextureCoordinates(); return attributeOr(MaterialAttribute::TextureCoordinates, 0u); } diff --git a/src/Magnum/Trade/PbrSpecularGlossinessMaterialData.h b/src/Magnum/Trade/PbrSpecularGlossinessMaterialData.h index 753bae0ae3..39b6ec6f3f 100644 --- a/src/Magnum/Trade/PbrSpecularGlossinessMaterialData.h +++ b/src/Magnum/Trade/PbrSpecularGlossinessMaterialData.h @@ -116,9 +116,23 @@ class MAGNUM_TRADE_EXPORT PbrSpecularGlossinessMaterialData: public MaterialData * @ref MaterialAttribute::EmissiveTextureMatrix or * @ref MaterialAttribute::TextureMatrix attributes is present, * @cpp false @ce otherwise. + * @see @ref hasCommonTextureTransformation() */ bool hasTextureTransformation() const; + /** + * @brief Whether the material has a common transformation for all textures + * + * Returns @cpp true @ce if, for each texture that is present, + * @ref diffuseTextureMatrix(), @ref specularTextureMatrix(), + * @ref glossinessTextureMatrix(), @ref normalTextureMatrix(), + * @ref occlusionTextureMatrix() and @ref emissiveTextureMatrix() have + * the same value, @cpp false @ce otherwise. In particular, returns + * @cpp true @ce also if there's no texture transformation at all. Use + * @ref hasTextureTransformation() to distinguish that case. + */ + bool hasCommonTextureTransformation() const; + /** * @brief Whether the material uses extra texture coordinate sets * @@ -134,6 +148,20 @@ class MAGNUM_TRADE_EXPORT PbrSpecularGlossinessMaterialData: public MaterialData */ bool hasTextureCoordinates() const; + /** + * @brief Whether the material has a common coordinate set for all textures + * + * Returns @cpp true @ce if, for each texture that is present, + * @ref diffuseTextureCoordinates(), @ref specularTextureCoordinates(), + * @ref glossinessTextureCoordinates(), @ref normalTextureCoordinates(), + * @ref occlusionTextureCoordinates() and @ref emissiveTextureCoordinates() + * have the same value, @cpp false @ce otherwise. In particular, + * returns @cpp true @ce also if there's no extra texture coordinate + * set used at all. Use @ref hasTextureCoordinates() to distinguish + * that case. + */ + bool hasCommonTextureCoordinates() const; + /** * @brief Base color * @@ -439,44 +467,28 @@ class MAGNUM_TRADE_EXPORT PbrSpecularGlossinessMaterialData: public MaterialData /** * @brief Common texture coordinate transformation matrix for all textures * - * Convenience access to the @ref MaterialAttribute::TextureMatrix - * attribute. If not present, the default is an identity matrix. Note - * that the material may also define per-texture transformation using - * the @ref MaterialAttribute::DiffuseTextureMatrix, - * @ref MaterialAttribute::SpecularTextureMatrix, - * @ref MaterialAttribute::GlossinessTextureMatrix, - * @ref MaterialAttribute::NormalTextureMatrix, - * @ref MaterialAttribute::OcclusionTextureMatrix and - * @ref MaterialAttribute::EmissiveTextureMatrix attributes, which then - * take precedence over the common one. - * @see @ref hasAttribute(), @ref diffuseTextureMatrix(), - * @ref specularTextureMatrix(), @ref glossinessTextureMatrix(), - * @ref normalTextureMatrix(), @ref occlusionTextureMatrix(), - * @ref emissiveTextureMatrix() + * Expects that @ref hasCommonTextureTransformation() is @cpp true @ce; + * returns a coordinate set index that's the same for all of + * @ref diffuseTextureMatrix(), @ref specularTextureMatrix(), + * @ref glossinessTextureMatrix(), @ref normalTextureMatrix(), + * @ref occlusionTextureMatrix() and @ref emissiveTextureMatrix() where + * a texture is present. If no texture is present, returns an identity + * matrix. */ - Matrix3 textureMatrix() const; + Matrix3 commonTextureMatrix() const; /** * @brief Common texture coordinate set index for all textures * - * Convenience access to the @ref MaterialAttribute::TextureCoordinates - * attribute. If not present, the default is @cpp 0 @ce. Note that the - * material may also define per-texture coordinate set using the - * @ref MaterialAttribute::DiffuseTextureCoordinates, - * @ref MaterialAttribute::SpecularTextureCoordinates, - * @ref MaterialAttribute::GlossinessTextureCoordinates, - * @ref MaterialAttribute::NormalTextureCoordinates, - * @ref MaterialAttribute::OcclusionTextureCoordinates and - * @ref MaterialAttribute::EmissiveTextureCoordinates attributes, which - * then take precedence over the common one. - * @see @ref hasAttribute(), @ref diffuseTextureCoordinates(), - * @ref specularTextureCoordinates(), - * @ref glossinessTextureCoordinates(), - * @ref normalTextureCoordinates(), - * @ref occlusionTextureCoordinates(), - * @ref emissiveTextureCoordinates() - */ - UnsignedInt textureCoordinates() const; + * Expects that @ref hasCommonTextureCoordinates() is @cpp true @ce; + * returns a coordinate set index that's the same for all of + * @ref diffuseTextureCoordinates(), @ref specularTextureCoordinates(), + * @ref glossinessTextureCoordinates(), @ref normalTextureCoordinates(), + * @ref occlusionTextureCoordinates() and @ref emissiveTextureCoordinates() + * where a texture is present. If no texture is present, returns + * @cpp 0 @ce. + */ + UnsignedInt commonTextureCoordinates() const; }; }} diff --git a/src/Magnum/Trade/PhongMaterialData.cpp b/src/Magnum/Trade/PhongMaterialData.cpp index 8e8a5f929f..0447862562 100644 --- a/src/Magnum/Trade/PhongMaterialData.cpp +++ b/src/Magnum/Trade/PhongMaterialData.cpp @@ -142,6 +142,29 @@ bool PhongMaterialData::hasTextureTransformation() const { hasAttribute(MaterialAttribute::TextureMatrix); } +bool PhongMaterialData::hasCommonTextureTransformation() const { + auto check = [](Containers::Optional& transformation, Matrix3 current) { + if(!transformation) { + transformation = current; + return true; + } + return transformation == current; + }; + + Containers::Optional transformation; + /* First one can't fail */ + if(hasAttribute(MaterialAttribute::AmbientTexture) && !check(transformation, ambientTextureMatrix())) + CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ + if(hasAttribute(MaterialAttribute::DiffuseTexture) && !check(transformation, diffuseTextureMatrix())) + return false; + if(hasSpecularTexture() && !check(transformation, specularTextureMatrix())) + return false; + if(hasAttribute(MaterialAttribute::NormalTexture) && !check(transformation, normalTextureMatrix())) + return false; + + return true; +} + bool PhongMaterialData::hasTextureCoordinates() const { return attributeOr(MaterialAttribute::AmbientTextureCoordinates, 0u) || attributeOr(MaterialAttribute::DiffuseTextureCoordinates, 0u) || @@ -150,6 +173,29 @@ bool PhongMaterialData::hasTextureCoordinates() const { attributeOr(MaterialAttribute::TextureCoordinates, 0u); } +bool PhongMaterialData::hasCommonTextureCoordinates() const { + auto check = [](Containers::Optional& coordinates, UnsignedInt current) { + if(!coordinates) { + coordinates = current; + return true; + } + return coordinates == current; + }; + + Containers::Optional coordinates; + /* First one can't fail */ + if(hasAttribute(MaterialAttribute::AmbientTexture) && !check(coordinates, ambientTextureCoordinates())) + CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ + if(hasAttribute(MaterialAttribute::DiffuseTexture) && !check(coordinates, diffuseTextureCoordinates())) + return false; + if(hasSpecularTexture() && !check(coordinates, specularTextureCoordinates())) + return false; + if(hasAttribute(MaterialAttribute::NormalTexture) && !check(coordinates, normalTextureCoordinates())) + return false; + + return true; +} + Color4 PhongMaterialData::ambientColor() const { return attributeOr(MaterialAttribute::AmbientColor, hasAttribute(MaterialAttribute::AmbientTexture) ? 0xffffffff_rgbaf : 0x000000ff_rgbaf); @@ -269,11 +315,37 @@ UnsignedInt PhongMaterialData::normalTextureCoordinates() const { return attributeOr(MaterialAttribute::TextureCoordinates, 0u); } +Matrix3 PhongMaterialData::commonTextureMatrix() const { + CORRADE_ASSERT(hasCommonTextureTransformation(), + "Trade::PhongMaterialData::commonTextureMatrix(): the material doesn't have a common texture coordinate transformation", {}); + if(hasAttribute(MaterialAttribute::AmbientTexture)) + return ambientTextureMatrix(); + if(hasAttribute(MaterialAttribute::DiffuseTexture)) + return diffuseTextureMatrix(); + if(hasSpecularTexture()) + return specularTextureMatrix(); + if(hasAttribute(MaterialAttribute::NormalTexture)) + return normalTextureMatrix(); + return attributeOr(MaterialAttribute::TextureMatrix, Matrix3{}); +} + +#ifdef MAGNUM_BUILD_DEPRECATED Matrix3 PhongMaterialData::textureMatrix() const { return attributeOr(MaterialAttribute::TextureMatrix, Matrix3{}); } +#endif -UnsignedInt PhongMaterialData::textureCoordinates() const { +UnsignedInt PhongMaterialData::commonTextureCoordinates() const { + CORRADE_ASSERT(hasCommonTextureCoordinates(), + "Trade::PhongMaterialData::commonTextureCoordinates(): the material doesn't have a common texture coordinate set", {}); + if(hasAttribute(MaterialAttribute::AmbientTexture)) + return ambientTextureCoordinates(); + if(hasAttribute(MaterialAttribute::DiffuseTexture)) + return diffuseTextureCoordinates(); + if(hasSpecularTexture()) + return specularTextureCoordinates(); + if(hasAttribute(MaterialAttribute::NormalTexture)) + return normalTextureCoordinates(); return attributeOr(MaterialAttribute::TextureCoordinates, 0u); } diff --git a/src/Magnum/Trade/PhongMaterialData.h b/src/Magnum/Trade/PhongMaterialData.h index 695f6daa02..0d265908fe 100644 --- a/src/Magnum/Trade/PhongMaterialData.h +++ b/src/Magnum/Trade/PhongMaterialData.h @@ -253,9 +253,23 @@ class MAGNUM_TRADE_EXPORT PhongMaterialData: public MaterialData { * @ref MaterialAttribute::NormalTextureMatrix or * @ref MaterialAttribute::TextureMatrix attributes is present, * @cpp false @ce otherwise. + * @see @ref hasCommonTextureTransformation() */ bool hasTextureTransformation() const; + /** + * @brief Whether the material has a common transformation for all textures + * @m_since_latest + * + * Returns @cpp true @ce if, for each texture that is present, + * @ref ambientTextureMatrix(), @ref diffuseTextureMatrix(), + * @ref specularTextureMatrix() and @ref normalTextureMatrix() have the + * same value, @cpp false @ce otherwise. In particular, returns + * @cpp true @ce also if there's no texture transformation at all. Use + * @ref hasTextureTransformation() to distinguish that case. + */ + bool hasCommonTextureTransformation() const; + /** * @brief Whether the material uses extra texture coordinate sets * @m_since_latest @@ -267,9 +281,24 @@ class MAGNUM_TRADE_EXPORT PhongMaterialData: public MaterialData { * @ref MaterialAttribute::NormalTextureCoordinates or * @ref MaterialAttribute::TextureCoordinates attributes is present and * has a non-zero value, @cpp false @ce otherwise. + * @see @ref hasCommonTextureCoordinates() */ bool hasTextureCoordinates() const; + /** + * @brief Whether the material has a common coordinate set for all textures + * @m_since_latest + * + * Returns @cpp true @ce if, for each texture that is present, + * @ref ambientTextureCoordinates(), @ref diffuseTextureCoordinates(), + * @ref specularTextureCoordinates() and @ref normalTextureCoordinates() + * have the same value, @cpp false @ce otherwise. In particular, + * returns @cpp true @ce also if there's no extra texture coordinate + * set used at all. Use @ref hasTextureCoordinates() to distinguish + * that case. + */ + bool hasCommonTextureCoordinates() const; + #ifdef MAGNUM_BUILD_DEPRECATED /** * @brief Material flags @@ -540,40 +569,43 @@ class MAGNUM_TRADE_EXPORT PhongMaterialData: public MaterialData { /** * @brief Common texture coordinate transformation matrix for all textures - * @m_since{2020,06} + * @m_since_latest * - * Convenience access to the @ref MaterialAttribute::TextureMatrix - * attribute. If not present, the default is an identity matrix. Note - * that the material may also define per-texture transformation using - * the @ref MaterialAttribute::AmbientTextureMatrix, - * @ref MaterialAttribute::DiffuseTextureMatrix, - * @ref MaterialAttribute::SpecularTextureMatrix and - * @ref MaterialAttribute::NormalTextureMatrix attributes, which then - * take precedence over the common one. - * @see @ref hasAttribute(), @ref ambientTextureMatrix(), - * @ref diffuseTextureMatrix(), @ref specularTextureMatrix(), - * @ref normalTextureMatrix() + * Expects that @ref hasCommonTextureTransformation() is @cpp true @ce; + * returns a coordinate set index that's the same for all of + * @ref ambientTextureMatrix(), @ref diffuseTextureMatrix(), + * @ref specularTextureMatrix() and @ref normalTextureMatrix() where a + * texture is present. If no texture is present, returns an identity + * matrix. */ - Matrix3 textureMatrix() const; + Matrix3 commonTextureMatrix() const; + + #ifdef MAGNUM_BUILD_DEPRECATED + /** + * @brief Common texture coordinate transformation matrix for all textures + * @m_deprecated_since_latest Because the material may now also define + * per-texture transformations, which take precedence over the + * common one, this value is misleading. Use either + * @ref commonTextureMatrix() or separate + * @ref ambientTextureMatrix(), @ref diffuseTextureMatrix(), + * @ref specularTextureMatrix() and @ref normalTextureMatrix() + * accessors instead. + */ + CORRADE_DEPRECATED("use commonTextureMatrix() or per-texture accessors instead") Matrix3 textureMatrix() const; + #endif /** * @brief Common texture coordinate set index for all textures * @m_since_latest * - * Convenience access to the @ref MaterialAttribute::TextureCoordinates - * attribute. If not present, the default is @cpp 0 @ce. Note that the - * material may also define per-texture coordinate set using the - * @ref MaterialAttribute::AmbientTextureCoordinates, - * @ref MaterialAttribute::DiffuseTextureCoordinates, - * @ref MaterialAttribute::SpecularTextureCoordinates and - * @ref MaterialAttribute::NormalTextureCoordinates attributes, which - * then take precedence over the common one. - * @see @ref hasAttribute(), @ref ambientTextureCoordinates(), - * @ref diffuseTextureCoordinates(), - * @ref specularTextureCoordinates(), - * @ref normalTextureCoordinates() - */ - UnsignedInt textureCoordinates() const; + * Expects that @ref hasCommonTextureCoordinates() is @cpp true @ce; + * returns a coordinate set index that's the same for all of + * @ref ambientTextureCoordinates(), @ref diffuseTextureCoordinates(), + * @ref specularTextureCoordinates() and @ref normalTextureCoordinates() + * where a texture is present. If no texture is present, returns + * @cpp 0 @ce. + */ + UnsignedInt commonTextureCoordinates() const; /** * @brief Shininess diff --git a/src/Magnum/Trade/Test/MaterialDataTest.cpp b/src/Magnum/Trade/Test/MaterialDataTest.cpp index 51fa51bd41..62bce1c655 100644 --- a/src/Magnum/Trade/Test/MaterialDataTest.cpp +++ b/src/Magnum/Trade/Test/MaterialDataTest.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -159,6 +160,10 @@ class MaterialDataTest: public TestSuite::Tester { void pbrMetallicRoughnessAccessTexturedExplicitPackedNormalRoughnessMetallic(); void pbrMetallicRoughnessAccessTexturedSingleMatrixCoordinates(); void pbrMetallicRoughnessAccessInvalidTextures(); + void pbrMetallicRoughnessAccessCommonTransformationCoordinatesNoTextures(); + void pbrMetallicRoughnessAccessCommonTransformationCoordinatesOneTexture(); + void pbrMetallicRoughnessAccessCommonTransformationCoordinatesOneDifferentTexture(); + void pbrMetallicRoughnessAccessNoCommonTransformationCoordinates(); void pbrSpecularGlossinessAccess(); void pbrSpecularGlossinessAccessDefaults(); @@ -168,6 +173,10 @@ class MaterialDataTest: public TestSuite::Tester { void pbrSpecularGlossinessAccessTexturedExplicitPackedSpecularGlossiness(); void pbrSpecularGlossinessAccessTexturedSingleMatrixCoordinates(); void pbrSpecularGlossinessAccessInvalidTextures(); + void pbrSpecularGlossinessAccessCommonTransformationCoordinatesNoTextures(); + void pbrSpecularGlossinessAccessCommonTransformationCoordinatesOneTexture(); + void pbrSpecularGlossinessAccessCommonTransformationCoordinatesOneDifferentTexture(); + void pbrSpecularGlossinessAccessNoCommonTransformationCoordinates(); void phongAccess(); void phongAccessDefaults(); @@ -176,6 +185,10 @@ class MaterialDataTest: public TestSuite::Tester { void phongAccessTexturedSingleMatrixCoordinates(); void phongAccessTexturedImplicitPackedSpecularGlossiness(); void phongAccessInvalidTextures(); + void phongAccessCommonTransformationCoordinatesNoTextures(); + void phongAccessCommonTransformationCoordinatesOneTexture(); + void phongAccessCommonTransformationCoordinatesOneDifferentTexture(); + void phongAccessNoCommonTransformationCoordinates(); void flatAccessBaseColor(); void flatAccessDiffuseColor(); @@ -197,6 +210,10 @@ class MaterialDataTest: public TestSuite::Tester { void pbrClearCoatAccessTexturedSingleMatrixCoordinates(); void pbrClearCoatAccessTexturedBaseMaterialMatrixCoordinates(); void pbrClearCoatAccessInvalidTextures(); + void pbrClearCoatAccessCommonTransformationCoordinatesNoTextures(); + void pbrClearCoatAccessCommonTransformationCoordinatesOneTexture(); + void pbrClearCoatAccessCommonTransformationCoordinatesOneDifferentTexture(); + void pbrClearCoatAccessNoCommonTransformationCoordinates(); void debugLayer(); void debugAttribute(); @@ -217,6 +234,37 @@ class MaterialDataTest: public TestSuite::Tester { #endif }; +const Containers::StringView PbrMetallicRoughnessTextureData[] { + "BaseColorTexture", + "MetalnessTexture", + "RoughnessTexture", + "NormalTexture", + "OcclusionTexture", + "EmissiveTexture" +}; + +const Containers::StringView PbrSpecularGlossinessTextureData[] { + "DiffuseTexture", + "SpecularTexture", + "GlossinessTexture", + "NormalTexture", + "OcclusionTexture", + "EmissiveTexture" +}; + +const Containers::StringView PhongTextureData[] { + "AmbientTexture", + "DiffuseTexture", + "SpecularTexture", + "NormalTexture" +}; + +const Containers::StringView PbrClearCoatTextureData[] { + "LayerFactorTexture", + "RoughnessTexture", + "NormalTexture" +}; + MaterialDataTest::MaterialDataTest() { addTests({&MaterialDataTest::textureSwizzleComponentCount, @@ -358,6 +406,14 @@ MaterialDataTest::MaterialDataTest() { &MaterialDataTest::pbrMetallicRoughnessAccessTexturedExplicitPackedNormalRoughnessMetallic, &MaterialDataTest::pbrMetallicRoughnessAccessTexturedSingleMatrixCoordinates, &MaterialDataTest::pbrMetallicRoughnessAccessInvalidTextures, + &MaterialDataTest::pbrMetallicRoughnessAccessCommonTransformationCoordinatesNoTextures}); + + addInstancedTests({ + &MaterialDataTest::pbrMetallicRoughnessAccessCommonTransformationCoordinatesOneTexture, + &MaterialDataTest::pbrMetallicRoughnessAccessCommonTransformationCoordinatesOneDifferentTexture}, + Containers::arraySize(PbrMetallicRoughnessTextureData)); + + addTests({&MaterialDataTest::pbrMetallicRoughnessAccessNoCommonTransformationCoordinates, &MaterialDataTest::pbrSpecularGlossinessAccess, &MaterialDataTest::pbrSpecularGlossinessAccessDefaults, @@ -367,6 +423,14 @@ MaterialDataTest::MaterialDataTest() { &MaterialDataTest::pbrSpecularGlossinessAccessTexturedExplicitPackedSpecularGlossiness, &MaterialDataTest::pbrSpecularGlossinessAccessTexturedSingleMatrixCoordinates, &MaterialDataTest::pbrSpecularGlossinessAccessInvalidTextures, + &MaterialDataTest::pbrSpecularGlossinessAccessCommonTransformationCoordinatesNoTextures}); + + addInstancedTests({ + &MaterialDataTest::pbrSpecularGlossinessAccessCommonTransformationCoordinatesOneTexture, + &MaterialDataTest::pbrSpecularGlossinessAccessCommonTransformationCoordinatesOneDifferentTexture}, + Containers::arraySize(PbrSpecularGlossinessTextureData)); + + addTests({&MaterialDataTest::pbrSpecularGlossinessAccessNoCommonTransformationCoordinates, &MaterialDataTest::phongAccess, &MaterialDataTest::phongAccessDefaults, @@ -375,6 +439,14 @@ MaterialDataTest::MaterialDataTest() { &MaterialDataTest::phongAccessTexturedSingleMatrixCoordinates, &MaterialDataTest::phongAccessTexturedImplicitPackedSpecularGlossiness, &MaterialDataTest::phongAccessInvalidTextures, + &MaterialDataTest::phongAccessCommonTransformationCoordinatesNoTextures}); + + addInstancedTests({ + &MaterialDataTest::phongAccessCommonTransformationCoordinatesOneTexture, + &MaterialDataTest::phongAccessCommonTransformationCoordinatesOneDifferentTexture}, + Containers::arraySize(PhongTextureData)); + + addTests({&MaterialDataTest::phongAccessNoCommonTransformationCoordinates, &MaterialDataTest::flatAccessBaseColor, &MaterialDataTest::flatAccessDiffuseColor, @@ -396,6 +468,14 @@ MaterialDataTest::MaterialDataTest() { &MaterialDataTest::pbrClearCoatAccessTexturedSingleMatrixCoordinates, &MaterialDataTest::pbrClearCoatAccessTexturedBaseMaterialMatrixCoordinates, &MaterialDataTest::pbrClearCoatAccessInvalidTextures, + &MaterialDataTest::pbrClearCoatAccessCommonTransformationCoordinatesNoTextures}); + + addInstancedTests({ + &MaterialDataTest::pbrClearCoatAccessCommonTransformationCoordinatesOneTexture, + &MaterialDataTest::pbrClearCoatAccessCommonTransformationCoordinatesOneDifferentTexture}, + Containers::arraySize(PbrClearCoatTextureData)); + + addTests({&MaterialDataTest::pbrClearCoatAccessNoCommonTransformationCoordinates, &MaterialDataTest::debugLayer, &MaterialDataTest::debugAttribute, @@ -2522,7 +2602,9 @@ void MaterialDataTest::constructPhongDeprecated() { CORRADE_COMPARE(data.ambientColor(), 0xccffbb_rgbf); CORRADE_COMPARE(data.diffuseColor(), 0xebefbf_rgbf); CORRADE_COMPARE(data.specularColor(), 0xacabad_rgbf); + CORRADE_IGNORE_DEPRECATED_PUSH CORRADE_COMPARE(data.textureMatrix(), Matrix3{}); + CORRADE_IGNORE_DEPRECATED_POP CORRADE_COMPARE(data.alphaMode(), MaterialAlphaMode::Mask); CORRADE_COMPARE(data.alphaMask(), 0.3f); CORRADE_COMPARE(data.shininess(), 80.0f); @@ -2552,7 +2634,9 @@ void MaterialDataTest::constructPhongDeprecatedTextured() { CORRADE_COMPARE(data.specularColor(), 0xacabad_rgbf); CORRADE_COMPARE(data.specularTexture(), 17); CORRADE_COMPARE(data.specularTextureCoordinates(), 0); + CORRADE_IGNORE_DEPRECATED_PUSH CORRADE_COMPARE(data.textureMatrix(), Matrix3{}); + CORRADE_IGNORE_DEPRECATED_POP CORRADE_COMPARE(data.alphaMode(), MaterialAlphaMode::Blend); CORRADE_COMPARE(data.alphaMask(), 0.37f); CORRADE_COMPARE(data.shininess(), 96.0f); @@ -2581,7 +2665,9 @@ void MaterialDataTest::constructPhongDeprecatedTexturedTextureTransform() { CORRADE_COMPARE(data.diffuseTexture(), 42); CORRADE_COMPARE(data.specularColor(), 0xacabad_rgbf); CORRADE_COMPARE(data.normalTexture(), 17); + CORRADE_IGNORE_DEPRECATED_PUSH CORRADE_COMPARE(data.textureMatrix(), Matrix3::rotation(90.0_degf)); + CORRADE_IGNORE_DEPRECATED_POP CORRADE_COMPARE(data.alphaMode(), MaterialAlphaMode::Mask); CORRADE_COMPARE(data.alphaMask(), 0.5f); CORRADE_COMPARE(data.shininess(), 96.0f); @@ -2615,7 +2701,9 @@ void MaterialDataTest::constructPhongDeprecatedTexturedCoordinates() { CORRADE_COMPARE(data.specularTextureCoordinates(), 1); CORRADE_COMPARE(data.normalTexture(), 0); CORRADE_COMPARE(data.normalTextureCoordinates(), 8); + CORRADE_IGNORE_DEPRECATED_PUSH CORRADE_COMPARE(data.textureMatrix(), Matrix3{}); + CORRADE_IGNORE_DEPRECATED_POP CORRADE_COMPARE(data.ambientTextureCoordinates(), 3); CORRADE_COMPARE(data.alphaMode(), MaterialAlphaMode::Blend); CORRADE_COMPARE(data.alphaMask(), 0.37f); @@ -2722,8 +2810,6 @@ void MaterialDataTest::pbrMetallicRoughnessAccessDefaults() { CORRADE_COMPARE(data.metalness(), 1.0f); CORRADE_COMPARE(data.roughness(), 1.0f); CORRADE_COMPARE(data.emissiveColor(), 0x000000_rgbf); - CORRADE_COMPARE(data.textureMatrix(), Matrix3{}); - CORRADE_COMPARE(data.textureCoordinates(), 0); } void MaterialDataTest::pbrMetallicRoughnessAccessTextured() { @@ -2794,9 +2880,6 @@ void MaterialDataTest::pbrMetallicRoughnessAccessTextured() { CORRADE_COMPARE(data.emissiveTextureMatrix(), Matrix3::scaling({0.75f, 0.5f})); CORRADE_COMPARE(data.emissiveTexture(), 5); CORRADE_COMPARE(data.emissiveTextureCoordinates(), 7); - - CORRADE_COMPARE(data.textureMatrix(), Matrix3{}); - CORRADE_COMPARE(data.textureCoordinates(), 0); } void MaterialDataTest::pbrMetallicRoughnessAccessTexturedDefaults() { @@ -2845,9 +2928,6 @@ void MaterialDataTest::pbrMetallicRoughnessAccessTexturedDefaults() { CORRADE_COMPARE(data.emissiveTexture(), 6); CORRADE_COMPARE(data.emissiveTextureMatrix(), Matrix3{}); CORRADE_COMPARE(data.emissiveTextureCoordinates(), 0); - - CORRADE_COMPARE(data.textureMatrix(), Matrix3{}); - CORRADE_COMPARE(data.textureCoordinates(), 0); } void MaterialDataTest::pbrMetallicRoughnessAccessTexturedSingleMatrixCoordinates() { @@ -2874,9 +2954,6 @@ void MaterialDataTest::pbrMetallicRoughnessAccessTexturedSingleMatrixCoordinates CORRADE_COMPARE(data.occlusionTextureCoordinates(), 7); CORRADE_COMPARE(data.emissiveTextureMatrix(), Matrix3::scaling({0.5f, 0.5f})); CORRADE_COMPARE(data.emissiveTextureCoordinates(), 7); - - CORRADE_COMPARE(data.textureMatrix(), Matrix3::scaling({0.5f, 0.5f})); - CORRADE_COMPARE(data.textureCoordinates(), 7); } void MaterialDataTest::pbrMetallicRoughnessAccessTexturedImplicitPackedMetallicRoughness() { @@ -3315,6 +3392,94 @@ void MaterialDataTest::pbrMetallicRoughnessAccessInvalidTextures() { "Trade::PbrMetallicRoughnessMaterialData::emissiveTextureCoordinates(): the material doesn't have an emissive texture\n"); } +void MaterialDataTest::pbrMetallicRoughnessAccessCommonTransformationCoordinatesNoTextures() { + PbrMetallicRoughnessMaterialData a{{}, {}}; + CORRADE_VERIFY(a.hasCommonTextureTransformation()); + CORRADE_VERIFY(a.hasCommonTextureCoordinates()); + CORRADE_COMPARE(a.commonTextureMatrix(), Matrix3{}); + CORRADE_COMPARE(a.commonTextureCoordinates(), 0); + + PbrMetallicRoughnessMaterialData b{{}, { + {MaterialAttribute::TextureMatrix, Matrix3::scaling({0.5f, 0.5f})}, + {MaterialAttribute::TextureCoordinates, 7u} + }}; + CORRADE_VERIFY(b.hasCommonTextureTransformation()); + CORRADE_VERIFY(b.hasCommonTextureCoordinates()); + CORRADE_COMPARE(b.commonTextureMatrix(), Matrix3::scaling({0.5f, 0.5f})); + CORRADE_COMPARE(b.commonTextureCoordinates(), 7); +} + +void MaterialDataTest::pbrMetallicRoughnessAccessCommonTransformationCoordinatesOneTexture() { + Containers::StringView textureName = PbrMetallicRoughnessTextureData[testCaseInstanceId()]; + setTestCaseDescription(textureName); + + PbrMetallicRoughnessMaterialData data{{}, { + {textureName, 5u}, + {std::string{textureName} + "Matrix", Matrix3::scaling({0.5f, 1.0f})}, + {std::string{textureName} + "Coordinates", 17u}, + + /* These shouldn't affect the above */ + {MaterialAttribute::TextureMatrix, Matrix3::translation({0.5f, 0.0f})}, + {MaterialAttribute::TextureCoordinates, 3u} + }}; + + CORRADE_VERIFY(data.hasCommonTextureTransformation()); + CORRADE_COMPARE(data.commonTextureMatrix(), Matrix3::scaling({0.5f, 1.0f})); + CORRADE_VERIFY(data.hasCommonTextureCoordinates()); + CORRADE_COMPARE(data.commonTextureCoordinates(), 17u); +} + +void MaterialDataTest::pbrMetallicRoughnessAccessCommonTransformationCoordinatesOneDifferentTexture() { + Containers::StringView textureName = PbrMetallicRoughnessTextureData[testCaseInstanceId()]; + setTestCaseDescription(textureName); + + PbrMetallicRoughnessMaterialData data{{}, { + {MaterialAttribute::BaseColorTexture, 2u}, + {MaterialAttribute::MetalnessTexture, 3u}, + {MaterialAttribute::RoughnessTexture, 4u}, + {MaterialAttribute::NormalTexture, 5u}, + {MaterialAttribute::OcclusionTexture, 6u}, + {MaterialAttribute::EmissiveTexture, 7u}, + {std::string{textureName} + "Matrix", Matrix3::scaling({0.5f, 1.0f})}, + {std::string{textureName} + "Coordinates", 17u}, + + /* These are used by all textures except the one above, failing the + check */ + {MaterialAttribute::TextureMatrix, Matrix3::translation({0.5f, 0.0f})}, + {MaterialAttribute::TextureCoordinates, 3u} + }}; + + CORRADE_VERIFY(!data.hasCommonTextureTransformation()); + CORRADE_VERIFY(!data.hasCommonTextureCoordinates()); +} + +void MaterialDataTest::pbrMetallicRoughnessAccessNoCommonTransformationCoordinates() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + PbrMetallicRoughnessMaterialData data{{}, { + {MaterialAttribute::BaseColorTexture, 3u}, + {MaterialAttribute::BaseColorTextureMatrix, Matrix3::translation({0.5f, 0.0f})}, + {MaterialAttribute::BaseColorTextureCoordinates, 3u}, + {MaterialAttribute::MetalnessTexture, 4u}, + {MaterialAttribute::MetalnessTextureMatrix, Matrix3::scaling({0.5f, 1.0f})}, + {MaterialAttribute::RoughnessTexture, 5u}, + {MaterialAttribute::RoughnessTextureCoordinates, 17u} + }}; + + CORRADE_VERIFY(!data.hasCommonTextureTransformation()); + CORRADE_VERIFY(!data.hasCommonTextureCoordinates()); + + std::ostringstream out; + Error redirectError{&out}; + data.commonTextureMatrix(); + data.commonTextureCoordinates(); + CORRADE_COMPARE(out.str(), + "Trade::PbrMetallicRoughnessMaterialData::commonTextureMatrix(): the material doesn't have a common texture coordinate transformation\n" + "Trade::PbrMetallicRoughnessMaterialData::commonTextureCoordinates(): the material doesn't have a common texture coordinate set\n"); +} + void MaterialDataTest::pbrSpecularGlossinessAccess() { MaterialData base{MaterialType::PbrSpecularGlossiness, { {MaterialAttribute::DiffuseColor, 0xccffbbff_rgbaf}, @@ -3418,9 +3583,6 @@ void MaterialDataTest::pbrSpecularGlossinessAccessTextured() { CORRADE_COMPARE(data.emissiveTextureMatrix(), Matrix3::scaling({0.75f, 0.5f})); CORRADE_COMPARE(data.emissiveTexture(), 5); CORRADE_COMPARE(data.emissiveTextureCoordinates(), 7); - - CORRADE_COMPARE(data.textureMatrix(), Matrix3{}); - CORRADE_COMPARE(data.textureCoordinates(), 0); } void MaterialDataTest::pbrSpecularGlossinessAccessTexturedDefaults() { @@ -3466,9 +3628,6 @@ void MaterialDataTest::pbrSpecularGlossinessAccessTexturedDefaults() { CORRADE_COMPARE(data.emissiveTextureMatrix(), Matrix3{}); CORRADE_COMPARE(data.emissiveTexture(), 6); CORRADE_COMPARE(data.emissiveTextureCoordinates(), 0); - - CORRADE_COMPARE(data.textureMatrix(), Matrix3{}); - CORRADE_COMPARE(data.textureCoordinates(), 0); } void MaterialDataTest::pbrSpecularGlossinessAccessTexturedSingleMatrixCoordinates() { @@ -3497,9 +3656,6 @@ void MaterialDataTest::pbrSpecularGlossinessAccessTexturedSingleMatrixCoordinate CORRADE_COMPARE(data.occlusionTextureCoordinates(), 7); CORRADE_COMPARE(data.emissiveTextureMatrix(), Matrix3::scaling({0.5f, 0.5f})); CORRADE_COMPARE(data.emissiveTextureCoordinates(), 7); - - CORRADE_COMPARE(data.textureMatrix(), Matrix3::scaling({0.5f, 0.5f})); - CORRADE_COMPARE(data.textureCoordinates(), 7); } void MaterialDataTest::pbrSpecularGlossinessAccessTexturedImplicitPackedSpecularGlossiness() { @@ -3707,6 +3863,94 @@ void MaterialDataTest::pbrSpecularGlossinessAccessInvalidTextures() { "Trade::PbrSpecularGlossinessMaterialData::emissiveTextureCoordinates(): the material doesn't have an emissive texture\n"); } +void MaterialDataTest::pbrSpecularGlossinessAccessCommonTransformationCoordinatesNoTextures() { + PbrSpecularGlossinessMaterialData a{{}, {}}; + CORRADE_VERIFY(a.hasCommonTextureTransformation()); + CORRADE_VERIFY(a.hasCommonTextureCoordinates()); + CORRADE_COMPARE(a.commonTextureMatrix(), Matrix3{}); + CORRADE_COMPARE(a.commonTextureCoordinates(), 0); + + PbrSpecularGlossinessMaterialData b{{}, { + {MaterialAttribute::TextureMatrix, Matrix3::scaling({0.5f, 0.5f})}, + {MaterialAttribute::TextureCoordinates, 7u} + }}; + CORRADE_VERIFY(b.hasCommonTextureTransformation()); + CORRADE_VERIFY(b.hasCommonTextureCoordinates()); + CORRADE_COMPARE(b.commonTextureMatrix(), Matrix3::scaling({0.5f, 0.5f})); + CORRADE_COMPARE(b.commonTextureCoordinates(), 7); +} + +void MaterialDataTest::pbrSpecularGlossinessAccessCommonTransformationCoordinatesOneTexture() { + Containers::StringView textureName = PbrSpecularGlossinessTextureData[testCaseInstanceId()]; + setTestCaseDescription(textureName); + + PbrSpecularGlossinessMaterialData data{{}, { + {textureName, 5u}, + {std::string{textureName} + "Matrix", Matrix3::scaling({0.5f, 1.0f})}, + {std::string{textureName} + "Coordinates", 17u}, + + /* These shouldn't affect the above */ + {MaterialAttribute::TextureMatrix, Matrix3::translation({0.5f, 0.0f})}, + {MaterialAttribute::TextureCoordinates, 3u} + }}; + + CORRADE_VERIFY(data.hasCommonTextureTransformation()); + CORRADE_COMPARE(data.commonTextureMatrix(), Matrix3::scaling({0.5f, 1.0f})); + CORRADE_VERIFY(data.hasCommonTextureCoordinates()); + CORRADE_COMPARE(data.commonTextureCoordinates(), 17u); +} + +void MaterialDataTest::pbrSpecularGlossinessAccessCommonTransformationCoordinatesOneDifferentTexture() { + Containers::StringView textureName = PbrSpecularGlossinessTextureData[testCaseInstanceId()]; + setTestCaseDescription(textureName); + + PbrSpecularGlossinessMaterialData data{{}, { + {MaterialAttribute::DiffuseTexture, 2u}, + {MaterialAttribute::SpecularTexture, 3u}, + {MaterialAttribute::GlossinessTexture, 4u}, + {MaterialAttribute::NormalTexture, 5u}, + {MaterialAttribute::OcclusionTexture, 6u}, + {MaterialAttribute::EmissiveTexture, 7u}, + {std::string{textureName} + "Matrix", Matrix3::scaling({0.5f, 1.0f})}, + {std::string{textureName} + "Coordinates", 17u}, + + /* These are used by all textures except the one above, failing the + check */ + {MaterialAttribute::TextureMatrix, Matrix3::translation({0.5f, 0.0f})}, + {MaterialAttribute::TextureCoordinates, 3u} + }}; + + CORRADE_VERIFY(!data.hasCommonTextureTransformation()); + CORRADE_VERIFY(!data.hasCommonTextureCoordinates()); +} + +void MaterialDataTest::pbrSpecularGlossinessAccessNoCommonTransformationCoordinates() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + PbrSpecularGlossinessMaterialData data{{}, { + {MaterialAttribute::DiffuseTexture, 3u}, + {MaterialAttribute::DiffuseTextureMatrix, Matrix3::translation({0.5f, 0.0f})}, + {MaterialAttribute::DiffuseTextureCoordinates, 3u}, + {MaterialAttribute::SpecularTexture, 4u}, + {MaterialAttribute::SpecularTextureMatrix, Matrix3::scaling({0.5f, 1.0f})}, + {MaterialAttribute::OcclusionTexture, 5u}, + {MaterialAttribute::OcclusionTextureCoordinates, 17u} + }}; + + CORRADE_VERIFY(!data.hasCommonTextureTransformation()); + CORRADE_VERIFY(!data.hasCommonTextureCoordinates()); + + std::ostringstream out; + Error redirectError{&out}; + data.commonTextureMatrix(); + data.commonTextureCoordinates(); + CORRADE_COMPARE(out.str(), + "Trade::PbrSpecularGlossinessMaterialData::commonTextureMatrix(): the material doesn't have a common texture coordinate transformation\n" + "Trade::PbrSpecularGlossinessMaterialData::commonTextureCoordinates(): the material doesn't have a common texture coordinate set\n"); +} + void MaterialDataTest::phongAccess() { MaterialData base{MaterialType::Phong, { {MaterialAttribute::AmbientColor, 0xccffbbff_rgbaf}, @@ -3739,8 +3983,6 @@ void MaterialDataTest::phongAccessDefaults() { CORRADE_COMPARE(data.ambientColor(), 0x000000_rgbf); CORRADE_COMPARE(data.diffuseColor(), 0xffffff_rgbf); CORRADE_COMPARE(data.specularColor(), 0xffffff00_rgbaf); - CORRADE_COMPARE(data.textureMatrix(), Matrix3{}); - CORRADE_COMPARE(data.textureCoordinates(), 0); CORRADE_COMPARE(data.shininess(), 80.0f); } @@ -3787,9 +4029,6 @@ void MaterialDataTest::phongAccessTextured() { CORRADE_COMPARE(data.normalTextureSwizzle(), MaterialTextureSwizzle::GB); CORRADE_COMPARE(data.normalTextureMatrix(), Matrix3::scaling({1.0f, 0.5f})); CORRADE_COMPARE(data.normalTextureCoordinates(), 5); - - CORRADE_COMPARE(data.textureMatrix(), Matrix3{}); - CORRADE_COMPARE(data.textureCoordinates(), 0); } void MaterialDataTest::phongAccessTexturedDefaults() { @@ -3821,9 +4060,6 @@ void MaterialDataTest::phongAccessTexturedDefaults() { CORRADE_COMPARE(data.normalTextureSwizzle(), MaterialTextureSwizzle::RGB); CORRADE_COMPARE(data.normalTextureMatrix(), Matrix3{}); CORRADE_COMPARE(data.normalTextureCoordinates(), 0); - - CORRADE_COMPARE(data.textureMatrix(), Matrix3{}); - CORRADE_COMPARE(data.textureCoordinates(), 0); } void MaterialDataTest::phongAccessTexturedSingleMatrixCoordinates() { @@ -3846,9 +4082,6 @@ void MaterialDataTest::phongAccessTexturedSingleMatrixCoordinates() { CORRADE_COMPARE(data.specularTextureCoordinates(), 2); CORRADE_COMPARE(data.normalTextureMatrix(), Matrix3::translation({0.5f, 1.0f})); CORRADE_COMPARE(data.normalTextureCoordinates(), 2); - - CORRADE_COMPARE(data.textureMatrix(), Matrix3::translation({0.5f, 1.0f})); - CORRADE_COMPARE(data.textureCoordinates(), 2); } void MaterialDataTest::phongAccessTexturedImplicitPackedSpecularGlossiness() { @@ -3916,6 +4149,92 @@ void MaterialDataTest::phongAccessInvalidTextures() { "Trade::PhongMaterialData::normalTextureCoordinates(): the material doesn't have a normal texture\n"); } +void MaterialDataTest::phongAccessCommonTransformationCoordinatesNoTextures() { + PhongMaterialData a{{}, {}}; + CORRADE_VERIFY(a.hasCommonTextureTransformation()); + CORRADE_VERIFY(a.hasCommonTextureCoordinates()); + CORRADE_COMPARE(a.commonTextureMatrix(), Matrix3{}); + CORRADE_COMPARE(a.commonTextureCoordinates(), 0); + + PhongMaterialData b{{}, { + {MaterialAttribute::TextureMatrix, Matrix3::scaling({0.5f, 0.5f})}, + {MaterialAttribute::TextureCoordinates, 7u} + }}; + CORRADE_VERIFY(b.hasCommonTextureTransformation()); + CORRADE_VERIFY(b.hasCommonTextureCoordinates()); + CORRADE_COMPARE(b.commonTextureMatrix(), Matrix3::scaling({0.5f, 0.5f})); + CORRADE_COMPARE(b.commonTextureCoordinates(), 7); +} + +void MaterialDataTest::phongAccessCommonTransformationCoordinatesOneTexture() { + Containers::StringView textureName = PhongTextureData[testCaseInstanceId()]; + setTestCaseDescription(textureName); + + PhongMaterialData data{{}, { + {textureName, 5u}, + {std::string{textureName} + "Matrix", Matrix3::scaling({0.5f, 1.0f})}, + {std::string{textureName} + "Coordinates", 17u}, + + /* These shouldn't affect the above */ + {MaterialAttribute::TextureMatrix, Matrix3::translation({0.5f, 0.0f})}, + {MaterialAttribute::TextureCoordinates, 3u} + }}; + + CORRADE_VERIFY(data.hasCommonTextureTransformation()); + CORRADE_COMPARE(data.commonTextureMatrix(), Matrix3::scaling({0.5f, 1.0f})); + CORRADE_VERIFY(data.hasCommonTextureCoordinates()); + CORRADE_COMPARE(data.commonTextureCoordinates(), 17u); +} + +void MaterialDataTest::phongAccessCommonTransformationCoordinatesOneDifferentTexture() { + Containers::StringView textureName = PhongTextureData[testCaseInstanceId()]; + setTestCaseDescription(textureName); + + PhongMaterialData data{{}, { + {MaterialAttribute::AmbientTexture, 2u}, + {MaterialAttribute::DiffuseTexture, 3u}, + {MaterialAttribute::SpecularTexture, 4u}, + {MaterialAttribute::NormalTexture, 5u}, + {std::string{textureName} + "Matrix", Matrix3::scaling({0.5f, 1.0f})}, + {std::string{textureName} + "Coordinates", 17u}, + + /* These are used by all textures except the one above, failing the + check */ + {MaterialAttribute::TextureMatrix, Matrix3::translation({0.5f, 0.0f})}, + {MaterialAttribute::TextureCoordinates, 3u} + }}; + + CORRADE_VERIFY(!data.hasCommonTextureTransformation()); + CORRADE_VERIFY(!data.hasCommonTextureCoordinates()); +} + +void MaterialDataTest::phongAccessNoCommonTransformationCoordinates() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + PhongMaterialData data{{}, { + {MaterialAttribute::DiffuseTexture, 3u}, + {MaterialAttribute::DiffuseTextureMatrix, Matrix3::translation({0.5f, 0.0f})}, + {MaterialAttribute::DiffuseTextureCoordinates, 3u}, + {MaterialAttribute::SpecularTexture, 4u}, + {MaterialAttribute::SpecularTextureMatrix, Matrix3::scaling({0.5f, 1.0f})}, + {MaterialAttribute::NormalTexture, 5u}, + {MaterialAttribute::NormalTextureCoordinates, 17u} + }}; + + CORRADE_VERIFY(!data.hasCommonTextureTransformation()); + CORRADE_VERIFY(!data.hasCommonTextureCoordinates()); + + std::ostringstream out; + Error redirectError{&out}; + data.commonTextureMatrix(); + data.commonTextureCoordinates(); + CORRADE_COMPARE(out.str(), + "Trade::PhongMaterialData::commonTextureMatrix(): the material doesn't have a common texture coordinate transformation\n" + "Trade::PhongMaterialData::commonTextureCoordinates(): the material doesn't have a common texture coordinate set\n"); +} + void MaterialDataTest::flatAccessBaseColor() { MaterialData base{MaterialType::Flat, { {MaterialAttribute::BaseColor, 0xccffbbff_rgbaf}, @@ -4280,6 +4599,11 @@ void MaterialDataTest::pbrClearCoatAccessTexturedBaseMaterialMatrixCoordinates() CORRADE_COMPARE(data.roughnessTextureCoordinates(), 7u); CORRADE_COMPARE(data.normalTextureMatrix(), Matrix3::translation({0.0f, 0.5f})); CORRADE_COMPARE(data.normalTextureCoordinates(), 7u); + + CORRADE_VERIFY(data.hasCommonTextureTransformation()); + CORRADE_VERIFY(data.hasCommonTextureCoordinates()); + CORRADE_COMPARE(data.commonTextureMatrix(), Matrix3::translation({0.0f, 0.5f})); + CORRADE_COMPARE(data.commonTextureCoordinates(), 7); } void MaterialDataTest::pbrClearCoatAccessInvalidTextures() { @@ -4314,6 +4638,108 @@ void MaterialDataTest::pbrClearCoatAccessInvalidTextures() { "Trade::PbrClearCoatMaterialData::normalTextureCoordinates(): the layer doesn't have a normal texture\n"); } +void MaterialDataTest::pbrClearCoatAccessCommonTransformationCoordinatesNoTextures() { + PbrClearCoatMaterialData a{{}, { + {MaterialLayer::ClearCoat}, + }, {0, 1}}; + CORRADE_VERIFY(a.hasCommonTextureTransformation()); + CORRADE_VERIFY(a.hasCommonTextureCoordinates()); + CORRADE_COMPARE(a.commonTextureMatrix(), Matrix3{}); + CORRADE_COMPARE(a.commonTextureCoordinates(), 0); + + PbrClearCoatMaterialData b{{}, { + {MaterialAttribute::TextureMatrix, Matrix3::scaling({0.5f, 0.5f})}, + {MaterialAttribute::TextureCoordinates, 7u}, + + {MaterialLayer::ClearCoat} + }, {2, 3}}; + CORRADE_VERIFY(b.hasCommonTextureTransformation()); + CORRADE_VERIFY(b.hasCommonTextureCoordinates()); + CORRADE_COMPARE(b.commonTextureMatrix(), Matrix3::scaling({0.5f, 0.5f})); + CORRADE_COMPARE(b.commonTextureCoordinates(), 7); + + PbrClearCoatMaterialData c{{}, { + {MaterialLayer::ClearCoat}, + {MaterialAttribute::TextureMatrix, Matrix3::scaling({0.5f, 0.5f})}, + {MaterialAttribute::TextureCoordinates, 7u}, + }, {0, 3}}; + CORRADE_VERIFY(c.hasCommonTextureTransformation()); + CORRADE_VERIFY(c.hasCommonTextureCoordinates()); + CORRADE_COMPARE(c.commonTextureMatrix(), Matrix3::scaling({0.5f, 0.5f})); + CORRADE_COMPARE(c.commonTextureCoordinates(), 7); +} + +void MaterialDataTest::pbrClearCoatAccessCommonTransformationCoordinatesOneTexture() { + Containers::StringView textureName = PbrClearCoatTextureData[testCaseInstanceId()]; + setTestCaseDescription(textureName); + + PbrClearCoatMaterialData data{{}, { + /* These shouldn't affect the below */ + {MaterialAttribute::TextureMatrix, Matrix3::translation({0.5f, 0.0f})}, + {MaterialAttribute::TextureCoordinates, 3u}, + + {MaterialLayer::ClearCoat}, + {textureName, 5u}, + {std::string{textureName} + "Matrix", Matrix3::scaling({0.5f, 1.0f})}, + {std::string{textureName} + "Coordinates", 17u}, + }, {2, 6}}; + + CORRADE_VERIFY(data.hasCommonTextureTransformation()); + CORRADE_COMPARE(data.commonTextureMatrix(), Matrix3::scaling({0.5f, 1.0f})); + CORRADE_VERIFY(data.hasCommonTextureCoordinates()); + CORRADE_COMPARE(data.commonTextureCoordinates(), 17u); +} + +void MaterialDataTest::pbrClearCoatAccessCommonTransformationCoordinatesOneDifferentTexture() { + Containers::StringView textureName = PbrClearCoatTextureData[testCaseInstanceId()]; + setTestCaseDescription(textureName); + + PbrClearCoatMaterialData data{{}, { + /* These are used by all textures except the one below, failing the + check */ + {MaterialAttribute::TextureMatrix, Matrix3::translation({0.5f, 0.0f})}, + {MaterialAttribute::TextureCoordinates, 3u}, + + {MaterialLayer::ClearCoat}, + {MaterialAttribute::LayerFactorTexture, 2u}, + {MaterialAttribute::RoughnessTexture, 3u}, + {MaterialAttribute::NormalTexture, 5u}, + {std::string{textureName} + "Matrix", Matrix3::scaling({0.5f, 1.0f})}, + {std::string{textureName} + "Coordinates", 17u} + }, {2, 8}}; + + CORRADE_VERIFY(!data.hasCommonTextureTransformation()); + CORRADE_VERIFY(!data.hasCommonTextureCoordinates()); +} + +void MaterialDataTest::pbrClearCoatAccessNoCommonTransformationCoordinates() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + PbrClearCoatMaterialData data{{}, { + {MaterialLayer::ClearCoat}, + {MaterialAttribute::LayerFactorTexture, 3u}, + {MaterialAttribute::LayerFactorTextureMatrix, Matrix3::translation({0.5f, 0.0f})}, + {MaterialAttribute::LayerFactorTextureCoordinates, 3u}, + {MaterialAttribute::RoughnessTexture, 4u}, + {MaterialAttribute::RoughnessTextureMatrix, Matrix3::scaling({0.5f, 1.0f})}, + {MaterialAttribute::NormalTexture, 5u}, + {MaterialAttribute::NormalTextureCoordinates, 17u} + }, {0, 8}}; + + CORRADE_VERIFY(!data.hasCommonTextureTransformation()); + CORRADE_VERIFY(!data.hasCommonTextureCoordinates()); + + std::ostringstream out; + Error redirectError{&out}; + data.commonTextureMatrix(); + data.commonTextureCoordinates(); + CORRADE_COMPARE(out.str(), + "Trade::PbrClearCoatMaterialData::commonTextureMatrix(): the layer doesn't have a common texture coordinate transformation\n" + "Trade::PbrClearCoatMaterialData::commonTextureCoordinates(): the layer doesn't have a common texture coordinate set\n"); +} + void MaterialDataTest::debugLayer() { std::ostringstream out; From c2942ce7ac16238ad709d073bdcac44bcf22426e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 16 Aug 2020 12:48:17 +0200 Subject: [PATCH 32/36] Trade: material packing queries definitely check for different IDs, right? Err, nope. Not a single one of them. --- src/Magnum/Trade/Test/MaterialDataTest.cpp | 53 ++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/src/Magnum/Trade/Test/MaterialDataTest.cpp b/src/Magnum/Trade/Test/MaterialDataTest.cpp index 62bce1c655..0e712b21cb 100644 --- a/src/Magnum/Trade/Test/MaterialDataTest.cpp +++ b/src/Magnum/Trade/Test/MaterialDataTest.cpp @@ -3045,6 +3045,16 @@ void MaterialDataTest::pbrMetallicRoughnessAccessTexturedExplicitPackedMetallicR }}; CORRADE_VERIFY(data.hasNoneRoughnessMetallicTexture()); + /* Different texture ID */ + } { + PbrMetallicRoughnessMaterialData data{{}, { + {MaterialAttribute::RoughnessTexture, 2u}, + {MaterialAttribute::MetalnessTexture, 3u}, + {MaterialAttribute::RoughnessTextureSwizzle, MaterialTextureSwizzle::G}, + {MaterialAttribute::MetalnessTextureSwizzle, MaterialTextureSwizzle::B} + }}; + CORRADE_VERIFY(!data.hasNoneRoughnessMetallicTexture()); + /* One texture missing */ } { PbrMetallicRoughnessMaterialData data{{}, { @@ -3121,6 +3131,17 @@ void MaterialDataTest::pbrMetallicRoughnessAccessTexturedExplicitPackedRoughness /* This isn't a superset */ CORRADE_VERIFY(!data.hasNoneRoughnessMetallicTexture()); + /* Different texture ID */ + } { + PbrMetallicRoughnessMaterialData data{{}, { + {MaterialAttribute::RoughnessTexture, 2u}, + {MaterialAttribute::MetalnessTexture, 3u}, + {MaterialAttribute::MetalnessTextureSwizzle, MaterialTextureSwizzle::G}, + {MaterialAttribute::OcclusionTexture, 2u}, + {MaterialAttribute::OcclusionTextureSwizzle, MaterialTextureSwizzle::B}, + }}; + CORRADE_VERIFY(!data.hasRoughnessMetallicOcclusionTexture()); + /* One texture missing */ } { PbrMetallicRoughnessMaterialData data{{}, { @@ -3203,6 +3224,17 @@ void MaterialDataTest::pbrMetallicRoughnessAccessTexturedExplicitPackedOcclusion /* This is a superset */ CORRADE_VERIFY(data.hasNoneRoughnessMetallicTexture()); + /* Different texture ID */ + } { + PbrMetallicRoughnessMaterialData data{{}, { + {MaterialAttribute::OcclusionTexture, 2u}, + {MaterialAttribute::RoughnessTexture, 3u}, + {MaterialAttribute::RoughnessTextureSwizzle, MaterialTextureSwizzle::G}, + {MaterialAttribute::MetalnessTexture, 2u}, + {MaterialAttribute::MetalnessTextureSwizzle, MaterialTextureSwizzle::B}, + }}; + CORRADE_VERIFY(!data.hasOcclusionRoughnessMetallicTexture()); + /* One texture missing */ } { PbrMetallicRoughnessMaterialData data{{}, { @@ -3282,6 +3314,18 @@ void MaterialDataTest::pbrMetallicRoughnessAccessTexturedExplicitPackedNormalRou }}; CORRADE_VERIFY(data.hasNormalRoughnessMetallicTexture()); + /* Different texture ID */ + } { + PbrMetallicRoughnessMaterialData data{{}, { + {MaterialAttribute::NormalTexture, 2u}, + {MaterialAttribute::NormalTextureSwizzle, MaterialTextureSwizzle::RG}, + {MaterialAttribute::RoughnessTexture, 2u}, + {MaterialAttribute::RoughnessTextureSwizzle, MaterialTextureSwizzle::B}, + {MaterialAttribute::MetalnessTexture, 3u}, + {MaterialAttribute::MetalnessTextureSwizzle, MaterialTextureSwizzle::A} + }}; + CORRADE_VERIFY(!data.hasNormalRoughnessMetallicTexture()); + /* One texture missing */ } { PbrMetallicRoughnessMaterialData data{{}, { @@ -3762,6 +3806,15 @@ void MaterialDataTest::pbrSpecularGlossinessAccessTexturedExplicitPackedSpecular CORRADE_COMPARE(data.glossinessTextureMatrix(), Matrix3::scaling({0.5f, 0.5f})); CORRADE_COMPARE(data.glossinessTextureCoordinates(), 3); + /* Different texture ID */ + } { + PbrSpecularGlossinessMaterialData data{{}, { + {MaterialAttribute::SpecularTexture, 2u}, + {MaterialAttribute::GlossinessTexture, 3u}, + {MaterialAttribute::GlossinessTextureSwizzle, MaterialTextureSwizzle::A} + }}; + CORRADE_VERIFY(!data.hasSpecularGlossinessTexture()); + /* Unexpected swizzle 1 */ } { PbrSpecularGlossinessMaterialData data{{}, { From a2b1fa05721a47bac75b4a62bd7b853eed6cd499 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 16 Aug 2020 13:10:36 +0200 Subject: [PATCH 33/36] Trade: properly consider texture IDs when checking for packing. --- .../PbrMetallicRoughnessMaterialData.cpp | 52 ++++++++++++------- .../PbrSpecularGlossinessMaterialData.cpp | 1 + 2 files changed, 35 insertions(+), 18 deletions(-) diff --git a/src/Magnum/Trade/PbrMetallicRoughnessMaterialData.cpp b/src/Magnum/Trade/PbrMetallicRoughnessMaterialData.cpp index 2da8aa7100..08898f3b30 100644 --- a/src/Magnum/Trade/PbrMetallicRoughnessMaterialData.cpp +++ b/src/Magnum/Trade/PbrMetallicRoughnessMaterialData.cpp @@ -46,6 +46,7 @@ bool PbrMetallicRoughnessMaterialData::hasNoneRoughnessMetallicTexture() const { return (hasAttribute(MaterialAttribute::NoneRoughnessMetallicTexture) || (hasAttribute(MaterialAttribute::MetalnessTexture) && hasAttribute(MaterialAttribute::RoughnessTexture) && + attribute(MaterialAttribute::MetalnessTexture) == attribute(MaterialAttribute::RoughnessTexture) && roughnessTextureSwizzle() == MaterialTextureSwizzle::G && metalnessTextureSwizzle() == MaterialTextureSwizzle::B)) && roughnessTextureMatrix() == metalnessTextureMatrix() && @@ -53,12 +54,17 @@ bool PbrMetallicRoughnessMaterialData::hasNoneRoughnessMetallicTexture() const { } bool PbrMetallicRoughnessMaterialData::hasRoughnessMetallicOcclusionTexture() const { - if(!(hasAttribute(MaterialAttribute::RoughnessTexture) && - hasAttribute(MaterialAttribute::MetalnessTexture) && - hasAttribute(MaterialAttribute::OcclusionTexture) && - roughnessTextureSwizzle() == MaterialTextureSwizzle::R && - metalnessTextureSwizzle() == MaterialTextureSwizzle::G && - occlusionTextureSwizzle() == MaterialTextureSwizzle::B)) + if(!hasAttribute(MaterialAttribute::RoughnessTexture) || + !hasAttribute(MaterialAttribute::MetalnessTexture) || + !hasAttribute(MaterialAttribute::OcclusionTexture)) + return false; + + const UnsignedInt roughnessTexture = attribute(MaterialAttribute::RoughnessTexture); + if(attribute(MaterialAttribute::MetalnessTexture) != roughnessTexture || + attribute(MaterialAttribute::OcclusionTexture) != roughnessTexture || + roughnessTextureSwizzle() != MaterialTextureSwizzle::R || + metalnessTextureSwizzle() != MaterialTextureSwizzle::G || + occlusionTextureSwizzle() != MaterialTextureSwizzle::B) return false; const Matrix3 roughnessTextureMatrix = this->roughnessTextureMatrix(); @@ -70,12 +76,17 @@ bool PbrMetallicRoughnessMaterialData::hasRoughnessMetallicOcclusionTexture() co } bool PbrMetallicRoughnessMaterialData::hasOcclusionRoughnessMetallicTexture() const { - if(!(hasAttribute(MaterialAttribute::OcclusionTexture) && - hasAttribute(MaterialAttribute::RoughnessTexture) && - hasAttribute(MaterialAttribute::MetalnessTexture) && - occlusionTextureSwizzle() == MaterialTextureSwizzle::R && - roughnessTextureSwizzle() == MaterialTextureSwizzle::G && - metalnessTextureSwizzle() == MaterialTextureSwizzle::B)) + if(!hasAttribute(MaterialAttribute::OcclusionTexture) || + !hasAttribute(MaterialAttribute::RoughnessTexture) || + !hasAttribute(MaterialAttribute::MetalnessTexture)) + return false; + + const UnsignedInt occlusionTexture = attribute(MaterialAttribute::OcclusionTexture); + if(attribute(MaterialAttribute::RoughnessTexture) != occlusionTexture || + attribute(MaterialAttribute::MetalnessTexture) != occlusionTexture || + occlusionTextureSwizzle() != MaterialTextureSwizzle::R || + roughnessTextureSwizzle() != MaterialTextureSwizzle::G || + metalnessTextureSwizzle() != MaterialTextureSwizzle::B) return false; const Matrix3 occlusionTextureMatrix = this->occlusionTextureMatrix(); @@ -87,12 +98,17 @@ bool PbrMetallicRoughnessMaterialData::hasOcclusionRoughnessMetallicTexture() co } bool PbrMetallicRoughnessMaterialData::hasNormalRoughnessMetallicTexture() const { - if(!(hasAttribute(MaterialAttribute::NormalTexture) && - hasAttribute(MaterialAttribute::RoughnessTexture) && - hasAttribute(MaterialAttribute::MetalnessTexture) && - normalTextureSwizzle() == MaterialTextureSwizzle::RG && - roughnessTextureSwizzle() == MaterialTextureSwizzle::B && - metalnessTextureSwizzle() == MaterialTextureSwizzle::A)) + if(!hasAttribute(MaterialAttribute::NormalTexture) || + !hasAttribute(MaterialAttribute::RoughnessTexture) || + !hasAttribute(MaterialAttribute::MetalnessTexture)) + return false; + + const UnsignedInt normalTexture = attribute(MaterialAttribute::NormalTexture); + if(attribute(MaterialAttribute::RoughnessTexture) != normalTexture || + attribute(MaterialAttribute::MetalnessTexture) != normalTexture || + normalTextureSwizzle() != MaterialTextureSwizzle::RG || + roughnessTextureSwizzle() != MaterialTextureSwizzle::B || + metalnessTextureSwizzle() != MaterialTextureSwizzle::A) return false; const Matrix3 normalTextureMatrix = this->normalTextureMatrix(); diff --git a/src/Magnum/Trade/PbrSpecularGlossinessMaterialData.cpp b/src/Magnum/Trade/PbrSpecularGlossinessMaterialData.cpp index 083cdc1715..11fd16b71d 100644 --- a/src/Magnum/Trade/PbrSpecularGlossinessMaterialData.cpp +++ b/src/Magnum/Trade/PbrSpecularGlossinessMaterialData.cpp @@ -46,6 +46,7 @@ bool PbrSpecularGlossinessMaterialData::hasSpecularGlossinessTexture() const { return (hasAttribute(MaterialAttribute::SpecularGlossinessTexture) || (hasAttribute(MaterialAttribute::SpecularTexture) && hasAttribute(MaterialAttribute::GlossinessTexture) && + attribute(MaterialAttribute::SpecularTexture) == attribute(MaterialAttribute::GlossinessTexture) && specularTextureSwizzle() == MaterialTextureSwizzle::RGB && glossinessTextureSwizzle() == MaterialTextureSwizzle::A)) && specularTextureMatrix() == glossinessTextureMatrix() && From 79e8fa0b05ae3fadfe8c343c220be424cd6fbf50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 16 Aug 2020 13:15:04 +0200 Subject: [PATCH 34/36] Trade: add a query for packed clearcoat factor/roughness texture. glTF, always some surprise waiting around the corner. --- src/Magnum/Trade/PbrClearCoatMaterialData.cpp | 10 ++ src/Magnum/Trade/PbrClearCoatMaterialData.h | 30 ++++++ src/Magnum/Trade/Test/MaterialDataTest.cpp | 97 +++++++++++++++++++ 3 files changed, 137 insertions(+) diff --git a/src/Magnum/Trade/PbrClearCoatMaterialData.cpp b/src/Magnum/Trade/PbrClearCoatMaterialData.cpp index 87761a2470..73bd1bd881 100644 --- a/src/Magnum/Trade/PbrClearCoatMaterialData.cpp +++ b/src/Magnum/Trade/PbrClearCoatMaterialData.cpp @@ -27,6 +27,16 @@ namespace Magnum { namespace Trade { +bool PbrClearCoatMaterialData::hasLayerFactorRoughnessTexture() const { + return hasAttribute(MaterialAttribute::LayerFactorTexture) && + hasAttribute(MaterialAttribute::RoughnessTexture) && + attribute(MaterialAttribute::LayerFactorTexture) == attribute(MaterialAttribute::RoughnessTexture) && + layerFactorTextureSwizzle() == MaterialTextureSwizzle::R && + roughnessTextureSwizzle() == MaterialTextureSwizzle::G && + layerFactorTextureMatrix() == roughnessTextureMatrix() && + layerFactorTextureCoordinates() == roughnessTextureCoordinates(); +} + bool PbrClearCoatMaterialData::hasTextureTransformation() const { return hasAttribute(MaterialAttribute::LayerFactorTextureMatrix) || hasAttribute(MaterialAttribute::RoughnessTextureMatrix) || diff --git a/src/Magnum/Trade/PbrClearCoatMaterialData.h b/src/Magnum/Trade/PbrClearCoatMaterialData.h index 9323581e8f..9622599ce2 100644 --- a/src/Magnum/Trade/PbrClearCoatMaterialData.h +++ b/src/Magnum/Trade/PbrClearCoatMaterialData.h @@ -56,6 +56,36 @@ class MAGNUM_TRADE_EXPORT PbrClearCoatMaterialData: public MaterialLayerData::MaterialLayerData; #endif + /** + * @brief Whether the material has a combined layer factor / roughness texture + * + * Returns @cpp true @ce if both + * @ref MaterialAttribute::LayerFactorTexture and + * @ref MaterialAttribute::RoughnessTexture attributes are present, + * point to the same texture ID, + * @ref MaterialAttribute::LayerFactorTextureSwizzle is either not + * present or set to @ref MaterialTextureSwizzle::R and + * @ref MaterialAttribute::RoughnessTextureSwizzle is set to + * @ref MaterialTextureSwizzle::G, and additionally + * @ref MaterialAttribute::LayerFactorTextureMatrix and + * @ref MaterialAttribute::RoughnessTextureMatrix are both either not + * present or have the same value, and + * @ref MaterialAttribute::LayerFactorTextureCoordinates and + * @ref MaterialAttribute::RoughnessTextureCoordinates are both either + * not present or have the smae value; @cpp false @ce otherwise. + * + * In other words, if this function returns @cpp true @ce, + * @ref layerFactorTexture(), @ref layerFactorTextureMatrix() and + * @ref layerFactorTextureCoordinates() return values common for both + * layer factor and roughness texture, and the two are packed together + * with layer factor occupying the R channel and roughness the G + * channel. This check is present in order to provide support for the + * [KHR_materials_clearcoat](https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_clearcoat/README.md) + * glTF extension. + * @see @ref hasAttribute() + */ + bool hasLayerFactorRoughnessTexture() const; + /** * @brief Whether the material has texture transformation * diff --git a/src/Magnum/Trade/Test/MaterialDataTest.cpp b/src/Magnum/Trade/Test/MaterialDataTest.cpp index 0e712b21cb..e469b782e0 100644 --- a/src/Magnum/Trade/Test/MaterialDataTest.cpp +++ b/src/Magnum/Trade/Test/MaterialDataTest.cpp @@ -207,6 +207,7 @@ class MaterialDataTest: public TestSuite::Tester { void pbrClearCoatAccessDefaults(); void pbrClearCoatAccessTextured(); void pbrClearCoatAccessTexturedDefaults(); + void pbrClearCoatAccessTexturedExplicitPackedLayerFactorRoughness(); void pbrClearCoatAccessTexturedSingleMatrixCoordinates(); void pbrClearCoatAccessTexturedBaseMaterialMatrixCoordinates(); void pbrClearCoatAccessInvalidTextures(); @@ -465,6 +466,7 @@ MaterialDataTest::MaterialDataTest() { &MaterialDataTest::pbrClearCoatAccessDefaults, &MaterialDataTest::pbrClearCoatAccessTextured, &MaterialDataTest::pbrClearCoatAccessTexturedDefaults, + &MaterialDataTest::pbrClearCoatAccessTexturedExplicitPackedLayerFactorRoughness, &MaterialDataTest::pbrClearCoatAccessTexturedSingleMatrixCoordinates, &MaterialDataTest::pbrClearCoatAccessTexturedBaseMaterialMatrixCoordinates, &MaterialDataTest::pbrClearCoatAccessInvalidTextures, @@ -4619,6 +4621,101 @@ void MaterialDataTest::pbrClearCoatAccessTexturedDefaults() { CORRADE_COMPARE(data.normalTextureCoordinates(), 0u); } +void MaterialDataTest::pbrClearCoatAccessTexturedExplicitPackedLayerFactorRoughness() { + /* Just the texture ID and swizzles, the rest is implicit */ + { + PbrClearCoatMaterialData data{{}, { + {MaterialLayer::ClearCoat}, + {MaterialAttribute::LayerFactorTexture, 2u}, + {MaterialAttribute::RoughnessTexture, 2u}, + {MaterialAttribute::RoughnessTextureSwizzle, MaterialTextureSwizzle::G} + }, {0, 4}}; + CORRADE_VERIFY(data.hasLayerFactorRoughnessTexture()); + CORRADE_COMPARE(data.layerFactorTexture(), 2); + CORRADE_COMPARE(data.layerFactorTextureMatrix(), Matrix3{}); + CORRADE_COMPARE(data.layerFactorTextureCoordinates(), 0); + CORRADE_COMPARE(data.roughnessTexture(), 2); + CORRADE_COMPARE(data.roughnessTextureSwizzle(), MaterialTextureSwizzle::G); + CORRADE_COMPARE(data.roughnessTextureMatrix(), Matrix3{}); + CORRADE_COMPARE(data.roughnessTextureCoordinates(), 0); + + /* Explicit parameters for everything, but all the same */ + } { + PbrClearCoatMaterialData data{{}, { + {MaterialLayer::ClearCoat}, + {MaterialAttribute::LayerFactorTexture, 2u}, + {MaterialAttribute::LayerFactorTextureSwizzle, MaterialTextureSwizzle::R}, + {MaterialAttribute::LayerFactorTextureMatrix, Matrix3::scaling({0.5f, 0.5f})}, + {MaterialAttribute::LayerFactorTextureCoordinates, 3u}, + {MaterialAttribute::RoughnessTexture, 2u}, + {MaterialAttribute::RoughnessTextureSwizzle, MaterialTextureSwizzle::G}, + {MaterialAttribute::RoughnessTextureMatrix, Matrix3::scaling({0.5f, 0.5f})}, + {MaterialAttribute::RoughnessTextureCoordinates, 3u} + }, {0, 9}}; + CORRADE_VERIFY(data.hasLayerFactorRoughnessTexture()); + CORRADE_COMPARE(data.layerFactorTexture(), 2); + CORRADE_COMPARE(data.layerFactorTextureMatrix(), Matrix3::scaling({0.5f, 0.5f})); + CORRADE_COMPARE(data.layerFactorTextureCoordinates(), 3); + CORRADE_COMPARE(data.roughnessTexture(), 2); + CORRADE_COMPARE(data.roughnessTextureSwizzle(), MaterialTextureSwizzle::G); + CORRADE_COMPARE(data.roughnessTextureMatrix(), Matrix3::scaling({0.5f, 0.5f})); + CORRADE_COMPARE(data.roughnessTextureCoordinates(), 3); + + /* Different texture ID */ + } { + PbrClearCoatMaterialData data{{}, { + {MaterialLayer::ClearCoat}, + {MaterialAttribute::LayerFactorTexture, 2u}, + {MaterialAttribute::RoughnessTexture, 3u}, + {MaterialAttribute::RoughnessTextureSwizzle, MaterialTextureSwizzle::G}, + }, {0, 4}}; + CORRADE_VERIFY(!data.hasLayerFactorRoughnessTexture()); + + /* Unexpected swizzle 1 */ + } { + PbrClearCoatMaterialData data{{}, { + {MaterialLayer::ClearCoat}, + {MaterialAttribute::LayerFactorTexture, 2u}, + {MaterialAttribute::LayerFactorTextureSwizzle, MaterialTextureSwizzle::B}, + {MaterialAttribute::RoughnessTexture, 2u}, + {MaterialAttribute::RoughnessTextureSwizzle, MaterialTextureSwizzle::G}, + }, {0, 5}}; + CORRADE_VERIFY(!data.hasLayerFactorRoughnessTexture()); + + /* Unexpected swizzle 2 */ + } { + PbrClearCoatMaterialData data{{}, { + {MaterialLayer::ClearCoat}, + {MaterialAttribute::LayerFactorTexture, 2u}, + {MaterialAttribute::RoughnessTexture, 2u}, + {MaterialAttribute::RoughnessTextureSwizzle, MaterialTextureSwizzle::B} + }, {0, 4}}; + CORRADE_VERIFY(!data.hasLayerFactorRoughnessTexture()); + + /* Unexpected texture matrix */ + } { + PbrClearCoatMaterialData data{{}, { + {MaterialLayer::ClearCoat}, + {MaterialAttribute::LayerFactorTexture, 2u}, + {MaterialAttribute::LayerFactorTextureMatrix, Matrix3::scaling({0.5f, 1.0f})}, + {MaterialAttribute::RoughnessTexture, 2u}, + {MaterialAttribute::RoughnessTextureSwizzle, MaterialTextureSwizzle::G} + }, {0, 5}}; + CORRADE_VERIFY(!data.hasLayerFactorRoughnessTexture()); + + /* Unexpected texture coordinates */ + } { + PbrClearCoatMaterialData data{{}, { + {MaterialLayer::ClearCoat}, + {MaterialAttribute::LayerFactorTexture, 2u}, + {MaterialAttribute::RoughnessTexture, 2u}, + {MaterialAttribute::RoughnessTextureSwizzle, MaterialTextureSwizzle::G}, + {MaterialAttribute::RoughnessTextureCoordinates, 1u} + }, {0, 5}}; + CORRADE_VERIFY(!data.hasLayerFactorRoughnessTexture()); + } +} + void MaterialDataTest::pbrClearCoatAccessTexturedSingleMatrixCoordinates() { PbrClearCoatMaterialData data{{}, { {MaterialLayer::ClearCoat}, From 3ffcdf1865737ad1d0f4ef8821dc65225f8e7749 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 16 Aug 2020 13:20:54 +0200 Subject: [PATCH 35/36] Trade: improve compat of deprecated PhongMaterialData::textureMatrix(). The new materials now commonly import separate per-texture matrices instead of a single one even if they're all the same because that makes the plugin implementation *much* simpler. However, existing code that assumes there's just one matrix would get broken because textureMatrix() would not return something else. By changing that to return a common matrix if present and falling back to the global one we can preserve the original behavior. --- src/Magnum/Trade/PhongMaterialData.cpp | 1 + src/Magnum/Trade/Test/MaterialDataTest.cpp | 32 ++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/src/Magnum/Trade/PhongMaterialData.cpp b/src/Magnum/Trade/PhongMaterialData.cpp index 0447862562..1a8b9150e1 100644 --- a/src/Magnum/Trade/PhongMaterialData.cpp +++ b/src/Magnum/Trade/PhongMaterialData.cpp @@ -331,6 +331,7 @@ Matrix3 PhongMaterialData::commonTextureMatrix() const { #ifdef MAGNUM_BUILD_DEPRECATED Matrix3 PhongMaterialData::textureMatrix() const { + if(hasCommonTextureTransformation()) return commonTextureMatrix(); return attributeOr(MaterialAttribute::TextureMatrix, Matrix3{}); } #endif diff --git a/src/Magnum/Trade/Test/MaterialDataTest.cpp b/src/Magnum/Trade/Test/MaterialDataTest.cpp index e469b782e0..56cb0485bc 100644 --- a/src/Magnum/Trade/Test/MaterialDataTest.cpp +++ b/src/Magnum/Trade/Test/MaterialDataTest.cpp @@ -4211,6 +4211,14 @@ void MaterialDataTest::phongAccessCommonTransformationCoordinatesNoTextures() { CORRADE_COMPARE(a.commonTextureMatrix(), Matrix3{}); CORRADE_COMPARE(a.commonTextureCoordinates(), 0); + #ifdef MAGNUM_BUILD_DEPRECATED + /* textureMatrix() should return the common matrix, if possible, and + fall back to the global one if not */ + CORRADE_IGNORE_DEPRECATED_PUSH + CORRADE_COMPARE(a.textureMatrix(), Matrix3{}); + CORRADE_IGNORE_DEPRECATED_POP + #endif + PhongMaterialData b{{}, { {MaterialAttribute::TextureMatrix, Matrix3::scaling({0.5f, 0.5f})}, {MaterialAttribute::TextureCoordinates, 7u} @@ -4219,6 +4227,14 @@ void MaterialDataTest::phongAccessCommonTransformationCoordinatesNoTextures() { CORRADE_VERIFY(b.hasCommonTextureCoordinates()); CORRADE_COMPARE(b.commonTextureMatrix(), Matrix3::scaling({0.5f, 0.5f})); CORRADE_COMPARE(b.commonTextureCoordinates(), 7); + + #ifdef MAGNUM_BUILD_DEPRECATED + /* textureMatrix() should return the common matrix, if possible, and + fall back to the global one if not */ + CORRADE_IGNORE_DEPRECATED_PUSH + CORRADE_COMPARE(b.textureMatrix(), Matrix3::scaling({0.5f, 0.5f})); + CORRADE_IGNORE_DEPRECATED_POP + #endif } void MaterialDataTest::phongAccessCommonTransformationCoordinatesOneTexture() { @@ -4239,6 +4255,14 @@ void MaterialDataTest::phongAccessCommonTransformationCoordinatesOneTexture() { CORRADE_COMPARE(data.commonTextureMatrix(), Matrix3::scaling({0.5f, 1.0f})); CORRADE_VERIFY(data.hasCommonTextureCoordinates()); CORRADE_COMPARE(data.commonTextureCoordinates(), 17u); + + #ifdef MAGNUM_BUILD_DEPRECATED + /* textureMatrix() should return the common matrix, if possible, and + fall back to the global one if not */ + CORRADE_IGNORE_DEPRECATED_PUSH + CORRADE_COMPARE(data.textureMatrix(), Matrix3::scaling({0.5f, 1.0f})); + CORRADE_IGNORE_DEPRECATED_POP + #endif } void MaterialDataTest::phongAccessCommonTransformationCoordinatesOneDifferentTexture() { @@ -4261,6 +4285,14 @@ void MaterialDataTest::phongAccessCommonTransformationCoordinatesOneDifferentTex CORRADE_VERIFY(!data.hasCommonTextureTransformation()); CORRADE_VERIFY(!data.hasCommonTextureCoordinates()); + + #ifdef MAGNUM_BUILD_DEPRECATED + /* textureMatrix() should return the common matrix, if possible, and + fall back to the global one if not */ + CORRADE_IGNORE_DEPRECATED_PUSH + CORRADE_COMPARE(data.textureMatrix(), Matrix3::translation({0.5f, 0.0f})); + CORRADE_IGNORE_DEPRECATED_POP + #endif } void MaterialDataTest::phongAccessNoCommonTransformationCoordinates() { From 8c5a2d6a88392e6bf6b280d8bc04ee6ed695bdcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 16 Aug 2020 19:03:46 +0200 Subject: [PATCH 36/36] Platform: Xlib strikes again, this time with #define Bool int. THe new MaterialData APIs introduce MaterialAttributeType::Bool and everything kinda explodes with that. Seriously, what were the people in 1979 thinking. --- src/Magnum/Platform/AbstractXApplication.h | 6 ++++++ src/Magnum/Platform/WindowlessEglApplication.h | 6 ++++++ src/Magnum/Platform/WindowlessGlxApplication.h | 6 ++++++ 3 files changed, 18 insertions(+) diff --git a/src/Magnum/Platform/AbstractXApplication.h b/src/Magnum/Platform/AbstractXApplication.h index 1231ccc64a..6eb9cb9cb5 100644 --- a/src/Magnum/Platform/AbstractXApplication.h +++ b/src/Magnum/Platform/AbstractXApplication.h @@ -40,6 +40,7 @@ #include /* undef Xlib nonsense to avoid conflicts */ #undef Always +#undef Bool #undef Complex #undef Convex #undef None @@ -51,6 +52,11 @@ #undef Button4 #undef Button5 +#ifndef DOXYGEN_GENERATING_OUTPUT +/* Unfortunately Xlib *needs* the Bool type, so provide a typedef instead */ +typedef int Bool; +#endif + #include "Magnum/Magnum.h" #include "Magnum/Tags.h" #include "Magnum/GL/GL.h" diff --git a/src/Magnum/Platform/WindowlessEglApplication.h b/src/Magnum/Platform/WindowlessEglApplication.h index dd250f2b4f..44c5ec3b6d 100644 --- a/src/Magnum/Platform/WindowlessEglApplication.h +++ b/src/Magnum/Platform/WindowlessEglApplication.h @@ -37,12 +37,18 @@ #include /* undef Xlib nonsense to avoid conflicts */ #undef Always +#undef Bool #undef Complex #undef None #undef Status #include #include +#ifndef DOXYGEN_GENERATING_OUTPUT +/* Unfortunately Xlib *needs* the Bool type, so provide a typedef instead */ +typedef int Bool; +#endif + #include "Magnum/Magnum.h" #include "Magnum/GL/OpenGL.h" #include "Magnum/Tags.h" diff --git a/src/Magnum/Platform/WindowlessGlxApplication.h b/src/Magnum/Platform/WindowlessGlxApplication.h index 6f3fcf1d2c..7fb71f1987 100644 --- a/src/Magnum/Platform/WindowlessGlxApplication.h +++ b/src/Magnum/Platform/WindowlessGlxApplication.h @@ -46,6 +46,7 @@ #include /* undef Xlib nonsense to avoid conflicts */ #undef Always +#undef Bool #undef Complex #undef Convex #undef None @@ -57,6 +58,11 @@ #undef Button4 #undef Button5 +#ifndef DOXYGEN_GENERATING_OUTPUT +/* Unfortunately Xlib *needs* the Bool type, so provide a typedef instead */ +typedef int Bool; +#endif + namespace Magnum { namespace Platform { /**