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] 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 51b36b765..ba560ed56 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 783c589aa..ed0f089e1 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());