diff --git a/doc/snippets/MagnumTrade.cpp b/doc/snippets/MagnumTrade.cpp index ef5be24aa7..33750cbd6f 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 8560265283..d0732faf1e 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 e01ec7ae47..558d3fcf99 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 0e2255031f..154c8425dd 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 c97777bb6a..905dafed80 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;