diff --git a/src/Magnum/Trade/SceneData.cpp b/src/Magnum/Trade/SceneData.cpp index e1904cbe2..56029e269 100644 --- a/src/Magnum/Trade/SceneData.cpp +++ b/src/Magnum/Trade/SceneData.cpp @@ -95,6 +95,7 @@ Debug& operator<<(Debug& debug, const SceneField value) { _c(Light) _c(Camera) _c(Skin) + _c(ImporterState) #undef _c /* LCOV_EXCL_STOP */ @@ -202,6 +203,8 @@ Debug& operator<<(Debug& debug, const SceneFieldType value) { _c(Rad) _c(Radh) _c(Radd) + _c(Pointer) + _c(MutablePointer) #undef _c /* LCOV_EXCL_STOP */ } @@ -323,6 +326,9 @@ UnsignedInt sceneFieldTypeSize(const SceneFieldType type) { return 96; case SceneFieldType::Matrix4x4d: return 128; + case SceneFieldType::Pointer: + case SceneFieldType::MutablePointer: + return sizeof(void*); } #ifdef CORRADE_TARGET_GCC #pragma GCC diagnostic pop @@ -368,7 +374,7 @@ SceneData::SceneData(const SceneObjectType objectType, const UnsignedLong object #ifndef CORRADE_NO_ASSERT /* Check various assumptions about field data */ - Math::BoolVector<11> fieldsPresent; /** @todo some constant for this */ + Math::BoolVector<12> fieldsPresent; /** @todo some constant for this */ const UnsignedInt objectTypeSize = sceneObjectTypeSize(_objectType); UnsignedInt translationField = ~UnsignedInt{}; UnsignedInt rotationField = ~UnsignedInt{}; @@ -1561,6 +1567,47 @@ Containers::Array SceneData::skinsAsArray() const { return unsignedIndexFieldAsArrayInternal(fieldId); } +void SceneData::importerStateIntoInternal(const UnsignedInt fieldId, const std::size_t offset, const Containers::StridedArrayView1D& destination) const { + /* fieldId, offset and destination.size() is assumed to be in bounds, + checked by the callers */ + + const SceneFieldData& field = _fields[fieldId]; + CORRADE_INTERNAL_ASSERT(field._fieldType == SceneFieldType::Pointer || + field._fieldType == SceneFieldType::MutablePointer); + Utility::copy(Containers::arrayCast(fieldDataFieldViewInternal(field, offset, destination.size())), destination); +} + +void SceneData::importerStateInto(const Containers::StridedArrayView1D& destination) const { + const UnsignedInt fieldId = fieldFor(SceneField::ImporterState); + CORRADE_ASSERT(fieldId != ~UnsignedInt{}, + "Trade::SceneData::importerStateInto(): field not found", ); + CORRADE_ASSERT(destination.size() == _fields[fieldId]._size, + "Trade::SceneData::importerStateInto(): expected a view with" << _fields[fieldId]._size << "elements but got" << destination.size(), ); + importerStateIntoInternal(fieldId, 0, destination); +} + +std::size_t SceneData::importerStateInto(const std::size_t offset, const Containers::StridedArrayView1D& destination) const { + const UnsignedInt fieldId = fieldFor(SceneField::ImporterState); + CORRADE_ASSERT(fieldId != ~UnsignedInt{}, + "Trade::SceneData::importerStateInto(): field not found", {}); + CORRADE_ASSERT(offset <= _fields[fieldId]._size, + "Trade::SceneData::importerStateInto(): offset" << offset << "out of bounds for a field of size" << _fields[fieldId]._size, {}); + const std::size_t size = Math::min(destination.size(), std::size_t(_fields[fieldId]._size) - offset); + importerStateIntoInternal(fieldId, offset, destination.prefix(size)); + return size; +} + +Containers::Array SceneData::importerStateAsArray() const { + const UnsignedInt fieldId = fieldFor(SceneField::ImporterState); + CORRADE_ASSERT(fieldId != ~UnsignedInt{}, + /* Using the same message as in Into() to avoid too many redundant + strings in the binary */ + "Trade::SceneData::importerStateInto(): field not found", {}); + Containers::Array out{NoInit, std::size_t(_fields[fieldId]._size)}; + importerStateIntoInternal(fieldId, 0, out); + return out; +} + namespace { template std::size_t findObject(const Containers::StridedArrayView1D& objects, const UnsignedInt object) { @@ -1917,6 +1964,22 @@ Containers::Array SceneData::skinsFor(const UnsignedInt object) con return out; } +Containers::Optional SceneData::importerStateFor(const UnsignedInt object) const { + CORRADE_ASSERT(object < _objectCount, + "Trade::SceneData::importerStateFor(): object" << object << "out of bounds for" << _objectCount << "objects", {}); + + const UnsignedInt fieldId = fieldFor(SceneField::ImporterState); + if(fieldId == ~UnsignedInt{}) return {}; + + const SceneFieldData& field = _fields[fieldId]; + const std::size_t offset = fieldFor(field, 0, object); + if(offset == field._size) return {}; + + const void* importerState[1]; + importerStateIntoInternal(fieldId, offset, importerState); + return *importerState; +} + Containers::Array SceneData::releaseFieldData() { Containers::Array out = std::move(_fields); _fields = {}; diff --git a/src/Magnum/Trade/SceneData.h b/src/Magnum/Trade/SceneData.h index 3e7b30544..27b86899e 100644 --- a/src/Magnum/Trade/SceneData.h +++ b/src/Magnum/Trade/SceneData.h @@ -273,6 +273,18 @@ enum class SceneField: UnsignedInt { */ Skin, + /** + * Importer state for given object, per-object counterpart to + * scene-specific @ref SceneData::importerState(). Type is usually + * @ref SceneFieldType::Pointer but can be also + * @ref SceneFieldType::MutablePointer. An object should have only one + * importer state, altough this isn't enforced in any way, and which of the + * duplicate fields gets used is not defined. + * @see @ref SceneData::importerStateAsArray(), + * @ref SceneData::importerStateFor() + */ + ImporterState, + /** * This and all higher values are for importer-specific fields. Can be * of any type. See documentation of a particular importer for details. @@ -455,7 +467,21 @@ enum class SceneFieldType: UnsignedShort { Degd, /**< @relativeref{Magnum,Degh} */ Rad, /**< @relativeref{Magnum,Rad} */ Radh, /**< @relativeref{Magnum,Radh} */ - Radd /**< @relativeref{Magnum,Radd} */ + Radd, /**< @relativeref{Magnum,Radd} */ + + /** + * @cpp const void* @ce, type is not preserved. For convenience it's + * possible to retrieve the value by calling @cpp field() @ce + * with an arbitrary `T` but the user has to ensure the type is correct. + */ + Pointer, + + /** + * @cpp void* @ce, type is not preserved. For convenience it's possible to + * retrieve the value by calling @cpp field() @ce with an arbitrary `T` + * but the user has to ensure the type is correct. + */ + MutablePointer, }; /** @@ -1142,9 +1168,10 @@ class MAGNUM_TRADE_EXPORT SceneData { * @ref translationsRotationsScalings2DAsArray(), * @ref translationsRotationsScalings3DAsArray(), * @ref meshesMaterialsAsArray(), @ref lightsAsArray(), - * @ref camerasAsArray(), @ref skinsAsArray() accessors to get common - * fields converted to usual types, but note that these operations - * involve extra allocation and data conversion. + * @ref camerasAsArray(), @ref skinsAsArray(), + * @ref importerStateAsArray() accessors to get common fields converted + * to usual types, but note that these operations involve extra + * allocation and data conversion. * @see @ref field(SceneField) const, @ref mutableField(UnsignedInt), * @ref fieldArraySize() */ @@ -1222,9 +1249,10 @@ class MAGNUM_TRADE_EXPORT SceneData { * @ref translationsRotationsScalings2DAsArray(), * @ref translationsRotationsScalings3DAsArray(), * @ref meshesMaterialsAsArray(), @ref lightsAsArray(), - * @ref camerasAsArray(), @ref skinsAsArray() accessors to get common - * fields converted to usual types, but note that these operations - * involve extra allocation and data conversion. + * @ref camerasAsArray(), @ref skinsAsArray(), + * @ref importerStateAsArray() accessors to get common fields converted + * to usual types, but note that these operations involve extra + * allocation and data conversion. * @see @ref field(UnsignedInt) const, @ref mutableField(SceneField) */ template::value>::type> Containers::StridedArrayView1D field(SceneField name) const; @@ -1721,6 +1749,45 @@ class MAGNUM_TRADE_EXPORT SceneData { */ std::size_t skinsInto(std::size_t offset, const Containers::StridedArrayView1D& destination) const; + /** + * @brief Per-object importer state as `void` pointers + * @m_since_latest + * + * Convenience alternative to @ref field(SceneField) const with + * @ref SceneField::ImporterState as the argument that converts the + * field from an arbitrary underlying type and returns it in a + * newly-allocated array. The field is expected to exist. + * + * This is different from @ref importerState(), which returns importer + * state for the scene itself, not particular objects. + * @see @ref importerStateInto(), @ref hasField(), @ref importerStateFor() + */ + Containers::Array importerStateAsArray() const; + + /** + * @brief Per-object importer state as `void` pointers into a pre-allocated view + * @m_since_latest + * + * Like @ref importerStateAsArray(), but puts the result into + * @p destination instead of allocating a new array. Expects that + * @p destination is sized to contain exactly all data. + * @see @ref fieldSize(SceneField) const + */ + void importerStateInto(const Containers::StridedArrayView1D& destination) const; + + /** + * @brief A subrange of per-object importer state as `void` pointers into a pre-allocated view + * @m_since_latest + * + * Compared to @ref importerStateInto(const Containers::StridedArrayView1D&) const + * extracts only a subrange of the field defined by @p offset and size + * of the @p destination view, returning the count of items actually + * extracted. The @p offset is expected to not be larger than the field + * size. + * @see @ref fieldSize(SceneField) const + */ + std::size_t importerStateInto(std::size_t offset, const Containers::StridedArrayView1D& destination) const; + /** * @brief Parent for given object * @m_since_latest @@ -1945,6 +2012,25 @@ class MAGNUM_TRADE_EXPORT SceneData { */ Containers::Array skinsFor(UnsignedInt object) const; + /** + * @brief Importer state for given object + * @m_since_latest + * + * Looks up the @ref SceneField::ImporterState field for @p object. The + * lookup is done in a @f$ \mathcal{O}(m + n) @f$ complexity with + * @f$ m @f$ being the field count and @f$ n @f$ the size of the + * importer state field, thus for retrieving importer state info for + * many objects it's recommended to access the field data directly with + * @ref importerStateAsArray() and related APIs. + * + * If the @ref SceneField::ImporterState field is not present or if + * there's no importer state for @p object, returns + * @ref Containers::NullOpt. + * + * The @p object is expected to be less than @ref objectCount(). + */ + Containers::Optional importerStateFor(UnsignedInt object) const; + /** * @brief Release field data storage * @m_since_latest @@ -1983,7 +2069,11 @@ class MAGNUM_TRADE_EXPORT SceneData { /** * @brief Importer-specific state * - * See @ref AbstractImporter::importerState() for more information. + * Scene-specific importer state. For object-specific importer state + * look for the @ref SceneField::ImporterState field or access it via + * @ref importerStateAsArray(), @ref importerStateFor() and related + * convenience functions. See @ref AbstractImporter::importerState() + * for general information about importer state pointers. */ const void* importerState() const { return _importerState; } @@ -2020,6 +2110,7 @@ class MAGNUM_TRADE_EXPORT SceneData { MAGNUM_TRADE_LOCAL void indexFieldIntoInternal(const UnsignedInt fieldId, std::size_t offset, const Containers::StridedArrayView1D& destination) const; MAGNUM_TRADE_LOCAL Containers::Array unsignedIndexFieldAsArrayInternal(const UnsignedInt fieldId) const; MAGNUM_TRADE_LOCAL void meshesMaterialsIntoInternal(UnsignedInt fieldId, std::size_t offset, const Containers::StridedArrayView1D& meshDestination, const Containers::StridedArrayView1D& meshMaterialDestination) const; + MAGNUM_TRADE_LOCAL void importerStateIntoInternal(const UnsignedInt fieldId, std::size_t offset, const Containers::StridedArrayView1D& destination) const; DataFlags _dataFlags; SceneObjectType _objectType; @@ -2142,6 +2233,16 @@ namespace Implementation { template struct SceneFieldTypeFor>: SceneFieldTypeFor> {}; template struct SceneFieldTypeFor>: SceneFieldTypeFor> {}; template struct SceneFieldTypeFor>: SceneFieldTypeFor> {}; + template struct SceneFieldTypeFor { + constexpr static SceneFieldType type() { + return SceneFieldType::Pointer; + } + }; + template struct SceneFieldTypeFor { + constexpr static SceneFieldType type() { + return SceneFieldType::MutablePointer; + } + }; template constexpr SceneObjectType sceneObjectTypeFor() { static_assert(sizeof(T) == 0, "unsupported object type"); @@ -2196,6 +2297,9 @@ namespace Implementation { (type == SceneFieldType::Byte || type == SceneFieldType::Short || type == SceneFieldType::Int)) || + (name == SceneField::ImporterState && + (type == SceneFieldType::Pointer || + type == SceneFieldType::MutablePointer)) || /* Custom fields can be anything */ isSceneFieldCustom(name); } diff --git a/src/Magnum/Trade/Test/SceneDataTest.cpp b/src/Magnum/Trade/Test/SceneDataTest.cpp index 17e268625..82dbb87f4 100644 --- a/src/Magnum/Trade/Test/SceneDataTest.cpp +++ b/src/Magnum/Trade/Test/SceneDataTest.cpp @@ -141,6 +141,9 @@ struct SceneDataTest: TestSuite::Tester { template void skinsAsArray(); void skinsIntoArray(); void skinsIntoArrayInvalidSizeOrOffset(); + template void importerStateAsArray(); + void importerStateIntoArray(); + void importerStateIntoArrayInvalidSizeOrOffset(); void mutableAccessNotAllowed(); @@ -149,6 +152,7 @@ struct SceneDataTest: TestSuite::Tester { void fieldNotFound(); void fieldWrongType(); + void fieldWrongPointerType(); void fieldWrongArrayAccess(); /* Different object types checked just for the parentFor(), other APIs @@ -167,6 +171,7 @@ struct SceneDataTest: TestSuite::Tester { void lightsFor(); void camerasFor(); void skinsFor(); + void importerStateFor(); void fieldForFieldMissing(); void fieldForInvalidObject(); @@ -351,6 +356,13 @@ SceneDataTest::SceneDataTest() { Containers::arraySize(IntoArrayOffsetData)); addTests({&SceneDataTest::skinsIntoArrayInvalidSizeOrOffset, + &SceneDataTest::importerStateAsArray, + &SceneDataTest::importerStateAsArray}); + + addInstancedTests({&SceneDataTest::importerStateIntoArray}, + Containers::arraySize(IntoArrayOffsetData)); + + addTests({&SceneDataTest::importerStateIntoArrayInvalidSizeOrOffset, &SceneDataTest::mutableAccessNotAllowed, @@ -359,6 +371,7 @@ SceneDataTest::SceneDataTest() { &SceneDataTest::fieldNotFound, &SceneDataTest::fieldWrongType, + &SceneDataTest::fieldWrongPointerType, &SceneDataTest::fieldWrongArrayAccess, &SceneDataTest::parentFor, @@ -386,6 +399,7 @@ SceneDataTest::SceneDataTest() { &SceneDataTest::lightsFor, &SceneDataTest::camerasFor, &SceneDataTest::skinsFor, + &SceneDataTest::importerStateFor, &SceneDataTest::fieldForFieldMissing, &SceneDataTest::fieldForInvalidObject, @@ -494,6 +508,7 @@ void SceneDataTest::fieldTypeSize() { CORRADE_COMPARE(sceneFieldTypeSize(SceneFieldType::Matrix3x3d), sizeof(Matrix3x3d)); CORRADE_COMPARE(sceneFieldTypeSize(SceneFieldType::Matrix3x4d), sizeof(Matrix3x4d)); CORRADE_COMPARE(sceneFieldTypeSize(SceneFieldType::Matrix4x4d), sizeof(Matrix4x4d)); + CORRADE_COMPARE(sceneFieldTypeSize(SceneFieldType::Pointer), sizeof(const void*)); } void SceneDataTest::fieldTypeSizeInvalid() { @@ -1763,6 +1778,12 @@ _c(DualComplex) _c(DualComplexd) _c(DualQuaternion) _c(DualQuaterniond) +template struct NameTraits { + static const char* name() { return "Pointer"; } +}; +template struct NameTraits { + static const char* name() { return "MutablePointer"; } +}; #undef _c template void SceneDataTest::objectsAsArrayByIndex() { @@ -3714,6 +3735,107 @@ void SceneDataTest::skinsIntoArrayInvalidSizeOrOffset() { "Trade::SceneData::skinsInto(): offset 4 out of bounds for a field of size 3\n"); } +template void SceneDataTest::importerStateAsArray() { + setTestCaseTemplateName(NameTraits::name()); + + int a, b; + + struct Field { + UnsignedByte object; + T importerState; + } fields[]{ + {0, &a}, + {1, nullptr}, + {15, &b} + }; + + Containers::StridedArrayView1D view = fields; + + SceneData scene{SceneObjectType::UnsignedByte, 50, {}, fields, { + /* To verify it isn't just picking the first ever field */ + SceneFieldData{SceneField::Parent, SceneObjectType::UnsignedByte, nullptr, SceneFieldType::Int, nullptr}, + SceneFieldData{SceneField::ImporterState, view.slice(&Field::object), view.slice(&Field::importerState)} + }}; + + CORRADE_COMPARE_AS(scene.importerStateAsArray(), + Containers::arrayView({&a, nullptr, &b}), + TestSuite::Compare::Container); +} + +void SceneDataTest::importerStateIntoArray() { + auto&& data = IntoArrayOffsetData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + /* Both AsArray() and Into() share a common helper. The AsArray() test + above verified handling of various data types and this checks the + offset/size parameters of the Into() variant. */ + + int a, b; + + struct Field { + UnsignedInt object; + const void* importerState; + } fields[]{ + {1, &a}, + {0, nullptr}, + {4, &b} + }; + + Containers::StridedArrayView1D view = fields; + + SceneData scene{SceneObjectType::UnsignedInt, 5, {}, fields, { + /* To verify it isn't just picking the first ever field */ + SceneFieldData{SceneField::Parent, SceneObjectType::UnsignedInt, nullptr, SceneFieldType::Int, nullptr}, + SceneFieldData{SceneField::ImporterState, + view.slice(&Field::object), + view.slice(&Field::importerState)}, + }}; + + /* The offset-less overload should give back all data */ + { + const void* out[3]; + scene.importerStateInto(out); + CORRADE_COMPARE_AS(Containers::stridedArrayView(out), + view.slice(&Field::importerState), + TestSuite::Compare::Container); + + /* The offset variant only a subset */ + } { + Containers::Array out{data.size}; + CORRADE_COMPARE(scene.importerStateInto(data.offset, out), data.expectedSize); + CORRADE_COMPARE_AS(out.prefix(data.expectedSize), + view.slice(&Field::importerState) + .slice(data.offset, data.offset + data.expectedSize), + TestSuite::Compare::Container); + } +} + +void SceneDataTest::importerStateIntoArrayInvalidSizeOrOffset() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + struct Field { + UnsignedInt object; + const void* importerState; + } fields[3]{}; + + Containers::StridedArrayView1D view = fields; + + SceneData scene{SceneObjectType::UnsignedInt, 5, {}, fields, { + SceneFieldData{SceneField::ImporterState, view.slice(&Field::object), view.slice(&Field::importerState)} + }}; + + std::ostringstream out; + Error redirectError{&out}; + const void* destination[2]; + scene.importerStateInto(destination); + scene.importerStateInto(4, destination); + CORRADE_COMPARE(out.str(), + "Trade::SceneData::importerStateInto(): expected a view with 3 elements but got 2\n" + "Trade::SceneData::importerStateInto(): offset 4 out of bounds for a field of size 3\n"); +} + void SceneDataTest::mutableAccessNotAllowed() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); @@ -3908,6 +4030,9 @@ void SceneDataTest::fieldNotFound() { scene.skinsAsArray(); scene.skinsInto(nullptr); scene.skinsInto(0, nullptr); + scene.importerStateAsArray(); + scene.importerStateInto(nullptr); + scene.importerStateInto(0, nullptr); CORRADE_COMPARE(out.str(), "Trade::SceneData::fieldData(): index 2 out of range for 2 fields\n" "Trade::SceneData::fieldName(): index 2 out of range for 2 fields\n" @@ -3961,7 +4086,10 @@ void SceneDataTest::fieldNotFound() { "Trade::SceneData::camerasInto(): field not found\n" "Trade::SceneData::skinsInto(): field not found\n" "Trade::SceneData::skinsInto(): field not found\n" - "Trade::SceneData::skinsInto(): field not found\n"); + "Trade::SceneData::skinsInto(): field not found\n" + "Trade::SceneData::importerStateInto(): field not found\n" + "Trade::SceneData::importerStateInto(): field not found\n" + "Trade::SceneData::importerStateInto(): field not found\n"); } void SceneDataTest::fieldWrongType() { @@ -4003,6 +4131,78 @@ void SceneDataTest::fieldWrongType() { "Trade::SceneData::mutableField(): Trade::SceneField::Mesh is Trade::SceneFieldType::UnsignedShort but requested a type equivalent to Trade::SceneFieldType::UnsignedByte\n"); } +void SceneDataTest::fieldWrongPointerType() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + struct Thing { + UnsignedInt object; + Int* foobar; + const Int* importerState; + } things[2]; + Containers::StridedArrayView1D view = things; + + SceneData scene{SceneObjectType::UnsignedInt, 5, DataFlag::Mutable, things, { + SceneFieldData{sceneFieldCustom(35), view.slice(&Thing::object), Containers::arrayCast<2, Int*>(view.slice(&Thing::foobar))}, + SceneFieldData{SceneField::ImporterState, view.slice(&Thing::object), view.slice(&Thing::importerState)}, + }}; + + /* These are fine (type is not checked) */ + scene.field(0); + scene.field(1); + scene.mutableField(0); + scene.mutableField(1); + scene.field(sceneFieldCustom(35)); + scene.field(SceneField::ImporterState); + scene.mutableField(sceneFieldCustom(35)); + scene.mutableField(SceneField::ImporterState); + + std::ostringstream out; + Error redirectError{&out}; + scene.field(0); + scene.field(0); + scene.field(0); + scene.field(1); + scene.field(1); + scene.mutableField(0); + scene.mutableField(0); + scene.mutableField(0); + scene.mutableField(1); + scene.mutableField(1); + scene.field(sceneFieldCustom(35)); + scene.field(sceneFieldCustom(35)); + scene.field(sceneFieldCustom(35)); + scene.field(SceneField::ImporterState); + scene.field(SceneField::ImporterState); + scene.mutableField(sceneFieldCustom(35)); + scene.mutableField(sceneFieldCustom(35)); + scene.mutableField(sceneFieldCustom(35)); + scene.mutableField(SceneField::ImporterState); + scene.mutableField(SceneField::ImporterState); + CORRADE_COMPARE(out.str(), + "Trade::SceneData::field(): Trade::SceneField::Custom(35) is Trade::SceneFieldType::MutablePointer but requested a type equivalent to Trade::SceneFieldType::Int\n" + "Trade::SceneData::field(): Trade::SceneField::Custom(35) is Trade::SceneFieldType::MutablePointer but requested a type equivalent to Trade::SceneFieldType::Pointer\n" + "Trade::SceneData::field(): Trade::SceneField::Custom(35) is Trade::SceneFieldType::MutablePointer but requested a type equivalent to Trade::SceneFieldType::Pointer\n" + "Trade::SceneData::field(): Trade::SceneField::ImporterState is Trade::SceneFieldType::Pointer but requested a type equivalent to Trade::SceneFieldType::MutablePointer\n" + "Trade::SceneData::field(): Trade::SceneField::ImporterState is Trade::SceneFieldType::Pointer but requested a type equivalent to Trade::SceneFieldType::MutablePointer\n" + "Trade::SceneData::mutableField(): Trade::SceneField::Custom(35) is Trade::SceneFieldType::MutablePointer but requested a type equivalent to Trade::SceneFieldType::Int\n" + "Trade::SceneData::mutableField(): Trade::SceneField::Custom(35) is Trade::SceneFieldType::MutablePointer but requested a type equivalent to Trade::SceneFieldType::Pointer\n" + "Trade::SceneData::mutableField(): Trade::SceneField::Custom(35) is Trade::SceneFieldType::MutablePointer but requested a type equivalent to Trade::SceneFieldType::Pointer\n" + "Trade::SceneData::mutableField(): Trade::SceneField::ImporterState is Trade::SceneFieldType::Pointer but requested a type equivalent to Trade::SceneFieldType::MutablePointer\n" + "Trade::SceneData::mutableField(): Trade::SceneField::ImporterState is Trade::SceneFieldType::Pointer but requested a type equivalent to Trade::SceneFieldType::MutablePointer\n" + "Trade::SceneData::field(): Trade::SceneField::Custom(35) is Trade::SceneFieldType::MutablePointer but requested a type equivalent to Trade::SceneFieldType::Int\n" + "Trade::SceneData::field(): Trade::SceneField::Custom(35) is Trade::SceneFieldType::MutablePointer but requested a type equivalent to Trade::SceneFieldType::Pointer\n" + "Trade::SceneData::field(): Trade::SceneField::Custom(35) is Trade::SceneFieldType::MutablePointer but requested a type equivalent to Trade::SceneFieldType::Pointer\n" + "Trade::SceneData::field(): Trade::SceneField::ImporterState is Trade::SceneFieldType::Pointer but requested a type equivalent to Trade::SceneFieldType::MutablePointer\n" + "Trade::SceneData::field(): Trade::SceneField::ImporterState is Trade::SceneFieldType::Pointer but requested a type equivalent to Trade::SceneFieldType::MutablePointer\n" + "Trade::SceneData::mutableField(): Trade::SceneField::Custom(35) is Trade::SceneFieldType::MutablePointer but requested a type equivalent to Trade::SceneFieldType::Int\n" + "Trade::SceneData::mutableField(): Trade::SceneField::Custom(35) is Trade::SceneFieldType::MutablePointer but requested a type equivalent to Trade::SceneFieldType::Pointer\n" + "Trade::SceneData::mutableField(): Trade::SceneField::Custom(35) is Trade::SceneFieldType::MutablePointer but requested a type equivalent to Trade::SceneFieldType::Pointer\n" + "Trade::SceneData::mutableField(): Trade::SceneField::ImporterState is Trade::SceneFieldType::Pointer but requested a type equivalent to Trade::SceneFieldType::MutablePointer\n" + "Trade::SceneData::mutableField(): Trade::SceneField::ImporterState is Trade::SceneFieldType::Pointer but requested a type equivalent to Trade::SceneFieldType::MutablePointer\n"); +} + void SceneDataTest::fieldWrongArrayAccess() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); @@ -4512,6 +4712,36 @@ void SceneDataTest::skinsFor() { TestSuite::Compare::Container); } +void SceneDataTest::importerStateFor() { + int a, b, c; + + struct Field { + UnsignedInt object; + const void* importerState; + } fields[]{ + {3, &a}, + {4, &b}, + {2, nullptr}, + {4, &c} + }; + + Containers::StridedArrayView1D view = fields; + + SceneData scene{SceneObjectType::UnsignedInt, 7, {}, fields, { + SceneFieldData{SceneField::ImporterState, view.slice(&Field::object), view.slice(&Field::importerState)} + }}; + + CORRADE_COMPARE(scene.importerStateFor(2), nullptr); + CORRADE_COMPARE(scene.importerStateFor(3), &a); + + /* Duplicate entries -- only the first one gets used, it doesn't traverse + further */ + CORRADE_COMPARE(scene.importerStateFor(4), &b); + + /* Object that's not in the array at all */ + CORRADE_COMPARE(scene.importerStateFor(1), Containers::NullOpt); +} + void SceneDataTest::fieldForFieldMissing() { SceneData scene{SceneObjectType::UnsignedInt, 7, nullptr, {}};