From 78cf81bfcad047fcc56e3c03f4579b16bcd0fd37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Thu, 7 Oct 2021 13:08:41 +0200 Subject: [PATCH] Trade: mutable access to MaterialData attribute values. Hah, so many overloads. Not providing mutable access to keys or layer offsets as that would break the invariant of the internal array always being sorted. --- doc/snippets/MagnumTrade.cpp | 9 + src/Magnum/Trade/MaterialData.cpp | 100 +++- src/Magnum/Trade/MaterialData.h | 315 ++++++++++++- src/Magnum/Trade/MaterialLayerData.h | 87 ++++ src/Magnum/Trade/Test/MaterialDataTest.cpp | 519 ++++++++++++++++++++- 5 files changed, 1005 insertions(+), 25 deletions(-) diff --git a/doc/snippets/MagnumTrade.cpp b/doc/snippets/MagnumTrade.cpp index ef5be24aa..33750cbd6 100644 --- a/doc/snippets/MagnumTrade.cpp +++ b/doc/snippets/MagnumTrade.cpp @@ -468,6 +468,15 @@ if(data.types() & Trade::MaterialType::PbrClearCoat) { /* [MaterialData-usage-layers-types] */ } +{ +Trade::MaterialData data{{}, {}}; +/* [MaterialData-usage-mutable] */ +Color4& color = data.mutableAttribute(Trade::MaterialAttribute::BaseColor); +ColorHsv hsv = color.toHsv(); +color.rgb() = Color3::fromHsv({hsv.hue, hsv.saturation*0.85f, hsv.value}); +/* [MaterialData-usage-mutable] */ +} + { /* [MaterialData-populating] */ Trade::MaterialData data{Trade::MaterialType::PbrMetallicRoughness, { diff --git a/src/Magnum/Trade/MaterialData.cpp b/src/Magnum/Trade/MaterialData.cpp index 856026528..d0732faf1 100644 --- a/src/Magnum/Trade/MaterialData.cpp +++ b/src/Magnum/Trade/MaterialData.cpp @@ -203,7 +203,7 @@ template<> MAGNUM_TRADE_EXPORT Containers::StringView MaterialAttributeData::val } #endif -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} { +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}, _attributeDataFlags{DataFlag::Owned|DataFlag::Mutable}, _layerDataFlags{DataFlag::Owned|DataFlag::Mutable}, _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. */ @@ -247,7 +247,14 @@ 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(), reinterpret_cast(Implementation::nonOwnedArrayDeleter)}}, _layerOffsets{Containers::Array{const_cast(layerData.data()), layerData.size(), reinterpret_cast(Implementation::nonOwnedArrayDeleter)}}, _types{types}, _importerState{importerState} { +MaterialData::MaterialData(const MaterialTypes types, const DataFlags attributeDataFlags, const Containers::ArrayView attributeData, const DataFlags layerDataFlags, 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} { + CORRADE_ASSERT(!(attributeDataFlags & DataFlag::Owned), + "Trade::MaterialData: can't construct with non-owned attribute data but" << attributeDataFlags, ); + CORRADE_ASSERT(!(layerDataFlags & DataFlag::Owned), + "Trade::MaterialData: can't construct with non-owned layer data but" << layerDataFlags, ); + _attributeDataFlags = attributeDataFlags; + _layerDataFlags = layerDataFlags; + #ifndef CORRADE_NO_ASSERT /* Not checking what's already done in MaterialAttributeData constructor */ for(std::size_t i = 0; i != _data.size(); ++i) @@ -685,6 +692,16 @@ const void* MaterialData::attribute(const UnsignedInt layer, const UnsignedInt i return _data[layerOffset(layer) + id].value(); } +void* MaterialData::mutableAttribute(const UnsignedInt layer, const UnsignedInt id) { + CORRADE_ASSERT(_attributeDataFlags & DataFlag::Mutable, + "Trade::MaterialData::mutableAttribute(): attribute data not mutable", {}); + CORRADE_ASSERT(layer < layerCount(), + "Trade::MaterialData::mutableAttribute(): index" << layer << "out of range for" << layerCount() << "layers", {}); + CORRADE_ASSERT(id < attributeCount(layer), + "Trade::MaterialData::mutableAttribute(): index" << id << "out of range for" << attributeCount(layer) << "attributes in layer" << layer, {}); + return const_cast(_data[layerOffset(layer) + id].value()); +} + 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", {}); @@ -694,12 +711,29 @@ const void* MaterialData::attribute(const UnsignedInt layer, const Containers::S return _data[layerOffset(layer) + id].value(); } +void* MaterialData::mutableAttribute(const UnsignedInt layer, const Containers::StringView name) { + CORRADE_ASSERT(_attributeDataFlags & DataFlag::Mutable, + "Trade::MaterialData::mutableAttribute(): attribute data not mutable", {}); + CORRADE_ASSERT(layer < layerCount(), + "Trade::MaterialData::mutableAttribute(): index" << layer << "out of range for" << layerCount() << "layers", {}); + const UnsignedInt id = attributeFor(layer, name); + CORRADE_ASSERT(id != ~UnsignedInt{}, + "Trade::MaterialData::mutableAttribute(): attribute" << name << "not found in layer" << layer, {}); + return const_cast(_data[layerOffset(layer) + id].value()); +} + 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(layer, string); } +void* MaterialData::mutableAttribute(const UnsignedInt layer, const MaterialAttribute name) { + const Containers::StringView string = attributeString(name); + CORRADE_ASSERT(string.data(), "Trade::MaterialData::mutableAttribute(): invalid name" << name, {}); + return mutableAttribute(layer, string); +} + const void* MaterialData::attribute(const Containers::StringView layer, const UnsignedInt id) const { const UnsignedInt layerId = layerFor(layer); CORRADE_ASSERT(layerId != ~UnsignedInt{}, @@ -709,6 +743,17 @@ const void* MaterialData::attribute(const Containers::StringView layer, const Un return _data[layerOffset(layerId) + id].value(); } +void* MaterialData::mutableAttribute(const Containers::StringView layer, const UnsignedInt id) { + CORRADE_ASSERT(_attributeDataFlags & DataFlag::Mutable, + "Trade::MaterialData::mutableAttribute(): attribute data not mutable", {}); + const UnsignedInt layerId = layerFor(layer); + CORRADE_ASSERT(layerId != ~UnsignedInt{}, + "Trade::MaterialData::mutableAttribute(): layer" << layer << "not found", {}); + CORRADE_ASSERT(id < attributeCount(layer), + "Trade::MaterialData::mutableAttribute(): index" << id << "out of range for" << attributeCount(layer) << "attributes in layer" << layer, {}); + return const_cast(_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{}, @@ -719,30 +764,66 @@ const void* MaterialData::attribute(const Containers::StringView layer, const Co return _data[layerOffset(layerId) + id].value(); } +void* MaterialData::mutableAttribute(const Containers::StringView layer, const Containers::StringView name) { + CORRADE_ASSERT(_attributeDataFlags & DataFlag::Mutable, + "Trade::MaterialData::mutableAttribute(): attribute data not mutable", {}); + const UnsignedInt layerId = layerFor(layer); + CORRADE_ASSERT(layerId != ~UnsignedInt{}, + "Trade::MaterialData::mutableAttribute(): layer" << layer << "not found", {}); + const UnsignedInt id = attributeFor(layerId, name); + CORRADE_ASSERT(id != ~UnsignedInt{}, + "Trade::MaterialData::mutableAttribute(): attribute" << name << "not found in layer" << layer, {}); + return const_cast(_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); } +void* MaterialData::mutableAttribute(const Containers::StringView layer, const MaterialAttribute name) { + const Containers::StringView string = attributeString(name); + CORRADE_ASSERT(string.data(), "Trade::MaterialData::mutableAttribute(): invalid name" << name, {}); + return mutableAttribute(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); } +void* MaterialData::mutableAttribute(const MaterialLayer layer, const UnsignedInt id) { + const Containers::StringView string = layerString(layer); + CORRADE_ASSERT(string.data(), "Trade::MaterialData::mutableAttribute(): invalid name" << layer, {}); + return mutableAttribute(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); } +void* MaterialData::mutableAttribute(const MaterialLayer layer, const Containers::StringView name) { + const Containers::StringView string = layerString(layer); + CORRADE_ASSERT(string.data(), "Trade::MaterialData::mutableAttribute(): invalid name" << layer, {}); + return mutableAttribute(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); } +void* MaterialData::mutableAttribute(const MaterialLayer layer, const MaterialAttribute name) { + const Containers::StringView string = layerString(layer); + CORRADE_ASSERT(string.data(), "Trade::MaterialData::mutableAttribute(): invalid name" << layer, {}); + return mutableAttribute(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. */ @@ -758,6 +839,21 @@ template<> MAGNUM_TRADE_EXPORT Containers::StringView MaterialData::attribute MAGNUM_TRADE_EXPORT Containers::MutableStringView MaterialData::mutableAttribute(const UnsignedInt layer, const UnsignedInt id) { + CORRADE_ASSERT(_attributeDataFlags & DataFlag::Mutable, + "Trade::MaterialData::mutableAttribute(): attribute data not mutable", {}); + /* Can't delegate to mutableAttribute() returning void* because that + doesn't include the size */ + CORRADE_ASSERT(layer < layerCount(), + "Trade::MaterialData::mutableAttribute(): index" << layer << "out of range for" << layerCount() << "layers", {}); + CORRADE_ASSERT(id < attributeCount(layer), + "Trade::MaterialData::mutableAttribute(): 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::mutableAttribute():" << (data._data.data + 1) << "of" << data._data.type << "can't be retrieved as a string", {}); + return {const_cast(data._data.s.nameValue) + Implementation::MaterialAttributeDataSize - data._data.s.size - 3, data._data.s.size, Containers::StringViewFlag::NullTerminated}; +} #endif const void* MaterialData::tryAttribute(const UnsignedInt layer, const Containers::StringView name) const { diff --git a/src/Magnum/Trade/MaterialData.h b/src/Magnum/Trade/MaterialData.h index e01ec7ae4..558d3fcf9 100644 --- a/src/Magnum/Trade/MaterialData.h +++ b/src/Magnum/Trade/MaterialData.h @@ -38,6 +38,7 @@ #include "Magnum/Magnum.h" #include "Magnum/Math/RectangularMatrix.h" +#include "Magnum/Trade/Data.h" #include "Magnum/Trade/Trade.h" #include "Magnum/Trade/visibility.h" @@ -1533,6 +1534,24 @@ accessor APIs. The above can be written in a more compact way using @snippet MagnumTrade.cpp MaterialData-usage-layers-types +@subsection Trade-MaterialData-usage-mutable Mutable data access + +The interfaces implicitly return attribute values by copy or through +@cpp const @ce views on the contained data through the @ref attributeData(), +@ref layerData() and @ref attribute() accessors. This is done because in +general case the data can also refer to a memory-mapped file or constant +memory. In cases when it's desirable to modify the attribute values in-place, +there's a set of @ref mutableAttribute() functions. To use these, you need to +check that the data are mutable using @ref attributeDataFlags() first. The +following snippet desaturates the base color of a PBR material: + +@snippet MagnumTrade.cpp MaterialData-usage-mutable + +Because the class internally expects the attribute data to be sorted and +partitioned into layers, it's not possible to modify attribute names, +add/remove attributes or change layer offsets --- only to edit values of +existing attributes. + @section Trade-MaterialData-populating Populating an instance A @ref MaterialData instance by default takes over ownership of an @@ -1550,11 +1569,12 @@ internally sorted by name to allow a @f$ \mathcal{O}(\log n) @f$ lookup. 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. +memory-mapped file, constant memory etc.). Instead of moving in an +@relativeref{Corrade,Containers::Array} you pass @ref DataFlags describing if +the data is mutable or not together with an +@relativeref{Corrade,Containers::ArrayView}. Note that in this case, since the +attribute data is treated as immutable, you *have to* ensure the list is +already sorted by name. @snippet MagnumTrade.cpp MaterialData-populating-non-owned @@ -1680,6 +1700,12 @@ class MAGNUM_TRADE_EXPORT MaterialData { * * The @p attributeData gets sorted by name internally, expecting no * duplicates. + * + * The @ref attributeDataFlags() / @ref layerDataFlags() are implicitly + * set to a combination of @ref DataFlag::Owned and + * @ref DataFlag::Mutable. For non-owned data use the + * @ref MaterialData(MaterialTypes, DataFlags, Containers::ArrayView, const void*) + * constructor instead. */ explicit MaterialData(MaterialTypes types, Containers::Array&& attributeData, const void* importerState = nullptr) noexcept: MaterialData{types, std::move(attributeData), nullptr, importerState} {} @@ -1691,13 +1717,17 @@ class MAGNUM_TRADE_EXPORT MaterialData { * @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 attributeDataFlags Attribute data flags * @param attributeData Attribute data * @param importerState Importer-specific state * - * The @p attributeData is expected to be already sorted by name, - * without duplicates. + * Compared to @ref MaterialData(MaterialTypes, Containers::Array&&, const void*) + * creates an instance that doesn't own the passed attribute data. The + * @p attributeData is expected to be already sorted by name, without + * duplicates. The @p attributeDataFlags can contain + * @ref DataFlag::Mutable to indicate the external data can be + * modified, and is expected to *not* have @ref DataFlag::Owned set. + * The @ref layerDataFlags() are implicitly set to empty @ref DataFlags. */ explicit MaterialData(MaterialTypes types, DataFlags attributeDataFlags, Containers::ArrayView attributeData, const void* importerState = nullptr) noexcept: MaterialData{types, attributeDataFlags, attributeData, {}, nullptr, importerState} {} @@ -1714,6 +1744,12 @@ class MAGNUM_TRADE_EXPORT MaterialData { * 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. + * + * The @ref attributeDataFlags() / @ref layerDataFlags() are implicitly + * set to a combination of @ref DataFlag::Owned and + * @ref DataFlag::Mutable. For non-owned data use the + * @ref MaterialData(MaterialTypes, DataFlags, Containers::ArrayView, DataFlags, Containers::ArrayView, const void*) + * constructor instead. */ explicit MaterialData(MaterialTypes types, Containers::Array&& attributeData, Containers::Array&& layerData, const void* importerState = nullptr) noexcept; @@ -1725,19 +1761,21 @@ class MAGNUM_TRADE_EXPORT MaterialData { * @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 attributeDataFlags Attribute data flags * @param attributeData Attribute data - * @param layerDataFlags Ignored. Used only for a safer - * distinction from the owning constructor. + * @param layerDataFlags Layer offset data flags * @param layerData Layer offset data - * @param importerState Importer-specific state + * @param importerState Importer-specific state * * The @p data is expected to be already sorted by name, without * 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. + * + * The @p attributeDataFlags / @p layerDataFlags parameters can contain + * @ref DataFlag::Mutable to indicate the external data can be + * modified, and are expected to *not* have @ref DataFlag::Owned set. */ /* 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. @@ -1758,6 +1796,27 @@ class MAGNUM_TRADE_EXPORT MaterialData { /** @brief Move assignment */ MaterialData& operator=(MaterialData&&) noexcept; + /** + * @brief Attribute data flags + *ยจ + * Since the attribute list is always assumed to be sorted and + * partitioned into layers, only attribute values can be edited when + * the @ref DataFlag::Mutable flag is present. + * @see @ref releaseAttributeData(), @ref mutableAttribute() + */ + DataFlags attributeDataFlags() const { return _attributeDataFlags; } + + /** + * @brief Layer data flags + * + * Since the attribute list is always assumed to be sorted and + * partitioned into layers, the @ref DataFlag::Mutable flag has no + * effect here --- only attribute values can be edited when + * @ref DataFlag::Mutable is present in @ref attributeDataFlags(). + * @see @ref releaseLayerData() + */ + DataFlags layerDataFlags() const { return _layerDataFlags; } + /** * @brief Material types * @@ -2201,6 +2260,15 @@ class MAGNUM_TRADE_EXPORT MaterialData { */ const void* attribute(UnsignedInt layer, UnsignedInt id) const; + /** + * @brief Type-erased mutable value of an attribute in given material layer + * + * Like @ref attribute(UnsignedInt, UnsignedInt) const but returns a + * mutable pointer. Expects that the material is mutable. + * @see @ref attributeDataFlags() + */ + void* mutableAttribute(UnsignedInt layer, UnsignedInt id); + /** * @brief Type-erased value of a named attribute in given material layer * @@ -2222,6 +2290,16 @@ class MAGNUM_TRADE_EXPORT MaterialData { const void* attribute(UnsignedInt layer, Containers::StringView name) const; const void* attribute(UnsignedInt layer, MaterialAttribute name) const; /**< @overload */ + /** + * @brief Type-erased value of a named attribute in given material layer + * + * Like @ref attribute(UnsignedInt, Containers::StringView) const, but + * returns a mutable pointer. Expects that the material is mutable. + * @see @ref attributeDataFlags() + */ + void* mutableAttribute(UnsignedInt layer, Containers::StringView name); + void* mutableAttribute(UnsignedInt layer, MaterialAttribute name); /**< @overload */ + /** * @brief Type-erased value of an attribute in a named material layer * @@ -2243,6 +2321,16 @@ class MAGNUM_TRADE_EXPORT MaterialData { const void* attribute(Containers::StringView layer, UnsignedInt id) const; const void* attribute(MaterialLayer layer, UnsignedInt id) const; /**< @overload */ + /** + * @brief Type-erased mutable value of an attribute in a named material layer + * + * Like @ref attribute(Containers::StringView, UnsignedInt) const but + * returns a mutable pointer. Expects that the material is mutable. + * @see @ref attributeDataFlags() + */ + void* mutableAttribute(Containers::StringView layer, UnsignedInt id); + void* mutableAttribute(MaterialLayer layer, UnsignedInt id); /**< @overload */ + /** * @brief Type-erased value of a named attribute in a named material layer * @@ -2266,6 +2354,18 @@ class MAGNUM_TRADE_EXPORT MaterialData { const void* attribute(MaterialLayer layer, Containers::StringView name) const; /**< @overload */ const void* attribute(MaterialLayer layer, MaterialAttribute name) const; /**< @overload */ + /** + * @brief Type-erased mutable value of a named attribute in a named material layer + * + * Like @ref attribute(Containers::StringView, Containers::StringView) const + * but returns a mutable pointer. Expects that the material is mutable. + * @see @ref attributeDataFlags() + */ + void* mutableAttribute(Containers::StringView layer, Containers::StringView name); + void* mutableAttribute(Containers::StringView layer, MaterialAttribute name); /**< @overload */ + void* mutableAttribute(MaterialLayer layer, Containers::StringView name); /**< @overload */ + void* mutableAttribute(MaterialLayer layer, MaterialAttribute name); /**< @overload */ + /** * @brief Type-erased value of an attribute in the base material * @@ -2276,6 +2376,17 @@ class MAGNUM_TRADE_EXPORT MaterialData { return attribute(0, id); } + /** + * @brief Type-erased mutable value of an attribute in the base material + * + * Like @ref attribute(UnsignedInt) const but returns a mutable + * pointer. Expects that the material is mutable. + * @see @ref attributeDataFlags() + */ + void* mutableAttribute(UnsignedInt id) { + return mutableAttribute(0, id); + } + /** * @brief Type-erased value of a named attribute in the base material * @@ -2289,6 +2400,20 @@ class MAGNUM_TRADE_EXPORT MaterialData { return attribute(0, name); } /**< @overload */ + /** + * @brief Type-erased mutable value of a named attribute in the base material + * + * Like @ref attribute(Containers::StringView) const but returns a + * mutable pointer. Expects that the material is mutable. + * @see @ref attributeDataFlags() + */ + void* mutableAttribute(Containers::StringView name) { + return mutableAttribute(0, name); + } + void* mutableAttribute(MaterialAttribute name) { + return mutableAttribute(0, name); + } /**< @overload */ + /** * @brief Value of an attribute in given material layer * @@ -2302,6 +2427,20 @@ class MAGNUM_TRADE_EXPORT MaterialData { */ template T attribute(UnsignedInt layer, UnsignedInt id) const; + /** + * @brief Mutable value of an attribute in given material layer + * + * Like @ref attribute(UnsignedInt, UnsignedInt) const but returns a + * mutable reference. Expects that the material is mutable. In case of + * a string, you're expected to use + * @relativeref{Corrade,Containers::MutableStringView} instead of + * @relativeref{Corrade,Containers::StringView} for @p T and you get a + * @relativeref{Corrade,Containers::MutableStringView} back by-value, + * not by-reference. Changing the string size is not possible. + * @see @ref attributeDataFlags() + */ + template typename std::conditional::value, Containers::MutableStringView, T&>::type mutableAttribute(UnsignedInt layer, UnsignedInt id); + /** * @brief Value of a named attribute in given material layer * @@ -2316,6 +2455,21 @@ class MAGNUM_TRADE_EXPORT MaterialData { template T attribute(UnsignedInt layer, Containers::StringView name) const; template T attribute(UnsignedInt layer, MaterialAttribute name) const; /**< @overload */ + /** + * @brief Mutable value of a named attribute in given material layer + * + * Like @ref attribute(UnsignedInt, Containers::StringView) const but + * returns a mutable reference. Expects that the material is mutable. + * In case of a string, you're expected to use + * @relativeref{Corrade,Containers::MutableStringView} instead of + * @relativeref{Corrade,Containers::StringView} for @p T and you get a + * @relativeref{Corrade,Containers::MutableStringView} back by-value, + * not by-reference. Changing the string size is not possible. + * @see @ref attributeDataFlags() + */ + template typename std::conditional::value, Containers::MutableStringView, T&>::type mutableAttribute(UnsignedInt layer, Containers::StringView name); + template typename std::conditional::value, Containers::MutableStringView, T&>::type mutableAttribute(UnsignedInt layer, MaterialAttribute name); /**< @overload */ + /** * @brief Value of an attribute in a named material layer * @@ -2331,6 +2485,21 @@ class MAGNUM_TRADE_EXPORT MaterialData { template T attribute(Containers::StringView layer, UnsignedInt id) const; template T attribute(MaterialLayer layer, UnsignedInt id) const; /**< @overload */ + /** + * @brief Mutable value of an attribute in a named material layer + * + * Like @ref attribute(Containers::StringView, UnsignedInt) const but + * returns a mutable reference. Expects that the material is mutable. + * In case of a string, you're expected to use + * @relativeref{Corrade,Containers::MutableStringView} instead of + * @relativeref{Corrade,Containers::StringView} for @p T and you get a + * @relativeref{Corrade,Containers::MutableStringView} back by-value, + * not by-reference. Changing the string size is not possible. + * @see @ref attributeDataFlags() + */ + template typename std::conditional::value, Containers::MutableStringView, T&>::type mutableAttribute(Containers::StringView layer, UnsignedInt id); + template typename std::conditional::value, Containers::MutableStringView, T&>::type mutableAttribute(MaterialLayer layer, UnsignedInt id); /**< @overload */ + /** * @brief Value of a named attribute in a named material layer * @@ -2347,6 +2516,23 @@ class MAGNUM_TRADE_EXPORT MaterialData { template T attribute(MaterialLayer layer, Containers::StringView name) const; /**< @overload */ template T attribute(MaterialLayer layer, MaterialAttribute name) const; /**< @overload */ + /** + * @brief Mutable value of a named attribute in a named material layer + * + * Like @ref attribute(Containers::StringView, Containers::StringView) const + * but returns a mutable reference. Expects that the material is + * mutable. In case of a string, you're expected to use + * @relativeref{Corrade,Containers::MutableStringView} instead of + * @relativeref{Corrade,Containers::StringView} for @p T and you get a + * @relativeref{Corrade,Containers::MutableStringView} back by-value, + * not by-reference. Changing the string size is not possible. + * @see @ref attributeDataFlags() + */ + template typename std::conditional::value, Containers::MutableStringView, T&>::type mutableAttribute(Containers::StringView layer, Containers::StringView name); + template typename std::conditional::value, Containers::MutableStringView, T&>::type mutableAttribute(Containers::StringView layer, MaterialAttribute name); /**< @overload */ + template typename std::conditional::value, Containers::MutableStringView, T&>::type mutableAttribute(MaterialLayer layer, Containers::StringView name); /**< @overload */ + template typename std::conditional::value, Containers::MutableStringView, T&>::type mutableAttribute(MaterialLayer layer, MaterialAttribute name); /**< @overload */ + /** * @brief Value of an attribute in the base material * @@ -2357,6 +2543,16 @@ class MAGNUM_TRADE_EXPORT MaterialData { return attribute(0, id); } + /** + * @brief Mutable value of an attribute in the base material + * + * Equivalent to calling @ref mutableAttribute(UnsignedInt, UnsignedInt) + * with @p layer set to @cpp 0 @ce. + */ + template typename std::conditional::value, Containers::MutableStringView, T&>::type mutableAttribute(UnsignedInt id) { + return mutableAttribute(0, id); + } + /** * @brief Value of a named attribute in the base material * @@ -2370,6 +2566,19 @@ class MAGNUM_TRADE_EXPORT MaterialData { return attribute(0, name); } /**< @overload */ + /** + * @brief Mutable value of a named attribute in the base material + * + * Equivalent to calling @ref mutableAttribute(UnsignedInt, Containers::StringView) + * with @p layer set to @cpp 0 @ce. + */ + template typename std::conditional::value, Containers::MutableStringView, T&>::type mutableAttribute(Containers::StringView name) { + return mutableAttribute(0, name); + } + template typename std::conditional::value, Containers::MutableStringView, T&>::type mutableAttribute(MaterialAttribute name) { + return mutableAttribute(0, name); + } /**< @overload */ + /** * @brief Type-erased attribute value in given material layer, if exists * @@ -2545,7 +2754,7 @@ class MAGNUM_TRADE_EXPORT MaterialData { * 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() + * @see @ref layerData(), @ref layerDataFlags() */ Containers::Array releaseLayerData(); @@ -2562,7 +2771,7 @@ class MAGNUM_TRADE_EXPORT MaterialData { * 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() + * @see @ref attributeData(), @ref attributeDataFlags() */ Containers::Array releaseAttributeData(); @@ -2591,6 +2800,8 @@ class MAGNUM_TRADE_EXPORT MaterialData { Containers::Array _data; Containers::Array _layerOffsets; MaterialTypes _types; + DataFlags _attributeDataFlags, _layerDataFlags; + /* 2 bytes free */ const void* _importerState; }; @@ -2732,8 +2943,22 @@ template T MaterialData::attribute(const UnsignedInt layer, const Unsig return *reinterpret_cast(value); } +template typename std::conditional::value, Containers::MutableStringView, T&>::type MaterialData::mutableAttribute(const UnsignedInt layer, const UnsignedInt id) { + void* const value = mutableAttribute(layer, id); + #ifdef CORRADE_GRACEFUL_ASSERT + if(!value) return *reinterpret_cast(this); + #endif + #ifndef CORRADE_NO_ASSERT + const Trade::MaterialAttributeData& data = _data[layerOffset(layer) + id]; + #endif + CORRADE_ASSERT(Implementation::MaterialAttributeTypeFor::type() == data._data.type, + "Trade::MaterialData::mutableAttribute():" << (data._data.data + 1) << "is" << data._data.type << "but requested a type equivalent to" << Implementation::MaterialAttributeTypeFor::type(), *reinterpret_cast(this)); + return *reinterpret_cast(value); +} + #ifndef DOXYGEN_GENERATING_OUTPUT template<> Containers::StringView MaterialData::attribute(UnsignedInt, UnsignedInt) const; +template<> Containers::MutableStringView MaterialData::mutableAttribute(UnsignedInt, UnsignedInt); #endif template T MaterialData::attribute(const UnsignedInt layer, const Containers::StringView name) const { @@ -2745,12 +2970,27 @@ template T MaterialData::attribute(const UnsignedInt layer, const Conta return attribute(layer, id); } +template typename std::conditional::value, Containers::MutableStringView, T&>::type MaterialData::mutableAttribute(const UnsignedInt layer, const Containers::StringView name) { + CORRADE_ASSERT(layer < layerCount(), + "Trade::MaterialData::mutableAttribute(): index" << layer << "out of range for" << layerCount() << "layers", *reinterpret_cast(this)); + const UnsignedInt id = attributeFor(layer, name); + CORRADE_ASSERT(id != ~UnsignedInt{}, + "Trade::MaterialData::mutableAttribute(): attribute" << name << "not found in layer" << layer, *reinterpret_cast(this)); + return mutableAttribute(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 typename std::conditional::value, Containers::MutableStringView, T&>::type MaterialData::mutableAttribute(const UnsignedInt layer, const MaterialAttribute name) { + const Containers::StringView string = attributeString(name); + CORRADE_ASSERT(string.data(), "Trade::MaterialData::mutableAttribute(): invalid name" << name, *reinterpret_cast(this)); + return mutableAttribute(layer, string); +} + template T MaterialData::attribute(const Containers::StringView layer, const UnsignedInt id) const { const UnsignedInt layerId = layerFor(layer); CORRADE_ASSERT(layerId != ~UnsignedInt{}, @@ -2760,6 +3000,15 @@ template T MaterialData::attribute(const Containers::StringView layer, return attribute(layerId, id); } +template typename std::conditional::value, Containers::MutableStringView, T&>::type MaterialData::mutableAttribute(const Containers::StringView layer, const UnsignedInt id) { + const UnsignedInt layerId = layerFor(layer); + CORRADE_ASSERT(layerId != ~UnsignedInt{}, + "Trade::MaterialData::mutableAttribute(): layer" << layer << "not found", *reinterpret_cast(this)); + CORRADE_ASSERT(id < attributeCount(layer), + "Trade::MaterialData::mutableAttribute(): index" << id << "out of range for" << attributeCount(layer) << "attributes in layer" << layer, *reinterpret_cast(this)); + return mutableAttribute(layerId, id); +} + template T MaterialData::attribute(const Containers::StringView layer, const Containers::StringView name) const { const UnsignedInt layerId = layerFor(layer); CORRADE_ASSERT(layerId != ~UnsignedInt{}, @@ -2770,30 +3019,64 @@ template T MaterialData::attribute(const Containers::StringView layer, return attribute(layerId, id); } +template typename std::conditional::value, Containers::MutableStringView, T&>::type MaterialData::mutableAttribute(const Containers::StringView layer, const Containers::StringView name) { + const UnsignedInt layerId = layerFor(layer); + CORRADE_ASSERT(layerId != ~UnsignedInt{}, + "Trade::MaterialData::mutableAttribute(): layer" << layer << "not found", *reinterpret_cast(this)); + const UnsignedInt id = attributeFor(layerId, name); + CORRADE_ASSERT(id != ~UnsignedInt{}, + "Trade::MaterialData::mutableAttribute(): attribute" << name << "not found in layer" << layer, *reinterpret_cast(this)); + return mutableAttribute(layerId, id); +} + 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(layer, string); } +template typename std::conditional::value, Containers::MutableStringView, T&>::type MaterialData::mutableAttribute(const Containers::StringView layer, const MaterialAttribute name) { + const Containers::StringView string = attributeString(name); + CORRADE_ASSERT(string.data(), "Trade::MaterialData::mutableAttribute(): invalid name" << name, *reinterpret_cast(this)); + return mutableAttribute(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 typename std::conditional::value, Containers::MutableStringView, T&>::type MaterialData::mutableAttribute(const MaterialLayer layer, const UnsignedInt id) { + const Containers::StringView string = layerString(layer); + CORRADE_ASSERT(string.data(), "Trade::MaterialData::mutableAttribute(): invalid name" << layer, *reinterpret_cast(this)); + return mutableAttribute(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 typename std::conditional::value, Containers::MutableStringView, T&>::type MaterialData::mutableAttribute(const MaterialLayer layer, const Containers::StringView name) { + const Containers::StringView string = layerString(layer); + CORRADE_ASSERT(string.data(), "Trade::MaterialData::mutableAttribute(): invalid name" << layer, *reinterpret_cast(this)); + return mutableAttribute(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 typename std::conditional::value, Containers::MutableStringView, T&>::type MaterialData::mutableAttribute(const MaterialLayer layer, const MaterialAttribute name) { + const Containers::StringView string = layerString(layer); + CORRADE_ASSERT(string.data(), "Trade::MaterialData::mutableAttribute(): invalid name" << layer, *reinterpret_cast(this)); + return mutableAttribute(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", {}); diff --git a/src/Magnum/Trade/MaterialLayerData.h b/src/Magnum/Trade/MaterialLayerData.h index 0e2255031..154c8425d 100644 --- a/src/Magnum/Trade/MaterialLayerData.h +++ b/src/Magnum/Trade/MaterialLayerData.h @@ -182,6 +182,21 @@ template class MaterialLayerData: public MaterialData { return MaterialData::attribute(layer, name); } /**< @overload */ + /** + * @brief Type-erased mutable value of an attribute in this layer + * + * Same as calling @ref MaterialData::mutableAttribute() with @p layer. + */ + void* mutableAttribute(UnsignedInt id) { + return MaterialData::mutableAttribute(layer, id); + } + void* mutableAttribute(Containers::StringView name) { + return MaterialData::mutableAttribute(layer, name); + } /**< @overload */ + void* mutableAttribute(MaterialAttribute name) { + return MaterialData::mutableAttribute(layer, name); + } /**< @overload */ + /** * @brief Value of an attribute in this layer * @@ -197,6 +212,21 @@ template class MaterialLayerData: public MaterialData { return MaterialData::attribute(layer, name); } /**< @overload */ + /** + * @brief Mutable value of an attribute in this layer + * + * Same as calling @ref MaterialData::attribute() with @p layer. + */ + template typename std::conditional::value, Containers::MutableStringView, T&>::type mutableAttribute(UnsignedInt id) { + return MaterialData::mutableAttribute(layer, id); + } + template typename std::conditional::value, Containers::MutableStringView, T&>::type mutableAttribute(Containers::StringView name) { + return MaterialData::mutableAttribute(layer, name); + } /**< @overload */ + template typename std::conditional::value, Containers::MutableStringView, T&>::type mutableAttribute(MaterialAttribute name) { + return MaterialData::mutableAttribute(layer, name); + } /**< @overload */ + /** * @brief Type-erased attribute value in this layer, if exists * @@ -257,6 +287,7 @@ template class MaterialLayerData: public MaterialData { using MaterialData::attributeName; using MaterialData::attributeType; using MaterialData::attribute; + using MaterialData::mutableAttribute; using MaterialData::tryAttribute; using MaterialData::attributeOr; #else @@ -374,6 +405,34 @@ template class MaterialLayerData: public MaterialData { return MaterialData::attribute(layer_, name); } + void* mutableAttribute(UnsignedInt layer_, UnsignedInt id) { + return MaterialData::mutableAttribute(layer_, id); + } + void* mutableAttribute(Containers::StringView layer_, UnsignedInt id) { + return MaterialData::mutableAttribute(layer_, id); + } + void* mutableAttribute(MaterialLayer layer_, UnsignedInt id) { + return MaterialData::mutableAttribute(layer_, id); + } + void* mutableAttribute(UnsignedInt layer_, Containers::StringView name) { + return MaterialData::mutableAttribute(layer_, name); + } + void* mutableAttribute(UnsignedInt layer_, MaterialAttribute name) { + return MaterialData::mutableAttribute(layer_, name); + } + void* mutableAttribute(Containers::StringView layer_, Containers::StringView name) { + return MaterialData::mutableAttribute(layer_, name); + } + void* mutableAttribute(Containers::StringView layer_, MaterialAttribute name) { + return MaterialData::mutableAttribute(layer_, name); + } + void* mutableAttribute(MaterialLayer layer_, Containers::StringView name) { + return MaterialData::mutableAttribute(layer_, name); + } + void* mutableAttribute(MaterialLayer layer_, MaterialAttribute name) { + return MaterialData::mutableAttribute(layer_, name); + } + template T attribute(UnsignedInt layer_, UnsignedInt id) const { return MaterialData::attribute(layer_, id); } @@ -402,6 +461,34 @@ template class MaterialLayerData: public MaterialData { return MaterialData::attribute(layer_, name); } + template typename std::conditional::value, Containers::MutableStringView, T&>::type mutableAttribute(UnsignedInt layer_, UnsignedInt id) { + return MaterialData::mutableAttribute(layer_, id); + } + template typename std::conditional::value, Containers::MutableStringView, T&>::type mutableAttribute(Containers::StringView layer_, UnsignedInt id) { + return MaterialData::mutableAttribute(layer_, id); + } + template typename std::conditional::value, Containers::MutableStringView, T&>::type mutableAttribute(MaterialLayer layer_, UnsignedInt id) { + return MaterialData::mutableAttribute(layer_, id); + } + template typename std::conditional::value, Containers::MutableStringView, T&>::type mutableAttribute(UnsignedInt layer_, Containers::StringView name) { + return MaterialData::mutableAttribute(layer_, name); + } + template typename std::conditional::value, Containers::MutableStringView, T&>::type mutableAttribute(UnsignedInt layer_, MaterialAttribute name) { + return MaterialData::mutableAttribute(layer_, name); + } + template typename std::conditional::value, Containers::MutableStringView, T&>::type mutableAttribute(Containers::StringView layer_, Containers::StringView name) { + return MaterialData::mutableAttribute(layer_, name); + } + template typename std::conditional::value, Containers::MutableStringView, T&>::type mutableAttribute(Containers::StringView layer_, MaterialAttribute name) { + return MaterialData::mutableAttribute(layer_, name); + } + template typename std::conditional::value, Containers::MutableStringView, T&>::type mutableAttribute(MaterialLayer layer_, Containers::StringView name) { + return MaterialData::mutableAttribute(layer_, name); + } + template typename std::conditional::value, Containers::MutableStringView, T&>::type mutableAttribute(MaterialLayer layer_, MaterialAttribute name) { + return MaterialData::mutableAttribute(layer_, name); + } + const void* tryAttribute(UnsignedInt layer_, Containers::StringView name) const { return MaterialData::tryAttribute(layer_, name); } diff --git a/src/Magnum/Trade/Test/MaterialDataTest.cpp b/src/Magnum/Trade/Test/MaterialDataTest.cpp index c97777bb6..905dafed8 100644 --- a/src/Magnum/Trade/Test/MaterialDataTest.cpp +++ b/src/Magnum/Trade/Test/MaterialDataTest.cpp @@ -96,6 +96,8 @@ class MaterialDataTest: public TestSuite::Tester { void constructNonOwnedDuplicateAttribute(); void constructNonOwnedLayersNotMonotonic(); void constructNonOwnedLayersOffsetOutOfBounds(); + void constructNonOwnedAttributeFlagOwned(); + void constructNonOwnedLayerFlagOwned(); void constructCopy(); void constructMove(); @@ -107,6 +109,7 @@ class MaterialDataTest: public TestSuite::Tester { void accessPointer(); void accessString(); void accessTextureSwizzle(); + void accessMutable(); void accessOptional(); void accessOutOfBounds(); void accessNotFound(); @@ -125,6 +128,9 @@ class MaterialDataTest: public TestSuite::Tester { void accessLayerLayerNameInBaseMaterial(); void accessLayerEmptyLayer(); + void accessLayerIndexMutable(); + void accessLayerNameMutable(); + void accessLayerStringMutable(); void accessLayerIndexOptional(); void accessLayerNameOptional(); void accessLayerStringOptional(); @@ -135,11 +141,13 @@ class MaterialDataTest: public TestSuite::Tester { void accessOutOfBoundsInLayerString(); void accessNotFoundInLayerIndex(); void accessNotFoundInLayerString(); + void accessMutableNotAllowed(); void releaseAttributes(); void releaseLayers(); void templateLayerAccess(); + void templateLayerAccessMutable(); void debugLayer(); void debugAttribute(); @@ -232,6 +240,8 @@ MaterialDataTest::MaterialDataTest() { &MaterialDataTest::constructNonOwnedDuplicateAttribute, &MaterialDataTest::constructNonOwnedLayersNotMonotonic, &MaterialDataTest::constructNonOwnedLayersOffsetOutOfBounds, + &MaterialDataTest::constructNonOwnedAttributeFlagOwned, + &MaterialDataTest::constructNonOwnedLayerFlagOwned, &MaterialDataTest::constructCopy, &MaterialDataTest::constructMove, @@ -243,6 +253,7 @@ MaterialDataTest::MaterialDataTest() { &MaterialDataTest::accessPointer, &MaterialDataTest::accessString, &MaterialDataTest::accessTextureSwizzle, + &MaterialDataTest::accessMutable, &MaterialDataTest::accessOptional, &MaterialDataTest::accessOutOfBounds, &MaterialDataTest::accessNotFound, @@ -261,6 +272,9 @@ MaterialDataTest::MaterialDataTest() { &MaterialDataTest::accessLayerLayerNameInBaseMaterial, &MaterialDataTest::accessLayerEmptyLayer, + &MaterialDataTest::accessLayerIndexMutable, + &MaterialDataTest::accessLayerNameMutable, + &MaterialDataTest::accessLayerStringMutable, &MaterialDataTest::accessLayerIndexOptional, &MaterialDataTest::accessLayerNameOptional, &MaterialDataTest::accessLayerStringOptional, @@ -271,11 +285,13 @@ MaterialDataTest::MaterialDataTest() { &MaterialDataTest::accessOutOfBoundsInLayerString, &MaterialDataTest::accessNotFoundInLayerIndex, &MaterialDataTest::accessNotFoundInLayerString, + &MaterialDataTest::accessMutableNotAllowed, &MaterialDataTest::releaseAttributes, &MaterialDataTest::releaseLayers, &MaterialDataTest::templateLayerAccess, + &MaterialDataTest::templateLayerAccessMutable, &MaterialDataTest::debugLayer, &MaterialDataTest::debugAttribute, @@ -799,6 +815,8 @@ void MaterialDataTest::construct() { {MaterialAttribute::AmbientTextureMatrix, Matrix3::scaling({0.5f, 1.0f})} }, &state}; + CORRADE_COMPARE(data.attributeDataFlags(), DataFlag::Owned|DataFlag::Mutable); + CORRADE_COMPARE(data.layerDataFlags(), DataFlag::Owned|DataFlag::Mutable); CORRADE_COMPARE(data.types(), MaterialType::Phong); CORRADE_COMPARE(data.layerCount(), 1); CORRADE_VERIFY(!data.layerData()); @@ -825,11 +843,19 @@ void MaterialDataTest::construct() { CORRADE_COMPARE(data.attribute(1), 5); CORRADE_COMPARE(data.attribute(2), true); CORRADE_COMPARE(data.attribute(3), 0x335566ff_rgbaf); + CORRADE_COMPARE(data.mutableAttribute(0), Matrix3::scaling({0.5f, 1.0f})); + CORRADE_COMPARE(data.mutableAttribute(1), 5); + CORRADE_COMPARE(data.mutableAttribute(2), true); + CORRADE_COMPARE(data.mutableAttribute(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); + CORRADE_COMPARE(*static_cast(data.mutableAttribute(0)), Matrix3::scaling({0.5f, 1.0f})); + CORRADE_COMPARE(*static_cast(data.mutableAttribute(1)), 5); + CORRADE_COMPARE(*static_cast(data.mutableAttribute(2)), true); + CORRADE_COMPARE(*static_cast(data.mutableAttribute(3)), 0x335566ff_rgbaf); /* Access by name */ CORRADE_VERIFY(data.hasAttribute(MaterialAttribute::DoubleSided)); @@ -847,10 +873,16 @@ void MaterialDataTest::construct() { 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(data.mutableAttribute(MaterialAttribute::AmbientTextureMatrix), Matrix3::scaling({0.5f, 1.0f})); + CORRADE_COMPARE(data.mutableAttribute(MaterialAttribute::DiffuseTextureCoordinates), 5); + CORRADE_COMPARE(data.mutableAttribute(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); + CORRADE_COMPARE(*static_cast(data.mutableAttribute(MaterialAttribute::AmbientTextureMatrix)), Matrix3::scaling({0.5f, 1.0f})); + CORRADE_COMPARE(*static_cast(data.mutableAttribute(MaterialAttribute::DiffuseTextureCoordinates)), 5); + CORRADE_COMPARE(*static_cast(data.mutableAttribute(MaterialAttribute::DoubleSided)), true); /* Access by string */ CORRADE_VERIFY(data.hasAttribute("DoubleSided")); @@ -871,11 +903,19 @@ void MaterialDataTest::construct() { CORRADE_COMPARE(data.attribute("DiffuseTextureCoordinates"), 5); CORRADE_COMPARE(data.attribute("DoubleSided"), true); CORRADE_COMPARE(data.attribute("highlightColor"), 0x335566ff_rgbaf); + CORRADE_COMPARE(data.mutableAttribute("AmbientTextureMatrix"), Matrix3::scaling({0.5f, 1.0f})); + CORRADE_COMPARE(data.mutableAttribute("DiffuseTextureCoordinates"), 5); + CORRADE_COMPARE(data.mutableAttribute("DoubleSided"), true); + CORRADE_COMPARE(data.mutableAttribute("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); + CORRADE_COMPARE(*static_cast(data.mutableAttribute("AmbientTextureMatrix")), Matrix3::scaling({0.5f, 1.0f})); + CORRADE_COMPARE(*static_cast(data.mutableAttribute("DiffuseTextureCoordinates")), 5); + CORRADE_COMPARE(*static_cast(data.mutableAttribute("DoubleSided")), true); + CORRADE_COMPARE(*static_cast(data.mutableAttribute("highlightColor")), 0x335566ff_rgbaf); } void MaterialDataTest::constructEmptyAttribute() { @@ -952,6 +992,8 @@ void MaterialDataTest::constructLayers() { 2, 5, 5, 7 }, &state}; + CORRADE_COMPARE(data.attributeDataFlags(), DataFlag::Owned|DataFlag::Mutable); + CORRADE_COMPARE(data.layerDataFlags(), DataFlag::Owned|DataFlag::Mutable); CORRADE_COMPARE(data.types(), MaterialType::Phong); CORRADE_COMPARE(data.importerState(), &state); @@ -999,10 +1041,16 @@ void MaterialDataTest::constructLayers() { 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(data.mutableAttribute(0, 0), 5); + CORRADE_COMPARE(data.mutableAttribute(1, 2), 0x335566ff_rgbaf); + CORRADE_COMPARE(data.mutableAttribute(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); + CORRADE_COMPARE(*static_cast(data.mutableAttribute(0, 0)), 5); + CORRADE_COMPARE(*static_cast(data.mutableAttribute(1, 2)), 0x335566ff_rgbaf); + CORRADE_COMPARE(*static_cast(data.mutableAttribute(3, 1)), 0.015f); /* Access by layer ID and attribute name */ CORRADE_VERIFY(data.hasAttribute(0, MaterialAttribute::DiffuseTextureCoordinates)); @@ -1027,11 +1075,19 @@ void MaterialDataTest::constructLayers() { 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(data.mutableAttribute(0, MaterialAttribute::DiffuseTextureCoordinates), 5); + CORRADE_COMPARE(data.mutableAttribute(1, MaterialAttribute::AlphaBlend), true); + CORRADE_COMPARE(data.mutableAttribute(1, MaterialAttribute::LayerName), "ClearCoat"_s); + CORRADE_COMPARE(data.mutableAttribute(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); + CORRADE_COMPARE(*static_cast(data.mutableAttribute(0, MaterialAttribute::DiffuseTextureCoordinates)), 5); + CORRADE_COMPARE(*static_cast(data.mutableAttribute(1, MaterialAttribute::AlphaBlend)), true); + CORRADE_COMPARE(static_cast(data.mutableAttribute(1, MaterialAttribute::LayerName)), "ClearCoat"_s); + CORRADE_COMPARE(*static_cast(data.mutableAttribute(3, MaterialAttribute::NormalTexture)), 3); /* Access by layer ID and attribute string */ CORRADE_VERIFY(data.hasAttribute(0, "DoubleSided")); @@ -1056,11 +1112,19 @@ void MaterialDataTest::constructLayers() { CORRADE_COMPARE(data.attribute(1, "highlightColor"), 0x335566ff_rgbaf); CORRADE_COMPARE(data.attribute(1, "$LayerName"), "ClearCoat"); CORRADE_COMPARE(data.attribute(3, "NormalTexture"), 3); + CORRADE_COMPARE(data.mutableAttribute(0, "DoubleSided"), true); + CORRADE_COMPARE(data.mutableAttribute(1, "highlightColor"), 0x335566ff_rgbaf); + CORRADE_COMPARE(data.mutableAttribute(1, "$LayerName"), "ClearCoat"_s); + CORRADE_COMPARE(data.mutableAttribute(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); + CORRADE_COMPARE(*static_cast(data.mutableAttribute(0, "DoubleSided")), true); + CORRADE_COMPARE(*static_cast(data.mutableAttribute(1, "highlightColor")), 0x335566ff_rgbaf); + CORRADE_COMPARE(static_cast(data.mutableAttribute(1, "$LayerName")), "ClearCoat"_s); + CORRADE_COMPARE(*static_cast(data.mutableAttribute(3, "NormalTexture")), 3); /* Access by layer name and attribute ID */ CORRADE_COMPARE(data.attributeName(MaterialLayer::ClearCoat, 1), "AlphaBlend"); @@ -1071,9 +1135,13 @@ void MaterialDataTest::constructLayers() { CORRADE_COMPARE(data.attribute(MaterialLayer::ClearCoat, 1), true); CORRADE_COMPARE(data.attribute(MaterialLayer::ClearCoat, 2), 0x335566ff_rgbaf); + CORRADE_COMPARE(data.mutableAttribute(MaterialLayer::ClearCoat, 1), true); + CORRADE_COMPARE(data.mutableAttribute(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); + CORRADE_COMPARE(*static_cast(data.mutableAttribute(MaterialLayer::ClearCoat, 1)), true); + CORRADE_COMPARE(*static_cast(data.mutableAttribute(MaterialLayer::ClearCoat, 2)), 0x335566ff_rgbaf); /* Access by layer name and attribute name */ CORRADE_VERIFY(data.hasAttribute(MaterialLayer::ClearCoat, MaterialAttribute::AlphaBlend)); @@ -1087,9 +1155,13 @@ void MaterialDataTest::constructLayers() { CORRADE_COMPARE(data.attribute(MaterialLayer::ClearCoat, MaterialAttribute::AlphaBlend), true); CORRADE_COMPARE(data.attribute(MaterialLayer::ClearCoat, MaterialAttribute::LayerName), "ClearCoat"); + CORRADE_COMPARE(data.mutableAttribute(MaterialLayer::ClearCoat, MaterialAttribute::AlphaBlend), true); + CORRADE_COMPARE(data.mutableAttribute(MaterialLayer::ClearCoat, MaterialAttribute::LayerName), "ClearCoat"_s); CORRADE_COMPARE(*static_cast(data.attribute(MaterialLayer::ClearCoat, MaterialAttribute::AlphaBlend)), true); CORRADE_COMPARE(static_cast(data.attribute(MaterialLayer::ClearCoat, MaterialAttribute::LayerName)), "ClearCoat"_s); + CORRADE_COMPARE(*static_cast(data.mutableAttribute(MaterialLayer::ClearCoat, MaterialAttribute::AlphaBlend)), true); + CORRADE_COMPARE(static_cast(data.mutableAttribute(MaterialLayer::ClearCoat, MaterialAttribute::LayerName)), "ClearCoat"_s); /* Access by layer name and attribute string */ CORRADE_VERIFY(data.hasAttribute(MaterialLayer::ClearCoat, "highlightColor")); @@ -1103,9 +1175,13 @@ void MaterialDataTest::constructLayers() { CORRADE_COMPARE(data.attribute(MaterialLayer::ClearCoat, "highlightColor"), 0x335566ff_rgbaf); CORRADE_COMPARE(data.attribute(MaterialLayer::ClearCoat, "$LayerName"), "ClearCoat"); + CORRADE_COMPARE(data.mutableAttribute(MaterialLayer::ClearCoat, "highlightColor"), 0x335566ff_rgbaf); + CORRADE_COMPARE(data.mutableAttribute(MaterialLayer::ClearCoat, "$LayerName"), "ClearCoat"_s); CORRADE_COMPARE(*static_cast(data.attribute(MaterialLayer::ClearCoat, "highlightColor")), 0x335566ff_rgbaf); CORRADE_COMPARE(static_cast(data.attribute(MaterialLayer::ClearCoat, "$LayerName")), "ClearCoat"_s); + CORRADE_COMPARE(*static_cast(data.mutableAttribute(MaterialLayer::ClearCoat, "highlightColor")), 0x335566ff_rgbaf); + CORRADE_COMPARE(static_cast(data.mutableAttribute(MaterialLayer::ClearCoat, "$LayerName")), "ClearCoat"_s); /* Access by layer string and attribute ID */ CORRADE_COMPARE(data.attributeName("ClearCoat", 1), "AlphaBlend"); @@ -1116,9 +1192,13 @@ void MaterialDataTest::constructLayers() { CORRADE_COMPARE(data.attribute("ClearCoat", 1), true); CORRADE_COMPARE(data.attribute("ClearCoat", 2), 0x335566ff_rgbaf); + CORRADE_COMPARE(data.mutableAttribute("ClearCoat", 1), true); + CORRADE_COMPARE(data.mutableAttribute("ClearCoat", 2), 0x335566ff_rgbaf); CORRADE_COMPARE(*static_cast(data.attribute("ClearCoat", 1)), true); CORRADE_COMPARE(*static_cast(data.attribute("ClearCoat", 2)), 0x335566ff_rgbaf); + CORRADE_COMPARE(*static_cast(data.mutableAttribute("ClearCoat", 1)), true); + CORRADE_COMPARE(*static_cast(data.mutableAttribute("ClearCoat", 2)), 0x335566ff_rgbaf); /* Access by layer string and attribute name */ CORRADE_VERIFY(data.hasAttribute("ClearCoat", MaterialAttribute::AlphaBlend)); @@ -1132,9 +1212,13 @@ void MaterialDataTest::constructLayers() { CORRADE_COMPARE(data.attribute("ClearCoat", MaterialAttribute::AlphaBlend), true); CORRADE_COMPARE(data.attribute("ClearCoat", MaterialAttribute::LayerName), "ClearCoat"); + CORRADE_COMPARE(data.mutableAttribute("ClearCoat", MaterialAttribute::AlphaBlend), true); + CORRADE_COMPARE(data.mutableAttribute("ClearCoat", MaterialAttribute::LayerName), "ClearCoat"_s); CORRADE_COMPARE(*static_cast(data.attribute("ClearCoat", MaterialAttribute::AlphaBlend)), true); CORRADE_COMPARE(static_cast(data.attribute("ClearCoat", MaterialAttribute::LayerName)), "ClearCoat"_s); + CORRADE_COMPARE(*static_cast(data.mutableAttribute("ClearCoat", MaterialAttribute::AlphaBlend)), true); + CORRADE_COMPARE(static_cast(data.mutableAttribute("ClearCoat", MaterialAttribute::LayerName)), "ClearCoat"_s); /* Access by layer string and attribute string */ CORRADE_VERIFY(data.hasAttribute("ClearCoat", "highlightColor")); @@ -1148,9 +1232,13 @@ void MaterialDataTest::constructLayers() { CORRADE_COMPARE(data.attribute("ClearCoat", "highlightColor"), 0x335566ff_rgbaf); CORRADE_COMPARE(data.attribute("ClearCoat", "$LayerName"), "ClearCoat"); + CORRADE_COMPARE(data.mutableAttribute("ClearCoat", "highlightColor"), 0x335566ff_rgbaf); + CORRADE_COMPARE(data.mutableAttribute("ClearCoat", "$LayerName"), "ClearCoat"_s); CORRADE_COMPARE(*static_cast(data.attribute("ClearCoat", "highlightColor")), 0x335566ff_rgbaf); CORRADE_COMPARE(static_cast(data.attribute("ClearCoat", "$LayerName")), "ClearCoat"_s); + CORRADE_COMPARE(*static_cast(data.mutableAttribute("ClearCoat", "highlightColor")), 0x335566ff_rgbaf); + CORRADE_COMPARE(static_cast(data.mutableAttribute("ClearCoat", "$LayerName")), "ClearCoat"_s); } void MaterialDataTest::constructLayersNotMonotonic() { @@ -1200,6 +1288,8 @@ void MaterialDataTest::constructNonOwned() { int state{}; /* GCC 11 complains that "maybe uninitialized" w/o the {} */ MaterialData data{MaterialType::Phong, {}, attributes, &state}; + CORRADE_COMPARE(data.attributeDataFlags(), DataFlags{}); + CORRADE_COMPARE(data.layerDataFlags(), DataFlags{}); /* Expecting the same output as in construct() */ CORRADE_COMPARE(data.types(), MaterialType::Phong); CORRADE_COMPARE(data.layerCount(), 1); @@ -1244,6 +1334,8 @@ void MaterialDataTest::constructNonOwnedLayers() { {}, attributes, {}, layers, &state}; + CORRADE_COMPARE(data.attributeDataFlags(), DataFlags{}); + CORRADE_COMPARE(data.layerDataFlags(), DataFlags{}); /* Expecting the same output as in constructLayers() */ CORRADE_COMPARE(data.types(), MaterialType::Phong); CORRADE_COMPARE(data.importerState(), &state); @@ -1382,6 +1474,40 @@ void MaterialDataTest::constructNonOwnedLayersOffsetOutOfBounds() { CORRADE_COMPARE(out.str(), "Trade::MaterialData: invalid range (2, 6) for layer 1 with 5 attributes in total\n"); } +void MaterialDataTest::constructNonOwnedAttributeFlagOwned() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + MaterialAttributeData attributes[]{ + {MaterialAttribute::DoubleSided, true} + }; + + std::ostringstream out; + Error redirectError{&out}; + MaterialData data{{}, DataFlag::Owned, attributes}; + CORRADE_COMPARE(out.str(), "Trade::MaterialData: can't construct with non-owned attribute data but Trade::DataFlag::Owned\n"); +} + +void MaterialDataTest::constructNonOwnedLayerFlagOwned() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + MaterialAttributeData attributes[]{ + {MaterialAttribute::DoubleSided, true} + }; + + UnsignedInt layers[]{ + 0, 1 + }; + + std::ostringstream out; + Error redirectError{&out}; + MaterialData data{{}, {}, attributes, DataFlag::Owned, layers}; + CORRADE_COMPARE(out.str(), "Trade::MaterialData: can't construct with non-owned layer data but Trade::DataFlag::Owned\n"); +} + void MaterialDataTest::constructCopy() { CORRADE_VERIFY(!std::is_copy_constructible{}); CORRADE_VERIFY(!std::is_copy_assignable{}); @@ -1398,6 +1524,8 @@ void MaterialDataTest::constructMove() { }, &state}; MaterialData b{std::move(a)}; + CORRADE_COMPARE(b.attributeDataFlags(), DataFlag::Owned|DataFlag::Mutable); + CORRADE_COMPARE(b.layerDataFlags(), DataFlag::Owned|DataFlag::Mutable); CORRADE_COMPARE(a.layerCount(), 1); CORRADE_COMPARE(a.attributeCount(), 0); CORRADE_COMPARE(b.types(), MaterialType::Phong); @@ -1412,6 +1540,8 @@ void MaterialDataTest::constructMove() { c = std::move(b); CORRADE_COMPARE(b.attributeCount(), 1); CORRADE_COMPARE(b.layerCount(), 1); + CORRADE_COMPARE(c.attributeDataFlags(), DataFlag::Owned|DataFlag::Mutable); + CORRADE_COMPARE(c.layerDataFlags(), DataFlag::Owned|DataFlag::Mutable); CORRADE_COMPARE(c.types(), MaterialType::Phong); CORRADE_COMPARE(c.layerCount(), 3); CORRADE_COMPARE(c.attributeCount(2), 2); @@ -1541,6 +1671,29 @@ void MaterialDataTest::accessTextureSwizzle() { CORRADE_COMPARE(data.attribute(0), MaterialTextureSwizzle::BA); } +void MaterialDataTest::accessMutable() { + MaterialData data{{}, { + {MaterialAttribute::LayerName, "aye"_s}, + {MaterialAttribute::Roughness, 1.0f}, + }}; + + *static_cast(data.mutableAttribute(1)) *= 2.0f; + *static_cast(data.mutableAttribute(MaterialAttribute::Roughness)) *= 2.0f; + *static_cast(data.mutableAttribute("Roughness")) *= 2.0f; + data.mutableAttribute(1) *= 2.0f; + data.mutableAttribute(MaterialAttribute::Roughness) *= 2.0f; + data.mutableAttribute("Roughness") *= 2.0f; + CORRADE_COMPARE(data.attribute(MaterialAttribute::Roughness), 64.0f); + + ++*static_cast(data.mutableAttribute(0)); + ++*static_cast(data.mutableAttribute(MaterialAttribute::LayerName)); + ++*static_cast(data.mutableAttribute("$LayerName")); + ++data.mutableAttribute(0)[0]; + ++data.mutableAttribute(MaterialAttribute::LayerName)[0]; + ++data.mutableAttribute("$LayerName")[0]; + CORRADE_COMPARE(data.attribute(MaterialAttribute::LayerName), "gye"_s); +} + void MaterialDataTest::accessOptional() { MaterialData data{{}, { {MaterialAttribute::AlphaMask, 0.5f}, @@ -1583,12 +1736,18 @@ void MaterialDataTest::accessOutOfBounds() { data.attribute(2); data.attribute(2); data.attribute(2); + data.mutableAttribute(2); + data.mutableAttribute(2); + data.mutableAttribute(2); CORRADE_COMPARE(out.str(), "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"); + "Trade::MaterialData::attribute(): index 2 out of range for 2 attributes in layer 0\n" + "Trade::MaterialData::mutableAttribute(): index 2 out of range for 2 attributes in layer 0\n" + "Trade::MaterialData::mutableAttribute(): index 2 out of range for 2 attributes in layer 0\n" + "Trade::MaterialData::mutableAttribute(): index 2 out of range for 2 attributes in layer 0\n"); } void MaterialDataTest::accessNotFound() { @@ -1608,11 +1767,15 @@ void MaterialDataTest::accessNotFound() { data.attributeType("DiffuseColour"); data.attribute("DiffuseColour"); data.attribute("DiffuseColour"); + data.mutableAttribute("DiffuseColour"); + data.mutableAttribute("DiffuseColour"); CORRADE_COMPARE(out.str(), "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"); + "Trade::MaterialData::attribute(): attribute DiffuseColour not found in layer 0\n" + "Trade::MaterialData::mutableAttribute(): attribute DiffuseColour not found in layer 0\n" + "Trade::MaterialData::mutableAttribute(): attribute DiffuseColour not found in layer 0\n"); } void MaterialDataTest::accessWrongType() { @@ -1629,6 +1792,9 @@ void MaterialDataTest::accessWrongType() { data.attribute(0); data.attribute(MaterialAttribute::DiffuseColor); data.attribute("DiffuseColor"); + data.mutableAttribute(0); + data.mutableAttribute(MaterialAttribute::DiffuseColor); + data.mutableAttribute("DiffuseColor"); data.tryAttribute(MaterialAttribute::DiffuseColor); data.tryAttribute("DiffuseColor"); data.attributeOr(MaterialAttribute::DiffuseColor, Color3{1.0f}); @@ -1637,6 +1803,9 @@ void MaterialDataTest::accessWrongType() { "Trade::MaterialData::attribute(): DiffuseColor is Trade::MaterialAttributeType::Vector4 but requested a type equivalent to Trade::MaterialAttributeType::Vector3\n" "Trade::MaterialData::attribute(): DiffuseColor is Trade::MaterialAttributeType::Vector4 but requested a type equivalent to Trade::MaterialAttributeType::Vector3\n" "Trade::MaterialData::attribute(): DiffuseColor is Trade::MaterialAttributeType::Vector4 but requested a type equivalent to Trade::MaterialAttributeType::Vector3\n" + "Trade::MaterialData::mutableAttribute(): DiffuseColor is Trade::MaterialAttributeType::Vector4 but requested a type equivalent to Trade::MaterialAttributeType::Vector3\n" + "Trade::MaterialData::mutableAttribute(): DiffuseColor is Trade::MaterialAttributeType::Vector4 but requested a type equivalent to Trade::MaterialAttributeType::Vector3\n" + "Trade::MaterialData::mutableAttribute(): DiffuseColor is Trade::MaterialAttributeType::Vector4 but requested a type equivalent to Trade::MaterialAttributeType::Vector3\n" /* tryAttribute() and attributeOr() delegate to attribute() so the assert is the same */ "Trade::MaterialData::attribute(): DiffuseColor is Trade::MaterialAttributeType::Vector4 but requested a type equivalent to Trade::MaterialAttributeType::Vector3\n" @@ -1661,16 +1830,24 @@ void MaterialDataTest::accessWrongPointerType() { /* These are fine (type is not checked) */ data.attribute("mutablePointer"); data.attribute("pointer"); + data.mutableAttribute("mutablePointer"); + data.mutableAttribute("pointer"); std::ostringstream out; Error redirectError{&out}; data.attribute("mutablePointer"); data.attribute("mutablePointer"); data.attribute("pointer"); + data.mutableAttribute("mutablePointer"); + data.mutableAttribute("mutablePointer"); + data.mutableAttribute("pointer"); CORRADE_COMPARE(out.str(), "Trade::MaterialData::attribute(): mutablePointer is Trade::MaterialAttributeType::MutablePointer but requested a type equivalent to Trade::MaterialAttributeType::Int\n" "Trade::MaterialData::attribute(): mutablePointer is Trade::MaterialAttributeType::MutablePointer but requested a type equivalent to Trade::MaterialAttributeType::Pointer\n" - "Trade::MaterialData::attribute(): pointer is Trade::MaterialAttributeType::Pointer but requested a type equivalent to Trade::MaterialAttributeType::MutablePointer\n"); + "Trade::MaterialData::attribute(): pointer is Trade::MaterialAttributeType::Pointer but requested a type equivalent to Trade::MaterialAttributeType::MutablePointer\n" + "Trade::MaterialData::mutableAttribute(): mutablePointer is Trade::MaterialAttributeType::MutablePointer but requested a type equivalent to Trade::MaterialAttributeType::Int\n" + "Trade::MaterialData::mutableAttribute(): mutablePointer is Trade::MaterialAttributeType::MutablePointer but requested a type equivalent to Trade::MaterialAttributeType::Pointer\n" + "Trade::MaterialData::mutableAttribute(): pointer is Trade::MaterialAttributeType::Pointer but requested a type equivalent to Trade::MaterialAttributeType::MutablePointer\n"); } void MaterialDataTest::accessWrongTypeString() { @@ -1687,6 +1864,9 @@ void MaterialDataTest::accessWrongTypeString() { data.attribute(0); data.attribute(MaterialAttribute::Shininess); data.attribute("Shininess"); + data.mutableAttribute(0); + data.mutableAttribute(MaterialAttribute::Shininess); + data.mutableAttribute("Shininess"); data.tryAttribute(MaterialAttribute::Shininess); data.tryAttribute("Shininess"); data.attributeOr(MaterialAttribute::Shininess, Containers::StringView{}); @@ -1695,6 +1875,9 @@ void MaterialDataTest::accessWrongTypeString() { "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::mutableAttribute(): Shininess of Trade::MaterialAttributeType::Float can't be retrieved as a string\n" + "Trade::MaterialData::mutableAttribute(): Shininess of Trade::MaterialAttributeType::Float can't be retrieved as a string\n" + "Trade::MaterialData::mutableAttribute(): 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" @@ -1910,6 +2093,117 @@ void MaterialDataTest::accessLayerEmptyLayer() { CORRADE_COMPARE(data.attribute("crumples", MaterialAttribute::NormalTexture), 3u); } +void MaterialDataTest::accessLayerIndexMutable() { + MaterialData data{{}, { + {MaterialLayer::ClearCoat}, + {MaterialAttribute::Roughness, 1.0f} + }, {0, 2}}; + + *static_cast(data.mutableAttribute(1, 1)) *= 2.0f; + *static_cast(data.mutableAttribute(1, "Roughness")) *= 2.0f; + *static_cast(data.mutableAttribute(1, MaterialAttribute::Roughness)) *= 2.0f; + data.mutableAttribute(1, 1) *= 2.0f; + data.mutableAttribute(1, "Roughness") *= 2.0f; + data.mutableAttribute(1, MaterialAttribute::Roughness) *= 2.0f; + CORRADE_COMPARE(data.attribute(1, MaterialAttribute::Roughness), 64.0f); + + ++*static_cast(data.mutableAttribute(1, 0)); + ++*static_cast(data.mutableAttribute(1, "$LayerName")); + ++*static_cast(data.mutableAttribute(1, MaterialAttribute::LayerName)); + ++data.mutableAttribute(1, 0)[0]; + ++data.mutableAttribute(1, "$LayerName")[0]; + ++data.mutableAttribute(1, MaterialAttribute::LayerName)[0]; + CORRADE_COMPARE(data.attribute(1, MaterialAttribute::LayerName), "IlearCoat"_s); +} + +void MaterialDataTest::accessLayerNameMutable() { + MaterialData data{{}, { + {MaterialLayer::ClearCoat}, + {MaterialAttribute::Roughness, 1.0f} + }, {0, 2}}; + + *static_cast(data.mutableAttribute(MaterialLayer::ClearCoat, 1)) *= 2.0f; + *static_cast(data.mutableAttribute(MaterialLayer::ClearCoat, "Roughness")) *= 2.0f; + *static_cast(data.mutableAttribute(MaterialLayer::ClearCoat, MaterialAttribute::Roughness)) *= 2.0f; + data.mutableAttribute(MaterialLayer::ClearCoat, 1) *= 2.0f; + data.mutableAttribute(MaterialLayer::ClearCoat, "Roughness") *= 2.0f; + data.mutableAttribute(MaterialLayer::ClearCoat, MaterialAttribute::Roughness) *= 2.0f; + CORRADE_COMPARE(data.attribute(MaterialLayer::ClearCoat, MaterialAttribute::Roughness), 64.0f); + + /* Resetting back so the layer name always stays the same so the next call + can find it. Other than that, the result should be same as in + accessLayerIndexMutable(). */ + { + *static_cast(data.mutableAttribute(MaterialLayer::ClearCoat, 0)) = 'D'; + CORRADE_COMPARE(data.attribute(1, 0), "DlearCoat"); + *static_cast(data.mutableAttribute(1, 0)) = 'C'; + } { + *static_cast(data.mutableAttribute(MaterialLayer::ClearCoat, "$LayerName")) = 'E'; + CORRADE_COMPARE(data.attribute(1, 0), "ElearCoat"); + *static_cast(data.mutableAttribute(1, 0)) = 'C'; + } { + *static_cast(data.mutableAttribute(MaterialLayer::ClearCoat, MaterialAttribute::LayerName)) = 'F'; + CORRADE_COMPARE(data.attribute(1, 0), "FlearCoat"); + *static_cast(data.mutableAttribute(1, 0)) = 'C'; + } { + data.mutableAttribute(MaterialLayer::ClearCoat, 0)[0] = 'G'; + CORRADE_COMPARE(data.attribute(1, 0), "GlearCoat"); + *static_cast(data.mutableAttribute(1, 0)) = 'C'; + } { + data.mutableAttribute(MaterialLayer::ClearCoat, "$LayerName")[0] = 'H'; + CORRADE_COMPARE(data.attribute(1, 0), "HlearCoat"); + *static_cast(data.mutableAttribute(1, 0)) = 'C'; + } { + data.mutableAttribute(MaterialLayer::ClearCoat, "$LayerName")[0] = 'I'; + CORRADE_COMPARE(data.attribute(1, 0), "IlearCoat"); + *static_cast(data.mutableAttribute(1, 0)) = 'C'; + } +} + +void MaterialDataTest::accessLayerStringMutable() { + MaterialData data{{}, { + {MaterialLayer::ClearCoat}, + {MaterialAttribute::Roughness, 1.0f} + }, {0, 2}}; + + *static_cast(data.mutableAttribute("ClearCoat", 1)) *= 2.0f; + *static_cast(data.mutableAttribute("ClearCoat", "Roughness")) *= 2.0f; + *static_cast(data.mutableAttribute("ClearCoat", MaterialAttribute::Roughness)) *= 2.0f; + data.mutableAttribute("ClearCoat", 1) *= 2.0f; + data.mutableAttribute("ClearCoat", "Roughness") *= 2.0f; + data.mutableAttribute("ClearCoat", MaterialAttribute::Roughness) *= 2.0f; + CORRADE_COMPARE(data.attribute("ClearCoat", MaterialAttribute::Roughness), 64.0f); + + /* Resetting back so the layer name always stays the same so the next call + can find it. Other than that, the result should be same as in + accessLayerIndexMutable(). */ + { + *static_cast(data.mutableAttribute("ClearCoat", 0)) = 'D'; + CORRADE_COMPARE(data.attribute(1, 0), "DlearCoat"); + *static_cast(data.mutableAttribute(1, 0)) = 'C'; + } { + *static_cast(data.mutableAttribute("ClearCoat", "$LayerName")) = 'E'; + CORRADE_COMPARE(data.attribute(1, 0), "ElearCoat"); + *static_cast(data.mutableAttribute(1, 0)) = 'C'; + } { + *static_cast(data.mutableAttribute("ClearCoat", MaterialAttribute::LayerName)) = 'F'; + CORRADE_COMPARE(data.attribute(1, 0), "FlearCoat"); + *static_cast(data.mutableAttribute(1, 0)) = 'C'; + } { + data.mutableAttribute("ClearCoat", 0)[0] = 'G'; + CORRADE_COMPARE(data.attribute(1, 0), "GlearCoat"); + *static_cast(data.mutableAttribute(1, 0)) = 'C'; + } { + data.mutableAttribute("ClearCoat", "$LayerName")[0] = 'H'; + CORRADE_COMPARE(data.attribute(1, 0), "HlearCoat"); + *static_cast(data.mutableAttribute(1, 0)) = 'C'; + } { + data.mutableAttribute("ClearCoat", "$LayerName")[0] = 'I'; + CORRADE_COMPARE(data.attribute(1, 0), "IlearCoat"); + *static_cast(data.mutableAttribute(1, 0)) = 'C'; + } +} + void MaterialDataTest::accessLayerIndexOptional() { MaterialData data{{}, { {MaterialAttribute::DiffuseColor, 0x335566ff_rgbaf}, @@ -2024,6 +2318,13 @@ void MaterialDataTest::accessLayerOutOfBounds() { data.attribute(2, "AlphaMask"); data.attribute(2, MaterialAttribute::AlphaMask); data.attribute(2, 0); + data.mutableAttribute(2, 0); + data.mutableAttribute(2, "AlphaMask"); + data.mutableAttribute(2, MaterialAttribute::AlphaMask); + data.mutableAttribute(2, 0); + data.mutableAttribute(2, "AlphaMask"); + data.mutableAttribute(2, MaterialAttribute::AlphaMask); + data.mutableAttribute(2, 0); data.tryAttribute(2, "AlphaMask"); data.tryAttribute(2, MaterialAttribute::AlphaMask); data.tryAttribute(2, "AlphaMask"); @@ -2053,6 +2354,13 @@ void MaterialDataTest::accessLayerOutOfBounds() { "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::mutableAttribute(): index 2 out of range for 2 layers\n" + "Trade::MaterialData::mutableAttribute(): index 2 out of range for 2 layers\n" + "Trade::MaterialData::mutableAttribute(): index 2 out of range for 2 layers\n" + "Trade::MaterialData::mutableAttribute(): index 2 out of range for 2 layers\n" + "Trade::MaterialData::mutableAttribute(): index 2 out of range for 2 layers\n" + "Trade::MaterialData::mutableAttribute(): index 2 out of range for 2 layers\n" + "Trade::MaterialData::mutableAttribute(): 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" @@ -2095,6 +2403,13 @@ void MaterialDataTest::accessLayerNotFound() { data.attribute("ClearCoat", "AlphaMask"); data.attribute("ClearCoat", MaterialAttribute::AlphaMask); data.attribute("ClearCoat", 0); + data.mutableAttribute("ClearCoat", 0); + data.mutableAttribute("ClearCoat", "AlphaMask"); + data.mutableAttribute("ClearCoat", MaterialAttribute::AlphaMask); + data.mutableAttribute("ClearCoat", 0); + data.mutableAttribute("ClearCoat", "AlphaMask"); + data.mutableAttribute("ClearCoat", MaterialAttribute::AlphaMask); + data.mutableAttribute("ClearCoat", 0); data.tryAttribute("ClearCoat", "AlphaMask"); data.tryAttribute("ClearCoat", MaterialAttribute::AlphaMask); data.tryAttribute("ClearCoat", "AlphaMask"); @@ -2124,6 +2439,13 @@ void MaterialDataTest::accessLayerNotFound() { "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::mutableAttribute(): layer ClearCoat not found\n" + "Trade::MaterialData::mutableAttribute(): layer ClearCoat not found\n" + "Trade::MaterialData::mutableAttribute(): layer ClearCoat not found\n" + "Trade::MaterialData::mutableAttribute(): layer ClearCoat not found\n" + "Trade::MaterialData::mutableAttribute(): layer ClearCoat not found\n" + "Trade::MaterialData::mutableAttribute(): layer ClearCoat not found\n" + "Trade::MaterialData::mutableAttribute(): 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" @@ -2164,6 +2486,13 @@ void MaterialDataTest::accessInvalidLayerName() { data.attribute(MaterialLayer(0xfefe), "AlphaMask"); data.attribute(MaterialLayer(0xfefe), MaterialAttribute::AlphaMask); data.attribute(MaterialLayer(0xfefe), 0); + data.mutableAttribute(MaterialLayer(0xfefe), 0); + data.mutableAttribute(MaterialLayer(0xfefe), "AlphaMask"); + data.mutableAttribute(MaterialLayer(0xfefe), MaterialAttribute::AlphaMask); + data.mutableAttribute(MaterialLayer(0xfefe), 0); + data.mutableAttribute(MaterialLayer(0xfefe), "AlphaMask"); + data.mutableAttribute(MaterialLayer(0xfefe), MaterialAttribute::AlphaMask); + data.mutableAttribute(MaterialLayer(0xfefe), 0); data.tryAttribute(MaterialLayer(0xfefe), "AlphaMask"); data.tryAttribute(MaterialLayer(0xfefe), MaterialAttribute::AlphaMask); data.tryAttribute(MaterialLayer(0xfefe), "AlphaMask"); @@ -2194,6 +2523,13 @@ void MaterialDataTest::accessInvalidLayerName() { "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::mutableAttribute(): invalid name Trade::MaterialLayer(0xfefe)\n" + "Trade::MaterialData::mutableAttribute(): invalid name Trade::MaterialLayer(0xfefe)\n" + "Trade::MaterialData::mutableAttribute(): invalid name Trade::MaterialLayer(0xfefe)\n" + "Trade::MaterialData::mutableAttribute(): invalid name Trade::MaterialLayer(0xfefe)\n" + "Trade::MaterialData::mutableAttribute(): invalid name Trade::MaterialLayer(0xfefe)\n" + "Trade::MaterialData::mutableAttribute(): invalid name Trade::MaterialLayer(0xfefe)\n" + "Trade::MaterialData::mutableAttribute(): 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" @@ -2219,12 +2555,18 @@ void MaterialDataTest::accessOutOfBoundsInLayerIndex() { data.attribute(1, 2); data.attribute(1, 2); data.attribute(1, 2); + data.mutableAttribute(1, 2); + data.mutableAttribute(1, 2); + data.mutableAttribute(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"); + "Trade::MaterialData::attribute(): index 2 out of range for 2 attributes in layer 1\n" + "Trade::MaterialData::mutableAttribute(): index 2 out of range for 2 attributes in layer 1\n" + "Trade::MaterialData::mutableAttribute(): index 2 out of range for 2 attributes in layer 1\n" + "Trade::MaterialData::mutableAttribute(): index 2 out of range for 2 attributes in layer 1\n"); } void MaterialDataTest::accessOutOfBoundsInLayerString() { @@ -2244,12 +2586,18 @@ void MaterialDataTest::accessOutOfBoundsInLayerString() { data.attribute("ClearCoat", 2); data.attribute("ClearCoat", 2); data.attribute("ClearCoat", 2); + data.mutableAttribute("ClearCoat", 2); + data.mutableAttribute("ClearCoat", 2); + data.mutableAttribute("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"); + "Trade::MaterialData::attribute(): index 2 out of range for 2 attributes in layer ClearCoat\n" + "Trade::MaterialData::mutableAttribute(): index 2 out of range for 2 attributes in layer ClearCoat\n" + "Trade::MaterialData::mutableAttribute(): index 2 out of range for 2 attributes in layer ClearCoat\n" + "Trade::MaterialData::mutableAttribute(): index 2 out of range for 2 attributes in layer ClearCoat\n"); } void MaterialDataTest::accessNotFoundInLayerIndex() { @@ -2269,11 +2617,15 @@ void MaterialDataTest::accessNotFoundInLayerIndex() { data.attributeType(1, "DiffuseColour"); data.attribute(1, "DiffuseColour"); data.attribute(1, "DiffuseColour"); + data.mutableAttribute(1, "DiffuseColour"); + data.mutableAttribute(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"); + "Trade::MaterialData::attribute(): attribute DiffuseColour not found in layer 1\n" + "Trade::MaterialData::mutableAttribute(): attribute DiffuseColour not found in layer 1\n" + "Trade::MaterialData::mutableAttribute(): attribute DiffuseColour not found in layer 1\n"); } void MaterialDataTest::accessNotFoundInLayerString() { @@ -2294,11 +2646,15 @@ void MaterialDataTest::accessNotFoundInLayerString() { data.attributeType("ClearCoat", "DiffuseColour"); data.attribute("ClearCoat", "DiffuseColour"); data.attribute("ClearCoat", "DiffuseColour"); + data.mutableAttribute("ClearCoat", "DiffuseColour"); + data.mutableAttribute("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"); + "Trade::MaterialData::attribute(): attribute DiffuseColour not found in layer ClearCoat\n" + "Trade::MaterialData::mutableAttribute(): attribute DiffuseColour not found in layer ClearCoat\n" + "Trade::MaterialData::mutableAttribute(): attribute DiffuseColour not found in layer ClearCoat\n"); } void MaterialDataTest::accessInvalidAttributeName() { @@ -2323,6 +2679,10 @@ void MaterialDataTest::accessInvalidAttributeName() { data.attribute("Layer", MaterialAttribute(0xfefe)); data.attribute(0, MaterialAttribute(0x0)); data.attribute("Layer", MaterialAttribute(0xfefe)); + data.mutableAttribute(0, MaterialAttribute(0x0)); + data.mutableAttribute("Layer", MaterialAttribute(0xfefe)); + data.mutableAttribute(0, MaterialAttribute(0x0)); + data.mutableAttribute("Layer", MaterialAttribute(0xfefe)); data.tryAttribute(0, MaterialAttribute(0x0)); data.tryAttribute("Layer", MaterialAttribute(0xfefe)); data.tryAttribute(0, MaterialAttribute(0x0)); @@ -2340,6 +2700,10 @@ void MaterialDataTest::accessInvalidAttributeName() { "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::mutableAttribute(): invalid name Trade::MaterialAttribute(0x0)\n" + "Trade::MaterialData::mutableAttribute(): invalid name Trade::MaterialAttribute(0xfefe)\n" + "Trade::MaterialData::mutableAttribute(): invalid name Trade::MaterialAttribute(0x0)\n" + "Trade::MaterialData::mutableAttribute(): 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" @@ -2348,6 +2712,97 @@ void MaterialDataTest::accessInvalidAttributeName() { "Trade::MaterialData::attributeOr(): invalid name Trade::MaterialAttribute(0xfefe)\n"); } +void MaterialDataTest::accessMutableNotAllowed() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + const MaterialAttributeData attributes[]{ + {MaterialAttribute::DiffuseColor, 0x335566ff_rgbaf}, + {MaterialAttribute::LayerName, "ClearCoat"}, + {MaterialAttribute::Roughness, 0.5f} + }; + + const UnsignedInt layers[]{ + 1, 3 + }; + + MaterialData data{{}, {}, attributes, {}, layers}; + + std::ostringstream out; + Error redirectError{&out}; + data.mutableAttribute(0); + data.mutableAttribute("DiffuseColor"); + data.mutableAttribute(MaterialAttribute::DiffuseColor); + data.mutableAttribute(0); + data.mutableAttribute("DiffuseColor"); + data.mutableAttribute(MaterialAttribute::DiffuseColor); + + data.mutableAttribute(1, 1); + data.mutableAttribute(1, "Roughness"); + data.mutableAttribute(1, MaterialAttribute::Roughness); + data.mutableAttribute(1, 1); + data.mutableAttribute(1, "Roughness"); + data.mutableAttribute(1, MaterialAttribute::Roughness); + data.mutableAttribute(1, 0); + data.mutableAttribute(1, "$LayerName"); + data.mutableAttribute(1, MaterialAttribute::LayerName); + + data.mutableAttribute("ClearCoat", 1); + data.mutableAttribute("ClearCoat", "Roughness"); + data.mutableAttribute("ClearCoat", MaterialAttribute::Roughness); + data.mutableAttribute("ClearCoat", 1); + data.mutableAttribute("ClearCoat", "Roughness"); + data.mutableAttribute("ClearCoat", MaterialAttribute::Roughness); + data.mutableAttribute("ClearCoat", 0); + data.mutableAttribute("ClearCoat", "$LayerName"); + data.mutableAttribute("ClearCoat", MaterialAttribute::LayerName); + + data.mutableAttribute(MaterialLayer::ClearCoat, 1); + data.mutableAttribute(MaterialLayer::ClearCoat, "Roughness"); + data.mutableAttribute(MaterialLayer::ClearCoat, MaterialAttribute::Roughness); + data.mutableAttribute(MaterialLayer::ClearCoat, 1); + data.mutableAttribute(MaterialLayer::ClearCoat, "Roughness"); + data.mutableAttribute(MaterialLayer::ClearCoat, MaterialAttribute::Roughness); + data.mutableAttribute(MaterialLayer::ClearCoat, 0); + data.mutableAttribute(MaterialLayer::ClearCoat, "$LayerName"); + data.mutableAttribute(MaterialLayer::ClearCoat, MaterialAttribute::LayerName); + CORRADE_COMPARE(out.str(), + "Trade::MaterialData::mutableAttribute(): attribute data not mutable\n" + "Trade::MaterialData::mutableAttribute(): attribute data not mutable\n" + "Trade::MaterialData::mutableAttribute(): attribute data not mutable\n" + "Trade::MaterialData::mutableAttribute(): attribute data not mutable\n" + "Trade::MaterialData::mutableAttribute(): attribute data not mutable\n" + "Trade::MaterialData::mutableAttribute(): attribute data not mutable\n" + + "Trade::MaterialData::mutableAttribute(): attribute data not mutable\n" + "Trade::MaterialData::mutableAttribute(): attribute data not mutable\n" + "Trade::MaterialData::mutableAttribute(): attribute data not mutable\n" + "Trade::MaterialData::mutableAttribute(): attribute data not mutable\n" + "Trade::MaterialData::mutableAttribute(): attribute data not mutable\n" "Trade::MaterialData::mutableAttribute(): attribute data not mutable\n" + "Trade::MaterialData::mutableAttribute(): attribute data not mutable\n" + "Trade::MaterialData::mutableAttribute(): attribute data not mutable\n" + "Trade::MaterialData::mutableAttribute(): attribute data not mutable\n" + + "Trade::MaterialData::mutableAttribute(): attribute data not mutable\n" + "Trade::MaterialData::mutableAttribute(): attribute data not mutable\n" + "Trade::MaterialData::mutableAttribute(): attribute data not mutable\n" + "Trade::MaterialData::mutableAttribute(): attribute data not mutable\n" + "Trade::MaterialData::mutableAttribute(): attribute data not mutable\n" "Trade::MaterialData::mutableAttribute(): attribute data not mutable\n" + "Trade::MaterialData::mutableAttribute(): attribute data not mutable\n" + "Trade::MaterialData::mutableAttribute(): attribute data not mutable\n" + "Trade::MaterialData::mutableAttribute(): attribute data not mutable\n" + + "Trade::MaterialData::mutableAttribute(): attribute data not mutable\n" + "Trade::MaterialData::mutableAttribute(): attribute data not mutable\n" + "Trade::MaterialData::mutableAttribute(): attribute data not mutable\n" + "Trade::MaterialData::mutableAttribute(): attribute data not mutable\n" + "Trade::MaterialData::mutableAttribute(): attribute data not mutable\n" "Trade::MaterialData::mutableAttribute(): attribute data not mutable\n" + "Trade::MaterialData::mutableAttribute(): attribute data not mutable\n" + "Trade::MaterialData::mutableAttribute(): attribute data not mutable\n" + "Trade::MaterialData::mutableAttribute(): attribute data not mutable\n"); +} + void MaterialDataTest::releaseAttributes() { MaterialData data{{}, { {"DiffuseColor", 0xff3366aa_rgbaf}, @@ -2427,10 +2882,16 @@ void MaterialDataTest::templateLayerAccess() { 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(*static_cast(data.mutableAttribute(2)), 3); + CORRADE_COMPARE(*static_cast(data.mutableAttribute(MaterialAttribute::LayerFactorTexture)), 3); + CORRADE_COMPARE(*static_cast(data.mutableAttribute("LayerFactorTexture")), 3); CORRADE_COMPARE(data.attribute(2), 3); CORRADE_COMPARE(data.attribute(MaterialAttribute::LayerFactorTexture), 3); CORRADE_COMPARE(data.attribute("LayerFactorTexture"), 3); + CORRADE_COMPARE(data.mutableAttribute(2), 3); + CORRADE_COMPARE(data.mutableAttribute(MaterialAttribute::LayerFactorTexture), 3); + CORRADE_COMPARE(data.mutableAttribute("LayerFactorTexture"), 3); CORRADE_COMPARE(*static_cast(data.tryAttribute(MaterialAttribute::LayerFactorTexture)), 3); CORRADE_COMPARE(*static_cast(data.tryAttribute("LayerFactorTexture")), 3); @@ -2442,6 +2903,50 @@ void MaterialDataTest::templateLayerAccess() { CORRADE_COMPARE(data.attributeOr("LayerFactorTexture", 5u), 3); } +void MaterialDataTest::templateLayerAccessMutable() { + MaterialLayerData data{{}, { + {MaterialLayer::ClearCoat}, + {MaterialAttribute::Roughness, 1.0f}, + }, {0, 2}}; + + *static_cast(data.mutableAttribute(1)) *= 2.0f; + *static_cast(data.mutableAttribute(MaterialAttribute::Roughness)) *= 2.0f; + *static_cast(data.mutableAttribute("Roughness")) *= 2.0f; + data.mutableAttribute(1) *= 2.0f; + data.mutableAttribute(MaterialAttribute::Roughness) *= 2.0f; + data.mutableAttribute("Roughness") *= 2.0f; + CORRADE_COMPARE(data.attribute(MaterialAttribute::Roughness), 64.0f); + + /* Resetting back so the layer name always stays the same so the next call + can find it. Other than that, the result should be same as in + accessLayerIndexMutable(). */ + { + *static_cast(data.mutableAttribute("ClearCoat", 0)) = 'D'; + CORRADE_COMPARE(data.attribute(1, 0), "DlearCoat"); + *static_cast(data.mutableAttribute(1, 0)) = 'C'; + } { + *static_cast(data.mutableAttribute("ClearCoat", "$LayerName")) = 'E'; + CORRADE_COMPARE(data.attribute(1, 0), "ElearCoat"); + *static_cast(data.mutableAttribute(1, 0)) = 'C'; + } { + *static_cast(data.mutableAttribute("ClearCoat", MaterialAttribute::LayerName)) = 'F'; + CORRADE_COMPARE(data.attribute(1, 0), "FlearCoat"); + *static_cast(data.mutableAttribute(1, 0)) = 'C'; + } { + data.mutableAttribute("ClearCoat", 0)[0] = 'G'; + CORRADE_COMPARE(data.attribute(1, 0), "GlearCoat"); + *static_cast(data.mutableAttribute(1, 0)) = 'C'; + } { + data.mutableAttribute("ClearCoat", "$LayerName")[0] = 'H'; + CORRADE_COMPARE(data.attribute(1, 0), "HlearCoat"); + *static_cast(data.mutableAttribute(1, 0)) = 'C'; + } { + data.mutableAttribute("ClearCoat", "$LayerName")[0] = 'I'; + CORRADE_COMPARE(data.attribute(1, 0), "IlearCoat"); + *static_cast(data.mutableAttribute(1, 0)) = 'C'; + } +} + void MaterialDataTest::debugLayer() { std::ostringstream out;