mirror of https://github.com/mosra/magnum.git
Browse Source
Merging two sorted sequences is is something I always struggle with to write from scratch. Fortunately here I could just reuse what got done for DebugTools::CompareMaterial already, haha.pull/601/head
5 changed files with 568 additions and 1 deletions
@ -0,0 +1,120 @@
|
||||
/*
|
||||
This file is part of Magnum. |
||||
|
||||
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, |
||||
2020, 2021, 2022 Vladimír Vondruš <mosra@centrum.cz> |
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a |
||||
copy of this software and associated documentation files (the "Software"), |
||||
to deal in the Software without restriction, including without limitation |
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense, |
||||
and/or sell copies of the Software, and to permit persons to whom the |
||||
Software is furnished to do so, subject to the following conditions: |
||||
|
||||
The above copyright notice and this permission notice shall be included |
||||
in all copies or substantial portions of the Software. |
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
||||
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
||||
DEALINGS IN THE SOFTWARE. |
||||
*/ |
||||
|
||||
#include "Merge.h" |
||||
|
||||
#include <Corrade/Containers/GrowableArray.h> |
||||
#include <Corrade/Containers/Optional.h> |
||||
|
||||
#include "Magnum/Trade/MaterialData.h" |
||||
|
||||
namespace Magnum { namespace MaterialTools { |
||||
|
||||
Containers::Optional<Trade::MaterialData> merge(const Trade::MaterialData& first, const Trade::MaterialData& second, MergeConflicts conflicts) { |
||||
Containers::Array<Trade::MaterialAttributeData> attributes; |
||||
arrayReserve(attributes, first.attributeData().size() + second.attributeData().size()); |
||||
|
||||
Containers::Array<UnsignedInt> layers{NoInit, Math::max(first.layerCount(), second.layerCount())}; |
||||
|
||||
/* Go over all layers that are in both materials */ |
||||
std::size_t layer = 0; |
||||
for(const std::size_t layerMin = Math::min(first.layerCount(), second.layerCount()); layer != layerMin; ++layer) { |
||||
UnsignedInt attributeFirst = 0; |
||||
UnsignedInt attributeSecond = 0; |
||||
|
||||
/* Take the earliest-sorted attribute from either material */ |
||||
while(attributeFirst != first.attributeCount(layer) && attributeSecond != second.attributeCount(layer)) { |
||||
/* Attributes have the same name */ |
||||
/** @todo add StringView::compare() returning -1, 0, 1 and do the
|
||||
comparison just once instead of three times */ |
||||
if(first.attributeName(layer, attributeFirst) == second.attributeName(layer, attributeSecond)) { |
||||
/* Fail if we are told to not merge attributes of the same
|
||||
name */ |
||||
if(conflicts == MergeConflicts::Fail) { |
||||
Error{} << "MaterialTools::merge(): conflicting attribute" << first.attributeName(layer, attributeFirst) << "in layer" << layer; |
||||
return {}; |
||||
} |
||||
|
||||
/* Fail if we are told to not merge attributes of the same name
|
||||
but different type */ |
||||
if(first.attributeType(layer, attributeFirst) != second.attributeType(layer, attributeSecond) && conflicts == MergeConflicts::KeepFirstIfSameType) { |
||||
Error{} << "MaterialTools::merge(): conflicting type" << first.attributeType(layer, attributeFirst) << "vs" << Debug::packed << second.attributeType(layer, attributeSecond) << "of attribute" << first.attributeName(layer, attributeFirst) << "in layer" << layer; |
||||
return {}; |
||||
} |
||||
|
||||
/* Add the first attribute, ignore the second */ |
||||
arrayAppend(attributes, first.attributeData(layer, attributeFirst)); |
||||
++attributeFirst; |
||||
++attributeSecond; |
||||
|
||||
/* The attribute from first material should go first */ |
||||
} else if(first.attributeName(layer, attributeFirst) < second.attributeName(layer, attributeSecond)) { |
||||
arrayAppend(attributes, first.attributeData(layer, attributeFirst)); |
||||
++attributeFirst; |
||||
|
||||
/* The attribute from second material should go first */ |
||||
} else if(first.attributeName(layer, attributeFirst) > second.attributeName(layer, attributeSecond)) { |
||||
arrayAppend(attributes, second.attributeData(layer, attributeSecond)); |
||||
++attributeSecond; |
||||
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ |
||||
} |
||||
|
||||
/* Consume remaining leftover attributes in either. Only one of these
|
||||
loops get entered. */ |
||||
while(attributeFirst < first.attributeCount(layer)) { |
||||
arrayAppend(attributes, first.attributeData(layer, attributeFirst)); |
||||
++attributeFirst; |
||||
} |
||||
while(attributeSecond < second.attributeCount(layer)) { |
||||
arrayAppend(attributes, second.attributeData(layer, attributeSecond)); |
||||
++attributeSecond; |
||||
} |
||||
|
||||
layers[layer] = attributes.size(); |
||||
} |
||||
|
||||
/* Go over remaining layers which weren't in the other attribute and
|
||||
add them as a whole. Only one of these loops get entered. */ |
||||
for(; layer < first.layerCount(); ++layer) { |
||||
arrayAppend(attributes, first.attributeData().slice( |
||||
first.attributeDataOffset(layer), |
||||
first.attributeDataOffset(layer + 1))); |
||||
|
||||
layers[layer] = attributes.size(); |
||||
} |
||||
for(; layer < second.layerCount(); ++layer) { |
||||
arrayAppend(attributes, second.attributeData().slice( |
||||
second.attributeDataOffset(layer), |
||||
second.attributeDataOffset(layer + 1))); |
||||
|
||||
layers[layer] = attributes.size(); |
||||
} |
||||
|
||||
CORRADE_INTERNAL_ASSERT(layer == layers.size()); |
||||
|
||||
return Trade::MaterialData{first.types()|second.types(), std::move(attributes), std::move(layers)}; |
||||
} |
||||
|
||||
}} |
||||
@ -0,0 +1,96 @@
|
||||
#ifndef Magnum_MaterialTools_Merge_h |
||||
#define Magnum_MaterialTools_Merge_h |
||||
/*
|
||||
This file is part of Magnum. |
||||
|
||||
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, |
||||
2020, 2021, 2022 Vladimír Vondruš <mosra@centrum.cz> |
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a |
||||
copy of this software and associated documentation files (the "Software"), |
||||
to deal in the Software without restriction, including without limitation |
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense, |
||||
and/or sell copies of the Software, and to permit persons to whom the |
||||
Software is furnished to do so, subject to the following conditions: |
||||
|
||||
The above copyright notice and this permission notice shall be included |
||||
in all copies or substantial portions of the Software. |
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
||||
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
||||
DEALINGS IN THE SOFTWARE. |
||||
*/ |
||||
|
||||
/** @file
|
||||
* @brief Function @ref Magnum::MaterialTools::merge(), enum @ref Magnum::MaterialTools::MergeConflicts |
||||
* @m_since_latest |
||||
*/ |
||||
|
||||
#include "Magnum/MaterialTools/visibility.h" |
||||
#include "Magnum/Trade/Trade.h" |
||||
|
||||
namespace Magnum { namespace MaterialTools { |
||||
|
||||
/**
|
||||
@brief Material merge conflict resolution |
||||
@m_since_latest |
||||
|
||||
@see @ref merge() |
||||
*/ |
||||
enum MergeConflicts: UnsignedInt { |
||||
/**
|
||||
* Print a message to @relativeref{Magnum,Error} and return |
||||
* @ref Containers::NullOpt in case both materials contain an attribute of |
||||
* the same name in the same layer index. Neither its type nor its value is |
||||
* checked, so this fails also in case the values are the same. |
||||
*/ |
||||
Fail, |
||||
|
||||
/**
|
||||
* Keep the value from the first material in case both materials contain an |
||||
* attribute of the same name in the same layer index and both attributes |
||||
* have the same type. Print a message to @relativeref{Magnum,Error} and |
||||
* return @ref Containers::NullOpt if they have a different type, for |
||||
* example in case of custom attributes. |
||||
* |
||||
* If you want to keep the value from the second material instead, call |
||||
* @ref merge() with this option and the materials swapped. |
||||
*/ |
||||
KeepFirstIfSameType, |
||||
|
||||
/**
|
||||
* Keep the value from the first material in case both materials contain an |
||||
* attribute of the same name in the same layer index, regardless of their |
||||
* type. With this option the operation always succeeds. |
||||
* |
||||
* If you want to keep the value from the second material instead, call |
||||
* @ref merge() with this option and the materials swapped. |
||||
*/ |
||||
KeepFirstIgnoreType |
||||
}; |
||||
|
||||
/**
|
||||
@brief Merge two materials |
||||
@m_since_latest |
||||
|
||||
Takes attributes from @p second and inserts them to layers of the same index in |
||||
@p first. If @p second has more layers than @p first, the additional layers are |
||||
added at the end of @p first. @ref Trade::MaterialTypes from @p first and |
||||
@p second are merged together. If both materials contain an attribute of the |
||||
same name in the same layer index, conflict resolution is performed according |
||||
to the @p conflicts option. |
||||
|
||||
As the input materials have the attributes sorted already, the operation is |
||||
done in an @f$ \mathcal{O}(m + n) @f$ execution time and memory complexity, |
||||
with @f$ m @f$ and @f$ n @f$ being count of all attributes and layers in |
||||
@p first and @p second, respectively. |
||||
*/ |
||||
Containers::Optional<Trade::MaterialData> MAGNUM_MATERIALTOOLS_EXPORT merge(const Trade::MaterialData& first, const Trade::MaterialData& second, MergeConflicts conflicts = MergeConflicts::Fail); |
||||
|
||||
}} |
||||
|
||||
#endif |
||||
@ -0,0 +1,348 @@
|
||||
/*
|
||||
This file is part of Magnum. |
||||
|
||||
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, |
||||
2020, 2021, 2022 Vladimír Vondruš <mosra@centrum.cz> |
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a |
||||
copy of this software and associated documentation files (the "Software"), |
||||
to deal in the Software without restriction, including without limitation |
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense, |
||||
and/or sell copies of the Software, and to permit persons to whom the |
||||
Software is furnished to do so, subject to the following conditions: |
||||
|
||||
The above copyright notice and this permission notice shall be included |
||||
in all copies or substantial portions of the Software. |
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
||||
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
||||
DEALINGS IN THE SOFTWARE. |
||||
*/ |
||||
|
||||
#include <sstream> |
||||
#include <Corrade/TestSuite/Tester.h> |
||||
#include <Corrade/Utility/DebugStl.h> |
||||
|
||||
#include "Magnum/DebugTools/CompareMaterial.h" |
||||
#include "Magnum/Math/Color.h" |
||||
#include "Magnum/MaterialTools/Merge.h" |
||||
#include "Magnum/Trade/MaterialData.h" |
||||
|
||||
namespace Magnum { namespace MaterialTools { namespace Test { namespace { |
||||
|
||||
struct MergeTest: TestSuite::Tester { |
||||
explicit MergeTest(); |
||||
|
||||
void singleLayer(); |
||||
|
||||
void multipleLayersIntoSingleLayer(); |
||||
void multipleLayers(); |
||||
|
||||
void conflictsSameType(); |
||||
void conflictsDifferentType(); |
||||
void conflictsFail(); |
||||
|
||||
void emptyInput(); |
||||
}; |
||||
|
||||
using namespace Math::Literals; |
||||
|
||||
MergeTest::MergeTest() { |
||||
addTests({&MergeTest::singleLayer, |
||||
|
||||
&MergeTest::multipleLayersIntoSingleLayer, |
||||
&MergeTest::multipleLayers, |
||||
|
||||
&MergeTest::conflictsSameType, |
||||
&MergeTest::conflictsDifferentType, |
||||
&MergeTest::conflictsFail, |
||||
|
||||
&MergeTest::emptyInput}); |
||||
} |
||||
|
||||
void MergeTest::singleLayer() { |
||||
Trade::MaterialData a{Trade::MaterialType::PbrMetallicRoughness, { |
||||
{Trade::MaterialAttribute::BaseColor, 0xffcc66ff_rgbaf}, |
||||
/* These two go at the end */ |
||||
{Trade::MaterialAttribute::RoughnessTexture, 7u}, |
||||
{Trade::MaterialAttribute::Roughness, 0.3f} |
||||
}}; |
||||
|
||||
Trade::MaterialData b{Trade::MaterialType::Phong|Trade::MaterialType::Flat, { |
||||
/* This attribute goes first */ |
||||
{Trade::MaterialAttribute::AlphaBlend, true}, |
||||
{Trade::MaterialAttribute::DiffuseColor, 0x808080ff_rgbaf}, |
||||
{Trade::MaterialAttribute::BaseColorTexture, 3u}, |
||||
{Trade::MaterialAttribute::BaseColorTextureCoordinates, 2u}, |
||||
}}; |
||||
|
||||
Trade::MaterialData expected{Trade::MaterialType::PbrMetallicRoughness|Trade::MaterialType::Phong|Trade::MaterialType::Flat, { |
||||
{Trade::MaterialAttribute::AlphaBlend, true}, |
||||
{Trade::MaterialAttribute::BaseColor, 0xffcc66ff_rgbaf}, |
||||
{Trade::MaterialAttribute::BaseColorTexture, 3u}, |
||||
{Trade::MaterialAttribute::BaseColorTextureCoordinates, 2u}, |
||||
{Trade::MaterialAttribute::DiffuseColor, 0x808080ff_rgbaf}, |
||||
{Trade::MaterialAttribute::Roughness, 0.3f}, |
||||
{Trade::MaterialAttribute::RoughnessTexture, 7u}, |
||||
}}; |
||||
|
||||
/* It should give the same result both ways */ |
||||
{ |
||||
Containers::Optional<Trade::MaterialData> actual = merge(a, b); |
||||
CORRADE_VERIFY(actual); |
||||
CORRADE_COMPARE_AS(*actual, expected, DebugTools::CompareMaterial); |
||||
} { |
||||
Containers::Optional<Trade::MaterialData> actual = merge(b, a); |
||||
CORRADE_VERIFY(actual); |
||||
CORRADE_COMPARE_AS(*actual, expected, DebugTools::CompareMaterial); |
||||
} |
||||
} |
||||
|
||||
void MergeTest::multipleLayersIntoSingleLayer() { |
||||
Trade::MaterialData a{Trade::MaterialType::PbrMetallicRoughness, { |
||||
{Trade::MaterialAttribute::BaseColor, 0xffcc66ff_rgbaf}, |
||||
{Trade::MaterialAttribute::RoughnessTexture, 7u}, |
||||
{Trade::MaterialAttribute::Roughness, 0.3f}, |
||||
}}; |
||||
|
||||
Trade::MaterialData b{Trade::MaterialType::PbrClearCoat|Trade::MaterialType::PbrMetallicRoughness, { |
||||
{Trade::MaterialAttribute::AlphaBlend, true}, |
||||
/* These two layers are only in this material */ |
||||
{Trade::MaterialLayer::ClearCoat}, |
||||
{Trade::MaterialAttribute::LayerFactor, 0.1f}, |
||||
{Trade::MaterialAttribute::Roughness, 0.5f}, |
||||
{Trade::MaterialAttribute::LayerFactor, 1.0f}, |
||||
{"layerBlendApproach", "irreversibly"} |
||||
}, {1, 4, 6}}; |
||||
|
||||
Trade::MaterialData expected{Trade::MaterialType::PbrMetallicRoughness|Trade::MaterialType::PbrClearCoat, { |
||||
{Trade::MaterialAttribute::AlphaBlend, true}, |
||||
{Trade::MaterialAttribute::BaseColor, 0xffcc66ff_rgbaf}, |
||||
{Trade::MaterialAttribute::Roughness, 0.3f}, |
||||
{Trade::MaterialAttribute::RoughnessTexture, 7u}, |
||||
{Trade::MaterialLayer::ClearCoat}, |
||||
{Trade::MaterialAttribute::LayerFactor, 0.1f}, |
||||
{Trade::MaterialAttribute::Roughness, 0.5f}, |
||||
{Trade::MaterialAttribute::LayerFactor, 1.0f}, |
||||
{"layerBlendApproach", "irreversibly"} |
||||
}, {4, 7, 9}}; |
||||
|
||||
/* It should give the same result both ways */ |
||||
{ |
||||
Containers::Optional<Trade::MaterialData> actual = merge(a, b); |
||||
CORRADE_VERIFY(actual); |
||||
CORRADE_COMPARE_AS(*actual, expected, DebugTools::CompareMaterial); |
||||
} { |
||||
Containers::Optional<Trade::MaterialData> actual = merge(b, a); |
||||
CORRADE_VERIFY(actual); |
||||
CORRADE_COMPARE_AS(*actual, expected, DebugTools::CompareMaterial); |
||||
} |
||||
} |
||||
|
||||
void MergeTest::multipleLayers() { |
||||
Trade::MaterialData a{Trade::MaterialType::PbrMetallicRoughness, { |
||||
{Trade::MaterialAttribute::BaseColor, 0xffcc66ff_rgbaf}, |
||||
{Trade::MaterialAttribute::RoughnessTexture, 7u}, |
||||
{Trade::MaterialAttribute::Roughness, 0.3f}, |
||||
/* This layer has no name but it'll get it from the other material
|
||||
(and that's fine) */ |
||||
{Trade::MaterialAttribute::LayerFactor, 0.1f}, |
||||
{Trade::MaterialAttribute::Roughness, 0.5f}, |
||||
}, {3, 5}}; |
||||
|
||||
Trade::MaterialData b{Trade::MaterialType::PbrClearCoat|Trade::MaterialType::PbrMetallicRoughness, { |
||||
{Trade::MaterialAttribute::AlphaBlend, true}, |
||||
/* These two layers are only in this material */ |
||||
{Trade::MaterialLayer::ClearCoat}, |
||||
/* Here's an empty layer that ends up being empty as well */ |
||||
{Trade::MaterialAttribute::LayerFactor, 1.0f}, |
||||
{"layerBlendApproach", "irreversibly"} |
||||
}, {1, 2, 2, 4}}; |
||||
|
||||
Trade::MaterialData expected{Trade::MaterialType::PbrMetallicRoughness|Trade::MaterialType::PbrClearCoat, { |
||||
{Trade::MaterialAttribute::AlphaBlend, true}, |
||||
{Trade::MaterialAttribute::BaseColor, 0xffcc66ff_rgbaf}, |
||||
{Trade::MaterialAttribute::Roughness, 0.3f}, |
||||
{Trade::MaterialAttribute::RoughnessTexture, 7u}, |
||||
{Trade::MaterialLayer::ClearCoat}, |
||||
{Trade::MaterialAttribute::LayerFactor, 0.1f}, |
||||
{Trade::MaterialAttribute::Roughness, 0.5f}, |
||||
/* Empty layer here */ |
||||
{Trade::MaterialAttribute::LayerFactor, 1.0f}, |
||||
{"layerBlendApproach", "irreversibly"} |
||||
}, {4, 7, 7, 9}}; |
||||
|
||||
/* It should give the same result both ways */ |
||||
{ |
||||
Containers::Optional<Trade::MaterialData> actual = merge(a, b); |
||||
CORRADE_VERIFY(actual); |
||||
CORRADE_COMPARE_AS(*actual, expected, DebugTools::CompareMaterial); |
||||
} { |
||||
Containers::Optional<Trade::MaterialData> actual = merge(b, a); |
||||
CORRADE_VERIFY(actual); |
||||
CORRADE_COMPARE_AS(*actual, expected, DebugTools::CompareMaterial); |
||||
} |
||||
} |
||||
|
||||
void MergeTest::conflictsSameType() { |
||||
Trade::MaterialData a{Trade::MaterialType::PbrMetallicRoughness, { |
||||
{Trade::MaterialAttribute::BaseColor, 0xffcc66ff_rgbaf}, |
||||
{Trade::MaterialAttribute::RoughnessTexture, 7u}, |
||||
/* Second layer */ |
||||
{Trade::MaterialAttribute::Roughness, 0.3f}, |
||||
{"customAttribute", 15.0f}, |
||||
}, {2, 4}}; |
||||
|
||||
Trade::MaterialData b{Trade::MaterialType::PbrClearCoat, { |
||||
{Trade::MaterialAttribute::Roughness, 0.5f}, |
||||
{Trade::MaterialAttribute::RoughnessTexture, 777u}, |
||||
{Trade::MaterialAttribute::LayerFactor, 1.0f}, |
||||
{"customAttribute", 223.0f}, |
||||
}, {2, 4}}; |
||||
|
||||
/* If called swapped it'll use the other values */ |
||||
{ |
||||
Containers::Optional<Trade::MaterialData> actual = merge(a, b, MergeConflicts::KeepFirstIfSameType); |
||||
CORRADE_VERIFY(actual); |
||||
CORRADE_COMPARE_AS(*actual, (Trade::MaterialData {Trade::MaterialType::PbrMetallicRoughness|Trade::MaterialType::PbrClearCoat, { |
||||
{Trade::MaterialAttribute::BaseColor, 0xffcc66ff_rgbaf}, |
||||
{Trade::MaterialAttribute::Roughness, 0.5f}, |
||||
{Trade::MaterialAttribute::RoughnessTexture, 7u}, |
||||
{Trade::MaterialAttribute::LayerFactor, 1.0f}, |
||||
{Trade::MaterialAttribute::Roughness, 0.3f}, |
||||
{"customAttribute", 15.0f}, |
||||
}, {3, 6}}), DebugTools::CompareMaterial); |
||||
} { |
||||
Containers::Optional<Trade::MaterialData> actual = merge(b, a, MergeConflicts::KeepFirstIfSameType); |
||||
CORRADE_VERIFY(actual); |
||||
CORRADE_COMPARE_AS(*actual, (Trade::MaterialData {Trade::MaterialType::PbrMetallicRoughness|Trade::MaterialType::PbrClearCoat, { |
||||
{Trade::MaterialAttribute::BaseColor, 0xffcc66ff_rgbaf}, |
||||
{Trade::MaterialAttribute::Roughness, 0.5f}, |
||||
{Trade::MaterialAttribute::RoughnessTexture, 777u}, /* different */ |
||||
{Trade::MaterialAttribute::LayerFactor, 1.0f}, |
||||
{Trade::MaterialAttribute::Roughness, 0.3f}, |
||||
{"customAttribute", 223.0f}, /* different */ |
||||
}, {3, 6}}), DebugTools::CompareMaterial); |
||||
} |
||||
} |
||||
|
||||
void MergeTest::conflictsDifferentType() { |
||||
Trade::MaterialData a{Trade::MaterialType::PbrMetallicRoughness, { |
||||
{Trade::MaterialAttribute::BaseColor, 0xffcc66ff_rgbaf}, |
||||
{Trade::MaterialAttribute::RoughnessTexture, 7u}, |
||||
/* Second layer */ |
||||
{Trade::MaterialAttribute::Roughness, 0.3f}, |
||||
{"customAttribute", 15.0f}, |
||||
}, {2, 4}}; |
||||
|
||||
/* Builtin attributes have an enforced type so this can only happen with
|
||||
custom ones. It should however handle (builtin) attributes of the same |
||||
type as well */ |
||||
Trade::MaterialData b{Trade::MaterialType::PbrClearCoat, { |
||||
{Trade::MaterialAttribute::Roughness, 0.5f}, |
||||
{Trade::MaterialAttribute::RoughnessTexture, 777u}, |
||||
{Trade::MaterialAttribute::LayerFactor, 1.0f}, |
||||
{"customAttribute", "hello!"}, |
||||
}, {2, 4}}; |
||||
|
||||
/* If called swapped it'll use the other values */ |
||||
{ |
||||
Containers::Optional<Trade::MaterialData> actual = merge(a, b, MergeConflicts::KeepFirstIgnoreType); |
||||
CORRADE_VERIFY(actual); |
||||
CORRADE_COMPARE_AS(*actual, (Trade::MaterialData {Trade::MaterialType::PbrMetallicRoughness|Trade::MaterialType::PbrClearCoat, { |
||||
{Trade::MaterialAttribute::BaseColor, 0xffcc66ff_rgbaf}, |
||||
{Trade::MaterialAttribute::Roughness, 0.5f}, |
||||
{Trade::MaterialAttribute::RoughnessTexture, 7u}, |
||||
{Trade::MaterialAttribute::LayerFactor, 1.0f}, |
||||
{Trade::MaterialAttribute::Roughness, 0.3f}, |
||||
{"customAttribute", 15.0f}, |
||||
}, {3, 6}}), DebugTools::CompareMaterial); |
||||
} { |
||||
Containers::Optional<Trade::MaterialData> actual = merge(b, a, MergeConflicts::KeepFirstIgnoreType); |
||||
CORRADE_VERIFY(actual); |
||||
CORRADE_COMPARE_AS(*actual, (Trade::MaterialData {Trade::MaterialType::PbrMetallicRoughness|Trade::MaterialType::PbrClearCoat, { |
||||
{Trade::MaterialAttribute::BaseColor, 0xffcc66ff_rgbaf}, |
||||
{Trade::MaterialAttribute::Roughness, 0.5f}, |
||||
{Trade::MaterialAttribute::RoughnessTexture, 777u}, /* different */ |
||||
{Trade::MaterialAttribute::LayerFactor, 1.0f}, |
||||
{Trade::MaterialAttribute::Roughness, 0.3f}, |
||||
{"customAttribute", "hello!"}, /* different */ |
||||
}, {3, 6}}), DebugTools::CompareMaterial); |
||||
} |
||||
} |
||||
|
||||
void MergeTest::conflictsFail() { |
||||
CORRADE_SKIP_IF_NO_ASSERT(); |
||||
|
||||
Trade::MaterialData a{{}, { |
||||
{Trade::MaterialAttribute::BaseColor, 0xffcc66ff_rgbaf}, |
||||
{Trade::MaterialAttribute::RoughnessTexture, 7u}, |
||||
/* Second layer */ |
||||
{Trade::MaterialAttribute::Roughness, 0.3f}, |
||||
{"customAttribute", 15.0f}, |
||||
}, {2, 4}}; |
||||
|
||||
/* Contains Roughness but in another layer (which should be fine),
|
||||
but has a conflicitng BaseColorTexture even though it's the same |
||||
value */ |
||||
Trade::MaterialData b{{}, { |
||||
{Trade::MaterialAttribute::Roughness, 0.5f}, |
||||
{Trade::MaterialAttribute::RoughnessTexture, 7u}, |
||||
{Trade::MaterialAttribute::LayerFactor, 1.0f}, |
||||
}, {2, 3}}; |
||||
|
||||
/* Contains customAttribute in second layer which is of a different type.
|
||||
The RoughnessTexture is also conflicting but that shouldn't produce a |
||||
message since it's the same type. */ |
||||
Trade::MaterialData c{{}, { |
||||
{Trade::MaterialAttribute::Roughness, 0.5f}, |
||||
{Trade::MaterialAttribute::RoughnessTexture, 1u}, |
||||
{"customAttribute", "hello"}, |
||||
}, {2, 3}}; |
||||
|
||||
/* Verify that it fails in all variants */ |
||||
std::ostringstream out; |
||||
Error redirectError{&out}; |
||||
CORRADE_VERIFY(!merge(a, b)); |
||||
CORRADE_VERIFY(!merge(b, a)); |
||||
CORRADE_VERIFY(!merge(a, c, MergeConflicts::KeepFirstIfSameType)); |
||||
CORRADE_VERIFY(!merge(c, a, MergeConflicts::KeepFirstIfSameType)); |
||||
CORRADE_COMPARE(out.str(), |
||||
"MaterialTools::merge(): conflicting attribute RoughnessTexture in layer 0\n" |
||||
"MaterialTools::merge(): conflicting attribute RoughnessTexture in layer 0\n" |
||||
"MaterialTools::merge(): conflicting type Trade::MaterialAttributeType::Float vs String of attribute customAttribute in layer 1\n" |
||||
"MaterialTools::merge(): conflicting type Trade::MaterialAttributeType::String vs Float of attribute customAttribute in layer 1\n"); |
||||
} |
||||
|
||||
void MergeTest::emptyInput() { |
||||
Trade::MaterialData a{Trade::MaterialType::PbrMetallicRoughness, { |
||||
{Trade::MaterialAttribute::BaseColor, 0xffcc66ff_rgbaf}, |
||||
{Trade::MaterialAttribute::Roughness, 0.3f}, |
||||
{Trade::MaterialLayer::ClearCoat}, |
||||
{Trade::MaterialAttribute::LayerFactor, 0.1f}, |
||||
}, {2, 4}}; |
||||
|
||||
Trade::MaterialData empty{Trade::MaterialType::PbrClearCoat, {}}; |
||||
|
||||
/* The result has just the types changed, nothing else */ |
||||
Trade::MaterialData expected{Trade::MaterialType::PbrMetallicRoughness|Trade::MaterialType::PbrClearCoat, {}, a.attributeData(), {}, a.layerData()}; |
||||
|
||||
/* It should give the same result both ways */ |
||||
{ |
||||
Containers::Optional<Trade::MaterialData> actual = merge(a, empty); |
||||
CORRADE_VERIFY(actual); |
||||
CORRADE_COMPARE_AS(*actual, expected, DebugTools::CompareMaterial); |
||||
} { |
||||
Containers::Optional<Trade::MaterialData> actual = merge(empty, a); |
||||
CORRADE_VERIFY(actual); |
||||
CORRADE_COMPARE_AS(*actual, expected, DebugTools::CompareMaterial); |
||||
} |
||||
} |
||||
|
||||
}}}} |
||||
|
||||
CORRADE_TEST_MAIN(Magnum::MaterialTools::Test::MergeTest) |
||||
Loading…
Reference in new issue