mirror of https://github.com/mosra/magnum.git
Browse Source
Now that there will be a whole new MaterialTools library, I really need this helper.pull/605/head
10 changed files with 930 additions and 8 deletions
@ -0,0 +1,69 @@
|
||||
/*
|
||||
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 <Corrade/TestSuite/Tester.h> |
||||
|
||||
#include "Magnum/Math/Color.h" |
||||
#include "Magnum/Trade/MaterialData.h" |
||||
#include "Magnum/DebugTools/CompareMaterial.h" |
||||
|
||||
using namespace Magnum; |
||||
|
||||
#define DOXYGEN_ELLIPSIS(...) __VA_ARGS__ |
||||
|
||||
struct MaterialTest: TestSuite::Tester { |
||||
explicit MaterialTest(); |
||||
|
||||
void conversion(); |
||||
}; |
||||
|
||||
MaterialTest::MaterialTest() { |
||||
addTests({&MaterialTest::conversion}); |
||||
} |
||||
|
||||
void MaterialTest::conversion() { |
||||
Trade::MaterialData actual{Trade::MaterialType::PbrClearCoat|Trade::MaterialType::PbrMetallicRoughness, { |
||||
{Trade::MaterialAttribute::BaseColor, Color4{0.851206f, 0.687386f, 0.416013f}}, |
||||
{Trade::MaterialAttribute::Metalness, 0.603401f}, |
||||
{Trade::MaterialAttribute::Roughness, 0.105112f}, |
||||
{Trade::MaterialAttribute::DoubleSided, true}, |
||||
{Trade::MaterialLayer::ClearCoat}, |
||||
{Trade::MaterialAttribute::LayerFactor, 0.02f}, |
||||
{Trade::MaterialAttribute::Roughness, 0.320856f}, |
||||
}, {4, 7}}; |
||||
|
||||
Trade::MaterialData expected{Trade::MaterialType::PbrMetallicRoughness, { |
||||
{Trade::MaterialAttribute::BaseColor, Color4{0.851206f, 0.687386f, 0.416013f}}, |
||||
{Trade::MaterialAttribute::Metalness, 0.603401f}, |
||||
{Trade::MaterialAttribute::Roughness, 0.105112f}, |
||||
{Trade::MaterialAttribute::DoubleSided, false} |
||||
}}; |
||||
|
||||
/* [usage] */ |
||||
CORRADE_COMPARE_AS(actual, expected, DebugTools::CompareMaterial); |
||||
/* [usage] */ |
||||
} |
||||
|
||||
CORRADE_TEST_MAIN(MaterialTest) |
||||
@ -0,0 +1,395 @@
|
||||
/*
|
||||
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 "CompareMaterial.h" |
||||
|
||||
#include <Corrade/Containers/Array.h> |
||||
#include <Corrade/Containers/GrowableArray.h> |
||||
#include <Corrade/Containers/Triple.h> |
||||
#include <Corrade/Utility/Math.h> /* max() that works with enums */ |
||||
#include <Corrade/TestSuite/Comparator.h> |
||||
|
||||
#include "Magnum/Math/Matrix.h" |
||||
#include "Magnum/Math/Vector4.h" |
||||
#include "Magnum/Trade/MaterialData.h" |
||||
|
||||
namespace Corrade { namespace TestSuite { |
||||
|
||||
using namespace Magnum; |
||||
|
||||
namespace { |
||||
|
||||
/* Higher values mean bigger change */ |
||||
enum class MaterialState { |
||||
Same, |
||||
DifferentTypes, |
||||
DifferentAttributeValues, |
||||
DifferentAttributeTypes, |
||||
DifferentAttributes, |
||||
DifferentLayers |
||||
}; |
||||
|
||||
enum class AttributeState { |
||||
Same, |
||||
DifferentValue, |
||||
DifferentType, |
||||
OnlyInExpected, |
||||
OnlyInActual |
||||
}; |
||||
|
||||
} |
||||
|
||||
struct Comparator<DebugTools::CompareMaterial>::State { |
||||
MaterialState materialState = MaterialState::Same; |
||||
|
||||
/* Second is attribute ID in the actual material (unless AttributeState is
|
||||
OnlyInExpected), third is attribute ID in the expected material (unless |
||||
AttributeState is OnlyInActual). */ |
||||
Containers::Array<Containers::Triple<AttributeState, UnsignedInt, UnsignedInt>> attributes; |
||||
/* Offsets into the attributes array for each layer. I.e., layer i is
|
||||
stored in `attributes[layerOffsets[i]]` to |
||||
`attributes[layerOffsets[i + 1]]`. */ |
||||
Containers::Array<UnsignedInt> layerOffsets; |
||||
|
||||
const Trade::MaterialData* actual{}; |
||||
const Trade::MaterialData* expected{}; |
||||
}; |
||||
|
||||
Comparator<DebugTools::CompareMaterial>::Comparator(): _state{InPlaceInit} {} |
||||
|
||||
Comparator<DebugTools::CompareMaterial>::~Comparator() = default; |
||||
|
||||
namespace { |
||||
|
||||
bool attributesEqual(const Trade::MaterialAttributeData& a, const Trade::MaterialAttributeData& b) { |
||||
#ifdef CORRADE_TARGET_GCC |
||||
#pragma GCC diagnostic push |
||||
#pragma GCC diagnostic error "-Wswitch" |
||||
#endif |
||||
CORRADE_INTERNAL_ASSERT(a.type() == b.type()); |
||||
switch(a.type()) { |
||||
#define _c(type) case Trade::MaterialAttributeType::type: \ |
||||
return a.value<type>() == b.value<type>(); |
||||
#define _ct(name, type) case Trade::MaterialAttributeType::name: \ |
||||
return a.value<type>() == b.value<type>(); |
||||
_ct(Bool, bool) |
||||
/* LCOV_EXCL_START */ |
||||
_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) |
||||
/* LCOV_EXCL_STOP */ |
||||
_ct(Pointer, const void*) |
||||
_ct(MutablePointer, void*) |
||||
_ct(String, Containers::StringView) |
||||
_ct(TextureSwizzle, Trade::MaterialTextureSwizzle) |
||||
#undef _c |
||||
#undef _ct |
||||
case Trade::MaterialAttributeType::Buffer: |
||||
return Containers::StringView{Containers::arrayCast<const char>(a.value<Containers::ArrayView<const void>>())} == Containers::StringView{Containers::arrayCast<const char>(b.value<Containers::ArrayView<const void>>())}; |
||||
} |
||||
#ifdef CORRADE_TARGET_GCC |
||||
#pragma GCC diagnostic pop |
||||
#endif |
||||
|
||||
CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ |
||||
} |
||||
|
||||
} |
||||
|
||||
TestSuite::ComparisonStatusFlags Comparator<DebugTools::CompareMaterial>::operator()(const Trade::MaterialData& actual, const Trade::MaterialData& expected) { |
||||
_state->actual = &actual; |
||||
_state->expected = &expected; |
||||
|
||||
if(actual.types() != expected.types()) |
||||
_state->materialState = MaterialState::DifferentTypes; |
||||
|
||||
/* The layer offset array has one extra item for the last layer count */ |
||||
const std::size_t layerMax = Math::max(actual.layerCount(), expected.layerCount()); |
||||
_state->layerOffsets = Containers::Array<UnsignedInt>{NoInit, layerMax + 1}; |
||||
|
||||
/* Go over all layers that are in both materials */ |
||||
std::size_t layer = 0; |
||||
for(const std::size_t layerMin = Math::min(actual.layerCount(), expected.layerCount()); layer != layerMin; ++layer) { |
||||
_state->layerOffsets[layer] = _state->attributes.size(); |
||||
UnsignedInt inActual = 0; |
||||
UnsignedInt inExpected = 0; |
||||
|
||||
/* Take the earliest-sorted attribute from either material */ |
||||
while(inActual != actual.attributeCount(layer) && inExpected != expected.attributeCount(layer)) { |
||||
if(actual.attributeName(layer, inActual) == expected.attributeName(layer, inExpected)) { |
||||
AttributeState attributeState; |
||||
if(actual.attributeType(layer, inActual) != expected.attributeType(layer, inExpected)) { |
||||
attributeState = AttributeState::DifferentType; |
||||
_state->materialState = Utility::max(_state->materialState, MaterialState::DifferentAttributeTypes); |
||||
} else if(!attributesEqual(actual.attributeData(layer, inActual), expected.attributeData(layer, inExpected))) { |
||||
attributeState = AttributeState::DifferentValue; |
||||
_state->materialState = Utility::max(_state->materialState, MaterialState::DifferentAttributeValues); |
||||
} else { |
||||
attributeState = AttributeState::Same; |
||||
} |
||||
|
||||
arrayAppend(_state->attributes, InPlaceInit, attributeState, inActual, inExpected); |
||||
++inActual; |
||||
++inExpected; |
||||
} else if(actual.attributeName(inActual) < expected.attributeName(inExpected)) { |
||||
arrayAppend(_state->attributes, InPlaceInit, AttributeState::OnlyInActual, inActual, ~UnsignedInt{}); |
||||
_state->materialState = Utility::max(_state->materialState, MaterialState::DifferentAttributes); |
||||
++inActual; |
||||
} else if(actual.attributeName(inActual) > expected.attributeName(inExpected)) { |
||||
arrayAppend(_state->attributes, InPlaceInit, AttributeState::OnlyInExpected, ~UnsignedInt{}, inExpected); |
||||
_state->materialState = Utility::max(_state->materialState, MaterialState::DifferentAttributes); |
||||
++inExpected; |
||||
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ |
||||
} |
||||
|
||||
/* Consume remaining leftover attributes in either. Only one of these
|
||||
loops get entered. */ |
||||
while(inActual < actual.attributeCount(layer)) { |
||||
arrayAppend(_state->attributes, InPlaceInit, AttributeState::OnlyInActual, inActual, ~UnsignedInt{}); |
||||
_state->materialState = Utility::max(_state->materialState, MaterialState::DifferentAttributes); |
||||
++inActual; |
||||
} |
||||
while(inExpected < expected.attributeCount(layer)) { |
||||
arrayAppend(_state->attributes, InPlaceInit, AttributeState::OnlyInExpected, ~UnsignedInt{}, inExpected); |
||||
_state->materialState = Utility::max(_state->materialState, MaterialState::DifferentAttributes); |
||||
++inExpected; |
||||
} |
||||
} |
||||
|
||||
/* Go over remaining actual/expected layers which weren't in
|
||||
expected/actual. Only one of these loops get entered. */ |
||||
for(; layer < actual.layerCount(); ++layer) { |
||||
_state->layerOffsets[layer] = _state->attributes.size(); |
||||
|
||||
for(UnsignedInt inActual = 0, inActualMax = actual.attributeCount(layer); inActual != inActualMax; ++inActual) |
||||
arrayAppend(_state->attributes, InPlaceInit, AttributeState::OnlyInActual, inActual, ~UnsignedInt{}); |
||||
|
||||
_state->materialState = Utility::max(_state->materialState, MaterialState::DifferentLayers); |
||||
} |
||||
for(; layer < expected.layerCount(); ++layer) { |
||||
_state->layerOffsets[layer] = _state->attributes.size(); |
||||
|
||||
for(UnsignedInt inExpected = 0, inExpectedMax = expected.attributeCount(layer); inExpected != inExpectedMax; ++inExpected) |
||||
arrayAppend(_state->attributes, InPlaceInit, AttributeState::OnlyInExpected, ~UnsignedInt{}, inExpected); |
||||
|
||||
_state->materialState = Utility::max(_state->materialState, MaterialState::DifferentLayers); |
||||
} |
||||
|
||||
CORRADE_INTERNAL_ASSERT(layer == _state->layerOffsets.size() - 1); |
||||
_state->layerOffsets[layer] = _state->attributes.size(); |
||||
|
||||
/** @todo If there's a large sequence of Same attributes, elide the middle
|
||||
(mark it with SameEllipsis, e.g., and leave at most 3 before and 3 |
||||
after each difference), and skip them when printing unless Verbose is |
||||
set. Would become practically useful only once there's really a lot |
||||
material attributes in a single layer, currently not really. */ |
||||
|
||||
return _state->materialState == MaterialState::Same ? TestSuite::ComparisonStatusFlags{} : TestSuite::ComparisonStatusFlag::Failed; |
||||
} |
||||
|
||||
namespace { |
||||
|
||||
void printAttribute(Debug& out, const Trade::MaterialAttributeData& attribute, const AttributeState state, const bool isActual) { |
||||
out << Debug::newline << " "; |
||||
if(state == AttributeState::Same) |
||||
out << " "; |
||||
else if(isActual) |
||||
out << Debug::color(Debug::Color::Green) << "+"; |
||||
else |
||||
out << Debug::color(Debug::Color::Red) << "-"; |
||||
if(state == AttributeState::DifferentType || |
||||
state == AttributeState::DifferentValue) |
||||
out << Debug::resetColor; |
||||
|
||||
out << "" << attribute.name() << "@"; |
||||
|
||||
if(state == AttributeState::DifferentType) |
||||
out << Debug::color(isActual ? Debug::Color::Green : Debug::Color::Red); |
||||
out << Debug::packed << attribute.type(); |
||||
if(state == AttributeState::DifferentType) |
||||
out << Debug::resetColor; |
||||
out << Debug::nospace << ":"; |
||||
|
||||
if(state == AttributeState::DifferentType || |
||||
state == AttributeState::DifferentValue) |
||||
out << Debug::color(isActual ? Debug::Color::Green : Debug::Color::Red); |
||||
#ifdef CORRADE_TARGET_GCC |
||||
#pragma GCC diagnostic push |
||||
#pragma GCC diagnostic error "-Wswitch" |
||||
#endif |
||||
switch(attribute.type()) { |
||||
#define _c(type) case Trade::MaterialAttributeType::type: \ |
||||
out << Debug::packed << attribute.value<type>(); \
|
||||
break; |
||||
#define _ct(name, type) case Trade::MaterialAttributeType::name: \ |
||||
out << Debug::packed << attribute.value<type>(); \
|
||||
break; |
||||
_ct(Bool, bool) |
||||
/* LCOV_EXCL_START */ |
||||
_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) |
||||
/* LCOV_EXCL_STOP */ |
||||
_ct(Pointer, const void*) |
||||
_ct(MutablePointer, void*) |
||||
_ct(String, Containers::StringView) |
||||
_ct(TextureSwizzle, Trade::MaterialTextureSwizzle) |
||||
#undef _c |
||||
#undef _ct |
||||
case Trade::MaterialAttributeType::Buffer: |
||||
out << Containers::arrayCast<const char>(attribute.value<Containers::ArrayView<const void>>()); |
||||
} |
||||
#ifdef CORRADE_TARGET_GCC |
||||
#pragma GCC diagnostic pop |
||||
#endif |
||||
|
||||
if(state != AttributeState::Same) |
||||
out << Debug::resetColor; |
||||
} |
||||
|
||||
} |
||||
|
||||
void Comparator<DebugTools::CompareMaterial>::printMessage(const TestSuite::ComparisonStatusFlags, Debug& out, const Containers::StringView actual, const Containers::StringView expected) const { |
||||
out << "Materials" << actual << "and" << expected; |
||||
|
||||
if(_state->materialState == MaterialState::DifferentLayers) { |
||||
out << "have different layers."; |
||||
} else if(_state->materialState == MaterialState::DifferentAttributes) { |
||||
out << "have different attributes."; |
||||
} else if(_state->materialState == MaterialState::DifferentAttributeTypes) { |
||||
out << "have different attribute types."; |
||||
} else if(_state->materialState == MaterialState::DifferentAttributeValues) { |
||||
out << "have different attribute values."; |
||||
} else if(_state->materialState == MaterialState::DifferentTypes) { |
||||
out << "have different types."; |
||||
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ |
||||
|
||||
out << Debug::color(Debug::Color::Green) << "Actual (+)" |
||||
<< Debug::resetColor << "vs" << Debug::color(Debug::Color::Red) |
||||
<< "expected (-)" << Debug::resetColor << Debug::nospace << ":"; |
||||
|
||||
/* Print the type, or both if they differ */ |
||||
/** @todo Or maybe if it's MaterialState::DifferentType print just this and
|
||||
skip the attributes? Because everything else that's printed is the |
||||
same... */ |
||||
if(_state->expected->types()) { |
||||
out << Debug::newline << " "; |
||||
if(_state->expected->types() != _state->actual->types()) |
||||
out << Debug::color(Debug::Color::Red) << "-" << Debug::nospace; |
||||
else |
||||
out << ""; |
||||
if(_state->actual->types() && _state->expected->types() != _state->actual->types()) |
||||
out << Debug::resetColor; |
||||
out << "Types:"; |
||||
if(_state->actual->types() && _state->expected->types() != _state->actual->types()) |
||||
out << Debug::color(Debug::Color::Red); |
||||
out << Debug::packed << _state->expected->types() << Debug::resetColor; |
||||
} |
||||
if(_state->actual->types() && _state->actual->types() != _state->expected->types()) { |
||||
out << Debug::newline << " " << Debug::color(Debug::Color::Green) << "+" << Debug::nospace; |
||||
if(_state->expected->types()) out << Debug::resetColor; |
||||
out << "Types:"; |
||||
if(_state->expected->types()) out << Debug::color(Debug::Color::Green); |
||||
out << Debug::packed << _state->actual->types() << Debug::resetColor; |
||||
} |
||||
|
||||
/* Print content of both materials, interleaved, with layers and attributes
|
||||
that differ marked with +/- */ |
||||
for(UnsignedInt layer = 0, layerMax = _state->layerOffsets.size() - 1; layer != layerMax; ++layer) { |
||||
/* Show layer header only if there's more than one and the base
|
||||
layer isn't empty */ |
||||
if(_state->layerOffsets.size() != 2 || _state->layerOffsets[1] != 0) { |
||||
out << Debug::newline << " "; |
||||
if(layer >= _state->actual->layerCount()) |
||||
out << Debug::color(Debug::Color::Red) << "-" << Debug::nospace; |
||||
else if(layer >= _state->expected->layerCount()) |
||||
out << Debug::color(Debug::Color::Green) << "+" << Debug::nospace; |
||||
else |
||||
out << ""; |
||||
if(layer == 0) |
||||
out << "Base layer:"; |
||||
else |
||||
out << "Layer" << layer << Debug::nospace << ":"; |
||||
if(layer >= _state->actual->layerCount() || layer >= _state->expected->layerCount()) |
||||
out << Debug::resetColor; |
||||
} |
||||
|
||||
/* Print attribute values indented only if there's more than one
|
||||
layer */ |
||||
for(UnsignedInt id = _state->layerOffsets[layer], idMax = _state->layerOffsets[layer + 1]; id != idMax; ++id) { |
||||
const Containers::Triple<AttributeState, UnsignedInt, UnsignedInt> attribute = _state->attributes[id]; |
||||
const AttributeState state =attribute.first(); |
||||
if(state != AttributeState::OnlyInActual) |
||||
printAttribute(out, _state->expected->attributeData(layer, attribute.third()), state, false); |
||||
if(state != AttributeState::Same && state != AttributeState::OnlyInExpected) { |
||||
printAttribute(out, _state->actual->attributeData(layer, attribute.second()), state, true); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
}} |
||||
@ -0,0 +1,109 @@
|
||||
#ifndef Magnum_DebugTools_CompareMaterial_h |
||||
#define Magnum_DebugTools_CompareMaterial_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 Class @ref Magnum::DebugTools::CompareMaterial |
||||
* @m_since_latest |
||||
*/ |
||||
|
||||
#include <Corrade/Containers/Pointer.h> |
||||
#include <Corrade/TestSuite/TestSuite.h> |
||||
|
||||
#include "Magnum/Magnum.h" |
||||
#include "Magnum/DebugTools/visibility.h" |
||||
#include "Magnum/Trade/Trade.h" |
||||
|
||||
namespace Magnum { namespace DebugTools { |
||||
|
||||
class CompareMaterial; |
||||
|
||||
}} |
||||
|
||||
#ifndef DOXYGEN_GENERATING_OUTPUT |
||||
/* If Doxygen sees this, all @ref Corrade::TestSuite links break (prolly
|
||||
because the namespace is undocumented in this project) */ |
||||
namespace Corrade { namespace TestSuite { |
||||
|
||||
template<> class MAGNUM_DEBUGTOOLS_EXPORT Comparator<Magnum::DebugTools::CompareMaterial> { |
||||
public: |
||||
explicit Comparator(); |
||||
~Comparator(); |
||||
|
||||
ComparisonStatusFlags operator()(const Magnum::Trade::MaterialData& actual, const Magnum::Trade::MaterialData& expected); |
||||
|
||||
void printMessage(ComparisonStatusFlags flags, Utility::Debug& out, Containers::StringView actual, Containers::StringView expected) const; |
||||
|
||||
private: |
||||
struct State; |
||||
Containers::Pointer<State> _state; |
||||
}; |
||||
|
||||
}} |
||||
#endif |
||||
|
||||
namespace Magnum { namespace DebugTools { |
||||
|
||||
/**
|
||||
@brief Material comparator for @ref Corrade::TestSuite |
||||
@m_since_latest |
||||
|
||||
Compares @ref Trade::MaterialData instances, printing the differences in the |
||||
two if they have a different type, different layer count, different attributes, |
||||
and different type or different value of the same attribute. Pass the |
||||
comparator to @ref CORRADE_COMPARE_AS() along with an actual and expected |
||||
material: |
||||
|
||||
@snippet debugtools-comparematerial.cpp usage |
||||
|
||||
Based on actual materials used, in case of a comparison failure the comparator |
||||
can give for example the following output: |
||||
|
||||
@m_class{m-console-wrap} |
||||
|
||||
@include debugtools-comparematerial.ansi |
||||
|
||||
All @ref Trade::MaterialAttributeType are supported. |
||||
|
||||
@see @ref CompareImage |
||||
*/ |
||||
class MAGNUM_DEBUGTOOLS_EXPORT CompareMaterial { |
||||
public: |
||||
explicit CompareMaterial(); |
||||
|
||||
#ifndef DOXYGEN_GENERATING_OUTPUT |
||||
TestSuite::Comparator<CompareMaterial>& comparator() { |
||||
return _c; |
||||
} |
||||
#endif |
||||
|
||||
private: |
||||
TestSuite::Comparator<CompareMaterial> _c; |
||||
}; |
||||
|
||||
}} |
||||
|
||||
#endif |
||||
@ -0,0 +1,323 @@
|
||||
/*
|
||||
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/Containers/ArrayView.h> |
||||
#include <Corrade/TestSuite/Tester.h> |
||||
#include <Corrade/Utility/DebugStl.h> |
||||
|
||||
#include "Magnum/DebugTools/CompareMaterial.h" |
||||
#include "Magnum/Math/Color.h" |
||||
#include "Magnum/Trade/MaterialData.h" |
||||
|
||||
namespace Magnum { namespace DebugTools { namespace Test { namespace { |
||||
|
||||
struct CompareMaterialTest: TestSuite::Tester { |
||||
explicit CompareMaterialTest(); |
||||
|
||||
void same(); |
||||
void different(); |
||||
void differentReverse(); |
||||
}; |
||||
|
||||
using namespace Containers::Literals; |
||||
using namespace Math::Literals; |
||||
|
||||
const struct { |
||||
const char* name; |
||||
Trade::MaterialData material; |
||||
} SameData[]{ |
||||
{"empty", Trade::MaterialData{{}, {}}}, |
||||
{"empty with types", Trade::MaterialData{Trade::MaterialType::PbrMetallicRoughness|Trade::MaterialType::PbrClearCoat, {}}}, |
||||
{"base attributes", Trade::MaterialData{Trade::MaterialType::Phong, { |
||||
{Trade::MaterialAttribute::DiffuseColor, 0x556699aa_rgbaf}, |
||||
{Trade::MaterialAttribute::NormalTexture, 5u}, |
||||
{"name", "hello"_s}, |
||||
}}}, |
||||
{"layers", Trade::MaterialData{Trade::MaterialType::PbrMetallicRoughness|Trade::MaterialType::PbrClearCoat, { |
||||
{Trade::MaterialAttribute::NormalTexture, 5u}, |
||||
{Trade::MaterialLayer::ClearCoat}, |
||||
{Trade::MaterialAttribute::LayerFactor, 0.76f}, |
||||
{"name", "hello"_s}, |
||||
}, {1, 3, 4}}}, |
||||
}; |
||||
|
||||
const struct { |
||||
const char* name; |
||||
Trade::MaterialData actual; |
||||
Trade::MaterialData expected; |
||||
const char* message; |
||||
const char* messageReverse; |
||||
} DifferentData[]{ |
||||
{"empty, different types", |
||||
Trade::MaterialData{Trade::MaterialType::Flat, {}}, |
||||
Trade::MaterialData{Trade::MaterialType::PbrClearCoat|Trade::MaterialType::PbrMetallicRoughness, {}}, |
||||
"Materials a and b have different types. Actual (+) vs expected (-):\n" |
||||
" -Types: PbrMetallicRoughness|PbrClearCoat\n" |
||||
" +Types: Flat\n", |
||||
"Materials b and a have different types. Actual (+) vs expected (-):\n" |
||||
" -Types: Flat\n" |
||||
" +Types: PbrMetallicRoughness|PbrClearCoat\n"}, |
||||
{"different types", |
||||
Trade::MaterialData{Trade::MaterialType::Flat, { |
||||
{Trade::MaterialAttribute::BaseColor, 0xff00ffff_rgbaf}, |
||||
}}, |
||||
Trade::MaterialData{Trade::MaterialType::PbrClearCoat|Trade::MaterialType::PbrMetallicRoughness, { |
||||
{Trade::MaterialAttribute::BaseColor, 0xff00ffff_rgbaf}, |
||||
}}, |
||||
"Materials a and b have different types. Actual (+) vs expected (-):\n" |
||||
" -Types: PbrMetallicRoughness|PbrClearCoat\n" |
||||
" +Types: Flat\n" |
||||
" Base layer:\n" |
||||
" BaseColor @ Vector4: {1, 0, 1, 1}\n", |
||||
"Materials b and a have different types. Actual (+) vs expected (-):\n" |
||||
" -Types: Flat\n" |
||||
" +Types: PbrMetallicRoughness|PbrClearCoat\n" |
||||
" Base layer:\n" |
||||
" BaseColor @ Vector4: {1, 0, 1, 1}\n"}, |
||||
{"different types, one empty", |
||||
Trade::MaterialData{Trade::MaterialType::Flat, { |
||||
{Trade::MaterialAttribute::BaseColor, 0xff00ffff_rgbaf} |
||||
}}, |
||||
Trade::MaterialData{{}, { |
||||
{Trade::MaterialAttribute::BaseColor, 0xff00ffff_rgbaf} |
||||
}}, |
||||
"Materials a and b have different types. Actual (+) vs expected (-):\n" |
||||
" +Types: Flat\n" |
||||
" Base layer:\n" |
||||
" BaseColor @ Vector4: {1, 0, 1, 1}\n", |
||||
"Materials b and a have different types. Actual (+) vs expected (-):\n" |
||||
" -Types: Flat\n" |
||||
" Base layer:\n" |
||||
" BaseColor @ Vector4: {1, 0, 1, 1}\n"}, |
||||
{"different attributes", |
||||
Trade::MaterialData{Trade::MaterialType::PbrMetallicRoughness, { |
||||
{Trade::MaterialAttribute::DoubleSided, true}, |
||||
{Trade::MaterialAttribute::NormalTexture, 5u}, |
||||
{Trade::MaterialAttribute::NormalTextureScale, 0.5f}, |
||||
{Trade::MaterialAttribute::OcclusionTexture, 3u}, |
||||
}}, |
||||
Trade::MaterialData{Trade::MaterialType::PbrMetallicRoughness, { |
||||
{Trade::MaterialAttribute::Metalness, 5.5f}, |
||||
{Trade::MaterialAttribute::NormalTexture, 5u}, |
||||
{Trade::MaterialAttribute::NormalTextureLayer, 2u}, |
||||
}}, |
||||
"Materials a and b have different attributes. Actual (+) vs expected (-):\n" |
||||
" Types: PbrMetallicRoughness\n" |
||||
" Base layer:\n" |
||||
" + DoubleSided @ Bool: true\n" |
||||
" - Metalness @ Float: 5.5\n" |
||||
" NormalTexture @ UnsignedInt: 5\n" |
||||
" - NormalTextureLayer @ UnsignedInt: 2\n" |
||||
" + NormalTextureScale @ Float: 0.5\n" |
||||
" + OcclusionTexture @ UnsignedInt: 3\n", |
||||
"Materials b and a have different attributes. Actual (+) vs expected (-):\n" |
||||
" Types: PbrMetallicRoughness\n" |
||||
" Base layer:\n" |
||||
" - DoubleSided @ Bool: true\n" |
||||
" + Metalness @ Float: 5.5\n" |
||||
" NormalTexture @ UnsignedInt: 5\n" |
||||
" + NormalTextureLayer @ UnsignedInt: 2\n" |
||||
" - NormalTextureScale @ Float: 0.5\n" |
||||
" - OcclusionTexture @ UnsignedInt: 3\n"}, |
||||
{"different attribute types", |
||||
Trade::MaterialData{{}, { |
||||
{"pointer", reinterpret_cast<void*>(0xdead)}, |
||||
{"integer", 5u}, |
||||
{"scale", 0.5f}, |
||||
}}, |
||||
Trade::MaterialData{{}, { |
||||
{"pointer", reinterpret_cast<const void*>(0xdead)}, |
||||
{"integer", 5}, |
||||
{"scale", "small"}, |
||||
}}, |
||||
"Materials a and b have different attribute types. Actual (+) vs expected (-):\n" |
||||
" Base layer:\n" |
||||
" - integer @ Int: 5\n" |
||||
" + integer @ UnsignedInt: 5\n" |
||||
" - pointer @ Pointer: 0xdead\n" |
||||
" + pointer @ MutablePointer: 0xdead\n" |
||||
" - scale @ String: small\n" |
||||
" + scale @ Float: 0.5\n", |
||||
"Materials b and a have different attribute types. Actual (+) vs expected (-):\n" |
||||
" Base layer:\n" |
||||
" - integer @ UnsignedInt: 5\n" |
||||
" + integer @ Int: 5\n" |
||||
" - pointer @ MutablePointer: 0xdead\n" |
||||
" + pointer @ Pointer: 0xdead\n" |
||||
" - scale @ Float: 0.5\n" |
||||
" + scale @ String: small\n"}, |
||||
{"different attribute values", |
||||
Trade::MaterialData{{}, { |
||||
{Trade::MaterialAttribute::Metalness, 0.3f}, |
||||
{Trade::MaterialAttribute::NormalTexture, 5u}, |
||||
{Trade::MaterialAttribute::NormalTextureSwizzle, Trade::MaterialTextureSwizzle::RGB}, |
||||
{"buffer", Containers::ArrayView<const void>{"\x56\x78\x22"}}, |
||||
{"pointer", reinterpret_cast<const void*>(0xbeef)}, |
||||
{"pointerMutable", reinterpret_cast<void*>(0xdead)}, |
||||
}}, |
||||
Trade::MaterialData{{}, { |
||||
{Trade::MaterialAttribute::Metalness, 5.5f}, |
||||
{Trade::MaterialAttribute::NormalTexture, 5u}, |
||||
{Trade::MaterialAttribute::NormalTextureSwizzle, Trade::MaterialTextureSwizzle::RG}, |
||||
{"buffer", Containers::ArrayView<const void>{"\x56\x78\x22"}}, |
||||
{"pointer", reinterpret_cast<const void*>(0xbeef)}, |
||||
{"pointerMutable", reinterpret_cast<void*>(0xdead)}, |
||||
}}, |
||||
"Materials a and b have different attribute values. Actual (+) vs expected (-):\n" |
||||
" Base layer:\n" |
||||
" - Metalness @ Float: 5.5\n" |
||||
" + Metalness @ Float: 0.3\n" |
||||
" NormalTexture @ UnsignedInt: 5\n" |
||||
" - NormalTextureSwizzle @ TextureSwizzle: RG\n" |
||||
" + NormalTextureSwizzle @ TextureSwizzle: RGB\n" |
||||
" buffer @ Buffer: {86, 120, 34, 0}\n" |
||||
" pointer @ Pointer: 0xbeef\n" |
||||
" pointerMutable @ MutablePointer: 0xdead\n", |
||||
"Materials b and a have different attribute values. Actual (+) vs expected (-):\n" |
||||
" Base layer:\n" |
||||
" - Metalness @ Float: 0.3\n" |
||||
" + Metalness @ Float: 5.5\n" |
||||
" NormalTexture @ UnsignedInt: 5\n" |
||||
" - NormalTextureSwizzle @ TextureSwizzle: RGB\n" |
||||
" + NormalTextureSwizzle @ TextureSwizzle: RG\n" |
||||
" buffer @ Buffer: {86, 120, 34, 0}\n" |
||||
" pointer @ Pointer: 0xbeef\n" |
||||
" pointerMutable @ MutablePointer: 0xdead\n"}, |
||||
{"different attributes in layers", |
||||
Trade::MaterialData{{}, { |
||||
{Trade::MaterialAttribute::DoubleSided, true}, |
||||
{Trade::MaterialAttribute::NormalTexture, 5u}, |
||||
{Trade::MaterialAttribute::NormalTextureScale, 0.5f}, |
||||
{Trade::MaterialAttribute::OcclusionTexture, 3u}, |
||||
{"texturePointer", reinterpret_cast<void*>(0xdead)}, |
||||
}, {3, 5}}, |
||||
Trade::MaterialData{{}, { |
||||
{Trade::MaterialAttribute::DoubleSided, false}, |
||||
{Trade::MaterialAttribute::NormalTexture, 5u}, |
||||
{Trade::MaterialAttribute::OcclusionTexture, 3u}, |
||||
{"texturePointer", reinterpret_cast<const void*>(0xdead)}, |
||||
{Trade::MaterialAttribute::NormalTextureLayer, 2u}, |
||||
}, {2, 4, 5}}, |
||||
"Materials a and b have different layers. Actual (+) vs expected (-):\n" |
||||
" Base layer:\n" |
||||
" - DoubleSided @ Bool: false\n" |
||||
" + DoubleSided @ Bool: true\n" |
||||
" NormalTexture @ UnsignedInt: 5\n" |
||||
" + NormalTextureScale @ Float: 0.5\n" |
||||
" Layer 1:\n" |
||||
" OcclusionTexture @ UnsignedInt: 3\n" |
||||
" - texturePointer @ Pointer: 0xdead\n" |
||||
" + texturePointer @ MutablePointer: 0xdead\n" |
||||
" -Layer 2:\n" |
||||
" - NormalTextureLayer @ UnsignedInt: 2\n", |
||||
"Materials b and a have different layers. Actual (+) vs expected (-):\n" |
||||
" Base layer:\n" |
||||
" - DoubleSided @ Bool: true\n" |
||||
" + DoubleSided @ Bool: false\n" |
||||
" NormalTexture @ UnsignedInt: 5\n" |
||||
" - NormalTextureScale @ Float: 0.5\n" |
||||
" Layer 1:\n" |
||||
" OcclusionTexture @ UnsignedInt: 3\n" |
||||
" - texturePointer @ MutablePointer: 0xdead\n" |
||||
" + texturePointer @ Pointer: 0xdead\n" |
||||
" +Layer 2:\n" |
||||
" + NormalTextureLayer @ UnsignedInt: 2\n"}, |
||||
{"different (empty) layer count", |
||||
Trade::MaterialData{{}, {}}, |
||||
Trade::MaterialData{{}, {}, {0, 0, 0}}, |
||||
"Materials a and b have different layers. Actual (+) vs expected (-):\n" |
||||
" Base layer:\n" |
||||
" -Layer 1:\n" |
||||
" -Layer 2:\n", |
||||
"Materials b and a have different layers. Actual (+) vs expected (-):\n" |
||||
" Base layer:\n" |
||||
" +Layer 1:\n" |
||||
" +Layer 2:\n"}, |
||||
}; |
||||
|
||||
CompareMaterialTest::CompareMaterialTest() { |
||||
addInstancedTests({&CompareMaterialTest::same}, |
||||
Containers::arraySize(SameData)); |
||||
|
||||
addInstancedTests({&CompareMaterialTest::different, |
||||
&CompareMaterialTest::differentReverse}, |
||||
Containers::arraySize(DifferentData)); |
||||
} |
||||
|
||||
void CompareMaterialTest::same() { |
||||
auto&& data = SameData[testCaseInstanceId()]; |
||||
setTestCaseDescription(data.name); |
||||
|
||||
CORRADE_COMPARE_AS(data.material, data.material, CompareMaterial); |
||||
} |
||||
|
||||
void CompareMaterialTest::different() { |
||||
auto&& data = DifferentData[testCaseInstanceId()]; |
||||
setTestCaseDescription(data.name); |
||||
|
||||
TestSuite::Comparator<CompareMaterial> compare; |
||||
TestSuite::ComparisonStatusFlags flags = compare(data.actual, data.expected); |
||||
CORRADE_COMPARE(flags, TestSuite::ComparisonStatusFlag::Failed); |
||||
|
||||
CORRADE_INFO("Visual color verification:"); |
||||
{ |
||||
Debug out; |
||||
compare.printMessage(flags, out, "a", "b"); |
||||
} |
||||
|
||||
std::ostringstream out; |
||||
{ |
||||
Debug dc{&out, Debug::Flag::DisableColors}; |
||||
compare.printMessage(flags, dc, "a", "b"); |
||||
} |
||||
CORRADE_COMPARE(out.str(), data.message); |
||||
} |
||||
|
||||
void CompareMaterialTest::differentReverse() { |
||||
auto&& data = DifferentData[testCaseInstanceId()]; |
||||
setTestCaseDescription(data.name); |
||||
|
||||
TestSuite::Comparator<CompareMaterial> compare; |
||||
TestSuite::ComparisonStatusFlags flags = compare(data.expected, data.actual); |
||||
CORRADE_COMPARE(flags, TestSuite::ComparisonStatusFlag::Failed); |
||||
|
||||
CORRADE_INFO("Visual color verification:"); |
||||
{ |
||||
Debug out; |
||||
compare.printMessage(flags, out, "b", "a"); |
||||
} |
||||
|
||||
std::ostringstream out; |
||||
{ |
||||
Debug dc{&out, Debug::Flag::DisableColors}; |
||||
compare.printMessage(flags, dc, "b", "a"); |
||||
} |
||||
CORRADE_COMPARE(out.str(), data.messageReverse); |
||||
} |
||||
|
||||
}}}} |
||||
|
||||
CORRADE_TEST_MAIN(Magnum::DebugTools::Test::CompareMaterialTest) |
||||
Loading…
Reference in new issue