From efd3b9cadbd5b89cf82e52da430fad90fb1295cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 22 Nov 2021 15:08:24 +0100 Subject: [PATCH] Trade: SceneData::*AsArray() and *Into() now return object IDs as well. Because that way one can query a field with *AsArray() and iterate through it in a single expression. This also resolves the pending issue where it was more than annoying to fetch object mapping for TRS fields when only a subset of the fields is available. --- src/Magnum/MeshTools/sceneconverter.cpp | 18 +- src/Magnum/Trade/Implementation/sceneTools.h | 3 +- src/Magnum/Trade/SceneData.cpp | 444 +++--- src/Magnum/Trade/SceneData.h | 422 ++++-- src/Magnum/Trade/Test/SceneDataTest.cpp | 1396 +++++++++++------- src/Magnum/Trade/Test/SceneToolsTest.cpp | 51 +- 6 files changed, 1399 insertions(+), 935 deletions(-) diff --git a/src/Magnum/MeshTools/sceneconverter.cpp b/src/Magnum/MeshTools/sceneconverter.cpp index 3d7eca537..98fbd95f8 100644 --- a/src/Magnum/MeshTools/sceneconverter.cpp +++ b/src/Magnum/MeshTools/sceneconverter.cpp @@ -417,22 +417,22 @@ used.)") for(UnsignedInt j = 0; j != scene->fieldCount(); ++j) { const Trade::SceneField name = scene->fieldName(j); - if(name == Trade::SceneField::Mesh) for(const Containers::Pair& meshMaterial: scene->meshesMaterialsAsArray()) { + if(name == Trade::SceneField::Mesh) for(const Containers::Pair>& meshMaterial: scene->meshesMaterialsAsArray()) { if(meshMaterial.first() < meshReferenceCount.size()) ++meshReferenceCount[meshMaterial.first()]; - if(UnsignedInt(meshMaterial.second()) < materialReferenceCount.size()) - ++materialReferenceCount[meshMaterial.second()]; + if(UnsignedInt(meshMaterial.second().second()) < materialReferenceCount.size()) + ++materialReferenceCount[meshMaterial.second().second()]; } - if(name == Trade::SceneField::Skin) for(const UnsignedInt skin: scene->skinsAsArray()) { - if(skin < skinReferenceCount.size()) - ++skinReferenceCount[skin]; + if(name == Trade::SceneField::Skin) for(const Containers::Pair skin: scene->skinsAsArray()) { + if(skin.second() < skinReferenceCount.size()) + ++skinReferenceCount[skin.second()]; /** @todo 2D/3D distinction */ } - if(name == Trade::SceneField::Light) for(const UnsignedInt light: scene->lightsAsArray()) { - if(light < lightReferenceCount.size()) - ++lightReferenceCount[light]; + if(name == Trade::SceneField::Light) for(const Containers::Pair& light: scene->lightsAsArray()) { + if(light.second() < lightReferenceCount.size()) + ++lightReferenceCount[light.second()]; } arrayAppend(info.fields, InPlaceInit, diff --git a/src/Magnum/Trade/Implementation/sceneTools.h b/src/Magnum/Trade/Implementation/sceneTools.h index db41c1373..fb032db54 100644 --- a/src/Magnum/Trade/Implementation/sceneTools.h +++ b/src/Magnum/Trade/Implementation/sceneTools.h @@ -254,8 +254,7 @@ inline SceneData sceneConvertToSingleFunctionObjects(const SceneData& scene, Con /* Copy existing parent object/field data to a prefix of the output */ const Containers::StridedArrayView1D outParentObjects = out.mutableObjects(parentFieldId); const Containers::StridedArrayView1D outParents = out.mutableField(parentFieldId); - CORRADE_INTERNAL_ASSERT_OUTPUT(scene.objectsInto(parentFieldId, 0, outParentObjects) == scene.fieldSize(parentFieldId)); - CORRADE_INTERNAL_ASSERT_OUTPUT(scene.parentsInto(0, outParents) == scene.fieldSize(parentFieldId)); + CORRADE_INTERNAL_ASSERT_OUTPUT(scene.parentsInto(0, outParentObjects, outParents) == scene.fieldSize(parentFieldId)); /* List new objects at the end of the extended parent field */ const Containers::StridedArrayView1D newParentObjects = outParentObjects.suffix(scene.fieldSize(parentFieldId)); diff --git a/src/Magnum/Trade/SceneData.cpp b/src/Magnum/Trade/SceneData.cpp index 4b1c6b34e..88cc20aac 100644 --- a/src/Magnum/Trade/SceneData.cpp +++ b/src/Magnum/Trade/SceneData.cpp @@ -1162,34 +1162,43 @@ void SceneData::parentsIntoInternal(const UnsignedInt fieldId, const std::size_t } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ } -void SceneData::parentsInto(const Containers::StridedArrayView1D& destination) const { +void SceneData::parentsInto(const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& fieldDestination) const { const UnsignedInt fieldId = findFieldIdInternal(SceneField::Parent); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::parentsInto(): field not found", ); - CORRADE_ASSERT(destination.size() == _fields[fieldId]._size, - "Trade::SceneData::parentsInto(): expected a view with" << _fields[fieldId]._size << "elements but got" << destination.size(), ); - parentsIntoInternal(fieldId, 0, destination); + CORRADE_ASSERT(!objectDestination || objectDestination.size() == _fields[fieldId]._size, + "Trade::SceneData::parentsInto(): expected object destination view either empty or with" << _fields[fieldId]._size << "elements but got" << objectDestination.size(), ); + CORRADE_ASSERT(!fieldDestination || fieldDestination.size() == _fields[fieldId]._size, + "Trade::SceneData::parentsInto(): expected field destination view either empty or with" << _fields[fieldId]._size << "elements but got" << fieldDestination.size(), ); + objectsIntoInternal(fieldId, 0, objectDestination); + parentsIntoInternal(fieldId, 0, fieldDestination); } -std::size_t SceneData::parentsInto(const std::size_t offset, const Containers::StridedArrayView1D& destination) const { +std::size_t SceneData::parentsInto(const std::size_t offset, const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& fieldDestination) const { const UnsignedInt fieldId = findFieldIdInternal(SceneField::Parent); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::parentsInto(): field not found", {}); - CORRADE_ASSERT(offset <= _fields[fieldId]._size, - "Trade::SceneData::parentsInto(): 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); - parentsIntoInternal(fieldId, offset, destination.prefix(size)); + const std::size_t fieldSize = _fields[fieldId]._size; + CORRADE_ASSERT(offset <= fieldSize, + "Trade::SceneData::parentsInto(): offset" << offset << "out of bounds for a field of size" << fieldSize, {}); + CORRADE_ASSERT(!objectDestination != !fieldDestination|| objectDestination.size() == fieldDestination.size(), + "Trade::SceneData::parentsInto(): object and field destination views have different size," << objectDestination.size() << "vs" << fieldDestination.size(), {}); + const std::size_t size = Math::min(Math::max(objectDestination.size(), fieldDestination.size()), fieldSize - offset); + if(objectDestination) objectsIntoInternal(fieldId, offset, objectDestination.prefix(size)); + if(fieldDestination) parentsIntoInternal(fieldId, offset, fieldDestination.prefix(size)); return size; } -Containers::Array SceneData::parentsAsArray() const { +Containers::Array> SceneData::parentsAsArray() const { const UnsignedInt fieldId = findFieldIdInternal(SceneField::Parent); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, /* Using the same message as in Into() to avoid too many redundant strings in the binary */ "Trade::SceneData::parentsInto(): field not found", {}); - Containers::Array out{NoInit, std::size_t(_fields[fieldId]._size)}; - parentsIntoInternal(fieldId, 0, out); + Containers::Array> out{NoInit, std::size_t(_fields[fieldId]._size)}; + /** @todo use slicing once Pair exposes members somehow */ + objectsIntoInternal(fieldId, 0, {out, reinterpret_cast(reinterpret_cast(out.data())), out.size(), sizeof(decltype(out)::Type)}); + parentsIntoInternal(fieldId, 0, {out, reinterpret_cast(reinterpret_cast(out.data()) + sizeof(UnsignedInt)), out.size(), sizeof(decltype(out)::Type)}); return out; } @@ -1241,7 +1250,7 @@ template void applyScaling(const Containers::St } -std::size_t SceneData::findTransformFields(UnsignedInt& transformationFieldId, UnsignedInt& translationFieldId, UnsignedInt& rotationFieldId, UnsignedInt& scalingFieldId, UnsignedInt* const fieldWithObjectMappingDestination) const { +UnsignedInt SceneData::findTransformFields(UnsignedInt& transformationFieldId, UnsignedInt& translationFieldId, UnsignedInt& rotationFieldId, UnsignedInt& scalingFieldId) const { UnsignedInt fieldWithObjectMapping = ~UnsignedInt{}; transformationFieldId = ~UnsignedInt{}; translationFieldId = ~UnsignedInt{}; @@ -1262,15 +1271,10 @@ std::size_t SceneData::findTransformFields(UnsignedInt& transformationFieldId, U } } - if(fieldWithObjectMappingDestination) - *fieldWithObjectMappingDestination = fieldWithObjectMapping; - - /* Assuming the caller fires an appropriate assertion */ - return fieldWithObjectMapping == ~UnsignedInt{} ? - ~std::size_t{} : _fields[fieldWithObjectMapping]._size; + return fieldWithObjectMapping; } -std::size_t SceneData::findTranslationRotationScalingFields(UnsignedInt& translationFieldId, UnsignedInt& rotationFieldId, UnsignedInt& scalingFieldId, UnsignedInt* const fieldWithObjectMappingDestination) const { +UnsignedInt SceneData::findTranslationRotationScalingFields(UnsignedInt& translationFieldId, UnsignedInt& rotationFieldId, UnsignedInt& scalingFieldId) const { UnsignedInt fieldWithObjectMapping = ~UnsignedInt{}; translationFieldId = ~UnsignedInt{}; rotationFieldId = ~UnsignedInt{}; @@ -1285,12 +1289,7 @@ std::size_t SceneData::findTranslationRotationScalingFields(UnsignedInt& transla } } - if(fieldWithObjectMappingDestination) - *fieldWithObjectMappingDestination = fieldWithObjectMapping; - - /* Assuming the caller fires an appropriate assertion */ - return fieldWithObjectMapping == ~UnsignedInt{} ? - ~std::size_t{} : _fields[fieldWithObjectMapping]._size; + return fieldWithObjectMapping; } void SceneData::transformations2DIntoInternal(const UnsignedInt transformationFieldId, const UnsignedInt translationFieldId, const UnsignedInt rotationFieldId, const UnsignedInt scalingFieldId, std::size_t offset, const Containers::StridedArrayView1D& destination) const { @@ -1369,40 +1368,46 @@ void SceneData::transformations2DIntoInternal(const UnsignedInt transformationFi } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ } -void SceneData::transformations2DInto(const Containers::StridedArrayView1D& destination) const { +void SceneData::transformations2DInto(const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& fieldDestination) const { UnsignedInt transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId; - #ifndef CORRADE_NO_ASSERT - const std::size_t expectedSize = - #endif - findTransformFields(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId); - CORRADE_ASSERT(expectedSize != ~std::size_t{}, + const UnsignedInt fieldWithObjectMapping = findTransformFields(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId); + CORRADE_ASSERT(fieldWithObjectMapping != ~UnsignedInt{}, "Trade::SceneData::transformations2DInto(): no transformation-related field found", ); - CORRADE_ASSERT(expectedSize == destination.size(), - "Trade::SceneData::transformations2DInto(): expected a view with" << expectedSize << "elements but got" << destination.size(), ); - transformations2DIntoInternal(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId, 0, destination); + CORRADE_ASSERT(!objectDestination || objectDestination.size() == _fields[fieldWithObjectMapping]._size, + "Trade::SceneData::transformations2DInto(): expected object destination view either empty or with" << _fields[fieldWithObjectMapping]._size << "elements but got" << objectDestination.size(), ); + CORRADE_ASSERT(!fieldDestination || fieldDestination.size() == _fields[fieldWithObjectMapping]._size, + "Trade::SceneData::transformations2DInto(): expected field destination view either empty or with" << _fields[fieldWithObjectMapping]._size << "elements but got" << fieldDestination.size(), ); + objectsIntoInternal(fieldWithObjectMapping, 0, objectDestination); + transformations2DIntoInternal(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId, 0, fieldDestination); } -std::size_t SceneData::transformations2DInto(const std::size_t offset, const Containers::StridedArrayView1D& destination) const { +std::size_t SceneData::transformations2DInto(const std::size_t offset, const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& fieldDestination) const { UnsignedInt transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId; - const std::size_t expectedSize = findTransformFields(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId); - CORRADE_ASSERT(expectedSize != ~std::size_t{}, + const UnsignedInt fieldWithObjectMapping = findTransformFields(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId); + CORRADE_ASSERT(fieldWithObjectMapping != ~UnsignedInt{}, "Trade::SceneData::transformations2DInto(): no transformation-related field found", {}); - CORRADE_ASSERT(offset <= expectedSize, - "Trade::SceneData::transformations2DInto(): offset" << offset << "out of bounds for a field of size" << expectedSize, {}); - const std::size_t size = Math::min(destination.size(), expectedSize - offset); - transformations2DIntoInternal(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId, offset, destination.prefix(size)); + const std::size_t fieldSize = _fields[fieldWithObjectMapping]._size; + CORRADE_ASSERT(offset <= fieldSize, + "Trade::SceneData::transformations2DInto(): offset" << offset << "out of bounds for a field of size" << fieldSize, {}); + CORRADE_ASSERT(!objectDestination != !fieldDestination|| objectDestination.size() == fieldDestination.size(), + "Trade::SceneData::transformations2DInto(): object and field destination views have different size," << objectDestination.size() << "vs" << fieldDestination.size(), {}); + const std::size_t size = Math::min(Math::max(objectDestination.size(), fieldDestination.size()), fieldSize - offset); + if(objectDestination) objectsIntoInternal(fieldWithObjectMapping, offset, objectDestination.prefix(size)); + if(fieldDestination) transformations2DIntoInternal(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId, offset, fieldDestination.prefix(size)); return size; } -Containers::Array SceneData::transformations2DAsArray() const { +Containers::Array> SceneData::transformations2DAsArray() const { UnsignedInt transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId; - const std::size_t expectedSize = findTransformFields(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId); - CORRADE_ASSERT(expectedSize != ~std::size_t{}, + const UnsignedInt fieldWithObjectMapping = findTransformFields(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId); + CORRADE_ASSERT(fieldWithObjectMapping != ~UnsignedInt{}, /* Using the same message as in Into() to avoid too many redundant strings in the binary */ "Trade::SceneData::transformations2DInto(): no transformation-related field found", {}); - Containers::Array out{NoInit, expectedSize}; - transformations2DIntoInternal(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId, 0, out); + Containers::Array> out{NoInit, std::size_t(_fields[fieldWithObjectMapping]._size)}; + /** @todo use slicing once Pair exposes members somehow */ + objectsIntoInternal(fieldWithObjectMapping, 0, {out, reinterpret_cast(reinterpret_cast(out.data())), out.size(), sizeof(decltype(out)::Type)}); + transformations2DIntoInternal(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId, 0, {out, reinterpret_cast(reinterpret_cast(out.data()) + sizeof(UnsignedInt)), out.size(), sizeof(decltype(out)::Type)}); return out; } @@ -1469,37 +1474,45 @@ void SceneData::translationsRotationsScalings2DIntoInternal(const UnsignedInt tr } } -void SceneData::translationsRotationsScalings2DInto(const Containers::StridedArrayView1D& translationDestination, const Containers::StridedArrayView1D& rotationDestination, const Containers::StridedArrayView1D& scalingDestination) const { +void SceneData::translationsRotationsScalings2DInto(const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& translationDestination, const Containers::StridedArrayView1D& rotationDestination, const Containers::StridedArrayView1D& scalingDestination) const { UnsignedInt translationFieldId, rotationFieldId, scalingFieldId; - #ifndef CORRADE_NO_ASSERT - const std::size_t expectedSize = - #endif - findTranslationRotationScalingFields(translationFieldId, rotationFieldId, scalingFieldId); - CORRADE_ASSERT(expectedSize != ~std::size_t{}, + const UnsignedInt fieldWithObjectMapping = findTranslationRotationScalingFields(translationFieldId, rotationFieldId, scalingFieldId); + CORRADE_ASSERT(fieldWithObjectMapping != ~UnsignedInt{}, "Trade::SceneData::translationsRotationsScalings2DInto(): no transformation-related field found", ); - CORRADE_ASSERT(!translationDestination || translationDestination.size() == expectedSize, - "Trade::SceneData::translationsRotationsScalings2DInto(): expected translation destination view either empty or with" << expectedSize << "elements but got" << translationDestination.size(), ); - CORRADE_ASSERT(!rotationDestination || rotationDestination.size() == expectedSize, - "Trade::SceneData::translationsRotationsScalings2DInto(): expected rotation destination view either empty or with" << expectedSize << "elements but got" << rotationDestination.size(), ); - CORRADE_ASSERT(!scalingDestination || scalingDestination.size() == expectedSize, - "Trade::SceneData::translationsRotationsScalings2DInto(): expected scaling destination view either empty or with" << expectedSize << "elements but got" << scalingDestination.size(), ); + CORRADE_ASSERT(!objectDestination || objectDestination.size() == _fields[fieldWithObjectMapping]._size, + "Trade::SceneData::translationsRotationsScalings2DInto(): expected object destination view either empty or with" << _fields[fieldWithObjectMapping]._size << "elements but got" << objectDestination.size(), ); + CORRADE_ASSERT(!translationDestination || translationDestination.size() == _fields[fieldWithObjectMapping]._size, + "Trade::SceneData::translationsRotationsScalings2DInto(): expected translation destination view either empty or with" << _fields[fieldWithObjectMapping]._size << "elements but got" << translationDestination.size(), ); + CORRADE_ASSERT(!rotationDestination || rotationDestination.size() == _fields[fieldWithObjectMapping]._size, + "Trade::SceneData::translationsRotationsScalings2DInto(): expected rotation destination view either empty or with" << _fields[fieldWithObjectMapping]._size << "elements but got" << rotationDestination.size(), ); + CORRADE_ASSERT(!scalingDestination || scalingDestination.size() == _fields[fieldWithObjectMapping]._size, + "Trade::SceneData::translationsRotationsScalings2DInto(): expected scaling destination view either empty or with" << _fields[fieldWithObjectMapping]._size << "elements but got" << scalingDestination.size(), ); + objectsIntoInternal(fieldWithObjectMapping, 0, objectDestination); translationsRotationsScalings2DIntoInternal(translationFieldId, rotationFieldId, scalingFieldId, 0, translationDestination, rotationDestination, scalingDestination); } -std::size_t SceneData::translationsRotationsScalings2DInto(const std::size_t offset, const Containers::StridedArrayView1D& translationDestination, const Containers::StridedArrayView1D& rotationDestination, const Containers::StridedArrayView1D& scalingDestination) const { +std::size_t SceneData::translationsRotationsScalings2DInto(const std::size_t offset, const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& translationDestination, const Containers::StridedArrayView1D& rotationDestination, const Containers::StridedArrayView1D& scalingDestination) const { UnsignedInt translationFieldId, rotationFieldId, scalingFieldId; - const std::size_t expectedSize = findTranslationRotationScalingFields(translationFieldId, rotationFieldId, scalingFieldId); - CORRADE_ASSERT(expectedSize != ~std::size_t{}, + const UnsignedInt fieldWithObjectMapping = findTranslationRotationScalingFields(translationFieldId, rotationFieldId, scalingFieldId); + CORRADE_ASSERT(fieldWithObjectMapping != ~UnsignedInt{}, "Trade::SceneData::translationsRotationsScalings2DInto(): no transformation-related field found", {}); - CORRADE_ASSERT(offset <= expectedSize, - "Trade::SceneData::translationsRotationsScalings2DInto(): offset" << offset << "out of bounds for a field of size" << expectedSize, {}); + const std::size_t fieldSize = _fields[fieldWithObjectMapping]._size; + CORRADE_ASSERT(offset <= fieldSize, + "Trade::SceneData::translationsRotationsScalings2DInto(): offset" << offset << "out of bounds for a field of size" << fieldSize, {}); + CORRADE_ASSERT(!objectDestination != !translationDestination || objectDestination.size() == translationDestination.size(), + "Trade::SceneData::translationsRotationsScalings2DInto(): object and translation destination views have different size," << objectDestination.size() << "vs" << translationDestination.size(), {}); + CORRADE_ASSERT(!objectDestination != !rotationDestination || objectDestination.size() == rotationDestination.size(), + "Trade::SceneData::translationsRotationsScalings2DInto(): object and rotation destination views have different size," << objectDestination.size() << "vs" << rotationDestination.size(), {}); + CORRADE_ASSERT(!objectDestination != !scalingDestination || objectDestination.size() == scalingDestination.size(), + "Trade::SceneData::translationsRotationsScalings2DInto(): object and scaling destination views have different size," << objectDestination.size() << "vs" << scalingDestination.size(), {}); CORRADE_ASSERT(!translationDestination != !rotationDestination || translationDestination.size() == rotationDestination.size(), "Trade::SceneData::translationsRotationsScalings2DInto(): translation and rotation destination views have different size," << translationDestination.size() << "vs" << rotationDestination.size(), {}); CORRADE_ASSERT(!translationDestination != !scalingDestination || translationDestination.size() == scalingDestination.size(), "Trade::SceneData::translationsRotationsScalings2DInto(): translation and scaling destination views have different size," << translationDestination.size() << "vs" << scalingDestination.size(), {}); CORRADE_ASSERT(!rotationDestination != !scalingDestination || rotationDestination.size() == scalingDestination.size(), "Trade::SceneData::translationsRotationsScalings2DInto(): rotation and scaling destination views have different size," << rotationDestination.size() << "vs" << scalingDestination.size(), {}); - const std::size_t size = Math::min(Math::max({translationDestination.size(), rotationDestination.size(), scalingDestination.size()}), expectedSize - offset); + const std::size_t size = Math::min(Math::max({objectDestination.size(), translationDestination.size(), rotationDestination.size(), scalingDestination.size()}), fieldSize - offset); + if(objectDestination) objectsIntoInternal(fieldWithObjectMapping, offset, objectDestination.prefix(size)); translationsRotationsScalings2DIntoInternal(translationFieldId, rotationFieldId, scalingFieldId, offset, translationDestination ? translationDestination.prefix(size) : nullptr, rotationDestination ? rotationDestination.prefix(size) : nullptr, @@ -1507,18 +1520,19 @@ std::size_t SceneData::translationsRotationsScalings2DInto(const std::size_t off return size; } -Containers::Array> SceneData::translationsRotationsScalings2DAsArray() const { +Containers::Array>> SceneData::translationsRotationsScalings2DAsArray() const { UnsignedInt translationFieldId, rotationFieldId, scalingFieldId; - const std::size_t expectedSize = findTranslationRotationScalingFields(translationFieldId, rotationFieldId, scalingFieldId); - CORRADE_ASSERT(expectedSize != ~std::size_t{}, + const UnsignedInt fieldWithObjectMapping = findTranslationRotationScalingFields(translationFieldId, rotationFieldId, scalingFieldId); + CORRADE_ASSERT(fieldWithObjectMapping != ~UnsignedInt{}, /* Using the same message as in Into() to avoid too many redundant strings in the binary */ "Trade::SceneData::translationsRotationsScalings2DInto(): no transformation-related field found", {}); - Containers::Array> out{NoInit, expectedSize}; + Containers::Array>> out{NoInit, std::size_t(_fields[fieldWithObjectMapping]._size)}; /** @todo use slicing once Triple exposes members somehow */ - const Containers::StridedArrayView1D translationsOut{out, reinterpret_cast(reinterpret_cast(out.data())), out.size(), sizeof(decltype(out)::Type)}; - const Containers::StridedArrayView1D rotationsOut{out, reinterpret_cast(reinterpret_cast(out.data()) + sizeof(Vector2)), out.size(), sizeof(decltype(out)::Type)}; - const Containers::StridedArrayView1D scalingsOut{out, reinterpret_cast(reinterpret_cast(out.data()) + sizeof(Vector2) + sizeof(Complex)), out.size(), sizeof(decltype(out)::Type)}; + objectsIntoInternal(fieldWithObjectMapping, 0, {out, reinterpret_cast(reinterpret_cast(out.data())), out.size(), sizeof(decltype(out)::Type)}); + const Containers::StridedArrayView1D translationsOut{out, reinterpret_cast(reinterpret_cast(out.data()) + sizeof(UnsignedInt)), out.size(), sizeof(decltype(out)::Type)}; + const Containers::StridedArrayView1D rotationsOut{out, reinterpret_cast(reinterpret_cast(out.data()) + sizeof(UnsignedInt) + sizeof(Vector2)), out.size(), sizeof(decltype(out)::Type)}; + const Containers::StridedArrayView1D scalingsOut{out, reinterpret_cast(reinterpret_cast(out.data()) + sizeof(UnsignedInt) + sizeof(Vector2) + sizeof(Complex)), out.size(), sizeof(decltype(out)::Type)}; translationsRotationsScalings2DIntoInternal(translationFieldId, rotationFieldId, scalingFieldId, 0, translationsOut, rotationsOut, scalingsOut); return out; } @@ -1599,40 +1613,46 @@ void SceneData::transformations3DIntoInternal(const UnsignedInt transformationFi } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ } -void SceneData::transformations3DInto(const Containers::StridedArrayView1D& destination) const { +void SceneData::transformations3DInto(const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& fieldDestination) const { UnsignedInt transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId; - #ifndef CORRADE_NO_ASSERT - const std::size_t expectedSize = - #endif - findTransformFields(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId); - CORRADE_ASSERT(expectedSize != ~std::size_t{}, + const std::size_t fieldWithObjectMapping = findTransformFields(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId); + CORRADE_ASSERT(fieldWithObjectMapping != ~UnsignedInt{}, "Trade::SceneData::transformations3DInto(): no transformation-related field found", ); - CORRADE_ASSERT(expectedSize == destination.size(), - "Trade::SceneData::transformations3DInto(): expected a view with" << expectedSize << "elements but got" << destination.size(), ); - transformations3DIntoInternal(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId, 0, destination); + CORRADE_ASSERT(!objectDestination || objectDestination.size() == _fields[fieldWithObjectMapping]._size, + "Trade::SceneData::transformations3DInto(): expected object destination view either empty or with" << _fields[fieldWithObjectMapping]._size << "elements but got" << objectDestination.size(), ); + CORRADE_ASSERT(!fieldDestination || fieldDestination.size() == _fields[fieldWithObjectMapping]._size, + "Trade::SceneData::transformations3DInto(): expected field destination view either empty or with" << _fields[fieldWithObjectMapping]._size << "elements but got" << fieldDestination.size(), ); + objectsIntoInternal(fieldWithObjectMapping, 0, objectDestination); + transformations3DIntoInternal(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId, 0, fieldDestination); } -std::size_t SceneData::transformations3DInto(const std::size_t offset, const Containers::StridedArrayView1D& destination) const { +std::size_t SceneData::transformations3DInto(const std::size_t offset, const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& fieldDestination) const { UnsignedInt transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId; - const std::size_t expectedSize = findTransformFields(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId); - CORRADE_ASSERT(expectedSize != ~std::size_t{}, + const UnsignedInt fieldWithObjectMapping = findTransformFields(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId); + CORRADE_ASSERT(fieldWithObjectMapping != ~UnsignedInt{}, "Trade::SceneData::transformations3DInto(): no transformation-related field found", {}); - CORRADE_ASSERT(offset <= expectedSize, - "Trade::SceneData::transformations3DInto(): offset" << offset << "out of bounds for a field of size" << expectedSize, {}); - const std::size_t size = Math::min(destination.size(), expectedSize - offset); - transformations3DIntoInternal(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId, offset, destination.prefix(size)); + const std::size_t fieldSize = _fields[fieldWithObjectMapping]._size; + CORRADE_ASSERT(offset <= fieldSize, + "Trade::SceneData::transformations3DInto(): offset" << offset << "out of bounds for a field of size" << fieldSize, {}); + CORRADE_ASSERT(!objectDestination != !fieldDestination|| objectDestination.size() == fieldDestination.size(), + "Trade::SceneData::transformations3DInto(): object and field destination views have different size," << objectDestination.size() << "vs" << fieldDestination.size(), {}); + const std::size_t size = Math::min(Math::max(objectDestination.size(), fieldDestination.size()), fieldSize - offset); + if(objectDestination) objectsIntoInternal(fieldWithObjectMapping, offset, objectDestination.prefix(size)); + if(fieldDestination) transformations3DIntoInternal(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId, offset, fieldDestination.prefix(size)); return size; } -Containers::Array SceneData::transformations3DAsArray() const { +Containers::Array> SceneData::transformations3DAsArray() const { UnsignedInt transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId; - const std::size_t expectedSize = findTransformFields(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId); - CORRADE_ASSERT(expectedSize != ~std::size_t{}, + const UnsignedInt fieldWithObjectMapping = findTransformFields(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId); + CORRADE_ASSERT(fieldWithObjectMapping != ~UnsignedInt{}, /* Using the same message as in Into() to avoid too many redundant strings in the binary */ "Trade::SceneData::transformations3DInto(): no transformation-related field found", {}); - Containers::Array out{NoInit, expectedSize}; - transformations3DIntoInternal(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId, 0, out); + Containers::Array> out{NoInit, std::size_t(_fields[fieldWithObjectMapping]._size)}; + /** @todo use slicing once Pair exposes members somehow */ + objectsIntoInternal(fieldWithObjectMapping, 0, {out, reinterpret_cast(reinterpret_cast(out.data())), out.size(), sizeof(decltype(out)::Type)}); + transformations3DIntoInternal(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId, 0, {out, reinterpret_cast(reinterpret_cast(out.data()) + sizeof(UnsignedInt)), out.size(), sizeof(decltype(out)::Type)}); return out; } @@ -1699,37 +1719,45 @@ void SceneData::translationsRotationsScalings3DIntoInternal(const UnsignedInt tr } } -void SceneData::translationsRotationsScalings3DInto(const Containers::StridedArrayView1D& translationDestination, const Containers::StridedArrayView1D& rotationDestination, const Containers::StridedArrayView1D& scalingDestination) const { +void SceneData::translationsRotationsScalings3DInto(const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& translationDestination, const Containers::StridedArrayView1D& rotationDestination, const Containers::StridedArrayView1D& scalingDestination) const { UnsignedInt translationFieldId, rotationFieldId, scalingFieldId; - #ifndef CORRADE_NO_ASSERT - const std::size_t expectedSize = - #endif - findTranslationRotationScalingFields(translationFieldId, rotationFieldId, scalingFieldId); - CORRADE_ASSERT(expectedSize != ~std::size_t{}, + const UnsignedInt fieldWithObjectMapping = findTranslationRotationScalingFields(translationFieldId, rotationFieldId, scalingFieldId); + CORRADE_ASSERT(fieldWithObjectMapping != ~UnsignedInt{}, "Trade::SceneData::translationsRotationsScalings3DInto(): no transformation-related field found", ); - CORRADE_ASSERT(!translationDestination || translationDestination.size() == expectedSize, - "Trade::SceneData::translationsRotationsScalings3DInto(): expected translation destination view either empty or with" << expectedSize << "elements but got" << translationDestination.size(), ); - CORRADE_ASSERT(!rotationDestination || rotationDestination.size() == expectedSize, - "Trade::SceneData::translationsRotationsScalings3DInto(): expected rotation destination view either empty or with" << expectedSize << "elements but got" << rotationDestination.size(), ); - CORRADE_ASSERT(!scalingDestination || scalingDestination.size() == expectedSize, - "Trade::SceneData::translationsRotationsScalings3DInto(): expected scaling destination view either empty or with" << expectedSize << "elements but got" << scalingDestination.size(), ); + CORRADE_ASSERT(!objectDestination || objectDestination.size() == _fields[fieldWithObjectMapping]._size, + "Trade::SceneData::translationsRotationsScalings3DInto(): expected object destination view either empty or with" << _fields[fieldWithObjectMapping]._size << "elements but got" << objectDestination.size(), ); + CORRADE_ASSERT(!translationDestination || translationDestination.size() == _fields[fieldWithObjectMapping]._size, + "Trade::SceneData::translationsRotationsScalings3DInto(): expected translation destination view either empty or with" << _fields[fieldWithObjectMapping]._size << "elements but got" << translationDestination.size(), ); + CORRADE_ASSERT(!rotationDestination || rotationDestination.size() == _fields[fieldWithObjectMapping]._size, + "Trade::SceneData::translationsRotationsScalings3DInto(): expected rotation destination view either empty or with" << _fields[fieldWithObjectMapping]._size << "elements but got" << rotationDestination.size(), ); + CORRADE_ASSERT(!scalingDestination || scalingDestination.size() == _fields[fieldWithObjectMapping]._size, + "Trade::SceneData::translationsRotationsScalings3DInto(): expected scaling destination view either empty or with" << _fields[fieldWithObjectMapping]._size << "elements but got" << scalingDestination.size(), ); + objectsIntoInternal(fieldWithObjectMapping, 0, objectDestination); translationsRotationsScalings3DIntoInternal(translationFieldId, rotationFieldId, scalingFieldId, 0, translationDestination, rotationDestination, scalingDestination); } -std::size_t SceneData::translationsRotationsScalings3DInto(const std::size_t offset, const Containers::StridedArrayView1D& translationDestination, const Containers::StridedArrayView1D& rotationDestination, const Containers::StridedArrayView1D& scalingDestination) const { +std::size_t SceneData::translationsRotationsScalings3DInto(const std::size_t offset, const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& translationDestination, const Containers::StridedArrayView1D& rotationDestination, const Containers::StridedArrayView1D& scalingDestination) const { UnsignedInt translationFieldId, rotationFieldId, scalingFieldId; - const std::size_t expectedSize = findTranslationRotationScalingFields(translationFieldId, rotationFieldId, scalingFieldId); - CORRADE_ASSERT(expectedSize != ~std::size_t{}, + const UnsignedInt fieldWithObjectMapping = findTranslationRotationScalingFields(translationFieldId, rotationFieldId, scalingFieldId); + CORRADE_ASSERT(fieldWithObjectMapping != ~UnsignedInt{}, "Trade::SceneData::translationsRotationsScalings3DInto(): no transformation-related field found", {}); - CORRADE_ASSERT(offset <= expectedSize, - "Trade::SceneData::translationsRotationsScalings3DInto(): offset" << offset << "out of bounds for a field of size" << expectedSize, {}); + const std::size_t fieldSize = _fields[fieldWithObjectMapping]._size; + CORRADE_ASSERT(offset <= fieldSize, + "Trade::SceneData::translationsRotationsScalings3DInto(): offset" << offset << "out of bounds for a field of size" << fieldSize, {}); + CORRADE_ASSERT(!objectDestination != !translationDestination || objectDestination.size() == translationDestination.size(), + "Trade::SceneData::translationsRotationsScalings3DInto(): object and translation destination views have different size," << objectDestination.size() << "vs" << translationDestination.size(), {}); + CORRADE_ASSERT(!objectDestination != !rotationDestination || objectDestination.size() == rotationDestination.size(), + "Trade::SceneData::translationsRotationsScalings3DInto(): object and rotation destination views have different size," << objectDestination.size() << "vs" << rotationDestination.size(), {}); + CORRADE_ASSERT(!objectDestination != !scalingDestination || objectDestination.size() == scalingDestination.size(), + "Trade::SceneData::translationsRotationsScalings3DInto(): object and scaling destination views have different size," << objectDestination.size() << "vs" << scalingDestination.size(), {}); CORRADE_ASSERT(!translationDestination != !rotationDestination || translationDestination.size() == rotationDestination.size(), "Trade::SceneData::translationsRotationsScalings3DInto(): translation and rotation destination views have different size," << translationDestination.size() << "vs" << rotationDestination.size(), {}); CORRADE_ASSERT(!translationDestination != !scalingDestination || translationDestination.size() == scalingDestination.size(), "Trade::SceneData::translationsRotationsScalings3DInto(): translation and scaling destination views have different size," << translationDestination.size() << "vs" << scalingDestination.size(), {}); CORRADE_ASSERT(!rotationDestination != !scalingDestination || rotationDestination.size() == scalingDestination.size(), "Trade::SceneData::translationsRotationsScalings3DInto(): rotation and scaling destination views have different size," << rotationDestination.size() << "vs" << scalingDestination.size(), {}); - const std::size_t size = Math::min(Math::max({translationDestination.size(), rotationDestination.size(), scalingDestination.size()}), expectedSize - offset); + const std::size_t size = Math::min(Math::max({objectDestination.size(), translationDestination.size(), rotationDestination.size(), scalingDestination.size()}), fieldSize - offset); + if(objectDestination) objectsIntoInternal(fieldWithObjectMapping, offset, objectDestination.prefix(size)); translationsRotationsScalings3DIntoInternal(translationFieldId, rotationFieldId, scalingFieldId, offset, translationDestination ? translationDestination.prefix(size) : nullptr, rotationDestination ? rotationDestination.prefix(size) : nullptr, @@ -1737,18 +1765,19 @@ std::size_t SceneData::translationsRotationsScalings3DInto(const std::size_t off return size; } -Containers::Array> SceneData::translationsRotationsScalings3DAsArray() const { +Containers::Array>> SceneData::translationsRotationsScalings3DAsArray() const { UnsignedInt translationFieldId, rotationFieldId, scalingFieldId; - const std::size_t expectedSize = findTranslationRotationScalingFields(translationFieldId, rotationFieldId, scalingFieldId); - CORRADE_ASSERT(expectedSize != ~std::size_t{}, + const UnsignedInt fieldWithObjectMapping = findTranslationRotationScalingFields(translationFieldId, rotationFieldId, scalingFieldId); + CORRADE_ASSERT(fieldWithObjectMapping != ~UnsignedInt{}, /* Using the same message as in Into() to avoid too many redundant strings in the binary */ "Trade::SceneData::translationsRotationsScalings3DInto(): no transformation-related field found", {}); - Containers::Array> out{NoInit, expectedSize}; + Containers::Array>> out{NoInit, std::size_t(_fields[fieldWithObjectMapping]._size)}; /** @todo use slicing once Triple exposes members somehow */ - const Containers::StridedArrayView1D translationsOut{out, reinterpret_cast(reinterpret_cast(out.data())), out.size(), sizeof(decltype(out)::Type)}; - const Containers::StridedArrayView1D rotationsOut{out, reinterpret_cast(reinterpret_cast(out.data()) + sizeof(Vector3)), out.size(), sizeof(decltype(out)::Type)}; - const Containers::StridedArrayView1D scalingsOut{out, reinterpret_cast(reinterpret_cast(out.data()) + sizeof(Vector3) + sizeof(Quaternion)), out.size(), sizeof(decltype(out)::Type)}; + objectsIntoInternal(fieldWithObjectMapping, 0, {out, reinterpret_cast(reinterpret_cast(out.data())), out.size(), sizeof(decltype(out)::Type)}); + const Containers::StridedArrayView1D translationsOut{out, reinterpret_cast(reinterpret_cast(out.data()) + sizeof(UnsignedInt)), out.size(), sizeof(decltype(out)::Type)}; + const Containers::StridedArrayView1D rotationsOut{out, reinterpret_cast(reinterpret_cast(out.data()) + sizeof(UnsignedInt) + sizeof(Vector3)), out.size(), sizeof(decltype(out)::Type)}; + const Containers::StridedArrayView1D scalingsOut{out, reinterpret_cast(reinterpret_cast(out.data()) + sizeof(UnsignedInt) + sizeof(Vector3) + sizeof(Quaternion)), out.size(), sizeof(decltype(out)::Type)}; translationsRotationsScalings3DIntoInternal(translationFieldId, rotationFieldId, scalingFieldId, 0, translationsOut, rotationsOut, scalingsOut); return out; } @@ -1787,9 +1816,11 @@ void SceneData::indexFieldIntoInternal(const UnsignedInt fieldId, const std::siz else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ } -Containers::Array SceneData::unsignedIndexFieldAsArrayInternal(const UnsignedInt fieldId) const { - Containers::Array out{NoInit, std::size_t(_fields[fieldId]._size)}; - unsignedIndexFieldIntoInternal(fieldId, 0, out); +Containers::Array> SceneData::unsignedIndexFieldAsArrayInternal(const UnsignedInt fieldId) const { + Containers::Array> out{NoInit, std::size_t(_fields[fieldId]._size)}; + /** @todo use slicing once Pair exposes members somehow */ + objectsIntoInternal(fieldId, 0, {out, reinterpret_cast(reinterpret_cast(out.data())), out.size(), sizeof(decltype(out)::Type)}); + unsignedIndexFieldIntoInternal(fieldId, 0, {out, reinterpret_cast(reinterpret_cast(out.data()) + sizeof(UnsignedInt)), out.size(), sizeof(decltype(out)::Type)}); return out; } @@ -1812,67 +1843,84 @@ void SceneData::meshesMaterialsIntoInternal(const UnsignedInt fieldId, const std } } -void SceneData::meshesMaterialsInto(const Containers::StridedArrayView1D& meshDestination, const Containers::StridedArrayView1D& meshMaterialDestination) const { +void SceneData::meshesMaterialsInto(const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& meshDestination, const Containers::StridedArrayView1D& meshMaterialDestination) const { const UnsignedInt fieldId = findFieldIdInternal(SceneField::Mesh); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::meshesMaterialsInto(): field" << SceneField::Mesh << "not found", ); + CORRADE_ASSERT(!objectDestination || objectDestination.size() == _fields[fieldId]._size, + "Trade::SceneData::meshesMaterialsInto(): expected object destination view either empty or with" << _fields[fieldId]._size << "elements but got" << objectDestination.size(), ); CORRADE_ASSERT(!meshDestination || meshDestination.size() == _fields[fieldId]._size, "Trade::SceneData::meshesMaterialsInto(): expected mesh destination view either empty or with" << _fields[fieldId]._size << "elements but got" << meshDestination.size(), ); CORRADE_ASSERT(!meshMaterialDestination || meshMaterialDestination.size() == _fields[fieldId]._size, "Trade::SceneData::meshesMaterialsInto(): expected mesh material destination view either empty or with" << _fields[fieldId]._size << "elements but got" << meshMaterialDestination.size(), ); + objectsIntoInternal(fieldId, 0, objectDestination); meshesMaterialsIntoInternal(fieldId, 0, meshDestination, meshMaterialDestination); } -std::size_t SceneData::meshesMaterialsInto(const std::size_t offset, const Containers::StridedArrayView1D& meshDestination, const Containers::StridedArrayView1D& meshMaterialDestination) const { +std::size_t SceneData::meshesMaterialsInto(const std::size_t offset, const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& meshDestination, const Containers::StridedArrayView1D& meshMaterialDestination) const { const UnsignedInt fieldId = findFieldIdInternal(SceneField::Mesh); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::meshesMaterialsInto(): field" << SceneField::Mesh << "not found", {}); - CORRADE_ASSERT(offset <= _fields[fieldId]._size, - "Trade::SceneData::meshesMaterialsInto(): offset" << offset << "out of bounds for a field of size" << _fields[fieldId]._size, {}); + const std::size_t fieldSize = _fields[fieldId]._size; + CORRADE_ASSERT(offset <= fieldSize, + "Trade::SceneData::meshesMaterialsInto(): offset" << offset << "out of bounds for a field of size" << fieldSize, {}); + CORRADE_ASSERT(!objectDestination != !meshDestination || objectDestination.size() == meshDestination.size(), + "Trade::SceneData::meshesMaterialsInto(): object and mesh destination views have different size," << objectDestination.size() << "vs" << meshDestination.size(), {}); + CORRADE_ASSERT(!objectDestination != !meshMaterialDestination || objectDestination.size() == meshMaterialDestination.size(), + "Trade::SceneData::meshesMaterialsInto(): object and mesh material destination views have different size," << objectDestination.size() << "vs" << meshMaterialDestination.size(), {}); CORRADE_ASSERT(!meshDestination != !meshMaterialDestination || meshMaterialDestination.size() == meshDestination.size(), "Trade::SceneData::meshesMaterialsInto(): mesh and mesh material destination views have different size," << meshDestination.size() << "vs" << meshMaterialDestination.size(), {}); - const std::size_t size = Math::min(Math::max(meshDestination.size(), meshMaterialDestination.size()), std::size_t(_fields[fieldId]._size) - offset); + const std::size_t size = Math::min(Math::max({objectDestination.size(), meshDestination.size(), meshMaterialDestination.size()}), fieldSize - offset); + if(objectDestination) objectsIntoInternal(fieldId, offset, objectDestination.prefix(size)); meshesMaterialsIntoInternal(fieldId, offset, meshDestination ? meshDestination.prefix(size) : nullptr, meshMaterialDestination ? meshMaterialDestination.prefix(size) : nullptr); return size; } -Containers::Array> SceneData::meshesMaterialsAsArray() const { +Containers::Array>> SceneData::meshesMaterialsAsArray() const { const UnsignedInt fieldId = findFieldIdInternal(SceneField::Mesh); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, /* Using the same message as in Into() to avoid too many redundant strings in the binary */ "Trade::SceneData::meshesMaterialsInto(): field" << SceneField::Mesh << "not found", {}); - Containers::Array> out{NoInit, std::size_t(_fields[fieldId]._size)}; + Containers::Array>> out{NoInit, std::size_t(_fields[fieldId]._size)}; /** @todo use slicing once Pair exposes members somehow */ - const Containers::StridedArrayView1D meshesOut{out, reinterpret_cast(reinterpret_cast(out.data())), out.size(), sizeof(decltype(out)::Type)}; - const Containers::StridedArrayView1D meshMaterialsOut{out, reinterpret_cast(reinterpret_cast(out.data()) + sizeof(UnsignedInt)), out.size(), sizeof(decltype(out)::Type)}; + objectsIntoInternal(fieldId, 0, {out, reinterpret_cast(reinterpret_cast(out.data())), out.size(), sizeof(decltype(out)::Type)}); + const Containers::StridedArrayView1D meshesOut{out, reinterpret_cast(reinterpret_cast(out.data()) + sizeof(UnsignedInt)), out.size(), sizeof(decltype(out)::Type)}; + const Containers::StridedArrayView1D meshMaterialsOut{out, reinterpret_cast(reinterpret_cast(out.data()) + sizeof(UnsignedInt) + sizeof(UnsignedInt)), out.size(), sizeof(decltype(out)::Type)}; meshesMaterialsIntoInternal(fieldId, 0, meshesOut, meshMaterialsOut); return out; } -void SceneData::lightsInto(const Containers::StridedArrayView1D& destination) const { +void SceneData::lightsInto(const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& fieldDestination) const { const UnsignedInt fieldId = findFieldIdInternal(SceneField::Light); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::lightsInto(): field not found", ); - CORRADE_ASSERT(destination.size() == _fields[fieldId]._size, - "Trade::SceneData::lightsInto(): expected a view with" << _fields[fieldId]._size << "elements but got" << destination.size(), ); - unsignedIndexFieldIntoInternal(fieldId, 0, destination); + CORRADE_ASSERT(!objectDestination || objectDestination.size() == _fields[fieldId]._size, + "Trade::SceneData::lightsInto(): expected object destination view either empty or with" << _fields[fieldId]._size << "elements but got" << objectDestination.size(), ); + CORRADE_ASSERT(!fieldDestination || fieldDestination.size() == _fields[fieldId]._size, + "Trade::SceneData::lightsInto(): expected field destination view either empty or with" << _fields[fieldId]._size << "elements but got" << fieldDestination.size(), ); + objectsIntoInternal(fieldId, 0, objectDestination); + unsignedIndexFieldIntoInternal(fieldId, 0, fieldDestination); } -std::size_t SceneData::lightsInto(const std::size_t offset, const Containers::StridedArrayView1D& destination) const { +std::size_t SceneData::lightsInto(const std::size_t offset, const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& fieldDestination) const { const UnsignedInt fieldId = findFieldIdInternal(SceneField::Light); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::lightsInto(): field not found", {}); - CORRADE_ASSERT(offset <= _fields[fieldId]._size, - "Trade::SceneData::lightsInto(): 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); - unsignedIndexFieldIntoInternal(fieldId, offset, destination.prefix(size)); + const std::size_t fieldSize = _fields[fieldId]._size; + CORRADE_ASSERT(offset <= fieldSize, + "Trade::SceneData::lightsInto(): offset" << offset << "out of bounds for a field of size" << fieldSize, {}); + CORRADE_ASSERT(!objectDestination != !fieldDestination|| objectDestination.size() == fieldDestination.size(), + "Trade::SceneData::lightsInto(): object and field destination views have different size," << objectDestination.size() << "vs" << fieldDestination.size(), {}); + const std::size_t size = Math::min(Math::max(objectDestination.size(), fieldDestination.size()), fieldSize - offset); + if(objectDestination) objectsIntoInternal(fieldId, offset, objectDestination.prefix(size)); + if(fieldDestination) unsignedIndexFieldIntoInternal(fieldId, offset, fieldDestination.prefix(size)); return size; } -Containers::Array SceneData::lightsAsArray() const { +Containers::Array> SceneData::lightsAsArray() const { const UnsignedInt fieldId = findFieldIdInternal(SceneField::Light); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, /* Using the same message as in Into() to avoid too many redundant @@ -1881,27 +1929,34 @@ Containers::Array SceneData::lightsAsArray() const { return unsignedIndexFieldAsArrayInternal(fieldId); } -void SceneData::camerasInto(const Containers::StridedArrayView1D& destination) const { +void SceneData::camerasInto(const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& fieldDestination) const { const UnsignedInt fieldId = findFieldIdInternal(SceneField::Camera); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::camerasInto(): field not found", ); - CORRADE_ASSERT(destination.size() == _fields[fieldId]._size, - "Trade::SceneData::camerasInto(): expected a view with" << _fields[fieldId]._size << "elements but got" << destination.size(), ); - unsignedIndexFieldIntoInternal(fieldId, 0, destination); + CORRADE_ASSERT(!objectDestination || objectDestination.size() == _fields[fieldId]._size, + "Trade::SceneData::camerasInto(): expected object destination view either empty or with" << _fields[fieldId]._size << "elements but got" << objectDestination.size(), ); + CORRADE_ASSERT(!fieldDestination || fieldDestination.size() == _fields[fieldId]._size, + "Trade::SceneData::camerasInto(): expected field destination view either empty or with" << _fields[fieldId]._size << "elements but got" << fieldDestination.size(), ); + objectsIntoInternal(fieldId, 0, objectDestination); + unsignedIndexFieldIntoInternal(fieldId, 0, fieldDestination); } -std::size_t SceneData::camerasInto(const std::size_t offset, const Containers::StridedArrayView1D& destination) const { +std::size_t SceneData::camerasInto(const std::size_t offset, const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& fieldDestination) const { const UnsignedInt fieldId = findFieldIdInternal(SceneField::Camera); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::camerasInto(): field not found", {}); - CORRADE_ASSERT(offset <= _fields[fieldId]._size, - "Trade::SceneData::camerasInto(): 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); - unsignedIndexFieldIntoInternal(fieldId, offset, destination.prefix(size)); + const std::size_t fieldSize = _fields[fieldId]._size; + CORRADE_ASSERT(offset <= fieldSize, + "Trade::SceneData::camerasInto(): offset" << offset << "out of bounds for a field of size" << fieldSize, {}); + CORRADE_ASSERT(!objectDestination != !fieldDestination|| objectDestination.size() == fieldDestination.size(), + "Trade::SceneData::camerasInto(): object and field destination views have different size," << objectDestination.size() << "vs" << fieldDestination.size(), {}); + const std::size_t size = Math::min(Math::max(objectDestination.size(), fieldDestination.size()), fieldSize - offset); + if(objectDestination) objectsIntoInternal(fieldId, offset, objectDestination.prefix(size)); + if(fieldDestination) unsignedIndexFieldIntoInternal(fieldId, offset, fieldDestination.prefix(size)); return size; } -Containers::Array SceneData::camerasAsArray() const { +Containers::Array> SceneData::camerasAsArray() const { const UnsignedInt fieldId = findFieldIdInternal(SceneField::Camera); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, /* Using the same message as in Into() to avoid too many redundant @@ -1910,27 +1965,34 @@ Containers::Array SceneData::camerasAsArray() const { return unsignedIndexFieldAsArrayInternal(fieldId); } -void SceneData::skinsInto(const Containers::StridedArrayView1D& destination) const { +void SceneData::skinsInto(const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& fieldDestination) const { const UnsignedInt fieldId = findFieldIdInternal(SceneField::Skin); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::skinsInto(): field not found", ); - CORRADE_ASSERT(destination.size() == _fields[fieldId]._size, - "Trade::SceneData::skinsInto(): expected a view with" << _fields[fieldId]._size << "elements but got" << destination.size(), ); - unsignedIndexFieldIntoInternal(fieldId, 0, destination); + CORRADE_ASSERT(!objectDestination || objectDestination.size() == _fields[fieldId]._size, + "Trade::SceneData::skinsInto(): expected object destination view either empty or with" << _fields[fieldId]._size << "elements but got" << objectDestination.size(), ); + CORRADE_ASSERT(!fieldDestination || fieldDestination.size() == _fields[fieldId]._size, + "Trade::SceneData::skinsInto(): expected field destination view either empty or with" << _fields[fieldId]._size << "elements but got" << fieldDestination.size(), ); + objectsIntoInternal(fieldId, 0, objectDestination); + unsignedIndexFieldIntoInternal(fieldId, 0, fieldDestination); } -std::size_t SceneData::skinsInto(const std::size_t offset, const Containers::StridedArrayView1D& destination) const { +std::size_t SceneData::skinsInto(const std::size_t offset, const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& fieldDestination) const { const UnsignedInt fieldId = findFieldIdInternal(SceneField::Skin); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, "Trade::SceneData::skinsInto(): field not found", {}); - CORRADE_ASSERT(offset <= _fields[fieldId]._size, - "Trade::SceneData::skinsInto(): 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); - unsignedIndexFieldIntoInternal(fieldId, offset, destination.prefix(size)); + const std::size_t fieldSize = _fields[fieldId]._size; + CORRADE_ASSERT(offset <= fieldSize, + "Trade::SceneData::skinsInto(): offset" << offset << "out of bounds for a field of size" << fieldSize, {}); + CORRADE_ASSERT(!objectDestination != !fieldDestination|| objectDestination.size() == fieldDestination.size(), + "Trade::SceneData::skinsInto(): object and field destination views have different size," << objectDestination.size() << "vs" << fieldDestination.size(), {}); + const std::size_t size = Math::min(Math::max(objectDestination.size(), fieldDestination.size()), fieldSize - offset); + if(objectDestination) objectsIntoInternal(fieldId, offset, objectDestination.prefix(size)); + if(fieldDestination) unsignedIndexFieldIntoInternal(fieldId, offset, fieldDestination.prefix(size)); return size; } -Containers::Array SceneData::skinsAsArray() const { +Containers::Array> SceneData::skinsAsArray() const { const UnsignedInt fieldId = findFieldIdInternal(SceneField::Skin); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, /* Using the same message as in Into() to avoid too many redundant @@ -1949,34 +2011,52 @@ void SceneData::importerStateIntoInternal(const UnsignedInt fieldId, const std:: Utility::copy(Containers::arrayCast(fieldDataFieldViewInternal(field, offset, destination.size())), destination); } -void SceneData::importerStateInto(const Containers::StridedArrayView1D& destination) const { +void SceneData::importerStateInto(const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& fieldDestination) const { const UnsignedInt fieldId = findFieldIdInternal(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); + CORRADE_ASSERT(!objectDestination || objectDestination.size() == _fields[fieldId]._size, + "Trade::SceneData::importerStateInto(): expected object destination view either empty or with" << _fields[fieldId]._size << "elements but got" << objectDestination.size(), ); + CORRADE_ASSERT(!fieldDestination || fieldDestination.size() == _fields[fieldId]._size, + "Trade::SceneData::importerStateInto(): expected field destination view either empty or with" << _fields[fieldId]._size << "elements but got" << fieldDestination.size(), ); + objectsIntoInternal(fieldId, 0, objectDestination); + importerStateIntoInternal(fieldId, 0, fieldDestination); } -std::size_t SceneData::importerStateInto(const std::size_t offset, const Containers::StridedArrayView1D& destination) const { +std::size_t SceneData::importerStateInto(const std::size_t offset, const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& fieldDestination) const { const UnsignedInt fieldId = findFieldIdInternal(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)); + const std::size_t fieldSize = _fields[fieldId]._size; + CORRADE_ASSERT(offset <= fieldSize, + "Trade::SceneData::importerStateInto(): offset" << offset << "out of bounds for a field of size" << fieldSize, {}); + CORRADE_ASSERT(!objectDestination != !fieldDestination|| objectDestination.size() == fieldDestination.size(), + "Trade::SceneData::importerStateInto(): object and field destination views have different size," << objectDestination.size() << "vs" << fieldDestination.size(), {}); + const std::size_t size = Math::min(Math::max(objectDestination.size(), fieldDestination.size()), fieldSize - offset); + if(objectDestination) objectsIntoInternal(fieldId, offset, objectDestination.prefix(size)); + if(fieldDestination) importerStateIntoInternal(fieldId, offset, fieldDestination.prefix(size)); return size; } -Containers::Array SceneData::importerStateAsArray() const { +Containers::Array> SceneData::importerStateAsArray() const { const UnsignedInt fieldId = findFieldIdInternal(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); + Containers::Array> out{ + /* There's padding before the pointer on 64bit, zero-initialize to + avoid keeping random bytes in there */ + #ifdef CORRADE_TARGET_32BIT + NoInit + #else + ValueInit + #endif + , std::size_t(_fields[fieldId]._size)}; + /** @todo use slicing once Pair exposes members somehow, especially because + this is EXTREMELY prone to bugs due to the padding before the pointer */ + objectsIntoInternal(fieldId, 0, {out, reinterpret_cast(reinterpret_cast(out.data())), out.size(), sizeof(decltype(out)::Type)}); + importerStateIntoInternal(fieldId, 0, {out, reinterpret_cast(reinterpret_cast(out.data()) + sizeof(const void*)), out.size(), sizeof(decltype(out)::Type)}); return out; } @@ -2040,8 +2120,9 @@ Containers::Optional SceneData::transformation2DFor(const UnsignedInt o CORRADE_ASSERT(object < _objectCount, "Trade::SceneData::transformation2DFor(): object" << object << "out of bounds for" << _objectCount << "objects", {}); - UnsignedInt transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId, fieldWithObjectMapping; - if(findTransformFields(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId, &fieldWithObjectMapping) == ~std::size_t{}) return {}; + UnsignedInt transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId; + const UnsignedInt fieldWithObjectMapping = findTransformFields(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId); + if(fieldWithObjectMapping == ~UnsignedInt{}) return {}; /* If is2D() returned false as well, all *FieldId would be invalid, which is handled above. */ @@ -2060,8 +2141,9 @@ Containers::Optional> SceneData::t CORRADE_ASSERT(object < _objectCount, "Trade::SceneData::translationRotationScaling2DFor(): object" << object << "out of bounds for" << _objectCount << "objects", {}); - UnsignedInt translationFieldId, rotationFieldId, scalingFieldId, fieldWithObjectMapping; - if(findTranslationRotationScalingFields(translationFieldId, rotationFieldId, scalingFieldId, &fieldWithObjectMapping) == ~std::size_t{}) return {}; + UnsignedInt translationFieldId, rotationFieldId, scalingFieldId; + const UnsignedInt fieldWithObjectMapping = findTranslationRotationScalingFields(translationFieldId, rotationFieldId, scalingFieldId); + if(fieldWithObjectMapping == ~UnsignedInt{}) return {}; /* If is2D() returned false as well, all *FieldId would be invalid, which is handled above. */ @@ -2082,8 +2164,9 @@ Containers::Optional SceneData::transformation3DFor(const UnsignedInt o CORRADE_ASSERT(object < _objectCount, "Trade::SceneData::transformation3DFor(): object" << object << "out of bounds for" << _objectCount << "objects", {}); - UnsignedInt transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId, fieldWithObjectMapping; - if(findTransformFields(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId, &fieldWithObjectMapping) == ~std::size_t{}) return {}; + UnsignedInt transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId; + const UnsignedInt fieldWithObjectMapping = findTransformFields(transformationFieldId, translationFieldId, rotationFieldId, scalingFieldId); + if(fieldWithObjectMapping == ~UnsignedInt{}) return {}; /* If is3D() returned false as well, all *FieldId would be invalid, which is handled above. */ @@ -2102,8 +2185,9 @@ Containers::Optional> SceneData CORRADE_ASSERT(object < _objectCount, "Trade::SceneData::translationRotationScaling3DFor(): object" << object << "out of bounds for" << _objectCount << "objects", {}); - UnsignedInt translationFieldId, rotationFieldId, scalingFieldId, fieldWithObjectMapping; - if(findTranslationRotationScalingFields(translationFieldId, rotationFieldId, scalingFieldId, &fieldWithObjectMapping) == ~std::size_t{}) return {}; + UnsignedInt translationFieldId, rotationFieldId, scalingFieldId; + const UnsignedInt fieldWithObjectMapping = findTranslationRotationScalingFields(translationFieldId, rotationFieldId, scalingFieldId); + if(fieldWithObjectMapping == ~UnsignedInt{}) return {}; /* If is3D() returned false as well, all *FieldId would be invalid, which is handled above. */ diff --git a/src/Magnum/Trade/SceneData.h b/src/Magnum/Trade/SceneData.h index 8e077cb54..b9b6d02be 100644 --- a/src/Magnum/Trade/SceneData.h +++ b/src/Magnum/Trade/SceneData.h @@ -1240,10 +1240,18 @@ class MAGNUM_TRADE_EXPORT SceneData { * @m_since_latest * * The @p fieldId is expected to be smaller than @ref fieldCount() and - * @p T is expected to correspond to @ref objectType(). You can also - * use the non-templated @ref objectsAsArray() accessor to get the - * object mapping converted to the usual type, but note that such - * operation involves extra allocation and data conversion. + * @p T is expected to correspond to @ref objectType(). + * + * You can also use the non-templated @ref objectsAsArray() accessor + * (or the combined @ref parentsAsArray(), + * @ref transformations2DAsArray(), @ref transformations3DAsArray(), + * @ref translationsRotationsScalings2DAsArray(), + * @ref translationsRotationsScalings3DAsArray(), + * @ref meshesMaterialsAsArray(), @ref lightsAsArray(), + * @ref camerasAsArray(), @ref skinsAsArray(), + * @ref importerStateAsArray() accessors) to get the object mapping + * converted to the usual type, but note that these operations involve + * extra allocation and data conversion. * @see @ref mutableObjects(UnsignedInt) */ template Containers::StridedArrayView1D objects(UnsignedInt fieldId) const; @@ -1287,10 +1295,18 @@ class MAGNUM_TRADE_EXPORT SceneData { * @m_since_latest * * The @p fieldName is expected to exist and @p T is expected to - * correspond to @ref objectType(). You can also use the non-templated - * @ref objectsAsArray() accessor to get the object mapping converted - * to the usual type, but note that such operation involves extra - * allocation and data conversion. + * correspond to @ref objectType(). + * + * You can also use the non-templated @ref objectsAsArray() accessor + * (or the combined @ref parentsAsArray(), + * @ref transformations2DAsArray(), @ref transformations3DAsArray(), + * @ref translationsRotationsScalings2DAsArray(), + * @ref translationsRotationsScalings3DAsArray(), + * @ref meshesMaterialsAsArray(), @ref lightsAsArray(), + * @ref camerasAsArray(), @ref skinsAsArray(), + * @ref importerStateAsArray() accessors) to get the object mapping + * converted to the usual type, but note that these operations involve + * extra allocation and data conversion. * @see @ref hasField(), @ref objects(UnsignedInt) const, * @ref mutableObjects(UnsignedInt) */ @@ -1338,6 +1354,7 @@ class MAGNUM_TRADE_EXPORT SceneData { * is expected to correspond to @ref fieldType(UnsignedInt) const. The * field is also expected to not be an array, in that case you need to * use the overload below by using @cpp T[] @ce instead of @cpp T @ce. + * * You can also use the non-templated @ref parentsAsArray(), * @ref transformations2DAsArray(), @ref transformations3DAsArray(), * @ref translationsRotationsScalings2DAsArray(), @@ -1418,8 +1435,9 @@ class MAGNUM_TRADE_EXPORT SceneData { * The @p name is expected to exist and @p T is expected to correspond * to @ref fieldType(SceneField) const. The field is also expected to * not be an array, in that case you need to use the overload below by - * using @cpp T[] @ce instead of @cpp T @ce. You can also use the - * non-templated @ref parentsAsArray(), + * using @cpp T[] @ce instead of @cpp T @ce. + * + * You can also use the non-templated @ref parentsAsArray(), * @ref transformations2DAsArray(), @ref transformations3DAsArray(), * @ref translationsRotationsScalings2DAsArray(), * @ref translationsRotationsScalings3DAsArray(), @@ -1474,6 +1492,16 @@ class MAGNUM_TRADE_EXPORT SceneData { * @ref objects(UnsignedInt) const that converts the field from an * arbitrary underlying type and returns it in a newly-allocated array. * The @p fieldId is expected to be smaller than @ref fieldCount(). + * + * Note that, for common fields, you can also use the + * @ref parentsAsArray(), @ref transformations2DAsArray(), + * @ref transformations3DAsArray(), + * @ref translationsRotationsScalings2DAsArray(), + * @ref translationsRotationsScalings3DAsArray(), + * @ref meshesMaterialsAsArray(), @ref lightsAsArray(), + * @ref camerasAsArray(), @ref skinsAsArray(), + * @ref importerStateAsArray() accessors, which give out the object + * mapping together with the field data. * @see @ref objectsInto(UnsignedInt, const Containers::StridedArrayView1D&) const */ Containers::Array objectsAsArray(UnsignedInt fieldId) const; @@ -1485,6 +1513,16 @@ class MAGNUM_TRADE_EXPORT SceneData { * Like @ref objectsAsArray(UnsignedInt) const, but puts the result * into @p destination instead of allocating a new array. Expects that * @p destination is sized to contain exactly all data. + * + * Note that, for common fields, you can also use the + * @ref parentsInto(), @ref transformations2DInto(), + * @ref transformations3DInto(), + * @ref translationsRotationsScalings2DInto(), + * @ref translationsRotationsScalings3DInto(), + * @ref meshesMaterialsInto(), @ref lightsInto(), @ref camerasInto(), + * @ref skinsInto(), @ref importerStateInto() accessors, which can give + * out the object mapping together with the field data. + * * @see @ref fieldSize(UnsignedInt) const */ void objectsInto(UnsignedInt fieldId, const Containers::StridedArrayView1D& destination) const; @@ -1498,6 +1536,16 @@ class MAGNUM_TRADE_EXPORT SceneData { * 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. + * + * Note that, for common fields, you can also use the + * @ref parentsInto(), @ref transformations2DInto(), + * @ref transformations3DInto(), + * @ref translationsRotationsScalings2DInto(), + * @ref translationsRotationsScalings3DInto(), + * @ref meshesMaterialsInto(), @ref lightsInto(), @ref camerasInto(), + * @ref skinsInto(), @ref importerStateInto() accessors, which can give + * out the object mapping together with the field data. + * * @see @ref fieldSize(UnsignedInt) const, * @ref fieldObjectOffset(UnsignedInt, UnsignedInt, std::size_t) const */ @@ -1511,6 +1559,16 @@ class MAGNUM_TRADE_EXPORT SceneData { * @ref objects(SceneField) const that converts the field from an * arbitrary underlying type and returns it in a newly-allocated array. * The @p fieldName is expected to exist. + * + * Note that, for common fields, you can also use the + * @ref parentsAsArray(), @ref transformations2DAsArray(), + * @ref transformations3DAsArray(), + * @ref translationsRotationsScalings2DAsArray(), + * @ref translationsRotationsScalings3DAsArray(), + * @ref meshesMaterialsAsArray(), @ref lightsAsArray(), + * @ref camerasAsArray(), @ref skinsAsArray(), + * @ref importerStateAsArray() accessors, which give out the object + * mapping together with the field data. * @see @ref objectsInto(SceneField, const Containers::StridedArrayView1D&) const, * @ref hasField() */ @@ -1523,6 +1581,16 @@ class MAGNUM_TRADE_EXPORT SceneData { * Like @ref objectsAsArray(SceneField) const, but puts the result into * @p destination instead of allocating a new array. Expects that * @p destination is sized to contain exactly all data. + * + * Note that, for common fields, you can also use the + * @ref parentsInto(), @ref transformations2DInto(), + * @ref transformations3DInto(), + * @ref translationsRotationsScalings2DInto(), + * @ref translationsRotationsScalings3DInto(), + * @ref meshesMaterialsInto(), @ref lightsInto(), @ref camerasInto(), + * @ref skinsInto(), @ref importerStateInto() accessors, which can give + * out the object mapping together with the field data. + * * @see @ref fieldSize(SceneField) const */ void objectsInto(SceneField fieldName, const Containers::StridedArrayView1D& destination) const; @@ -1536,6 +1604,16 @@ class MAGNUM_TRADE_EXPORT SceneData { * 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. + * + * Note that, for common fields, you can also use the + * @ref parentsInto(), @ref transformations2DInto(), + * @ref transformations3DInto(), + * @ref translationsRotationsScalings2DInto(), + * @ref translationsRotationsScalings3DInto(), + * @ref meshesMaterialsInto(), @ref lightsInto(), @ref camerasInto(), + * @ref skinsInto(), @ref importerStateInto() accessors, which can give + * out the object mapping together with the field data. + * * @see @ref fieldSize(SceneField) const, * @ref fieldObjectOffset(SceneField, UnsignedInt, std::size_t) const */ @@ -1545,95 +1623,107 @@ class MAGNUM_TRADE_EXPORT SceneData { * @brief Parent indices as 32-bit integers * @m_since_latest * - * Convenience alternative to @ref field(SceneField) const with - * @ref SceneField::Parent as the argument that converts the field from - * an arbitrary underlying type and returns it in a newly-allocated + * Convenience alternative to @ref objects(SceneField) const together + * with @ref field(SceneField) const with @ref SceneField::Parent as + * the argument. Converts the object mapping and the field from + * arbitrary underlying types and returns them in a newly-allocated * array. The field is expected to exist. * @see @ref parentsInto(), @ref hasField(), @ref parentFor(), * @ref childrenFor() */ - Containers::Array parentsAsArray() const; + Containers::Array> parentsAsArray() const; /** * @brief Parent indices as 32-bit integers into a pre-allocated view * @m_since_latest * - * Like @ref parentsAsArray(), but puts the result into @p destination - * instead of allocating a new array. Expects that @p destination is - * sized to contain exactly all data. + * Like @ref parentsAsArray(), but puts the result into + * @p objectDestination and @p fieldDestination instead of allocating a + * new array. Expects that each view is either @cpp nullptr @ce or + * sized to contain exactly all data. If @p fieldDestination is + * @cpp nullptr @ce, the effect is the same as calling + * @ref objectsInto() with @ref SceneField::Parent. * @see @ref fieldSize(SceneField) const */ - void parentsInto(const Containers::StridedArrayView1D& destination) const; + void parentsInto(const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& fieldDestination) const; /** * @brief A subrange of parent indices as 32-bit integers into a pre-allocated view * @m_since_latest * - * Compared to @ref parentsInto(const Containers::StridedArrayView1D&) const + * Compared to @ref parentsInto(const Containers::StridedArrayView1D&, 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. + * of the views, returning the count of items actually extracted. The + * @p offset is expected to not be larger than the field size, views + * that are not @cpp nullptr @ce are expected to have the same size. * @see @ref fieldSize(SceneField) const, * @ref fieldObjectOffset(SceneField, UnsignedInt, std::size_t) const */ - std::size_t parentsInto(std::size_t offset, const Containers::StridedArrayView1D& destination) const; + std::size_t parentsInto(std::size_t offset, const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& fieldDestination) const; /** * @brief 2D transformations as 3x3 float matrices * @m_since_latest * - * Convenience alternative to @ref field(SceneField) const with + * Convenience alternative to @ref objects(SceneField) const together + * with @ref field(SceneField) const with * @ref SceneField::Transformation as the argument, or, if not present, * to a matrix created out of a subset of the * @ref SceneField::Translation, @ref SceneField::Rotation and - * @ref SceneField::Scaling fields that's present. The transformation - * is converted and composed from an arbitrary underlying type and - * returned in a newly-allocated array. At least one of the fields is + * @ref SceneField::Scaling fields that's present. Converts the object + * mapping and the fields from arbitrary underlying types and returns + * them in a newly-allocated array. At least one of the fields is * expected to exist and they are expected to have a type corresponding * to 2D, otherwise you're supposed to use * @ref transformations3DAsArray(). * @see @ref is2D(), @ref transformations2DInto(), @ref hasField(), * @ref fieldType(SceneField) const, @ref transformation2DFor() */ - Containers::Array transformations2DAsArray() const; + Containers::Array> transformations2DAsArray() const; /** * @brief 2D transformations as 3x3 float matrices into a pre-allocated view * @m_since_latest * * Like @ref transformations2DAsArray(), but puts the result into - * @p destination instead of allocating a new array. Expects that - * @p destination is sized to contain exactly all data. + * @p objectDestination and @p fieldDestination instead of allocating a + * new array. Expects that each view is either @cpp nullptr @ce or + * sized to contain exactly all data. If @p fieldDestination is + * @cpp nullptr @ce, the effect is the same as calling + * @ref objectsInto() with the first of the + * @ref SceneField::Transformation, @ref SceneField::Translation, + * @ref SceneField::Rotation and @ref SceneField::Scaling fields that's + * present. * @see @ref fieldSize(SceneField) const */ - void transformations2DInto(const Containers::StridedArrayView1D& destination) const; + void transformations2DInto(const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& fieldDestination) const; /** * @brief A subrange of 2D transformations as 3x3 float matrices into a pre-allocated view * @m_since_latest * - * Compared to @ref transformations2DInto(const Containers::StridedArrayView1D&) const + * Compared to @ref transformations2DInto(const Containers::StridedArrayView1D&, 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. + * of the views, returning the count of items actually extracted. The + * @p offset is expected to not be larger than the field size, views + * that are not @cpp nullptr @ce are expected to have the same size. * @see @ref fieldSize(SceneField) const, * @ref fieldObjectOffset(SceneField, UnsignedInt, std::size_t) const */ - std::size_t transformations2DInto(std::size_t offset, const Containers::StridedArrayView1D& destination) const; + std::size_t transformations2DInto(std::size_t offset, const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& fieldDestination) const; /** * @brief 2D transformations as float translation, rotation and scaling components * @m_since_latest * - * Convenience alternative to @ref field(SceneField) const with - * @ref SceneField::Translation, @ref SceneField::Rotation and - * @ref SceneField::Scaling as the arguments, as these are required to - * share the same object mapping. Converts the fields from an arbitrary - * underlying type and returns them in a newly-allocated array. At - * least one of the fields is expected to exist and they are expected - * to have a type corresponding to 2D, otherwise you're supposed to use + * Convenience alternative to @ref objects(SceneField) const together + * with @ref field(SceneField) const with @ref SceneField::Translation, + * @ref SceneField::Rotation and @ref SceneField::Scaling as the + * arguments, as these are required to share the same object mapping. + * Converts the object mapping and the fields from arbitrary underlying + * types and returns them in a newly-allocated array. At least one of + * the fields is expected to exist and they are expected to have a type + * corresponding to 2D, otherwise you're supposed to use * @ref translationsRotationsScalings3DAsArray(). If the * @ref SceneField::Translation field isn't present, the first returned * value is a zero vector. If the @relativeref{SceneField,Rotation} @@ -1644,26 +1734,31 @@ class MAGNUM_TRADE_EXPORT SceneData { * @ref hasField(), @ref fieldType(SceneField) const, * @ref translationRotationScaling2DFor() */ - Containers::Array> translationsRotationsScalings2DAsArray() const; + Containers::Array>> translationsRotationsScalings2DAsArray() const; /** * @brief 2D transformations as float translation, rotation and scaling components into a pre-allocated view * @m_since_latest * * Like @ref translationsRotationsScalings2DAsArray(), but puts the - * result into @p translationDestination, @p rotationDestination and - * @p scalingDestination instead of allocating a new array. Expects - * that each view is either @cpp nullptr @ce or sized to contain - * exactly all data. + * result into @p objectDestination, @p translationDestination, + * @p rotationDestination and @p scalingDestination instead of + * allocating a new array. Expects that each view is either + * @cpp nullptr @ce or sized to contain exactly all data. If + * @p translationDestination, @p rotationDestination and + * @p scalingDestination are all @cpp nullptr @ce, the effect is the + * same as calling @ref objectsInto() with one of the + * @ref SceneField::Translation, @ref SceneField::Rotation and + * @ref SceneField::Scaling fields that's present. * @see @ref fieldSize(SceneField) const */ - void translationsRotationsScalings2DInto(const Containers::StridedArrayView1D& translationDestination, const Containers::StridedArrayView1D& rotationDestination, const Containers::StridedArrayView1D& scalingDestination) const; + void translationsRotationsScalings2DInto(const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& translationDestination, const Containers::StridedArrayView1D& rotationDestination, const Containers::StridedArrayView1D& scalingDestination) const; /** * @brief A subrange of 2D transformations as float translation, rotation and scaling components into a pre-allocated view * @m_since_latest * - * Compared to @ref translationsRotationsScalings2DInto(const Containers::StridedArrayView1D&, const Containers::StridedArrayView1D&, const Containers::StridedArrayView1D&) const + * Compared to @ref translationsRotationsScalings2DInto(const Containers::StridedArrayView1D&, const Containers::StridedArrayView1D&, const Containers::StridedArrayView1D&, const Containers::StridedArrayView1D&) const * extracts only a subrange of the field defined by @p offset and size * of the views, returning the count of items actually extracted. The * @p offset is expected to not be larger than the field size, views @@ -1671,63 +1766,71 @@ class MAGNUM_TRADE_EXPORT SceneData { * @see @ref fieldSize(SceneField) const, * @ref fieldObjectOffset(SceneField, UnsignedInt, std::size_t) const */ - std::size_t translationsRotationsScalings2DInto(std::size_t offset, const Containers::StridedArrayView1D& translationDestination, const Containers::StridedArrayView1D& rotationDestination, const Containers::StridedArrayView1D& scalingDestination) const; + std::size_t translationsRotationsScalings2DInto(std::size_t offset, const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& translationDestination, const Containers::StridedArrayView1D& rotationDestination, const Containers::StridedArrayView1D& scalingDestination) const; /** * @brief 3D transformations as 4x4 float matrices * @m_since_latest * - * Convenience alternative to @ref field(SceneField) const with + * Convenience alternative to @ref objects(SceneField) const together + * with @ref field(SceneField) const with * @ref SceneField::Transformation as the argument, or, if not present, * to a matrix created out of a subset of the * @ref SceneField::Translation, @ref SceneField::Rotation and - * @ref SceneField::Scaling fields that's present. The transformation - * is converted and composed from an arbitrary underlying type and - * returned in a newly-allocated array. At least one of the fields is + * @ref SceneField::Scaling fields that's present. Converts the object + * mapping and the fields from arbitrary underlying types and returns + * them in a newly-allocated array. At least one of the fields is * expected to exist and they are expected to have a type corresponding * to 3D, otherwise you're supposed to use * @ref transformations2DAsArray(). * @see @ref is3D(), @ref transformations3DInto(), @ref hasField(), * @ref fieldType(SceneField) const, @ref transformation3DFor() */ - Containers::Array transformations3DAsArray() const; + Containers::Array> transformations3DAsArray() const; /** * @brief 3D transformations as 4x4 float matrices into a pre-allocated view * @m_since_latest * * Like @ref transformations3DAsArray(), but puts the result into - * @p destination instead of allocating a new array. Expects that - * @p destination is sized to contain exactly all data. + * @p objectDestination and @p fieldDestination instead of allocating a + * new array. Expects that the two views are either @cpp nullptr @ce or + * sized to contain exactly all data. If @p fieldDestination is + * @cpp nullptr @ce, the effect is the same as calling + * @ref objectsInto() with the first of the + * @ref SceneField::Transformation, @ref SceneField::Translation, + * @ref SceneField::Rotation and @ref SceneField::Scaling fields that's + * present. * @see @ref fieldSize(SceneField) const */ - void transformations3DInto(const Containers::StridedArrayView1D& destination) const; + void transformations3DInto(const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& destination) const; /** * @brief A subrange of 3D transformations as 4x4 float matrices into a pre-allocated view * @m_since_latest * - * Compared to @ref transformations3DInto(const Containers::StridedArrayView1D&) const + * Compared to @ref transformations3DInto(const Containers::StridedArrayView1D&, 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. + * of the views, returning the count of items actually extracted. The + * @p offset is expected to not be larger than the field size, views + * that are not @cpp nullptr @ce are expected to have the same size. * @see @ref fieldSize(SceneField) const, * @ref fieldObjectOffset(SceneField, UnsignedInt, std::size_t) const */ - std::size_t transformations3DInto(std::size_t offset, const Containers::StridedArrayView1D& destination) const; + std::size_t transformations3DInto(std::size_t offset, const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& destination) const; /** * @brief 3D transformations as float translation, rotation and scaling components * @m_since_latest * - * Convenience alternative to @ref field(SceneField) const with - * @ref SceneField::Translation, @ref SceneField::Rotation and - * @ref SceneField::Scaling as the arguments, as these are required to - * share the same object mapping. Converts the fields from an arbitrary - * underlying type and returns them in a newly-allocated array. At - * least one of the fields is expected to exist and they are expected - * to have a type corresponding to 3D, otherwise you're supposed to use + * Convenience alternative to @ref objects(SceneField) const together + * with @ref field(SceneField) const with @ref SceneField::Translation, + * @ref SceneField::Rotation and @ref SceneField::Scaling as the + * arguments, as these are required to share the same object mapping. + * Converts the object mapping and the fields from arbitrary underlying + * types and returns them in a newly-allocated array. At least one of + * the fields is expected to exist and they are expected to have a type + * corresponding to 3D, otherwise you're supposed to use * @ref translationsRotationsScalings2DAsArray(). If the * @ref SceneField::Translation field isn't present, the first returned * value is a zero vector. If the @relativeref{SceneField,Rotation} @@ -1738,26 +1841,31 @@ class MAGNUM_TRADE_EXPORT SceneData { * @ref hasField(), @ref fieldType(SceneField) const, * @ref translationRotationScaling3DFor() */ - Containers::Array> translationsRotationsScalings3DAsArray() const; + Containers::Array>> translationsRotationsScalings3DAsArray() const; /** * @brief 3D transformations as float translation, rotation and scaling components into a pre-allocated view * @m_since_latest * * Like @ref translationsRotationsScalings3DAsArray(), but puts the - * result into @p translationDestination, @p rotationDestination and - * @p scalingDestination instead of allocating a new array. Expects - * that each view is either @cpp nullptr @ce or sized to contain - * exactly all data. + * result into @p objectDestination, @p translationDestination, + * @p rotationDestination and @p scalingDestination instead of + * allocating a new array. Expects that each view is either + * @cpp nullptr @ce or sized to contain exactly all data. If + * @p translationDestination, @p rotationDestination and + * @p scalingDestination are all @cpp nullptr @ce, the effect is the + * same as calling @ref objectsInto() with one of the + * @ref SceneField::Translation, @ref SceneField::Rotation and + * @ref SceneField::Scaling fields that's present. * @see @ref fieldSize(SceneField) const */ - void translationsRotationsScalings3DInto(const Containers::StridedArrayView1D& translationDestination, const Containers::StridedArrayView1D& rotationDestination, const Containers::StridedArrayView1D& scalingDestination) const; + void translationsRotationsScalings3DInto(const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& translationDestination, const Containers::StridedArrayView1D& rotationDestination, const Containers::StridedArrayView1D& scalingDestination) const; /** * @brief A subrange of 3D transformations as float translation, rotation and scaling components into a pre-allocated view * @m_since_latest * - * Compared to @ref translationsRotationsScalings3DInto(const Containers::StridedArrayView1D&, const Containers::StridedArrayView1D&, const Containers::StridedArrayView1D&) const + * Compared to @ref translationsRotationsScalings3DInto(const Containers::StridedArrayView1D&, const Containers::StridedArrayView1D&, const Containers::StridedArrayView1D&, const Containers::StridedArrayView1D&) const * extracts only a subrange of the field defined by @p offset and size * of the views, returning the count of items actually extracted. The * @p offset is expected to not be larger than the field size, views @@ -1765,41 +1873,45 @@ class MAGNUM_TRADE_EXPORT SceneData { * @see @ref fieldSize(SceneField) const, * @ref fieldObjectOffset(SceneField, UnsignedInt, std::size_t) const */ - std::size_t translationsRotationsScalings3DInto(std::size_t offset, const Containers::StridedArrayView1D& translationDestination, const Containers::StridedArrayView1D& rotationDestination, const Containers::StridedArrayView1D& scalingDestination) const; + std::size_t translationsRotationsScalings3DInto(std::size_t offset, const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& translationDestination, const Containers::StridedArrayView1D& rotationDestination, const Containers::StridedArrayView1D& scalingDestination) const; /** * @brief Mesh and material IDs as 32-bit integers * @m_since_latest * - * Convenience alternative to @ref field(SceneField) const with - * @ref SceneField::Mesh and @ref SceneField::MeshMaterial as the - * argument, as the two are required to share the same object mapping. - * Converts the fields from an arbitrary underlying type and returns it - * in a newly-allocated array. The @ref SceneField::Mesh field is + * Convenience alternative to @ref objects(SceneField) const together + * with @ref field(SceneField) const with @ref SceneField::Mesh and + * @ref SceneField::MeshMaterial as the argument, as the two are + * required to share the same object mapping. Converts the object + * mapping and the fields from arbitrary underlying types and returns + * them in a newly-allocated array. The @ref SceneField::Mesh field is * expected to exist, if @ref SceneField::MeshMaterial isn't present, * the second returned values are all @cpp -1 @ce. * @see @ref meshesMaterialsInto(), @ref hasField(), * @ref meshesMaterialsFor() */ - Containers::Array> meshesMaterialsAsArray() const; + Containers::Array>> meshesMaterialsAsArray() const; /** * @brief Mesh and material IDs as 32-bit integers into a pre-allocated view * @m_since_latest * * Like @ref meshesMaterialsAsArray(), but puts the results into - * @p meshDestination and @p meshMaterialDestination instead of - * allocating a new array. Expects that each view is either - * @cpp nullptr @ce or sized to contain exactly all data. + * @p objectDestination, @p meshDestination and + * @p meshMaterialDestination instead of allocating a new array. + * Expects that each view is either @cpp nullptr @ce or sized to + * contain exactly all data. If @p meshDestination and + * @p meshMaterialDestination are both @cpp nullptr @ce, the effect is + * the same as calling @ref objectsInto() with @ref SceneField::Mesh. * @see @ref fieldSize(SceneField) const */ - void meshesMaterialsInto(const Containers::StridedArrayView1D& meshDestination, const Containers::StridedArrayView1D& meshMaterialDestination) const; + void meshesMaterialsInto(const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& meshDestination, const Containers::StridedArrayView1D& meshMaterialDestination) const; /** * @brief A subrange of mesh and material IDs as 32-bit integers into a pre-allocated view * @m_since_latest * - * Compared to @ref meshesMaterialsInto(const Containers::StridedArrayView1D&, const Containers::StridedArrayView1D&) const + * Compared to @ref meshesMaterialsInto(const Containers::StridedArrayView1D&, const Containers::StridedArrayView1D&, const Containers::StridedArrayView1D&) const * extracts only a subrange of the field defined by @p offset and size * of the views, returning the count of items actually extracted. The * @p offset is expected to not be larger than the field size, views @@ -1807,158 +1919,174 @@ class MAGNUM_TRADE_EXPORT SceneData { * @see @ref fieldSize(SceneField) const, * @ref fieldObjectOffset(SceneField, UnsignedInt, std::size_t) const */ - std::size_t meshesMaterialsInto(std::size_t offset, const Containers::StridedArrayView1D& meshDestination, const Containers::StridedArrayView1D& meshMaterialsDestination) const; + std::size_t meshesMaterialsInto(std::size_t offset, const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& meshDestination, const Containers::StridedArrayView1D& meshMaterialsDestination) const; /** * @brief Light IDs as 32-bit integers * @m_since_latest * - * Convenience alternative to @ref field(SceneField) const with - * @ref SceneField::Light 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. + * Convenience alternative to @ref objects(SceneField) const together + * with @ref field(SceneField) const with @ref SceneField::Light as the + * argument. Converts the object mapping and the field from arbitrary + * underlying types and returns them in a newly-allocated array. The + * field is expected to exist. * @see @ref lightsInto(), @ref hasField(), @ref lightsFor() */ - Containers::Array lightsAsArray() const; + Containers::Array> lightsAsArray() const; /** * @brief Light IDs as 32-bit integers into a pre-allocated view * @m_since_latest * - * Like @ref lightsAsArray(), but puts the result into @p destination - * instead of allocating a new array. Expects that @p destination is - * sized to contain exactly all data. + * Like @ref lightsAsArray(), but puts the result into + * @p objectDestination and @p fieldDestination instead of allocating a + * new array. Expects that each view is either @cpp nullptr @ce or + * sized to contain exactly all data. If @p fieldDestination is + * @cpp nullptr @ce, the effect is the same as calling + * @ref lightsInto() with @ref SceneField::Light. * @see @ref fieldSize(SceneField) const */ - void lightsInto(const Containers::StridedArrayView1D& destination) const; + void lightsInto(const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& fieldDestination) const; /** * @brief A subrange of light IDs as 32-bit integers into a pre-allocated view * @m_since_latest * - * Compared to @ref lightsInto(const Containers::StridedArrayView1D&) const + * Compared to @ref lightsInto(const Containers::StridedArrayView1D&, 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. + * of the views, returning the count of items actually extracted. The + * @p offset is expected to not be larger than the field size, views + * that are not @cpp nullptr @ce are expected to have the same size. * @see @ref fieldSize(SceneField) const, * @ref fieldObjectOffset(SceneField, UnsignedInt, std::size_t) const */ - std::size_t lightsInto(std::size_t offset, const Containers::StridedArrayView1D& destination) const; + std::size_t lightsInto(std::size_t offset, const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& fieldDestination) const; /** * @brief Camera IDs as 32-bit integers * @m_since_latest * - * Convenience alternative to @ref field(SceneField) const with - * @ref SceneField::Camera as the argument that converts the field from - * an arbitrary underlying type and returns it in a newly-allocated + * Convenience alternative to @ref objects(SceneField) const together + * with @ref field(SceneField) const with @ref SceneField::Camera as + * the argument. Converts the object mapping and the field from + * arbitrary underlying types and returns them in a newly-allocated * array. The field is expected to exist. * @see @ref camerasInto(), @ref hasField(), @ref camerasFor() */ - Containers::Array camerasAsArray() const; + Containers::Array> camerasAsArray() const; /** * @brief Camera IDs as 32-bit integers into a pre-allocated view * @m_since_latest * * Like @ref camerasAsArray(), but puts the result into - * @p destination instead of allocating a new array. Expects that - * @p destination is sized to contain exactly all data. + * @p objectDestination and @p fieldDestination instead of allocating a + * new array. Expects that each view is either @cpp nullptr @ce or + * sized to contain exactly all data. If @p fieldDestination is + * @cpp nullptr @ce, the effect is the same as calling + * @ref objectsInto() with @ref SceneField::Camera. * @see @ref fieldSize(SceneField) const */ - void camerasInto(const Containers::StridedArrayView1D& destination) const; + void camerasInto(const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& fieldDestination) const; /** * @brief A subrange of camera IDs as 32-bit integers into a pre-allocated view * @m_since_latest * - * Compared to @ref camerasInto(const Containers::StridedArrayView1D&) const + * Compared to @ref camerasInto(const Containers::StridedArrayView1D&, 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. + * of the views, returning the count of items actually extracted. The + * @p offset is expected to not be larger than the field size, views + * that are not @cpp nullptr @ce are expected to have the same size. * @see @ref fieldSize(SceneField) const, * @ref fieldObjectOffset(SceneField, UnsignedInt, std::size_t) const */ - std::size_t camerasInto(std::size_t offset, const Containers::StridedArrayView1D& destination) const; + std::size_t camerasInto(std::size_t offset, const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& fieldDestination) const; /** * @brief Skin IDs as 32-bit integers * @m_since_latest * - * Convenience alternative to @ref field(SceneField) const with - * @ref SceneField::Skin 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. + * Convenience alternative to @ref objects(SceneField) const together + * with @ref field(SceneField) const with @ref SceneField::Skin as the + * argument. Converts the object mapping and the field from arbitrary + * underlying types and returns them in a newly-allocated array. The + * field is expected to exist. * @see @ref skinsInto(), @ref hasField(), @ref skinsFor() */ - Containers::Array skinsAsArray() const; + Containers::Array> skinsAsArray() const; /** * @brief Skin IDs as 32-bit integers into a pre-allocated view * @m_since_latest * - * Like @ref skinsAsArray(), but puts the result into @p destination - * instead of allocating a new array. Expects that @p destination is - * sized to contain exactly all data. + * Like @ref skinsAsArray(), but puts the result into + * @p objectDestination and @p fieldDestination instead of allocating a + * new array. Expects that each view is either @cpp nullptr @ce or + * sized to contain exactly all data. If @p fieldDestination is + * @cpp nullptr @ce, the effect is the same as calling + * @ref objectsInto() with @ref SceneField::Skin. * @see @ref fieldSize(SceneField) const */ - void skinsInto(const Containers::StridedArrayView1D& destination) const; + void skinsInto(const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& fieldDestination) const; /** * @brief A subrange of skin IDs as 32-bit integers into a pre-allocated view * @m_since_latest * - * Compared to @ref skinsInto(const Containers::StridedArrayView1D&) const + * Compared to @ref skinsInto(const Containers::StridedArrayView1D&, 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. + * of the views, returning the count of items actually extracted. The + * @p offset is expected to not be larger than the field size, views + * that are not @cpp nullptr @ce are expected to have the same size. * @see @ref fieldSize(SceneField) const, * @ref fieldObjectOffset(SceneField, UnsignedInt, std::size_t) const */ - std::size_t skinsInto(std::size_t offset, const Containers::StridedArrayView1D& destination) const; + std::size_t skinsInto(std::size_t offset, const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& fieldDestination) 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. + * Convenience alternative to @ref objects(SceneField) const together + * with @ref field(SceneField) const with @ref SceneField::ImporterState + * as the argument. Converts the object mapping and the field from + * arbitrary underlying types and returns them 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; + 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. + * @p objectDestination and @p fieldDestination instead of allocating a + * new array. Expects that each view is either @cpp nullptr @ce or + * sized to contain exactly all data. If @p fieldDestination is + * @cpp nullptr @ce, the effect is the same as calling + * @ref objectsInto() with @ref SceneField::ImporterState. * @see @ref fieldSize(SceneField) const */ - void importerStateInto(const Containers::StridedArrayView1D& destination) const; + void importerStateInto(const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& fieldDestination) 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 + * Compared to @ref importerStateInto(const Containers::StridedArrayView1D&, 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. + * of the views, returning the count of items actually extracted. The + * @p offset is expected to not be larger than the field size, views + * that are not @cpp nullptr @ce are expected to have the same size. * @see @ref fieldSize(SceneField) const, * @ref fieldObjectOffset(SceneField, UnsignedInt, std::size_t) const */ - std::size_t importerStateInto(std::size_t offset, const Containers::StridedArrayView1D& destination) const; + std::size_t importerStateInto(std::size_t offset, const Containers::StridedArrayView1D& objectDestination, const Containers::StridedArrayView1D& fieldDestination) const; /** * @brief Parent for given object @@ -2300,15 +2428,15 @@ class MAGNUM_TRADE_EXPORT SceneData { MAGNUM_TRADE_LOCAL void objectsIntoInternal(UnsignedInt fieldId, std::size_t offset, const Containers::StridedArrayView1D& destination) const; MAGNUM_TRADE_LOCAL void parentsIntoInternal(UnsignedInt fieldId, std::size_t offset, const Containers::StridedArrayView1D& destination) const; - MAGNUM_TRADE_LOCAL std::size_t findTransformFields(UnsignedInt& transformationFieldId, UnsignedInt& translationFieldId, UnsignedInt& rotationFieldId, UnsignedInt& scalingFieldId, UnsignedInt* fieldWithObjectMappingDestination = nullptr) const; - MAGNUM_TRADE_LOCAL std::size_t findTranslationRotationScalingFields(UnsignedInt& translationFieldId, UnsignedInt& rotationFieldId, UnsignedInt& scalingFieldId, UnsignedInt* fieldWithObjectMappingDestination = nullptr) const; + MAGNUM_TRADE_LOCAL UnsignedInt findTransformFields(UnsignedInt& transformationFieldId, UnsignedInt& translationFieldId, UnsignedInt& rotationFieldId, UnsignedInt& scalingFieldId) const; + MAGNUM_TRADE_LOCAL UnsignedInt findTranslationRotationScalingFields(UnsignedInt& translationFieldId, UnsignedInt& rotationFieldId, UnsignedInt& scalingFieldId) const; MAGNUM_TRADE_LOCAL void transformations2DIntoInternal(UnsignedInt transformationFieldId, UnsignedInt translationFieldId, UnsignedInt rotationFieldId, UnsignedInt scalingFieldId, std::size_t offset, const Containers::StridedArrayView1D& destination) const; MAGNUM_TRADE_LOCAL void translationsRotationsScalings2DIntoInternal(UnsignedInt translationFieldId, UnsignedInt rotationFieldId, UnsignedInt scalingFieldId, std::size_t offset, const Containers::StridedArrayView1D& translationDestination, const Containers::StridedArrayView1D& rotationDestination, const Containers::StridedArrayView1D& scalingDestination) const; MAGNUM_TRADE_LOCAL void transformations3DIntoInternal(UnsignedInt transformationFieldId, UnsignedInt translationFieldId, UnsignedInt rotationFieldId, UnsignedInt scalingFieldId, std::size_t offset, const Containers::StridedArrayView1D& destination) const; MAGNUM_TRADE_LOCAL void translationsRotationsScalings3DIntoInternal(UnsignedInt translationFieldId, UnsignedInt rotationFieldId, UnsignedInt scalingFieldId, std::size_t offset, const Containers::StridedArrayView1D& translationDestination, const Containers::StridedArrayView1D& rotationDestination, const Containers::StridedArrayView1D& scalingDestination) const; MAGNUM_TRADE_LOCAL void unsignedIndexFieldIntoInternal(const UnsignedInt fieldId, std::size_t offset, const Containers::StridedArrayView1D& destination) const; 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 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; diff --git a/src/Magnum/Trade/Test/SceneDataTest.cpp b/src/Magnum/Trade/Test/SceneDataTest.cpp index f0af111d2..57f4da2e4 100644 --- a/src/Magnum/Trade/Test/SceneDataTest.cpp +++ b/src/Magnum/Trade/Test/SceneDataTest.cpp @@ -122,6 +122,7 @@ struct SceneDataTest: TestSuite::Tester { void objectsIntoArrayByIndex(); void objectsIntoArrayByName(); void objectsIntoArrayInvalidSizeOrOffset(); + template void parentsAsArray(); void parentsIntoArray(); void parentsIntoArrayInvalidSizeOrOffset(); @@ -129,6 +130,7 @@ struct SceneDataTest: TestSuite::Tester { template void transformations2DAsArrayTRS(); void transformations2DAsArrayBut3DType(); void transformations2DIntoArray(); + void transformations2DTRSIntoArray(); void transformations2DIntoArrayTRS(); void transformations2DIntoArrayInvalidSizeOrOffset(); void transformations2DIntoArrayInvalidSizeOrOffsetTRS(); @@ -136,6 +138,7 @@ struct SceneDataTest: TestSuite::Tester { template void transformations3DAsArrayTRS(); void transformations3DAsArrayBut2DType(); void transformations3DIntoArray(); + void transformations3DTRSIntoArray(); void transformations3DIntoArrayTRS(); void transformations3DIntoArrayInvalidSizeOrOffset(); void transformations3DIntoArrayInvalidSizeOrOffsetTRS(); @@ -212,6 +215,59 @@ const struct { {"offset at the end", 3, 10, 0} }; +const struct { + const char* name; + std::size_t offset; + std::size_t size; + std::size_t expectedSize; + bool objects, field; +} IntoArrayOffset1Data[]{ + {"whole", 0, 3, 3, true, true}, + {"one element in the middle", 1, 1, 1, true, true}, + {"suffix to a larger array", 2, 10, 1, true, true}, + {"offset at the end", 3, 10, 0, true, true}, + {"only objects", 0, 3, 3, true, false}, + {"only field", 0, 3, 3, false, true}, + {"neither", 0, 3, 0, false, false} +}; + +const struct { + const char* name; + std::size_t offset; + std::size_t size; + std::size_t expectedSize; + bool objects, field1, field2; +} IntoArrayOffset2Data[]{ + {"whole", 0, 3, 3, true, true, true}, + {"one element in the middle", 1, 1, 1, true, true, true}, + {"suffix to a larger array", 2, 10, 1, true, true, true}, + {"offset at the end", 3, 10, 0, true, true, true}, + {"only objects", 0, 3, 3, true, false, false}, + {"only fields", 0, 3, 3, false, true, true}, + {"only first field", 0, 3, 3, false, true, false}, + {"only second field", 0, 3, 3, false, false, true}, + {"none", 0, 3, 0, false, false, false} +}; + +const struct { + const char* name; + std::size_t offset; + std::size_t size; + std::size_t expectedSize; + bool objects, field1, field2, field3; +} IntoArrayOffset3Data[]{ + {"whole", 0, 3, 3, true, true, true, true}, + {"one element in the middle", 1, 1, 1, true, true, true, true}, + {"suffix to a larger array", 2, 10, 1, true, true, true, true}, + {"offset at the end", 3, 10, 0, true, true, true, true}, + {"only objects", 0, 3, 3, true, false, false, true}, + {"only fields", 0, 3, 3, false, true, true, true}, + {"only first field", 0, 3, 3, false, true, false, false}, + {"only second field", 0, 3, 3, false, false, true, false}, + {"only third field", 0, 3, 3, false, false, false, true}, + {"none", 0, 3, 0, false, false, false, false} +}; + #ifdef MAGNUM_BUILD_DEPRECATED const struct { const char* name; @@ -319,13 +375,14 @@ SceneDataTest::SceneDataTest() { Containers::arraySize(IntoArrayOffsetData)); addTests({&SceneDataTest::objectsIntoArrayInvalidSizeOrOffset, + &SceneDataTest::parentsAsArray, &SceneDataTest::parentsAsArray, &SceneDataTest::parentsAsArray, &SceneDataTest::parentsAsArray}); addInstancedTests({&SceneDataTest::parentsIntoArray}, - Containers::arraySize(IntoArrayOffsetData)); + Containers::arraySize(IntoArrayOffset1Data)); addTests({&SceneDataTest::parentsIntoArrayInvalidSizeOrOffset, &SceneDataTest::transformations2DAsArray, @@ -339,8 +396,11 @@ SceneDataTest::SceneDataTest() { &SceneDataTest::transformations2DAsArrayBut3DType}); addInstancedTests({&SceneDataTest::transformations2DIntoArray, - &SceneDataTest::transformations2DIntoArrayTRS}, - Containers::arraySize(IntoArrayOffsetData)); + &SceneDataTest::transformations2DTRSIntoArray}, + Containers::arraySize(IntoArrayOffset1Data)); + + addInstancedTests({&SceneDataTest::transformations2DIntoArrayTRS}, + Containers::arraySize(IntoArrayOffset3Data)); addTests({&SceneDataTest::transformations2DIntoArrayInvalidSizeOrOffset, &SceneDataTest::transformations2DIntoArrayInvalidSizeOrOffsetTRS, @@ -355,8 +415,11 @@ SceneDataTest::SceneDataTest() { &SceneDataTest::transformations3DAsArrayBut2DType}); addInstancedTests({&SceneDataTest::transformations3DIntoArray, - &SceneDataTest::transformations3DIntoArrayTRS}, - Containers::arraySize(IntoArrayOffsetData)); + &SceneDataTest::transformations3DTRSIntoArray}, + Containers::arraySize(IntoArrayOffset1Data)); + + addInstancedTests({&SceneDataTest::transformations3DIntoArrayTRS}, + Containers::arraySize(IntoArrayOffset3Data)); addTests({&SceneDataTest::transformations3DIntoArrayInvalidSizeOrOffset, &SceneDataTest::transformations3DIntoArrayInvalidSizeOrOffsetTRS, @@ -365,7 +428,7 @@ SceneDataTest::SceneDataTest() { &SceneDataTest::meshesMaterialsAsArray}); addInstancedTests({&SceneDataTest::meshesMaterialsIntoArray}, - Containers::arraySize(IntoArrayOffsetData)); + Containers::arraySize(IntoArrayOffset2Data)); addTests({&SceneDataTest::meshesMaterialsIntoArrayInvalidSizeOrOffset, &SceneDataTest::lightsAsArray, @@ -373,7 +436,7 @@ SceneDataTest::SceneDataTest() { &SceneDataTest::lightsAsArray}); addInstancedTests({&SceneDataTest::lightsIntoArray}, - Containers::arraySize(IntoArrayOffsetData)); + Containers::arraySize(IntoArrayOffset1Data)); addTests({&SceneDataTest::lightsIntoArrayInvalidSizeOrOffset, &SceneDataTest::camerasAsArray, @@ -381,7 +444,7 @@ SceneDataTest::SceneDataTest() { &SceneDataTest::camerasAsArray}); addInstancedTests({&SceneDataTest::camerasIntoArray}, - Containers::arraySize(IntoArrayOffsetData)); + Containers::arraySize(IntoArrayOffset1Data)); addTests({&SceneDataTest::camerasIntoArrayInvalidSizeOrOffset, &SceneDataTest::skinsAsArray, @@ -389,14 +452,14 @@ SceneDataTest::SceneDataTest() { &SceneDataTest::skinsAsArray}); addInstancedTests({&SceneDataTest::skinsIntoArray}, - Containers::arraySize(IntoArrayOffsetData)); + Containers::arraySize(IntoArrayOffset1Data)); addTests({&SceneDataTest::skinsIntoArrayInvalidSizeOrOffset, &SceneDataTest::importerStateAsArray, &SceneDataTest::importerStateAsArray}); addInstancedTests({&SceneDataTest::importerStateIntoArray}, - Containers::arraySize(IntoArrayOffsetData)); + Containers::arraySize(IntoArrayOffset1Data)); addTests({&SceneDataTest::importerStateIntoArrayInvalidSizeOrOffset, @@ -2366,13 +2429,15 @@ template void SceneDataTest::parentsAsArray() { SceneFieldData{SceneField::Parent, view.slice(&Field::object), view.slice(&Field::parent)} }}; - CORRADE_COMPARE_AS(scene.parentsAsArray(), - Containers::arrayView({15, -1, 44}), - TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.parentsAsArray(), (Containers::arrayView>({ + {0, 15}, + {1, -1}, + {15, 44} + })), TestSuite::Compare::Container); } void SceneDataTest::parentsIntoArray() { - auto&& data = IntoArrayOffsetData[testCaseInstanceId()]; + auto&& data = IntoArrayOffset1Data[testCaseInstanceId()]; setTestCaseDescription(data.name); /* Both AsArray() and Into() share a common helper. The AsArray() test @@ -2400,17 +2465,32 @@ void SceneDataTest::parentsIntoArray() { /* The offset-less overload should give back all data */ { - Int out[3]; - scene.parentsInto(out); - CORRADE_COMPARE_AS(Containers::stridedArrayView(out), + UnsignedInt objects[3]; + Int field[3]; + scene.parentsInto( + data.objects ? Containers::arrayView(objects) : nullptr, + data.field ? Containers::arrayView(field) : nullptr + ); + if(data.objects) CORRADE_COMPARE_AS(Containers::stridedArrayView(objects), + view.slice(&Field::object), + TestSuite::Compare::Container); + if(data.field) CORRADE_COMPARE_AS(Containers::stridedArrayView(field), view.slice(&Field::parent), TestSuite::Compare::Container); /* The offset variant only a subset */ } { - Containers::Array out{data.size}; - CORRADE_COMPARE(scene.parentsInto(data.offset, out), data.expectedSize); - CORRADE_COMPARE_AS(out.prefix(data.expectedSize), + Containers::Array objects{data.size}; + Containers::Array field{data.size}; + CORRADE_COMPARE(scene.parentsInto(data.offset, + data.objects ? arrayView(objects) : nullptr, + data.field ? arrayView(field) : nullptr + ), data.expectedSize); + if(data.objects) CORRADE_COMPARE_AS(objects.prefix(data.expectedSize), + view.slice(&Field::object) + .slice(data.offset, data.offset + data.expectedSize), + TestSuite::Compare::Container); + if(data.field) CORRADE_COMPARE_AS(field.prefix(data.expectedSize), view.slice(&Field::parent) .slice(data.offset, data.offset + data.expectedSize), TestSuite::Compare::Container); @@ -2435,12 +2515,19 @@ void SceneDataTest::parentsIntoArrayInvalidSizeOrOffset() { std::ostringstream out; Error redirectError{&out}; - Int destination[2]; - scene.parentsInto(destination); - scene.parentsInto(4, destination); + UnsignedInt objectDestinationCorrect[3]; + UnsignedInt objectDestination[2]; + Int fieldDestinationCorrect[3]; + Int fieldDestination[2]; + scene.parentsInto(objectDestination, fieldDestinationCorrect); + scene.parentsInto(objectDestinationCorrect, fieldDestination); + scene.parentsInto(4, objectDestination, fieldDestination); + scene.parentsInto(0, objectDestinationCorrect, fieldDestination); CORRADE_COMPARE(out.str(), - "Trade::SceneData::parentsInto(): expected a view with 3 elements but got 2\n" - "Trade::SceneData::parentsInto(): offset 4 out of bounds for a field of size 3\n"); + "Trade::SceneData::parentsInto(): expected object destination view either empty or with 3 elements but got 2\n" + "Trade::SceneData::parentsInto(): expected field destination view either empty or with 3 elements but got 2\n" + "Trade::SceneData::parentsInto(): offset 4 out of bounds for a field of size 3\n" + "Trade::SceneData::parentsInto(): object and field destination views have different size, 3 vs 2\n"); } template struct TransformationTypeFor { @@ -2511,12 +2598,12 @@ template void SceneDataTest::transformations2DAsArray() { CORRADE_VERIFY(scene.is2D()); CORRADE_VERIFY(!scene.is3D()); - CORRADE_COMPARE_AS(scene.transformations2DAsArray(), Containers::arrayView({ - Matrix3::translation({3.0f, 2.0f}), - Matrix3::rotation(35.0_degf), - Matrix3::translation({1.5f, 2.5f})*Matrix3::rotation(-15.0_degf), - Matrix3::rotation(-15.0_degf)*Matrix3::translation({1.5f, 2.5f}) - }), TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.transformations2DAsArray(), (Containers::arrayView>({ + {1, Matrix3::translation({3.0f, 2.0f})}, + {0, Matrix3::rotation(35.0_degf)}, + {4, Matrix3::translation({1.5f, 2.5f})*Matrix3::rotation(-15.0_degf)}, + {5, Matrix3::rotation(-15.0_degf)*Matrix3::translation({1.5f, 2.5f})} + })), TestSuite::Compare::Container); } template void SceneDataTest::transformations2DAsArrayTRS() { @@ -2564,19 +2651,19 @@ template void SceneDataTest::transformations2DAsArray }}; CORRADE_VERIFY(scene.is2D()); CORRADE_VERIFY(!scene.is3D()); - CORRADE_COMPARE_AS(scene.transformations2DAsArray(), Containers::arrayView({ - Matrix3::translation({3.0f, 2.0f}), - Matrix3{Math::IdentityInit}, - Matrix3{Math::IdentityInit}, - Matrix3{Math::IdentityInit}, - Matrix3::translation({1.5f, 2.5f}) - }), TestSuite::Compare::Container); - CORRADE_COMPARE_AS(scene.translationsRotationsScalings2DAsArray(), (Containers::arrayView>({ - {{3.0f, 2.0f}, {}, Vector2{1.0f}}, - {{}, {}, Vector2{1.0f}}, - {{}, {}, Vector2{1.0f}}, - {{}, {}, Vector2{1.0f}}, - {{1.5f, 2.5f}, {}, Vector2{1.0f}}, + CORRADE_COMPARE_AS(scene.transformations2DAsArray(), (Containers::arrayView>({ + {1, Matrix3::translation({3.0f, 2.0f})}, + {0, Matrix3{Math::IdentityInit}}, + {2, Matrix3{Math::IdentityInit}}, + {4, Matrix3{Math::IdentityInit}}, + {7, Matrix3::translation({1.5f, 2.5f})} + })), TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.translationsRotationsScalings2DAsArray(), (Containers::arrayView>>({ + {1, {{3.0f, 2.0f}, {}, Vector2{1.0f}}}, + {0, {{}, {}, Vector2{1.0f}}}, + {2, {{}, {}, Vector2{1.0f}}}, + {4, {{}, {}, Vector2{1.0f}}}, + {7, {{1.5f, 2.5f}, {}, Vector2{1.0f}}}, })), TestSuite::Compare::Container); } { SceneData scene{SceneObjectType::UnsignedInt, 8, {}, fields, { @@ -2584,19 +2671,19 @@ template void SceneDataTest::transformations2DAsArray }}; CORRADE_VERIFY(scene.is2D()); CORRADE_VERIFY(!scene.is3D()); - CORRADE_COMPARE_AS(scene.transformations2DAsArray(), Containers::arrayView({ - Matrix3{Math::IdentityInit}, - Matrix3::rotation(35.0_degf), - Matrix3{Math::IdentityInit}, - Matrix3{Math::IdentityInit}, - Matrix3::rotation(-15.0_degf) - }), TestSuite::Compare::Container); - CORRADE_COMPARE_AS(scene.translationsRotationsScalings2DAsArray(), (Containers::arrayView>({ - {{}, {}, Vector2{1.0f}}, - {{}, Complex::rotation(35.0_degf), Vector2{1.0f}}, - {{}, {}, Vector2{1.0f}}, - {{}, {}, Vector2{1.0f}}, - {{}, Complex::rotation(-15.0_degf), Vector2{1.0f}}, + CORRADE_COMPARE_AS(scene.transformations2DAsArray(), (Containers::arrayView>({ + {1, Matrix3{Math::IdentityInit}}, + {0, Matrix3::rotation(35.0_degf)}, + {2, Matrix3{Math::IdentityInit}}, + {4, Matrix3{Math::IdentityInit}}, + {7, Matrix3::rotation(-15.0_degf)} + })), TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.translationsRotationsScalings2DAsArray(), (Containers::arrayView>>({ + {1, {{}, {}, Vector2{1.0f}}}, + {0, {{}, Complex::rotation(35.0_degf), Vector2{1.0f}}}, + {2, {{}, {}, Vector2{1.0f}}}, + {4, {{}, {}, Vector2{1.0f}}}, + {7, {{}, Complex::rotation(-15.0_degf), Vector2{1.0f}}}, })), TestSuite::Compare::Container); } { SceneData scene{SceneObjectType::UnsignedInt, 8, {}, fields, { @@ -2604,19 +2691,19 @@ template void SceneDataTest::transformations2DAsArray }}; CORRADE_VERIFY(scene.is2D()); CORRADE_VERIFY(!scene.is3D()); - CORRADE_COMPARE_AS(scene.transformations2DAsArray(), Containers::arrayView({ - Matrix3{Math::IdentityInit}, - Matrix3{Math::IdentityInit}, - Matrix3{Math::IdentityInit}, - Matrix3::scaling({2.0f, 1.0f}), - Matrix3::scaling({-0.5f, 4.0f}) - }), TestSuite::Compare::Container); - CORRADE_COMPARE_AS(scene.translationsRotationsScalings2DAsArray(), (Containers::arrayView>({ - {{}, {}, Vector2{1.0f}}, - {{}, {}, Vector2{1.0f}}, - {{}, {}, Vector2{1.0f}}, - {{}, {}, {2.0f, 1.0f}}, - {{}, {}, {-0.5f, 4.0f}}, + CORRADE_COMPARE_AS(scene.transformations2DAsArray(), (Containers::arrayView>({ + {1, Matrix3{Math::IdentityInit}}, + {0, Matrix3{Math::IdentityInit}}, + {2, Matrix3{Math::IdentityInit}}, + {4, Matrix3::scaling({2.0f, 1.0f})}, + {7, Matrix3::scaling({-0.5f, 4.0f})} + })), TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.translationsRotationsScalings2DAsArray(), (Containers::arrayView>>({ + {1, {{}, {}, Vector2{1.0f}}}, + {0, {{}, {}, Vector2{1.0f}}}, + {2, {{}, {}, Vector2{1.0f}}}, + {4, {{}, {}, {2.0f, 1.0f}}}, + {7, {{}, {}, {-0.5f, 4.0f}}}, })), TestSuite::Compare::Container); } @@ -2628,19 +2715,19 @@ template void SceneDataTest::transformations2DAsArray }}; CORRADE_VERIFY(scene.is2D()); CORRADE_VERIFY(!scene.is3D()); - CORRADE_COMPARE_AS(scene.transformations2DAsArray(), Containers::arrayView({ - Matrix3::translation({3.0f, 2.0f}), - Matrix3::rotation(35.0_degf), - Matrix3{Math::IdentityInit}, - Matrix3{Math::IdentityInit}, - Matrix3::translation({1.5f, 2.5f})*Matrix3::rotation({-15.0_degf}) - }), TestSuite::Compare::Container); - CORRADE_COMPARE_AS(scene.translationsRotationsScalings2DAsArray(), (Containers::arrayView>({ - {{3.0f, 2.0f}, {}, Vector2{1.0f}}, - {{}, Complex::rotation(35.0_degf), Vector2{1.0f}}, - {{}, {}, Vector2{1.0f}}, - {{}, {}, Vector2{1.0f}}, - {{1.5f, 2.5f}, Complex::rotation(-15.0_degf), Vector2{1.0f}}, + CORRADE_COMPARE_AS(scene.transformations2DAsArray(), (Containers::arrayView>({ + {1, Matrix3::translation({3.0f, 2.0f})}, + {0, Matrix3::rotation(35.0_degf)}, + {2, Matrix3{Math::IdentityInit}}, + {4, Matrix3{Math::IdentityInit}}, + {7, Matrix3::translation({1.5f, 2.5f})*Matrix3::rotation({-15.0_degf})} + })), TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.translationsRotationsScalings2DAsArray(), (Containers::arrayView>>({ + {1, {{3.0f, 2.0f}, {}, Vector2{1.0f}}}, + {0, {{}, Complex::rotation(35.0_degf), Vector2{1.0f}}}, + {2, {{}, {}, Vector2{1.0f}}}, + {4, {{}, {}, Vector2{1.0f}}}, + {7, {{1.5f, 2.5f}, Complex::rotation(-15.0_degf), Vector2{1.0f}}}, })), TestSuite::Compare::Container); } { SceneData scene{SceneObjectType::UnsignedInt, 8, {}, fields, { @@ -2649,19 +2736,19 @@ template void SceneDataTest::transformations2DAsArray }}; CORRADE_VERIFY(scene.is2D()); CORRADE_VERIFY(!scene.is3D()); - CORRADE_COMPARE_AS(scene.transformations2DAsArray(), Containers::arrayView({ - Matrix3::translation({3.0f, 2.0f}), - Matrix3{Math::IdentityInit}, - Matrix3{Math::IdentityInit}, - Matrix3::scaling({2.0f, 1.0f}), - Matrix3::translation({1.5f, 2.5f})*Matrix3::scaling({-0.5f, 4.0f}) - }), TestSuite::Compare::Container); - CORRADE_COMPARE_AS(scene.translationsRotationsScalings2DAsArray(), (Containers::arrayView>({ - {{3.0f, 2.0f}, {}, Vector2{1.0f}}, - {{}, {}, Vector2{1.0f}}, - {{}, {}, Vector2{1.0f}}, - {{}, {}, {2.0f, 1.0f}}, - {{1.5f, 2.5f}, {}, {-0.5f, 4.0f}}, + CORRADE_COMPARE_AS(scene.transformations2DAsArray(), (Containers::arrayView>({ + {1, Matrix3::translation({3.0f, 2.0f})}, + {0, Matrix3{Math::IdentityInit}}, + {2, Matrix3{Math::IdentityInit}}, + {4, Matrix3::scaling({2.0f, 1.0f})}, + {7, Matrix3::translation({1.5f, 2.5f})*Matrix3::scaling({-0.5f, 4.0f})} + })), TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.translationsRotationsScalings2DAsArray(), (Containers::arrayView>>({ + {1, {{3.0f, 2.0f}, {}, Vector2{1.0f}}}, + {0, {{}, {}, Vector2{1.0f}}}, + {2, {{}, {}, Vector2{1.0f}}}, + {4, {{}, {}, {2.0f, 1.0f}}}, + {7, {{1.5f, 2.5f}, {}, {-0.5f, 4.0f}}}, })), TestSuite::Compare::Container); } { SceneData scene{SceneObjectType::UnsignedInt, 8, {}, fields, { @@ -2670,19 +2757,19 @@ template void SceneDataTest::transformations2DAsArray }}; CORRADE_VERIFY(scene.is2D()); CORRADE_VERIFY(!scene.is3D()); - CORRADE_COMPARE_AS(scene.transformations2DAsArray(), Containers::arrayView({ - Matrix3{Math::IdentityInit}, - Matrix3::rotation(35.0_degf), - Matrix3{Math::IdentityInit}, - Matrix3::scaling({2.0f, 1.0f}), - Matrix3::rotation({-15.0_degf})*Matrix3::scaling({-0.5f, 4.0f}) - }), TestSuite::Compare::Container); - CORRADE_COMPARE_AS(scene.translationsRotationsScalings2DAsArray(), (Containers::arrayView>({ - {{}, {}, Vector2{1.0f}}, - {{}, Complex::rotation(35.0_degf), Vector2{1.0f}}, - {{}, {}, Vector2{1.0f}}, - {{}, {}, {2.0f, 1.0f}}, - {{}, Complex::rotation(-15.0_degf), {-0.5f, 4.0f}}, + CORRADE_COMPARE_AS(scene.transformations2DAsArray(), (Containers::arrayView>({ + {1, Matrix3{Math::IdentityInit}}, + {0, Matrix3::rotation(35.0_degf)}, + {2, Matrix3{Math::IdentityInit}}, + {4, Matrix3::scaling({2.0f, 1.0f})}, + {7, Matrix3::rotation({-15.0_degf})*Matrix3::scaling({-0.5f, 4.0f})} + })), TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.translationsRotationsScalings2DAsArray(), (Containers::arrayView>>({ + {1, {{}, {}, Vector2{1.0f}}}, + {0, {{}, Complex::rotation(35.0_degf), Vector2{1.0f}}}, + {2, {{}, {}, Vector2{1.0f}}}, + {4, {{}, {}, {2.0f, 1.0f}}}, + {7, {{}, Complex::rotation(-15.0_degf), {-0.5f, 4.0f}}}, })), TestSuite::Compare::Container); } @@ -2695,19 +2782,19 @@ template void SceneDataTest::transformations2DAsArray }}; CORRADE_VERIFY(scene.is2D()); CORRADE_VERIFY(!scene.is3D()); - CORRADE_COMPARE_AS(scene.transformations2DAsArray(), Containers::arrayView({ - Matrix3::translation({3.0f, 2.0f}), - Matrix3::rotation(35.0_degf), - Matrix3{Math::IdentityInit}, - Matrix3::scaling({2.0f, 1.0f}), - Matrix3::translation({1.5f, 2.5f})*Matrix3::rotation({-15.0_degf})*Matrix3::scaling({-0.5f, 4.0f}) - }), TestSuite::Compare::Container); - CORRADE_COMPARE_AS(scene.translationsRotationsScalings2DAsArray(), (Containers::arrayView>({ - {{3.0f, 2.0f}, {}, Vector2{1.0f}}, - {{}, Complex::rotation(35.0_degf), Vector2{1.0f}}, - {{}, {}, Vector2{1.0f}}, - {{}, {}, {2.0f, 1.0f}}, - {{1.5f, 2.5f}, Complex::rotation(-15.0_degf), {-0.5f, 4.0f}}, + CORRADE_COMPARE_AS(scene.transformations2DAsArray(), (Containers::arrayView>({ + {1, Matrix3::translation({3.0f, 2.0f})}, + {0, Matrix3::rotation(35.0_degf)}, + {2, Matrix3{Math::IdentityInit}}, + {4, Matrix3::scaling({2.0f, 1.0f})}, + {7, Matrix3::translation({1.5f, 2.5f})*Matrix3::rotation({-15.0_degf})*Matrix3::scaling({-0.5f, 4.0f})} + })), TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.translationsRotationsScalings2DAsArray(), (Containers::arrayView>>({ + {1, {{3.0f, 2.0f}, {}, Vector2{1.0f}}}, + {0, {{}, Complex::rotation(35.0_degf), Vector2{1.0f}}}, + {2, {{}, {}, Vector2{1.0f}}}, + {4, {{}, {}, {2.0f, 1.0f}}}, + {7, {{1.5f, 2.5f}, Complex::rotation(-15.0_degf), {-0.5f, 4.0f}}}, })), TestSuite::Compare::Container); } } @@ -2736,7 +2823,7 @@ void SceneDataTest::transformations2DAsArrayBut3DType() { } void SceneDataTest::transformations2DIntoArray() { - auto&& data = IntoArrayOffsetData[testCaseInstanceId()]; + auto&& data = IntoArrayOffset1Data[testCaseInstanceId()]; setTestCaseDescription(data.name); /* Both AsArray() and Into() share a common helper. The AsArray() test @@ -2764,30 +2851,44 @@ void SceneDataTest::transformations2DIntoArray() { /* The offset-less overload should give back all data */ { - Matrix3 out[3]; - scene.transformations2DInto(out); - CORRADE_COMPARE_AS(Containers::stridedArrayView(out), + UnsignedInt objects[3]; + Matrix3 field[3]; + scene.transformations2DInto( + data.objects ? Containers::arrayView(objects) : nullptr, + data.field ? Containers::arrayView(field) : nullptr + ); + if(data.objects) CORRADE_COMPARE_AS(Containers::stridedArrayView(objects), + view.slice(&Field::object), + TestSuite::Compare::Container); + if(data.field) CORRADE_COMPARE_AS(Containers::stridedArrayView(field), view.slice(&Field::transformation), TestSuite::Compare::Container); /* The offset variant only a subset */ } { - Containers::Array out{data.size}; - CORRADE_COMPARE(scene.transformations2DInto(data.offset, out), data.expectedSize); - CORRADE_COMPARE_AS(out.prefix(data.expectedSize), + Containers::Array objects{data.size}; + Containers::Array field{data.size}; + CORRADE_COMPARE(scene.transformations2DInto(data.offset, + data.objects ? arrayView(objects) : nullptr, + data.field ? arrayView(field) : nullptr + ), data.expectedSize); + if(data.objects) CORRADE_COMPARE_AS(objects.prefix(data.expectedSize), + view.slice(&Field::object) + .slice(data.offset, data.offset + data.expectedSize), + TestSuite::Compare::Container); + if(data.field) CORRADE_COMPARE_AS(field.prefix(data.expectedSize), view.slice(&Field::transformation) .slice(data.offset, data.offset + data.expectedSize), TestSuite::Compare::Container); } } -void SceneDataTest::transformations2DIntoArrayTRS() { - auto&& data = IntoArrayOffsetData[testCaseInstanceId()]; +void SceneDataTest::transformations2DTRSIntoArray() { + auto&& data = IntoArrayOffset1Data[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. */ + /* Same APIs tested as in transformations2DIntoArray(), but with a TRS + input */ struct Field { UnsignedInt object; @@ -2824,113 +2925,125 @@ void SceneDataTest::transformations2DIntoArrayTRS() { /* The offset-less overload should give back all data */ { - Matrix3 out[3]; - scene.transformations2DInto(out); - CORRADE_COMPARE_AS(Containers::arrayView(out), + UnsignedInt objects[3]; + Matrix3 field[3]; + scene.transformations2DInto( + data.objects ? Containers::arrayView(objects) : nullptr, + data.field ? Containers::arrayView(field) : nullptr + ); + if(data.objects) CORRADE_COMPARE_AS(Containers::stridedArrayView(objects), + view.slice(&Field::object), + TestSuite::Compare::Container); + if(data.field) CORRADE_COMPARE_AS(Containers::stridedArrayView(field), Containers::arrayView(expected), TestSuite::Compare::Container); - /* Variant with TRS components */ + /* The offset variant only a subset */ } { - Vector2 translationsOut[3]; - Complex rotationsOut[3]; - Vector2 scalingsOut[3]; - scene.translationsRotationsScalings2DInto(translationsOut, rotationsOut, scalingsOut); - CORRADE_COMPARE_AS(Containers::stridedArrayView(translationsOut), - view.slice(&Field::translation), - TestSuite::Compare::Container); - CORRADE_COMPARE_AS(Containers::stridedArrayView(rotationsOut), - view.slice(&Field::rotation), + Containers::Array objects{data.size}; + Containers::Array field{data.size}; + CORRADE_COMPARE(scene.transformations2DInto(data.offset, + data.objects ? arrayView(objects) : nullptr, + data.field ? arrayView(field) : nullptr + ), data.expectedSize); + if(data.objects) CORRADE_COMPARE_AS(objects.prefix(data.expectedSize), + view.slice(&Field::object) + .slice(data.offset, data.offset + data.expectedSize), TestSuite::Compare::Container); - CORRADE_COMPARE_AS(Containers::stridedArrayView(scalingsOut), - view.slice(&Field::scaling), + if(data.field) CORRADE_COMPARE_AS(field.prefix(data.expectedSize), + Containers::arrayView(expected).slice(data.offset, data.offset + data.expectedSize), TestSuite::Compare::Container); + } +} - /* Variant with just translations */ - } { - Vector2 translationsOut[3]; - scene.translationsRotationsScalings2DInto(translationsOut, nullptr, nullptr); - CORRADE_COMPARE_AS(Containers::stridedArrayView(translationsOut), +void SceneDataTest::transformations2DIntoArrayTRS() { + auto&& data = IntoArrayOffset3Data[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. */ + + struct Field { + UnsignedInt object; + Vector2 translation; + Complex rotation; + Vector2 scaling; + } fields[] { + {1, {3.0f, 2.0f}, {}, {1.5f, 2.0f}}, + {0, {}, Complex::rotation(35.0_degf), {1.0f, 1.0f}}, + {4, {3.0f, 2.0f}, Complex::rotation(35.0_degf), {1.0f, 1.0f}} + }; + + 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::Translation, + view.slice(&Field::object), + view.slice(&Field::translation)}, + SceneFieldData{SceneField::Rotation, + view.slice(&Field::object), + view.slice(&Field::rotation)}, + SceneFieldData{SceneField::Scaling, + view.slice(&Field::object), + view.slice(&Field::scaling)}, + }}; + + /* The offset-less overload should give back all data */ + { + UnsignedInt objects[3]; + Vector2 translations[3]; + Complex rotations[3]; + Vector2 scalings[3]; + scene.translationsRotationsScalings2DInto( + data.objects ? Containers::arrayView(objects) : nullptr, + data.field1 ? Containers::arrayView(translations) : nullptr, + data.field2 ? Containers::arrayView(rotations) : nullptr, + data.field3 ? Containers::arrayView(scalings) : nullptr + ); + if(data.objects) CORRADE_COMPARE_AS(Containers::stridedArrayView(objects), + view.slice(&Field::object), + TestSuite::Compare::Container); + if(data.field1) CORRADE_COMPARE_AS(Containers::stridedArrayView(translations), view.slice(&Field::translation), TestSuite::Compare::Container); - - /* Variant with just rotations */ - } { - Complex rotationsOut[3]; - scene.translationsRotationsScalings2DInto(nullptr, rotationsOut, nullptr); - CORRADE_COMPARE_AS(Containers::stridedArrayView(rotationsOut), + if(data.field2) CORRADE_COMPARE_AS(Containers::stridedArrayView(rotations), view.slice(&Field::rotation), TestSuite::Compare::Container); - - /* Variant with just scalings */ - } { - Vector2 scalingsOut[3]; - scene.translationsRotationsScalings2DInto(nullptr, nullptr, scalingsOut); - CORRADE_COMPARE_AS(Containers::stridedArrayView(scalingsOut), + if(data.field3) CORRADE_COMPARE_AS(Containers::stridedArrayView(scalings), view.slice(&Field::scaling), TestSuite::Compare::Container); - /* Variant with neither is stupid, but should work too */ - } { - scene.translationsRotationsScalings2DInto(nullptr, nullptr, nullptr); - /* The offset variant only a subset */ } { - Containers::Array out{data.size}; - CORRADE_COMPARE(scene.transformations2DInto(data.offset, out), data.expectedSize); - CORRADE_COMPARE_AS(out.prefix(data.expectedSize), - Containers::arrayView(expected).slice(data.offset, data.offset + data.expectedSize), - TestSuite::Compare::Container); - - /* Variant with TRS components */ - } { - Containers::Array translationsOut{data.size}; - Containers::Array rotationsOut{data.size}; - Containers::Array scalingsOut{data.size}; - CORRADE_COMPARE(scene.translationsRotationsScalings2DInto(data.offset, translationsOut, rotationsOut, scalingsOut), data.expectedSize); - CORRADE_COMPARE_AS(translationsOut.prefix(data.expectedSize), - view.slice(&Field::translation) - .slice(data.offset, data.offset + data.expectedSize), - TestSuite::Compare::Container); - CORRADE_COMPARE_AS(rotationsOut.prefix(data.expectedSize), - view.slice(&Field::rotation) - .slice(data.offset, data.offset + data.expectedSize), - TestSuite::Compare::Container); - CORRADE_COMPARE_AS(scalingsOut.prefix(data.expectedSize), - view.slice(&Field::scaling) + Containers::Array objects{data.size}; + Containers::Array translations{data.size}; + Containers::Array rotations{data.size}; + Containers::Array scalings{data.size}; + CORRADE_COMPARE(scene.translationsRotationsScalings2DInto(data.offset, + data.objects ? arrayView(objects) : nullptr, + data.field1 ? arrayView(translations) : nullptr, + data.field2 ? arrayView(rotations) : nullptr, + data.field3 ? arrayView(scalings) : nullptr + ), data.expectedSize); + if(data.objects) CORRADE_COMPARE_AS(objects.prefix(data.expectedSize), + view.slice(&Field::object) .slice(data.offset, data.offset + data.expectedSize), TestSuite::Compare::Container); - - /* Variant with just translations */ - } { - Containers::Array translationsOut{data.size}; - CORRADE_COMPARE(scene.translationsRotationsScalings2DInto(data.offset, translationsOut, nullptr, nullptr), data.expectedSize); - CORRADE_COMPARE_AS(translationsOut.prefix(data.expectedSize), + if(data.field1) CORRADE_COMPARE_AS(translations.prefix(data.expectedSize), view.slice(&Field::translation) .slice(data.offset, data.offset + data.expectedSize), TestSuite::Compare::Container); - - /* Variant with just rotations */ - } { - Containers::Array rotationsOut{data.size}; - CORRADE_COMPARE(scene.translationsRotationsScalings2DInto(data.offset, nullptr, rotationsOut, nullptr), data.expectedSize); - CORRADE_COMPARE_AS(rotationsOut.prefix(data.expectedSize), + if(data.field2) CORRADE_COMPARE_AS(rotations.prefix(data.expectedSize), view.slice(&Field::rotation) .slice(data.offset, data.offset + data.expectedSize), TestSuite::Compare::Container); - - /* Variant with just scalings */ - } { - Containers::Array scalingsOut{data.size}; - CORRADE_COMPARE(scene.translationsRotationsScalings2DInto(data.offset, nullptr, nullptr, scalingsOut), data.expectedSize); - CORRADE_COMPARE_AS(scalingsOut.prefix(data.expectedSize), + if(data.field3) CORRADE_COMPARE_AS(scalings.prefix(data.expectedSize), view.slice(&Field::scaling) .slice(data.offset, data.offset + data.expectedSize), TestSuite::Compare::Container); - - /* Variant with neither is stupid, but should work too */ - } { - CORRADE_COMPARE(scene.translationsRotationsScalings2DInto(data.offset, nullptr, nullptr, nullptr), 0); } } @@ -2952,12 +3065,19 @@ void SceneDataTest::transformations2DIntoArrayInvalidSizeOrOffset() { std::ostringstream out; Error redirectError{&out}; - Matrix3 destination[2]; - scene.transformations2DInto(destination); - scene.transformations2DInto(4, destination); + UnsignedInt objectDestinationCorrect[3]; + UnsignedInt objectDestination[2]; + Matrix3 fieldDestinationCorrect[3]; + Matrix3 fieldDestination[2]; + scene.transformations2DInto(objectDestination, fieldDestinationCorrect); + scene.transformations2DInto(objectDestinationCorrect, fieldDestination); + scene.transformations2DInto(4, objectDestination, fieldDestination); + scene.transformations2DInto(0, objectDestinationCorrect, fieldDestination); CORRADE_COMPARE(out.str(), - "Trade::SceneData::transformations2DInto(): expected a view with 3 elements but got 2\n" - "Trade::SceneData::transformations2DInto(): offset 4 out of bounds for a field of size 3\n"); + "Trade::SceneData::transformations2DInto(): expected object destination view either empty or with 3 elements but got 2\n" + "Trade::SceneData::transformations2DInto(): expected field destination view either empty or with 3 elements but got 2\n" + "Trade::SceneData::transformations2DInto(): offset 4 out of bounds for a field of size 3\n" + "Trade::SceneData::transformations2DInto(): object and field destination views have different size, 3 vs 2\n"); } void SceneDataTest::transformations2DIntoArrayInvalidSizeOrOffsetTRS() { @@ -2978,24 +3098,34 @@ void SceneDataTest::transformations2DIntoArrayInvalidSizeOrOffsetTRS() { std::ostringstream out; Error redirectError{&out}; + UnsignedInt objectDestinationCorrect[3]; + UnsignedInt objectDestination[2]; Vector2 translationDestinationCorrect[3]; Vector2 translationDestination[2]; Complex rotationDestinationCorrect[3]; Complex rotationDestination[2]; Vector2 scalingDestinationCorrect[3]; Vector2 scalingDestination[2]; - scene.translationsRotationsScalings2DInto(translationDestination, rotationDestinationCorrect, scalingDestinationCorrect); - scene.translationsRotationsScalings2DInto(translationDestinationCorrect, rotationDestination, scalingDestinationCorrect); - scene.translationsRotationsScalings2DInto(translationDestinationCorrect, rotationDestinationCorrect, scalingDestination); - scene.translationsRotationsScalings2DInto(4, translationDestination, rotationDestination, scalingDestination); - scene.translationsRotationsScalings2DInto(0, translationDestinationCorrect, rotationDestination, nullptr); - scene.translationsRotationsScalings2DInto(0, translationDestinationCorrect, nullptr, scalingDestination); - scene.translationsRotationsScalings2DInto(0, nullptr, rotationDestinationCorrect, scalingDestination); + scene.translationsRotationsScalings2DInto(objectDestination, translationDestinationCorrect, rotationDestinationCorrect, scalingDestinationCorrect); + scene.translationsRotationsScalings2DInto(objectDestinationCorrect, translationDestination, rotationDestinationCorrect, scalingDestinationCorrect); + scene.translationsRotationsScalings2DInto(objectDestinationCorrect, translationDestinationCorrect, rotationDestination, scalingDestinationCorrect); + scene.translationsRotationsScalings2DInto(objectDestinationCorrect, translationDestinationCorrect, rotationDestinationCorrect, scalingDestination); + scene.translationsRotationsScalings2DInto(4, objectDestination, translationDestination, rotationDestination, scalingDestination); + scene.translationsRotationsScalings2DInto(0, objectDestinationCorrect, translationDestination, nullptr, nullptr); + scene.translationsRotationsScalings2DInto(0, objectDestinationCorrect, nullptr, rotationDestination, nullptr); + scene.translationsRotationsScalings2DInto(0, objectDestinationCorrect, nullptr, nullptr, scalingDestination); + scene.translationsRotationsScalings2DInto(0, nullptr, translationDestinationCorrect, rotationDestination, nullptr); + scene.translationsRotationsScalings2DInto(0, nullptr, translationDestinationCorrect, nullptr, scalingDestination); + scene.translationsRotationsScalings2DInto(0, nullptr, nullptr, rotationDestinationCorrect, scalingDestination); CORRADE_COMPARE(out.str(), + "Trade::SceneData::translationsRotationsScalings2DInto(): expected object destination view either empty or with 3 elements but got 2\n" "Trade::SceneData::translationsRotationsScalings2DInto(): expected translation destination view either empty or with 3 elements but got 2\n" "Trade::SceneData::translationsRotationsScalings2DInto(): expected rotation destination view either empty or with 3 elements but got 2\n" "Trade::SceneData::translationsRotationsScalings2DInto(): expected scaling destination view either empty or with 3 elements but got 2\n" "Trade::SceneData::translationsRotationsScalings2DInto(): offset 4 out of bounds for a field of size 3\n" + "Trade::SceneData::translationsRotationsScalings2DInto(): object and translation destination views have different size, 3 vs 2\n" + "Trade::SceneData::translationsRotationsScalings2DInto(): object and rotation destination views have different size, 3 vs 2\n" + "Trade::SceneData::translationsRotationsScalings2DInto(): object and scaling destination views have different size, 3 vs 2\n" "Trade::SceneData::translationsRotationsScalings2DInto(): translation and rotation destination views have different size, 3 vs 2\n" "Trade::SceneData::translationsRotationsScalings2DInto(): translation and scaling destination views have different size, 3 vs 2\n" "Trade::SceneData::translationsRotationsScalings2DInto(): rotation and scaling destination views have different size, 3 vs 2\n"); @@ -3062,12 +3192,12 @@ template void SceneDataTest::transformations3DAsArray() { CORRADE_VERIFY(!scene.is2D()); CORRADE_VERIFY(scene.is3D()); - CORRADE_COMPARE_AS(scene.transformations3DAsArray(), Containers::arrayView({ - Matrix4::translation({3.0f, 2.0f, -0.5f}), - Matrix4::rotationY(35.0_degf), - Matrix4::translation({1.5f, 2.5f, 0.75f})*Matrix4::rotationX(-15.0_degf), - Matrix4::rotationX(-15.0_degf)*Matrix4::translation({1.5f, 2.5f, 0.75f}) - }), TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.transformations3DAsArray(), (Containers::arrayView>({ + {1, Matrix4::translation({3.0f, 2.0f, -0.5f})}, + {0, Matrix4::rotationY(35.0_degf)}, + {4, Matrix4::translation({1.5f, 2.5f, 0.75f})*Matrix4::rotationX(-15.0_degf)}, + {5, Matrix4::rotationX(-15.0_degf)*Matrix4::translation({1.5f, 2.5f, 0.75f})} + })), TestSuite::Compare::Container); } template void SceneDataTest::transformations3DAsArrayTRS() { @@ -3115,19 +3245,19 @@ template void SceneDataTest::transformations3DAsArray }}; CORRADE_VERIFY(!scene.is2D()); CORRADE_VERIFY(scene.is3D()); - CORRADE_COMPARE_AS(scene.transformations3DAsArray(), Containers::arrayView({ - Matrix4::translation({3.0f, 2.0, 1.0f}), - Matrix4{Math::IdentityInit}, - Matrix4{Math::IdentityInit}, - Matrix4{Math::IdentityInit}, - Matrix4::translation({1.5f, 2.5f, 3.5f}) - }), TestSuite::Compare::Container); - CORRADE_COMPARE_AS(scene.translationsRotationsScalings3DAsArray(), (Containers::arrayView>({ - {{3.0f, 2.0, 1.0f}, {}, Vector3{1.0f}}, - {{}, {}, Vector3{1.0f}}, - {{}, {}, Vector3{1.0f}}, - {{}, {}, Vector3{1.0f}}, - {{1.5f, 2.5f, 3.5f}, {}, Vector3{1.0f}}, + CORRADE_COMPARE_AS(scene.transformations3DAsArray(), (Containers::arrayView>({ + {1, Matrix4::translation({3.0f, 2.0, 1.0f})}, + {0, Matrix4{Math::IdentityInit}}, + {2, Matrix4{Math::IdentityInit}}, + {4, Matrix4{Math::IdentityInit}}, + {7, Matrix4::translation({1.5f, 2.5f, 3.5f})} + })), TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.translationsRotationsScalings3DAsArray(), (Containers::arrayView>>({ + {1, {{3.0f, 2.0, 1.0f}, {}, Vector3{1.0f}}}, + {0, {{}, {}, Vector3{1.0f}}}, + {2, {{}, {}, Vector3{1.0f}}}, + {4, {{}, {}, Vector3{1.0f}}}, + {7, {{1.5f, 2.5f, 3.5f}, {}, Vector3{1.0f}}}, })), TestSuite::Compare::Container); } { SceneData scene{SceneObjectType::UnsignedInt, 8, {}, fields, { @@ -3135,19 +3265,19 @@ template void SceneDataTest::transformations3DAsArray }}; CORRADE_VERIFY(!scene.is2D()); CORRADE_VERIFY(scene.is3D()); - CORRADE_COMPARE_AS(scene.transformations3DAsArray(), Containers::arrayView({ - Matrix4{Math::IdentityInit}, - Matrix4::rotationY(35.0_degf), - Matrix4{Math::IdentityInit}, - Matrix4{Math::IdentityInit}, - Matrix4::rotationX(-15.0_degf) - }), TestSuite::Compare::Container); - CORRADE_COMPARE_AS(scene.translationsRotationsScalings3DAsArray(), (Containers::arrayView>({ - {{}, {}, Vector3{1.0f}}, - {{}, Quaternion::rotation(35.0_degf, Vector3::yAxis()), Vector3{1.0f}}, - {{}, {}, Vector3{1.0f}}, - {{}, {}, Vector3{1.0f}}, - {{}, Quaternion::rotation(-15.0_degf, Vector3::xAxis()), Vector3{1.0f}}, + CORRADE_COMPARE_AS(scene.transformations3DAsArray(), (Containers::arrayView>({ + {1, Matrix4{Math::IdentityInit}}, + {0, Matrix4::rotationY(35.0_degf)}, + {2, Matrix4{Math::IdentityInit}}, + {4, Matrix4{Math::IdentityInit}}, + {7, Matrix4::rotationX(-15.0_degf)} + })), TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.translationsRotationsScalings3DAsArray(), (Containers::arrayView>>({ + {1, {{}, {}, Vector3{1.0f}}}, + {0, {{}, Quaternion::rotation(35.0_degf, Vector3::yAxis()), Vector3{1.0f}}}, + {2, {{}, {}, Vector3{1.0f}}}, + {4, {{}, {}, Vector3{1.0f}}}, + {7, {{}, Quaternion::rotation(-15.0_degf, Vector3::xAxis()), Vector3{1.0f}}}, })), TestSuite::Compare::Container); } { SceneData scene{SceneObjectType::UnsignedInt, 8, {}, fields, { @@ -3155,19 +3285,19 @@ template void SceneDataTest::transformations3DAsArray }}; CORRADE_VERIFY(!scene.is2D()); CORRADE_VERIFY(scene.is3D()); - CORRADE_COMPARE_AS(scene.transformations3DAsArray(), Containers::arrayView({ - Matrix4{Math::IdentityInit}, - Matrix4{Math::IdentityInit}, - Matrix4{Math::IdentityInit}, - Matrix4::scaling({2.0f, 1.0f, 0.0f}), - Matrix4::scaling({-0.5f, 4.0f, -16.0f}) - }), TestSuite::Compare::Container); - CORRADE_COMPARE_AS(scene.translationsRotationsScalings3DAsArray(), (Containers::arrayView>({ - {{}, {}, Vector3{1.0f}}, - {{}, {}, Vector3{1.0f}}, - {{}, {}, Vector3{1.0f}}, - {{}, {}, {2.0f, 1.0f, 0.0f}}, - {{}, {}, {-0.5f, 4.0f, -16.0f}}, + CORRADE_COMPARE_AS(scene.transformations3DAsArray(), (Containers::arrayView>({ + {1, Matrix4{Math::IdentityInit}}, + {0, Matrix4{Math::IdentityInit}}, + {2, Matrix4{Math::IdentityInit}}, + {4, Matrix4::scaling({2.0f, 1.0f, 0.0f})}, + {7, Matrix4::scaling({-0.5f, 4.0f, -16.0f})} + })), TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.translationsRotationsScalings3DAsArray(), (Containers::arrayView>>({ + {1, {{}, {}, Vector3{1.0f}}}, + {0, {{}, {}, Vector3{1.0f}}}, + {2, {{}, {}, Vector3{1.0f}}}, + {4, {{}, {}, {2.0f, 1.0f, 0.0f}}}, + {7, {{}, {}, {-0.5f, 4.0f, -16.0f}}}, })), TestSuite::Compare::Container); } @@ -3179,19 +3309,19 @@ template void SceneDataTest::transformations3DAsArray }}; CORRADE_VERIFY(!scene.is2D()); CORRADE_VERIFY(scene.is3D()); - CORRADE_COMPARE_AS(scene.transformations3DAsArray(), Containers::arrayView({ - Matrix4::translation({3.0f, 2.0, 1.0f}), - Matrix4::rotationY(35.0_degf), - Matrix4{Math::IdentityInit}, - Matrix4{Math::IdentityInit}, - Matrix4::translation({1.5f, 2.5f, 3.5f})*Matrix4::rotationX(-15.0_degf) - }), TestSuite::Compare::Container); - CORRADE_COMPARE_AS(scene.translationsRotationsScalings3DAsArray(), (Containers::arrayView>({ - {{3.0f, 2.0, 1.0f}, {}, Vector3{1.0f}}, - {{}, Quaternion::rotation(35.0_degf, Vector3::yAxis()), Vector3{1.0f}}, - {{}, {}, Vector3{1.0f}}, - {{}, {}, Vector3{1.0f}}, - {{1.5f, 2.5f, 3.5f}, Quaternion::rotation(-15.0_degf, Vector3::xAxis()), Vector3{1.0f}}, + CORRADE_COMPARE_AS(scene.transformations3DAsArray(), (Containers::arrayView>({ + {1, Matrix4::translation({3.0f, 2.0, 1.0f})}, + {0, Matrix4::rotationY(35.0_degf)}, + {2, Matrix4{Math::IdentityInit}}, + {4, Matrix4{Math::IdentityInit}}, + {7, Matrix4::translation({1.5f, 2.5f, 3.5f})*Matrix4::rotationX(-15.0_degf)} + })), TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.translationsRotationsScalings3DAsArray(), (Containers::arrayView>>({ + {1, {{3.0f, 2.0, 1.0f}, {}, Vector3{1.0f}}}, + {0, {{}, Quaternion::rotation(35.0_degf, Vector3::yAxis()), Vector3{1.0f}}}, + {2, {{}, {}, Vector3{1.0f}}}, + {4, {{}, {}, Vector3{1.0f}}}, + {7, {{1.5f, 2.5f, 3.5f}, Quaternion::rotation(-15.0_degf, Vector3::xAxis()), Vector3{1.0f}}}, })), TestSuite::Compare::Container); } { SceneData scene{SceneObjectType::UnsignedInt, 8, {}, fields, { @@ -3200,19 +3330,19 @@ template void SceneDataTest::transformations3DAsArray }}; CORRADE_VERIFY(!scene.is2D()); CORRADE_VERIFY(scene.is3D()); - CORRADE_COMPARE_AS(scene.transformations3DAsArray(), Containers::arrayView({ - Matrix4::translation({3.0f, 2.0, 1.0f}), - Matrix4{Math::IdentityInit}, - Matrix4{Math::IdentityInit}, - Matrix4::scaling({2.0f, 1.0f, 0.0f}), - Matrix4::translation({1.5f, 2.5f, 3.5f})*Matrix4::scaling({-0.5f, 4.0f, -16.0f}) - }), TestSuite::Compare::Container); - CORRADE_COMPARE_AS(scene.translationsRotationsScalings3DAsArray(), (Containers::arrayView>({ - {{3.0f, 2.0, 1.0f}, {}, Vector3{1.0f}}, - {{}, {}, Vector3{1.0f}}, - {{}, {}, Vector3{1.0f}}, - {{}, {}, {2.0f, 1.0f, 0.0f}}, - {{1.5f, 2.5f, 3.5f}, {}, {-0.5f, 4.0f, -16.0f}}, + CORRADE_COMPARE_AS(scene.transformations3DAsArray(), (Containers::arrayView>({ + {1, Matrix4::translation({3.0f, 2.0, 1.0f})}, + {0, Matrix4{Math::IdentityInit}}, + {2, Matrix4{Math::IdentityInit}}, + {4, Matrix4::scaling({2.0f, 1.0f, 0.0f})}, + {7, Matrix4::translation({1.5f, 2.5f, 3.5f})*Matrix4::scaling({-0.5f, 4.0f, -16.0f})} + })), TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.translationsRotationsScalings3DAsArray(), (Containers::arrayView>>({ + {1, {{3.0f, 2.0, 1.0f}, {}, Vector3{1.0f}}}, + {0, {{}, {}, Vector3{1.0f}}}, + {2, {{}, {}, Vector3{1.0f}}}, + {4, {{}, {}, {2.0f, 1.0f, 0.0f}}}, + {7, {{1.5f, 2.5f, 3.5f}, {}, {-0.5f, 4.0f, -16.0f}}}, })), TestSuite::Compare::Container); } { SceneData scene{SceneObjectType::UnsignedInt, 8, {}, fields, { @@ -3221,19 +3351,19 @@ template void SceneDataTest::transformations3DAsArray }}; CORRADE_VERIFY(!scene.is2D()); CORRADE_VERIFY(scene.is3D()); - CORRADE_COMPARE_AS(scene.transformations3DAsArray(), Containers::arrayView({ - Matrix4{Math::IdentityInit}, - Matrix4::rotationY(35.0_degf), - Matrix4{Math::IdentityInit}, - Matrix4::scaling({2.0f, 1.0f, 0.0f}), - Matrix4::rotationX({-15.0_degf})*Matrix4::scaling({-0.5f, 4.0f, -16.0f}) - }), TestSuite::Compare::Container); - CORRADE_COMPARE_AS(scene.translationsRotationsScalings3DAsArray(), (Containers::arrayView>({ - {{}, {}, Vector3{1.0f}}, - {{}, Quaternion::rotation(35.0_degf, Vector3::yAxis()), Vector3{1.0f}}, - {{}, {}, Vector3{1.0f}}, - {{}, {}, {2.0f, 1.0f, 0.0f}}, - {{}, Quaternion::rotation(-15.0_degf, Vector3::xAxis()), {-0.5f, 4.0f, -16.0f}}, + CORRADE_COMPARE_AS(scene.transformations3DAsArray(), (Containers::arrayView>({ + {1, Matrix4{Math::IdentityInit}}, + {0, Matrix4::rotationY(35.0_degf)}, + {2, Matrix4{Math::IdentityInit}}, + {4, Matrix4::scaling({2.0f, 1.0f, 0.0f})}, + {7, Matrix4::rotationX({-15.0_degf})*Matrix4::scaling({-0.5f, 4.0f, -16.0f})} + })), TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.translationsRotationsScalings3DAsArray(), (Containers::arrayView>>({ + {1, {{}, {}, Vector3{1.0f}}}, + {0, {{}, Quaternion::rotation(35.0_degf, Vector3::yAxis()), Vector3{1.0f}}}, + {2, {{}, {}, Vector3{1.0f}}}, + {4, {{}, {}, {2.0f, 1.0f, 0.0f}}}, + {7, {{}, Quaternion::rotation(-15.0_degf, Vector3::xAxis()), {-0.5f, 4.0f, -16.0f}}}, })), TestSuite::Compare::Container); } @@ -3246,19 +3376,19 @@ template void SceneDataTest::transformations3DAsArray }}; CORRADE_VERIFY(!scene.is2D()); CORRADE_VERIFY(scene.is3D()); - CORRADE_COMPARE_AS(scene.transformations3DAsArray(), Containers::arrayView({ - Matrix4::translation({3.0f, 2.0, 1.0f}), - Matrix4::rotationY(35.0_degf), - Matrix4{Math::IdentityInit}, - Matrix4::scaling({2.0f, 1.0f, 0.0f}), - Matrix4::translation({1.5f, 2.5f, 3.5f})*Matrix4::rotationX({-15.0_degf})*Matrix4::scaling({-0.5f, 4.0f, -16.0f}) - }), TestSuite::Compare::Container); - CORRADE_COMPARE_AS(scene.translationsRotationsScalings3DAsArray(), (Containers::arrayView>({ - {{3.0f, 2.0, 1.0f}, {}, Vector3{1.0f}}, - {{}, Quaternion::rotation(35.0_degf, Vector3::yAxis()), Vector3{1.0f}}, - {{}, {}, Vector3{1.0f}}, - {{}, {}, {2.0f, 1.0f, 0.0f}}, - {{1.5f, 2.5f, 3.5f}, Quaternion::rotation(-15.0_degf, Vector3::xAxis()), {-0.5f, 4.0f, -16.0f}}, + CORRADE_COMPARE_AS(scene.transformations3DAsArray(), (Containers::arrayView>({ + {1, Matrix4::translation({3.0f, 2.0, 1.0f})}, + {0, Matrix4::rotationY(35.0_degf)}, + {2, Matrix4{Math::IdentityInit}}, + {4, Matrix4::scaling({2.0f, 1.0f, 0.0f})}, + {7, Matrix4::translation({1.5f, 2.5f, 3.5f})*Matrix4::rotationX({-15.0_degf})*Matrix4::scaling({-0.5f, 4.0f, -16.0f})} + })), TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.translationsRotationsScalings3DAsArray(), (Containers::arrayView>>({ + {1, {{3.0f, 2.0, 1.0f}, {}, Vector3{1.0f}}}, + {0, {{}, Quaternion::rotation(35.0_degf, Vector3::yAxis()), Vector3{1.0f}}}, + {2, {{}, {}, Vector3{1.0f}}}, + {4, {{}, {}, {2.0f, 1.0f, 0.0f}}}, + {7, {{1.5f, 2.5f, 3.5f}, Quaternion::rotation(-15.0_degf, Vector3::xAxis()), {-0.5f, 4.0f, -16.0f}}}, })), TestSuite::Compare::Container); } } @@ -3287,7 +3417,7 @@ void SceneDataTest::transformations3DAsArrayBut2DType() { } void SceneDataTest::transformations3DIntoArray() { - auto&& data = IntoArrayOffsetData[testCaseInstanceId()]; + auto&& data = IntoArrayOffset1Data[testCaseInstanceId()]; setTestCaseDescription(data.name); /* Both AsArray() and Into() share a common helper. The AsArray() test @@ -3315,30 +3445,44 @@ void SceneDataTest::transformations3DIntoArray() { /* The offset-less overload should give back all data */ { - Matrix4 out[3]; - scene.transformations3DInto(out); - CORRADE_COMPARE_AS(Containers::stridedArrayView(out), + UnsignedInt objects[3]; + Matrix4 field[3]; + scene.transformations3DInto( + data.objects ? Containers::arrayView(objects) : nullptr, + data.field ? Containers::arrayView(field) : nullptr + ); + if(data.objects) CORRADE_COMPARE_AS(Containers::stridedArrayView(objects), + view.slice(&Field::object), + TestSuite::Compare::Container); + if(data.field) CORRADE_COMPARE_AS(Containers::stridedArrayView(field), view.slice(&Field::transformation), TestSuite::Compare::Container); /* The offset variant only a subset */ } { - Containers::Array out{data.size}; - CORRADE_COMPARE(scene.transformations3DInto(data.offset, out), data.expectedSize); - CORRADE_COMPARE_AS(out.prefix(data.expectedSize), + Containers::Array objects{data.size}; + Containers::Array field{data.size}; + CORRADE_COMPARE(scene.transformations3DInto(data.offset, + data.objects ? arrayView(objects) : nullptr, + data.field ? arrayView(field) : nullptr + ), data.expectedSize); + if(data.objects) CORRADE_COMPARE_AS(objects.prefix(data.expectedSize), + view.slice(&Field::object) + .slice(data.offset, data.offset + data.expectedSize), + TestSuite::Compare::Container); + if(data.field) CORRADE_COMPARE_AS(field.prefix(data.expectedSize), view.slice(&Field::transformation) .slice(data.offset, data.offset + data.expectedSize), TestSuite::Compare::Container); } } -void SceneDataTest::transformations3DIntoArrayTRS() { - auto&& data = IntoArrayOffsetData[testCaseInstanceId()]; +void SceneDataTest::transformations3DTRSIntoArray() { + auto&& data = IntoArrayOffset1Data[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. */ + /* Same APIs tested as in transformations3DIntoArray(), but with a TRS + input */ struct Field { UnsignedInt object; @@ -3375,113 +3519,125 @@ void SceneDataTest::transformations3DIntoArrayTRS() { /* The offset-less overload should give back all data */ { - Matrix4 out[3]; - scene.transformations3DInto(out); - CORRADE_COMPARE_AS(Containers::arrayView(out), + UnsignedInt objects[3]; + Matrix4 field[3]; + scene.transformations3DInto( + data.objects ? Containers::arrayView(objects) : nullptr, + data.field ? Containers::arrayView(field) : nullptr + ); + if(data.objects) CORRADE_COMPARE_AS(Containers::stridedArrayView(objects), + view.slice(&Field::object), + TestSuite::Compare::Container); + if(data.field) CORRADE_COMPARE_AS(Containers::stridedArrayView(field), Containers::arrayView(expected), TestSuite::Compare::Container); - /* Variant with TRS components */ + /* The offset variant only a subset */ } { - Vector3 translationsOut[3]; - Quaternion rotationsOut[3]; - Vector3 scalingsOut[3]; - scene.translationsRotationsScalings3DInto(translationsOut, rotationsOut, scalingsOut); - CORRADE_COMPARE_AS(Containers::stridedArrayView(translationsOut), - view.slice(&Field::translation), - TestSuite::Compare::Container); - CORRADE_COMPARE_AS(Containers::stridedArrayView(rotationsOut), - view.slice(&Field::rotation), + Containers::Array objects{data.size}; + Containers::Array field{data.size}; + CORRADE_COMPARE(scene.transformations3DInto(data.offset, + data.objects ? arrayView(objects) : nullptr, + data.field ? arrayView(field) : nullptr + ), data.expectedSize); + if(data.objects) CORRADE_COMPARE_AS(objects.prefix(data.expectedSize), + view.slice(&Field::object) + .slice(data.offset, data.offset + data.expectedSize), TestSuite::Compare::Container); - CORRADE_COMPARE_AS(Containers::stridedArrayView(scalingsOut), - view.slice(&Field::scaling), + if(data.field) CORRADE_COMPARE_AS(field.prefix(data.expectedSize), + Containers::arrayView(expected).slice(data.offset, data.offset + data.expectedSize), TestSuite::Compare::Container); + } +} - /* Variant with just translations */ - } { - Vector3 translationsOut[3]; - scene.translationsRotationsScalings3DInto(translationsOut, nullptr, nullptr); - CORRADE_COMPARE_AS(Containers::stridedArrayView(translationsOut), +void SceneDataTest::transformations3DIntoArrayTRS() { + auto&& data = IntoArrayOffset3Data[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. */ + + struct Field { + UnsignedInt object; + Vector3 translation; + Quaternion rotation; + Vector3 scaling; + } fields[] { + {1, {3.0f, 2.0f, 1.0f}, {}, {1.5f, 2.0f, 4.5f}}, + {0, {}, Quaternion::rotation(35.0_degf, Vector3::xAxis()), {1.0f, 1.0f, 1.0f}}, + {4, {3.0f, 2.0f, 1.0f}, Quaternion::rotation(35.0_degf, Vector3::xAxis()), {1.0f, 1.0f, 1.0f}} + }; + + 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::Translation, + view.slice(&Field::object), + view.slice(&Field::translation)}, + SceneFieldData{SceneField::Rotation, + view.slice(&Field::object), + view.slice(&Field::rotation)}, + SceneFieldData{SceneField::Scaling, + view.slice(&Field::object), + view.slice(&Field::scaling)}, + }}; + + /* The offset-less overload should give back all data */ + { + UnsignedInt objects[3]; + Vector3 translations[3]; + Quaternion rotations[3]; + Vector3 scalings[3]; + scene.translationsRotationsScalings3DInto( + data.objects ? Containers::arrayView(objects) : nullptr, + data.field1 ? Containers::arrayView(translations) : nullptr, + data.field2 ? Containers::arrayView(rotations) : nullptr, + data.field3 ? Containers::arrayView(scalings) : nullptr + ); + if(data.objects) CORRADE_COMPARE_AS(Containers::stridedArrayView(objects), + view.slice(&Field::object), + TestSuite::Compare::Container); + if(data.field1) CORRADE_COMPARE_AS(Containers::stridedArrayView(translations), view.slice(&Field::translation), TestSuite::Compare::Container); - - /* Variant with just rotations */ - } { - Quaternion rotationsOut[3]; - scene.translationsRotationsScalings3DInto(nullptr, rotationsOut, nullptr); - CORRADE_COMPARE_AS(Containers::stridedArrayView(rotationsOut), + if(data.field2) CORRADE_COMPARE_AS(Containers::stridedArrayView(rotations), view.slice(&Field::rotation), TestSuite::Compare::Container); - - /* Variant with just scalings */ - } { - Vector3 scalingsOut[3]; - scene.translationsRotationsScalings3DInto(nullptr, nullptr, scalingsOut); - CORRADE_COMPARE_AS(Containers::stridedArrayView(scalingsOut), + if(data.field3) CORRADE_COMPARE_AS(Containers::stridedArrayView(scalings), view.slice(&Field::scaling), TestSuite::Compare::Container); - /* Variant with neither is stupid, but should work too */ - } { - scene.translationsRotationsScalings3DInto(nullptr, nullptr, nullptr); - /* The offset variant only a subset */ } { - Containers::Array out{data.size}; - CORRADE_COMPARE(scene.transformations3DInto(data.offset, out), data.expectedSize); - CORRADE_COMPARE_AS(out.prefix(data.expectedSize), - Containers::arrayView(expected).slice(data.offset, data.offset + data.expectedSize), - TestSuite::Compare::Container); - - /* Variant with TRS components */ - } { - Containers::Array translationsOut{data.size}; - Containers::Array rotationsOut{data.size}; - Containers::Array scalingsOut{data.size}; - CORRADE_COMPARE(scene.translationsRotationsScalings3DInto(data.offset, translationsOut, rotationsOut, scalingsOut), data.expectedSize); - CORRADE_COMPARE_AS(translationsOut.prefix(data.expectedSize), - view.slice(&Field::translation) - .slice(data.offset, data.offset + data.expectedSize), - TestSuite::Compare::Container); - CORRADE_COMPARE_AS(rotationsOut.prefix(data.expectedSize), - view.slice(&Field::rotation) - .slice(data.offset, data.offset + data.expectedSize), - TestSuite::Compare::Container); - CORRADE_COMPARE_AS(scalingsOut.prefix(data.expectedSize), - view.slice(&Field::scaling) + Containers::Array objects{data.size}; + Containers::Array translations{data.size}; + Containers::Array rotations{data.size}; + Containers::Array scalings{data.size}; + CORRADE_COMPARE(scene.translationsRotationsScalings3DInto(data.offset, + data.objects ? arrayView(objects) : nullptr, + data.field1 ? arrayView(translations) : nullptr, + data.field2 ? arrayView(rotations) : nullptr, + data.field3 ? arrayView(scalings) : nullptr + ), data.expectedSize); + if(data.objects) CORRADE_COMPARE_AS(objects.prefix(data.expectedSize), + view.slice(&Field::object) .slice(data.offset, data.offset + data.expectedSize), TestSuite::Compare::Container); - - /* Variant with just translations */ - } { - Containers::Array translationsOut{data.size}; - CORRADE_COMPARE(scene.translationsRotationsScalings3DInto(data.offset, translationsOut, nullptr, nullptr), data.expectedSize); - CORRADE_COMPARE_AS(translationsOut.prefix(data.expectedSize), + if(data.field1) CORRADE_COMPARE_AS(translations.prefix(data.expectedSize), view.slice(&Field::translation) .slice(data.offset, data.offset + data.expectedSize), TestSuite::Compare::Container); - - /* Variant with just rotations */ - } { - Containers::Array rotationsOut{data.size}; - CORRADE_COMPARE(scene.translationsRotationsScalings3DInto(data.offset, nullptr, rotationsOut, nullptr), data.expectedSize); - CORRADE_COMPARE_AS(rotationsOut.prefix(data.expectedSize), + if(data.field2) CORRADE_COMPARE_AS(rotations.prefix(data.expectedSize), view.slice(&Field::rotation) .slice(data.offset, data.offset + data.expectedSize), TestSuite::Compare::Container); - - /* Variant with just scalings */ - } { - Containers::Array scalingsOut{data.size}; - CORRADE_COMPARE(scene.translationsRotationsScalings3DInto(data.offset, nullptr, nullptr, scalingsOut), data.expectedSize); - CORRADE_COMPARE_AS(scalingsOut.prefix(data.expectedSize), + if(data.field3) CORRADE_COMPARE_AS(scalings.prefix(data.expectedSize), view.slice(&Field::scaling) .slice(data.offset, data.offset + data.expectedSize), TestSuite::Compare::Container); - - /* Variant with neither is stupid, but should work too */ - } { - CORRADE_COMPARE(scene.translationsRotationsScalings3DInto(data.offset, nullptr, nullptr, nullptr), 0); } } @@ -3503,12 +3659,19 @@ void SceneDataTest::transformations3DIntoArrayInvalidSizeOrOffset() { std::ostringstream out; Error redirectError{&out}; - Matrix4 destination[2]; - scene.transformations3DInto(destination); - scene.transformations3DInto(4, destination); + UnsignedInt objectDestinationCorrect[3]; + UnsignedInt objectDestination[2]; + Matrix4 fieldDestinationCorrect[3]; + Matrix4 fieldDestination[2]; + scene.transformations3DInto(objectDestination, fieldDestinationCorrect); + scene.transformations3DInto(objectDestinationCorrect, fieldDestination); + scene.transformations3DInto(4, objectDestination, fieldDestination); + scene.transformations3DInto(0, objectDestinationCorrect, fieldDestination); CORRADE_COMPARE(out.str(), - "Trade::SceneData::transformations3DInto(): expected a view with 3 elements but got 2\n" - "Trade::SceneData::transformations3DInto(): offset 4 out of bounds for a field of size 3\n"); + "Trade::SceneData::transformations3DInto(): expected object destination view either empty or with 3 elements but got 2\n" + "Trade::SceneData::transformations3DInto(): expected field destination view either empty or with 3 elements but got 2\n" + "Trade::SceneData::transformations3DInto(): offset 4 out of bounds for a field of size 3\n" + "Trade::SceneData::transformations3DInto(): object and field destination views have different size, 3 vs 2\n"); } void SceneDataTest::transformations3DIntoArrayInvalidSizeOrOffsetTRS() { @@ -3529,24 +3692,34 @@ void SceneDataTest::transformations3DIntoArrayInvalidSizeOrOffsetTRS() { std::ostringstream out; Error redirectError{&out}; + UnsignedInt objectDestinationCorrect[3]; + UnsignedInt objectDestination[2]; Vector3 translationDestinationCorrect[3]; Vector3 translationDestination[2]; Quaternion rotationDestinationCorrect[3]; Quaternion rotationDestination[2]; Vector3 scalingDestinationCorrect[3]; Vector3 scalingDestination[2]; - scene.translationsRotationsScalings3DInto(translationDestination, rotationDestinationCorrect, scalingDestinationCorrect); - scene.translationsRotationsScalings3DInto(translationDestinationCorrect, rotationDestination, scalingDestinationCorrect); - scene.translationsRotationsScalings3DInto(translationDestinationCorrect, rotationDestinationCorrect, scalingDestination); - scene.translationsRotationsScalings3DInto(4, translationDestination, rotationDestination, scalingDestination); - scene.translationsRotationsScalings3DInto(0, translationDestinationCorrect, rotationDestination, nullptr); - scene.translationsRotationsScalings3DInto(0, translationDestinationCorrect, nullptr, scalingDestination); - scene.translationsRotationsScalings3DInto(0, nullptr, rotationDestinationCorrect, scalingDestination); + scene.translationsRotationsScalings3DInto(objectDestination, translationDestinationCorrect, rotationDestinationCorrect, scalingDestinationCorrect); + scene.translationsRotationsScalings3DInto(objectDestinationCorrect, translationDestination, rotationDestinationCorrect, scalingDestinationCorrect); + scene.translationsRotationsScalings3DInto(objectDestinationCorrect, translationDestinationCorrect, rotationDestination, scalingDestinationCorrect); + scene.translationsRotationsScalings3DInto(objectDestinationCorrect, translationDestinationCorrect, rotationDestinationCorrect, scalingDestination); + scene.translationsRotationsScalings3DInto(4, objectDestination, translationDestination, rotationDestination, scalingDestination); + scene.translationsRotationsScalings3DInto(0, objectDestinationCorrect, translationDestination, nullptr, nullptr); + scene.translationsRotationsScalings3DInto(0, objectDestinationCorrect, nullptr, rotationDestination, nullptr); + scene.translationsRotationsScalings3DInto(0, objectDestinationCorrect, nullptr, nullptr, scalingDestination); + scene.translationsRotationsScalings3DInto(0, nullptr, translationDestinationCorrect, rotationDestination, nullptr); + scene.translationsRotationsScalings3DInto(0, nullptr, translationDestinationCorrect, nullptr, scalingDestination); + scene.translationsRotationsScalings3DInto(0, nullptr, nullptr, rotationDestinationCorrect, scalingDestination); CORRADE_COMPARE(out.str(), + "Trade::SceneData::translationsRotationsScalings3DInto(): expected object destination view either empty or with 3 elements but got 2\n" "Trade::SceneData::translationsRotationsScalings3DInto(): expected translation destination view either empty or with 3 elements but got 2\n" "Trade::SceneData::translationsRotationsScalings3DInto(): expected rotation destination view either empty or with 3 elements but got 2\n" "Trade::SceneData::translationsRotationsScalings3DInto(): expected scaling destination view either empty or with 3 elements but got 2\n" "Trade::SceneData::translationsRotationsScalings3DInto(): offset 4 out of bounds for a field of size 3\n" + "Trade::SceneData::translationsRotationsScalings3DInto(): object and translation destination views have different size, 3 vs 2\n" + "Trade::SceneData::translationsRotationsScalings3DInto(): object and rotation destination views have different size, 3 vs 2\n" + "Trade::SceneData::translationsRotationsScalings3DInto(): object and scaling destination views have different size, 3 vs 2\n" "Trade::SceneData::translationsRotationsScalings3DInto(): translation and rotation destination views have different size, 3 vs 2\n" "Trade::SceneData::translationsRotationsScalings3DInto(): translation and scaling destination views have different size, 3 vs 2\n" "Trade::SceneData::translationsRotationsScalings3DInto(): rotation and scaling destination views have different size, 3 vs 2\n"); @@ -3583,12 +3756,11 @@ template void SceneDataTest::meshesMaterialsAsArray() { meshMaterials }}; - CORRADE_COMPARE_AS(scene.meshesMaterialsAsArray(), - (Containers::arrayView>({ - {15, 3}, - {37, -1}, - {44, 25} - })), TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.meshesMaterialsAsArray(), (Containers::arrayView>>({ + {0, {15, 3}}, + {1, {37, -1}}, + {15, {44, 25}} + })), TestSuite::Compare::Container); /* Only meshes */ } { @@ -3596,17 +3768,16 @@ template void SceneDataTest::meshesMaterialsAsArray() { meshes }}; - CORRADE_COMPARE_AS(scene.meshesMaterialsAsArray(), - (Containers::arrayView>({ - {15, -1}, - {37, -1}, - {44, -1} - })), TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.meshesMaterialsAsArray(), (Containers::arrayView>>({ + {0, {15, -1}}, + {1, {37, -1}}, + {15, {44, -1}} + })), TestSuite::Compare::Container); } } void SceneDataTest::meshesMaterialsIntoArray() { - auto&& data = IntoArrayOffsetData[testCaseInstanceId()]; + auto&& data = IntoArrayOffset2Data[testCaseInstanceId()]; setTestCaseDescription(data.name); /* Both AsArray() and Into() share a common helper. The AsArray() test @@ -3638,71 +3809,46 @@ void SceneDataTest::meshesMaterialsIntoArray() { /* The offset-less overload should give back all data */ { - UnsignedInt meshesOut[3]; - Int meshMaterialsOut[3]; - scene.meshesMaterialsInto(meshesOut, meshMaterialsOut); - CORRADE_COMPARE_AS(Containers::stridedArrayView(meshesOut), - view.slice(&Field::mesh), - TestSuite::Compare::Container); - CORRADE_COMPARE_AS(Containers::stridedArrayView(meshMaterialsOut), - view.slice(&Field::meshMaterial), + UnsignedInt objects[3]; + UnsignedInt meshes[3]; + Int meshMaterials[3]; + scene.meshesMaterialsInto( + data.objects ? Containers::arrayView(objects) : nullptr, + data.field1 ? Containers::arrayView(meshes) : nullptr, + data.field2 ? Containers::arrayView(meshMaterials) : nullptr + ); + if(data.objects) CORRADE_COMPARE_AS(Containers::stridedArrayView(objects), + view.slice(&Field::object), TestSuite::Compare::Container); - - /* Variant with just meshes */ - } { - UnsignedInt meshesOut[3]; - scene.meshesMaterialsInto(meshesOut, nullptr); - CORRADE_COMPARE_AS(Containers::stridedArrayView(meshesOut), + if(data.field1) CORRADE_COMPARE_AS(Containers::stridedArrayView(meshes), view.slice(&Field::mesh), TestSuite::Compare::Container); - - /* Variant with just materials */ - } { - Int meshMaterialsOut[3]; - scene.meshesMaterialsInto(nullptr, meshMaterialsOut); - CORRADE_COMPARE_AS(Containers::stridedArrayView(meshMaterialsOut), + if(data.field2) CORRADE_COMPARE_AS(Containers::stridedArrayView(meshMaterials), view.slice(&Field::meshMaterial), TestSuite::Compare::Container); - /* Variant with neither is stupid, but should work too */ - } { - scene.meshesMaterialsInto(nullptr, nullptr); - /* The offset variant should give back only a subset */ } { - Containers::Array meshesOut{data.size}; - Containers::Array meshMaterialsOut{data.size}; - CORRADE_COMPARE(scene.meshesMaterialsInto(data.offset, meshesOut, meshMaterialsOut), data.expectedSize); - CORRADE_COMPARE_AS(meshesOut.prefix(data.expectedSize), - view.slice(&Field::mesh) - .slice(data.offset, data.offset + data.expectedSize), - TestSuite::Compare::Container); - CORRADE_COMPARE_AS(meshMaterialsOut.prefix(data.expectedSize), - view.slice(&Field::meshMaterial) + Containers::Array objects{data.size}; + Containers::Array meshes{data.size}; + Containers::Array meshMaterials{data.size}; + CORRADE_COMPARE(scene.meshesMaterialsInto(data.offset, + data.objects ? arrayView(objects) : nullptr, + data.field1 ? arrayView(meshes) : nullptr, + data.field2 ? arrayView(meshMaterials) : nullptr + ), data.expectedSize); + if(data.objects) CORRADE_COMPARE_AS(objects.prefix(data.expectedSize), + view.slice(&Field::object) .slice(data.offset, data.offset + data.expectedSize), TestSuite::Compare::Container); - - /* Variant with just meshes */ - } { - Containers::Array meshesOut{data.size}; - CORRADE_COMPARE(scene.meshesMaterialsInto(data.offset, meshesOut, nullptr), data.expectedSize); - CORRADE_COMPARE_AS(meshesOut.prefix(data.expectedSize), + if(data.field1) CORRADE_COMPARE_AS(meshes.prefix(data.expectedSize), view.slice(&Field::mesh) .slice(data.offset, data.offset + data.expectedSize), TestSuite::Compare::Container); - - /* Variant with just materials */ - } { - Containers::Array meshMaterialsOut{data.size}; - CORRADE_COMPARE(scene.meshesMaterialsInto(data.offset, nullptr, meshMaterialsOut), data.expectedSize); - CORRADE_COMPARE_AS(meshMaterialsOut.prefix(data.expectedSize), + if(data.field2) CORRADE_COMPARE_AS(meshMaterials.prefix(data.expectedSize), view.slice(&Field::meshMaterial) .slice(data.offset, data.offset + data.expectedSize), TestSuite::Compare::Container); - - /* Variant with neither is stupid, but should work too */ - } { - CORRADE_COMPARE(scene.meshesMaterialsInto(data.offset, nullptr, nullptr), 0); } } @@ -3724,18 +3870,26 @@ void SceneDataTest::meshesMaterialsIntoArrayInvalidSizeOrOffset() { std::ostringstream out; Error redirectError{&out}; + UnsignedInt objectDestinationCorrect[3]; + UnsignedInt objectDestination[2]; UnsignedInt meshDestinationCorrect[3]; UnsignedInt meshDestination[2]; Int meshMaterialDestinationCorrect[3]; Int meshMaterialDestination[2]; - scene.meshesMaterialsInto(meshDestination, meshMaterialDestinationCorrect); - scene.meshesMaterialsInto(meshDestinationCorrect, meshMaterialDestination); - scene.meshesMaterialsInto(4, meshDestination, meshMaterialDestination); - scene.meshesMaterialsInto(0, meshDestinationCorrect, meshMaterialDestination); + scene.meshesMaterialsInto(objectDestination, meshDestinationCorrect, meshMaterialDestinationCorrect); + scene.meshesMaterialsInto(objectDestinationCorrect, meshDestination, meshMaterialDestinationCorrect); + scene.meshesMaterialsInto(objectDestinationCorrect, meshDestinationCorrect, meshMaterialDestination); + scene.meshesMaterialsInto(4, objectDestination, meshDestination, meshMaterialDestination); + scene.meshesMaterialsInto(0, objectDestinationCorrect, meshDestination, nullptr); + scene.meshesMaterialsInto(0, objectDestinationCorrect, nullptr, meshMaterialDestination); + scene.meshesMaterialsInto(0, nullptr, meshDestinationCorrect, meshMaterialDestination); CORRADE_COMPARE(out.str(), + "Trade::SceneData::meshesMaterialsInto(): expected object destination view either empty or with 3 elements but got 2\n" "Trade::SceneData::meshesMaterialsInto(): expected mesh destination view either empty or with 3 elements but got 2\n" "Trade::SceneData::meshesMaterialsInto(): expected mesh material destination view either empty or with 3 elements but got 2\n" "Trade::SceneData::meshesMaterialsInto(): offset 4 out of bounds for a field of size 3\n" + "Trade::SceneData::meshesMaterialsInto(): object and mesh destination views have different size, 3 vs 2\n" + "Trade::SceneData::meshesMaterialsInto(): object and mesh material destination views have different size, 3 vs 2\n" "Trade::SceneData::meshesMaterialsInto(): mesh and mesh material destination views have different size, 3 vs 2\n"); } @@ -3759,13 +3913,15 @@ template void SceneDataTest::lightsAsArray() { SceneFieldData{SceneField::Light, view.slice(&Field::object), view.slice(&Field::light)} }}; - CORRADE_COMPARE_AS(scene.lightsAsArray(), - Containers::arrayView({15, 37, 44}), - TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.lightsAsArray(), (Containers::arrayView>({ + {0, 15}, + {1, 37}, + {15, 44} + })), TestSuite::Compare::Container); } void SceneDataTest::lightsIntoArray() { - auto&& data = IntoArrayOffsetData[testCaseInstanceId()]; + auto&& data = IntoArrayOffset1Data[testCaseInstanceId()]; setTestCaseDescription(data.name); /* Both AsArray() and Into() share a common helper. The AsArray() test @@ -3793,17 +3949,32 @@ void SceneDataTest::lightsIntoArray() { /* The offset-less overload should give back all data */ { - UnsignedInt out[3]; - scene.lightsInto(out); - CORRADE_COMPARE_AS(Containers::stridedArrayView(out), + UnsignedInt objects[3]; + UnsignedInt field[3]; + scene.lightsInto( + data.objects ? Containers::arrayView(objects) : nullptr, + data.field ? Containers::arrayView(field) : nullptr + ); + if(data.objects) CORRADE_COMPARE_AS(Containers::stridedArrayView(objects), + view.slice(&Field::object), + TestSuite::Compare::Container); + if(data.field) CORRADE_COMPARE_AS(Containers::stridedArrayView(field), view.slice(&Field::light), TestSuite::Compare::Container); /* The offset variant only a subset */ } { - Containers::Array out{data.size}; - CORRADE_COMPARE(scene.lightsInto(data.offset, out), data.expectedSize); - CORRADE_COMPARE_AS(out.prefix(data.expectedSize), + Containers::Array objects{data.size}; + Containers::Array field{data.size}; + CORRADE_COMPARE(scene.lightsInto(data.offset, + data.objects ? arrayView(objects) : nullptr, + data.field ? arrayView(field) : nullptr + ), data.expectedSize); + if(data.objects) CORRADE_COMPARE_AS(objects.prefix(data.expectedSize), + view.slice(&Field::object) + .slice(data.offset, data.offset + data.expectedSize), + TestSuite::Compare::Container); + if(data.field) CORRADE_COMPARE_AS(field.prefix(data.expectedSize), view.slice(&Field::light) .slice(data.offset, data.offset + data.expectedSize), TestSuite::Compare::Container); @@ -3828,12 +3999,19 @@ void SceneDataTest::lightsIntoArrayInvalidSizeOrOffset() { std::ostringstream out; Error redirectError{&out}; - UnsignedInt destination[2]; - scene.lightsInto(destination); - scene.lightsInto(4, destination); + UnsignedInt objectDestinationCorrect[3]; + UnsignedInt objectDestination[2]; + UnsignedInt fieldDestinationCorrect[3]; + UnsignedInt fieldDestination[2]; + scene.lightsInto(objectDestination, fieldDestinationCorrect); + scene.lightsInto(objectDestinationCorrect, fieldDestination); + scene.lightsInto(4, objectDestination, fieldDestination); + scene.lightsInto(0, objectDestinationCorrect, fieldDestination); CORRADE_COMPARE(out.str(), - "Trade::SceneData::lightsInto(): expected a view with 3 elements but got 2\n" - "Trade::SceneData::lightsInto(): offset 4 out of bounds for a field of size 3\n"); + "Trade::SceneData::lightsInto(): expected object destination view either empty or with 3 elements but got 2\n" + "Trade::SceneData::lightsInto(): expected field destination view either empty or with 3 elements but got 2\n" + "Trade::SceneData::lightsInto(): offset 4 out of bounds for a field of size 3\n" + "Trade::SceneData::lightsInto(): object and field destination views have different size, 3 vs 2\n"); } template void SceneDataTest::camerasAsArray() { @@ -3856,13 +4034,15 @@ template void SceneDataTest::camerasAsArray() { SceneFieldData{SceneField::Camera, view.slice(&Field::object), view.slice(&Field::camera)} }}; - CORRADE_COMPARE_AS(scene.camerasAsArray(), - Containers::arrayView({15, 37, 44}), - TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.camerasAsArray(), (Containers::arrayView>({ + {0, 15}, + {1, 37}, + {15, 44} + })), TestSuite::Compare::Container); } void SceneDataTest::camerasIntoArray() { - auto&& data = IntoArrayOffsetData[testCaseInstanceId()]; + auto&& data = IntoArrayOffset1Data[testCaseInstanceId()]; setTestCaseDescription(data.name); /* Both AsArray() and Into() share a common helper. The AsArray() test @@ -3890,17 +4070,32 @@ void SceneDataTest::camerasIntoArray() { /* The offset-less overload should give back all data */ { - UnsignedInt out[3]; - scene.camerasInto(out); - CORRADE_COMPARE_AS(Containers::stridedArrayView(out), + UnsignedInt objects[3]; + UnsignedInt field[3]; + scene.camerasInto( + data.objects ? Containers::arrayView(objects) : nullptr, + data.field ? Containers::arrayView(field) : nullptr + ); + if(data.objects) CORRADE_COMPARE_AS(Containers::stridedArrayView(objects), + view.slice(&Field::object), + TestSuite::Compare::Container); + if(data.field) CORRADE_COMPARE_AS(Containers::stridedArrayView(field), view.slice(&Field::camera), TestSuite::Compare::Container); /* The offset variant only a subset */ } { - Containers::Array out{data.size}; - CORRADE_COMPARE(scene.camerasInto(data.offset, out), data.expectedSize); - CORRADE_COMPARE_AS(out.prefix(data.expectedSize), + Containers::Array objects{data.size}; + Containers::Array field{data.size}; + CORRADE_COMPARE(scene.camerasInto(data.offset, + data.objects ? arrayView(objects) : nullptr, + data.field ? arrayView(field) : nullptr + ), data.expectedSize); + if(data.objects) CORRADE_COMPARE_AS(objects.prefix(data.expectedSize), + view.slice(&Field::object) + .slice(data.offset, data.offset + data.expectedSize), + TestSuite::Compare::Container); + if(data.field) CORRADE_COMPARE_AS(field.prefix(data.expectedSize), view.slice(&Field::camera) .slice(data.offset, data.offset + data.expectedSize), TestSuite::Compare::Container); @@ -3925,12 +4120,19 @@ void SceneDataTest::camerasIntoArrayInvalidSizeOrOffset() { std::ostringstream out; Error redirectError{&out}; - UnsignedInt destination[2]; - scene.camerasInto(destination); - scene.camerasInto(4, destination); + UnsignedInt objectDestinationCorrect[3]; + UnsignedInt objectDestination[2]; + UnsignedInt fieldDestinationCorrect[3]; + UnsignedInt fieldDestination[2]; + scene.camerasInto(objectDestination, fieldDestinationCorrect); + scene.camerasInto(objectDestinationCorrect, fieldDestination); + scene.camerasInto(4, objectDestination, fieldDestination); + scene.camerasInto(0, objectDestinationCorrect, fieldDestination); CORRADE_COMPARE(out.str(), - "Trade::SceneData::camerasInto(): expected a view with 3 elements but got 2\n" - "Trade::SceneData::camerasInto(): offset 4 out of bounds for a field of size 3\n"); + "Trade::SceneData::camerasInto(): expected object destination view either empty or with 3 elements but got 2\n" + "Trade::SceneData::camerasInto(): expected field destination view either empty or with 3 elements but got 2\n" + "Trade::SceneData::camerasInto(): offset 4 out of bounds for a field of size 3\n" + "Trade::SceneData::camerasInto(): object and field destination views have different size, 3 vs 2\n"); } template void SceneDataTest::skinsAsArray() { @@ -3955,13 +4157,15 @@ template void SceneDataTest::skinsAsArray() { SceneFieldData{SceneField::Skin, view.slice(&Field::object), view.slice(&Field::skin)} }}; - CORRADE_COMPARE_AS(scene.skinsAsArray(), - Containers::arrayView({15, 37, 44}), - TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.skinsAsArray(), (Containers::arrayView>({ + {0, 15}, + {1, 37}, + {15, 44} + })), TestSuite::Compare::Container); } void SceneDataTest::skinsIntoArray() { - auto&& data = IntoArrayOffsetData[testCaseInstanceId()]; + auto&& data = IntoArrayOffset1Data[testCaseInstanceId()]; setTestCaseDescription(data.name); /* Both AsArray() and Into() share a common helper. The AsArray() test @@ -3991,17 +4195,32 @@ void SceneDataTest::skinsIntoArray() { /* The offset-less overload should give back all data */ { - UnsignedInt out[3]; - scene.skinsInto(out); - CORRADE_COMPARE_AS(Containers::stridedArrayView(out), + UnsignedInt objects[3]; + UnsignedInt field[3]; + scene.skinsInto( + data.objects ? Containers::arrayView(objects) : nullptr, + data.field ? Containers::arrayView(field) : nullptr + ); + if(data.objects) CORRADE_COMPARE_AS(Containers::stridedArrayView(objects), + view.slice(&Field::object), + TestSuite::Compare::Container); + if(data.field) CORRADE_COMPARE_AS(Containers::stridedArrayView(field), view.slice(&Field::skin), TestSuite::Compare::Container); /* The offset variant only a subset */ } { - Containers::Array out{data.size}; - CORRADE_COMPARE(scene.skinsInto(data.offset, out), data.expectedSize); - CORRADE_COMPARE_AS(out.prefix(data.expectedSize), + Containers::Array objects{data.size}; + Containers::Array field{data.size}; + CORRADE_COMPARE(scene.skinsInto(data.offset, + data.objects ? arrayView(objects) : nullptr, + data.field ? arrayView(field) : nullptr + ), data.expectedSize); + if(data.objects) CORRADE_COMPARE_AS(objects.prefix(data.expectedSize), + view.slice(&Field::object) + .slice(data.offset, data.offset + data.expectedSize), + TestSuite::Compare::Container); + if(data.field) CORRADE_COMPARE_AS(field.prefix(data.expectedSize), view.slice(&Field::skin) .slice(data.offset, data.offset + data.expectedSize), TestSuite::Compare::Container); @@ -4029,12 +4248,19 @@ void SceneDataTest::skinsIntoArrayInvalidSizeOrOffset() { std::ostringstream out; Error redirectError{&out}; - UnsignedInt destination[2]; - scene.skinsInto(destination); - scene.skinsInto(4, destination); + UnsignedInt objectDestinationCorrect[3]; + UnsignedInt objectDestination[2]; + UnsignedInt fieldDestinationCorrect[3]; + UnsignedInt fieldDestination[2]; + scene.skinsInto(objectDestination, fieldDestinationCorrect); + scene.skinsInto(objectDestinationCorrect, fieldDestination); + scene.skinsInto(4, objectDestination, fieldDestination); + scene.skinsInto(0, objectDestinationCorrect, fieldDestination); CORRADE_COMPARE(out.str(), - "Trade::SceneData::skinsInto(): expected a view with 3 elements but got 2\n" - "Trade::SceneData::skinsInto(): offset 4 out of bounds for a field of size 3\n"); + "Trade::SceneData::skinsInto(): expected object destination view either empty or with 3 elements but got 2\n" + "Trade::SceneData::skinsInto(): expected field destination view either empty or with 3 elements but got 2\n" + "Trade::SceneData::skinsInto(): offset 4 out of bounds for a field of size 3\n" + "Trade::SceneData::skinsInto(): object and field destination views have different size, 3 vs 2\n"); } template void SceneDataTest::importerStateAsArray() { @@ -4059,13 +4285,15 @@ template void SceneDataTest::importerStateAsArray() { SceneFieldData{SceneField::ImporterState, view.slice(&Field::object), view.slice(&Field::importerState)} }}; - CORRADE_COMPARE_AS(scene.importerStateAsArray(), - Containers::arrayView({&a, nullptr, &b}), - TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.importerStateAsArray(), (Containers::arrayView>({ + {0, &a}, + {1, nullptr}, + {15, &b} + })), TestSuite::Compare::Container); } void SceneDataTest::importerStateIntoArray() { - auto&& data = IntoArrayOffsetData[testCaseInstanceId()]; + auto&& data = IntoArrayOffset1Data[testCaseInstanceId()]; setTestCaseDescription(data.name); /* Both AsArray() and Into() share a common helper. The AsArray() test @@ -4095,17 +4323,32 @@ void SceneDataTest::importerStateIntoArray() { /* The offset-less overload should give back all data */ { - const void* out[3]; - scene.importerStateInto(out); - CORRADE_COMPARE_AS(Containers::stridedArrayView(out), + UnsignedInt objects[3]; + const void* field[3]; + scene.importerStateInto( + data.objects ? Containers::arrayView(objects) : nullptr, + data.field ? Containers::arrayView(field) : nullptr + ); + if(data.objects) CORRADE_COMPARE_AS(Containers::stridedArrayView(objects), + view.slice(&Field::object), + TestSuite::Compare::Container); + if(data.field) CORRADE_COMPARE_AS(Containers::stridedArrayView(field), 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), + Containers::Array objects{data.size}; + Containers::Array field{data.size}; + CORRADE_COMPARE(scene.importerStateInto(data.offset, + data.objects ? arrayView(objects) : nullptr, + data.field ? arrayView(field) : nullptr + ), data.expectedSize); + if(data.objects) CORRADE_COMPARE_AS(objects.prefix(data.expectedSize), + view.slice(&Field::object) + .slice(data.offset, data.offset + data.expectedSize), + TestSuite::Compare::Container); + if(data.field) CORRADE_COMPARE_AS(field.prefix(data.expectedSize), view.slice(&Field::importerState) .slice(data.offset, data.offset + data.expectedSize), TestSuite::Compare::Container); @@ -4130,12 +4373,19 @@ void SceneDataTest::importerStateIntoArrayInvalidSizeOrOffset() { std::ostringstream out; Error redirectError{&out}; - const void* destination[2]; - scene.importerStateInto(destination); - scene.importerStateInto(4, destination); + UnsignedInt objectDestinationCorrect[3]; + UnsignedInt objectDestination[2]; + const void* fieldDestinationCorrect[3]; + const void* fieldDestination[2]; + scene.importerStateInto(objectDestination, fieldDestinationCorrect); + scene.importerStateInto(objectDestinationCorrect, fieldDestination); + scene.importerStateInto(4, objectDestination, fieldDestination); + scene.importerStateInto(0, objectDestinationCorrect, fieldDestination); 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"); + "Trade::SceneData::importerStateInto(): expected object destination view either empty or with 3 elements but got 2\n" + "Trade::SceneData::importerStateInto(): expected field destination view either empty or with 3 elements but got 2\n" + "Trade::SceneData::importerStateInto(): offset 4 out of bounds for a field of size 3\n" + "Trade::SceneData::importerStateInto(): object and field destination views have different size, 3 vs 2\n"); } void SceneDataTest::mutableAccessNotAllowed() { @@ -4312,35 +4562,35 @@ void SceneDataTest::fieldNotFound() { scene.mutableField(sceneFieldCustom(666)); scene.parentsAsArray(); - scene.parentsInto(nullptr); - scene.parentsInto(0, nullptr); + scene.parentsInto(nullptr, nullptr); + scene.parentsInto(0, nullptr, nullptr); scene.transformations2DAsArray(); - scene.transformations2DInto(nullptr); - scene.transformations2DInto(0, nullptr); + scene.transformations2DInto(nullptr, nullptr); + scene.transformations2DInto(0, nullptr, nullptr); scene.translationsRotationsScalings2DAsArray(); - scene.translationsRotationsScalings2DInto(nullptr, nullptr, nullptr); - scene.translationsRotationsScalings2DInto(0, nullptr, nullptr, nullptr); + scene.translationsRotationsScalings2DInto(nullptr, nullptr, nullptr, nullptr); + scene.translationsRotationsScalings2DInto(0, nullptr, nullptr, nullptr, nullptr); scene.transformations3DAsArray(); - scene.transformations3DInto(nullptr); - scene.transformations3DInto(0, nullptr); + scene.transformations3DInto(nullptr, nullptr); + scene.transformations3DInto(0, nullptr, nullptr); scene.translationsRotationsScalings3DAsArray(); - scene.translationsRotationsScalings3DInto(nullptr, nullptr, nullptr); - scene.translationsRotationsScalings3DInto(0, nullptr, nullptr, nullptr); + scene.translationsRotationsScalings3DInto(nullptr, nullptr, nullptr, nullptr); + scene.translationsRotationsScalings3DInto(0, nullptr, nullptr, nullptr, nullptr); scene.meshesMaterialsAsArray(); - scene.meshesMaterialsInto(nullptr, nullptr); - scene.meshesMaterialsInto(0, nullptr, nullptr); + scene.meshesMaterialsInto(nullptr, nullptr, nullptr); + scene.meshesMaterialsInto(0, nullptr, nullptr, nullptr); scene.lightsAsArray(); - scene.lightsInto(nullptr); - scene.lightsInto(0, nullptr); + scene.lightsInto(nullptr, nullptr); + scene.lightsInto(0, nullptr, nullptr); scene.camerasAsArray(); - scene.camerasInto(nullptr); - scene.camerasInto(0, nullptr); + scene.camerasInto(nullptr, nullptr); + scene.camerasInto(0, nullptr, nullptr); scene.skinsAsArray(); - scene.skinsInto(nullptr); - scene.skinsInto(0, nullptr); + scene.skinsInto(nullptr, nullptr); + scene.skinsInto(0, nullptr, nullptr); scene.importerStateAsArray(); - scene.importerStateInto(nullptr); - scene.importerStateInto(0, nullptr); + scene.importerStateInto(nullptr, nullptr); + scene.importerStateInto(0, nullptr, nullptr); CORRADE_COMPARE(out.str(), "Trade::SceneData::findFieldObjectOffset(): index 2 out of range for 2 fields\n" "Trade::SceneData::fieldObjectOffset(): index 2 out of range for 2 fields\n" diff --git a/src/Magnum/Trade/Test/SceneToolsTest.cpp b/src/Magnum/Trade/Test/SceneToolsTest.cpp index 983ac8006..5fed43146 100644 --- a/src/Magnum/Trade/Test/SceneToolsTest.cpp +++ b/src/Magnum/Trade/Test/SceneToolsTest.cpp @@ -423,32 +423,35 @@ void SceneToolsTest::convertToSingleFunctionObjects() { /* To be extra sure, verify the actual data. Parents have a few objects added, the rest is the same */ - CORRADE_COMPARE_AS(scene.objectsAsArray(SceneField::Parent), Containers::arrayView({ - 15, 21, 22, 23, 1, 63, 64, 65, 66 - }), TestSuite::Compare::Container); - CORRADE_COMPARE_AS(scene.parentsAsArray(), Containers::arrayView({ - -1, -1, 1, 2, -1, 3, 3, 0, 4 - }), TestSuite::Compare::Container); - - /* Meshes have certain objects reassigned (and materials as well, as they - share the same object mapping view), field data stay the same */ - CORRADE_COMPARE_AS(scene.objectsAsArray(SceneField::Mesh), Containers::arrayView({ - 15, 23, 63, 64, 1, 65, 21 - }), TestSuite::Compare::Container); - CORRADE_COMPARE_AS(scene.objectsAsArray(SceneField::MeshMaterial), Containers::arrayView({ - 15, 23, 63, 64, 1, 65, 21 - }), TestSuite::Compare::Container); - CORRADE_COMPARE_AS(scene.meshesMaterialsAsArray(), - Containers::arrayView(meshesMaterials), - TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.parentsAsArray(), (Containers::arrayView>({ + {15, -1}, + {21, -1}, + {22, 1}, + {23, 2}, + {1, -1}, + {63, 3}, + {64, 3}, + {65, 0}, + {66, 4} + })), TestSuite::Compare::Container); + + /* Meshes / materials have certain objects reassigned, field data stay the + same */ + CORRADE_COMPARE_AS(scene.meshesMaterialsAsArray(), (Containers::arrayView>>({ + {15, {6, 4}}, + {23, {1, 0}}, + {63, {2, 3}}, + {64, {4, 2}}, + {1, {7, 2}}, + {65, {3, 1}}, + {21, {5, -1}} + })), TestSuite::Compare::Container); /* Cameras have certain objects reassigned, field data stay the same */ - CORRADE_COMPARE_AS(scene.objectsAsArray(SceneField::Camera), Containers::arrayView({ - 22, 66 - }), TestSuite::Compare::Container); - CORRADE_COMPARE_AS(scene.camerasAsArray(), - Containers::arrayView(cameras), - TestSuite::Compare::Container); + CORRADE_COMPARE_AS(scene.camerasAsArray(), (Containers::arrayView>({ + {22, 1}, + {66, 5} + })), TestSuite::Compare::Container); } }}}}