From 7192517e6a5169cd759d0732deb0d7c951f1951d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Thu, 16 Sep 2021 17:32:14 +0200 Subject: [PATCH] Trade: combine mesh and material SceneData convenience APIs together. In most cases the user would want to fetch both anyway, and since they're forced to share the same object mapping this is easy to do. --- src/Magnum/Trade/SceneData.cpp | 88 ++++---- src/Magnum/Trade/SceneData.h | 97 +++----- src/Magnum/Trade/Test/SceneDataTest.cpp | 283 +++++++++++------------- 3 files changed, 204 insertions(+), 264 deletions(-) diff --git a/src/Magnum/Trade/SceneData.cpp b/src/Magnum/Trade/SceneData.cpp index 02efa08ce..03965be46 100644 --- a/src/Magnum/Trade/SceneData.cpp +++ b/src/Magnum/Trade/SceneData.cpp @@ -25,6 +25,7 @@ #include "SceneData.h" +#include #include #include "Magnum/Math/Matrix3.h" @@ -1146,68 +1147,63 @@ Containers::Array SceneData::unsignedIndexFieldAsArrayInternal(cons return out; } -Containers::Array SceneData::indexFieldAsArrayInternal(const UnsignedInt fieldId) const { - Containers::Array out{NoInit, std::size_t(_fields[fieldId]._size)}; - indexFieldIntoInternal(fieldId, 0, out); - return out; -} +void SceneData::meshesMaterialsIntoInternal(const UnsignedInt fieldId, const std::size_t offset, const Containers::StridedArrayView1D& meshDestination, const Containers::StridedArrayView1D& meshMaterialDestination) const { + /* fieldId, offset, meshDestination.size() and + meshMaterialDestination.size() is assumed to be in bounds, checked by + the callers */ -void SceneData::meshesInto(const Containers::StridedArrayView1D& destination) const { - const UnsignedInt fieldId = fieldFor(SceneField::Mesh); - CORRADE_ASSERT(fieldId != ~UnsignedInt{}, - "Trade::SceneData::meshesInto(): field not found", ); - CORRADE_ASSERT(destination.size() == _fields[fieldId]._size, - "Trade::SceneData::meshesInto(): expected a view with" << _fields[fieldId]._size << "elements but got" << destination.size(), ); - unsignedIndexFieldIntoInternal(fieldId, 0, destination); -} + if(meshDestination) + unsignedIndexFieldIntoInternal(fieldId, offset, meshDestination); -std::size_t SceneData::meshesInto(const std::size_t offset, const Containers::StridedArrayView1D& destination) const { - const UnsignedInt fieldId = fieldFor(SceneField::Mesh); - CORRADE_ASSERT(fieldId != ~UnsignedInt{}, - "Trade::SceneData::meshesInto(): field not found", {}); - CORRADE_ASSERT(offset <= _fields[fieldId]._size, - "Trade::SceneData::meshesInto(): 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)); - return size; + /* Copy also the material, if desired. If no such field is present, output + -1 for all meshes. */ + if(meshMaterialDestination) { + const UnsignedInt materialFieldId = fieldFor(SceneField::MeshMaterial); + if(materialFieldId == ~UnsignedInt{}) { + constexpr Int invalid[]{-1}; + Utility::copy(Containers::stridedArrayView(invalid).broadcasted<0>(meshMaterialDestination.size()), meshMaterialDestination); + } else indexFieldIntoInternal(materialFieldId, offset, meshMaterialDestination); + } } -Containers::Array SceneData::meshesAsArray() const { +void SceneData::meshesMaterialsInto(const Containers::StridedArrayView1D& meshDestination, const Containers::StridedArrayView1D& meshMaterialDestination) const { const UnsignedInt fieldId = fieldFor(SceneField::Mesh); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, - /* Using the same message as in Into() to avoid too many redundant - strings in the binary */ - "Trade::SceneData::meshesInto(): field not found", {}); - return unsignedIndexFieldAsArrayInternal(fieldId); + "Trade::SceneData::meshesMaterialsInto(): field" << SceneField::Mesh << "not found", ); + 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(), ); + meshesMaterialsIntoInternal(fieldId, 0, meshDestination, meshMaterialDestination); } -void SceneData::meshMaterialsInto(const Containers::StridedArrayView1D& destination) const { - const UnsignedInt fieldId = fieldFor(SceneField::MeshMaterial); - CORRADE_ASSERT(fieldId != ~UnsignedInt{}, - "Trade::SceneData::meshMaterialsInto(): field not found", ); - CORRADE_ASSERT(destination.size() == _fields[fieldId]._size, - "Trade::SceneData::meshMaterialsInto(): expected a view with" << _fields[fieldId]._size << "elements but got" << destination.size(), ); - indexFieldIntoInternal(fieldId, 0, destination); -} - -std::size_t SceneData::meshMaterialsInto(const std::size_t offset, const Containers::StridedArrayView1D& destination) const { - const UnsignedInt fieldId = fieldFor(SceneField::MeshMaterial); +std::size_t SceneData::meshesMaterialsInto(const std::size_t offset, const Containers::StridedArrayView1D& meshDestination, const Containers::StridedArrayView1D& meshMaterialDestination) const { + const UnsignedInt fieldId = fieldFor(SceneField::Mesh); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, - "Trade::SceneData::meshMaterialsInto(): field not found", {}); + "Trade::SceneData::meshesMaterialsInto(): field" << SceneField::Mesh << "not found", {}); CORRADE_ASSERT(offset <= _fields[fieldId]._size, - "Trade::SceneData::meshMaterialsInto(): 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); - indexFieldIntoInternal(fieldId, offset, destination.prefix(size)); + "Trade::SceneData::meshesMaterialsInto(): offset" << offset << "out of bounds for a field of size" << _fields[fieldId]._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); + meshesMaterialsIntoInternal(fieldId, offset, + meshDestination ? meshDestination.prefix(size) : nullptr, + meshMaterialDestination ? meshMaterialDestination.prefix(size) : nullptr); return size; } -Containers::Array SceneData::meshMaterialsAsArray() const { - const UnsignedInt fieldId = fieldFor(SceneField::MeshMaterial); +Containers::Array> SceneData::meshesMaterialsAsArray() const { + const UnsignedInt fieldId = fieldFor(SceneField::Mesh); CORRADE_ASSERT(fieldId != ~UnsignedInt{}, /* Using the same message as in Into() to avoid too many redundant strings in the binary */ - "Trade::SceneData::meshMaterialsInto(): field not found", {}); - return indexFieldAsArrayInternal(fieldId); + "Trade::SceneData::meshesMaterialsInto(): field" << SceneField::Mesh << "not found", {}); + 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)}; + meshesMaterialsIntoInternal(fieldId, 0, meshesOut, meshMaterialsOut); + return out; } void SceneData::lightsInto(const Containers::StridedArrayView1D& destination) const { diff --git a/src/Magnum/Trade/SceneData.h b/src/Magnum/Trade/SceneData.h index 8e7b7b034..89bf35590 100644 --- a/src/Magnum/Trade/SceneData.h +++ b/src/Magnum/Trade/SceneData.h @@ -202,7 +202,7 @@ enum class SceneField: UnsignedInt { * required. If present, both should share the same object mapping view. * Objects with multiple meshes then have the Nth mesh associated with the * Nth material. - * @see @ref SceneData::meshesAsArray() + * @see @ref SceneData::meshesMaterialsAsArray() */ Mesh, @@ -213,7 +213,7 @@ enum class SceneField: UnsignedInt { * but can be also any of @relativeref{SceneFieldType,Byte} or * @relativeref{SceneFieldType,Short}. Expected to share the * object mapping view with @ref SceneField::Mesh. - * @see @ref SceneData::meshMaterialsAsArray() + * @see @ref SceneData::meshesMaterialsAsArray() */ MeshMaterial, @@ -1116,10 +1116,10 @@ class MAGNUM_TRADE_EXPORT SceneData { * 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 meshesAsArray(), @ref meshMaterialsAsArray(), - * @ref lightsAsArray(), @ref camerasAsArray(), @ref skinsAsArray() - * accessors to get common fields converted to usual types, but note - * that these operations involve extra allocation and data conversion. + * @ref meshesMaterialsAsArray(), @ref lightsAsArray(), + * @ref camerasAsArray(), @ref skinsAsArray() accessors to get common + * fields converted to usual types, but note that these operations + * involve extra allocation and data conversion. * @see @ref field(SceneField) const, @ref mutableField(UnsignedInt), * @ref fieldArraySize() */ @@ -1193,11 +1193,10 @@ class MAGNUM_TRADE_EXPORT SceneData { * 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 meshesAsArray(), - * @ref meshMaterialsAsArray(), @ref lightsAsArray(), - * @ref camerasAsArray(), @ref skinsAsArray() accessors to get common - * fields converted to usual types, but note that these operations - * involve extra allocation and data conversion. + * @ref transformations3DAsArray(), @ref meshesMaterialsAsArray(), + * @ref lightsAsArray(), @ref camerasAsArray(), @ref skinsAsArray() + * accessors to get common fields converted to usual types, but note + * that these operations involve extra allocation and data conversion. * @see @ref field(UnsignedInt) const, @ref mutableField(SceneField) */ template::value>::type> Containers::StridedArrayView1D field(SceneField name) const; @@ -1447,76 +1446,44 @@ class MAGNUM_TRADE_EXPORT SceneData { std::size_t transformations3DInto(std::size_t offset, const Containers::StridedArrayView1D& destination) const; /** - * @brief Mesh IDs as 32-bit integers + * @brief Mesh and material IDs as 32-bit integers * @m_since_latest * * Convenience alternative to @ref field(SceneField) const with - * @ref SceneField::Mesh 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. - * @see @ref meshesInto(), @ref hasField() + * @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 + * expected to exist, if @ref SceneField::MeshMaterial isn't present, + * the second returned values are all @cpp -1 @ce. + * @see @ref meshesMaterialsInto(), @ref hasField() */ - Containers::Array meshesAsArray() const; + Containers::Array> meshesMaterialsAsArray() const; /** - * @brief Mesh IDs as 32-bit integers into a pre-allocated view + * @brief Mesh and material IDs as 32-bit integers into a pre-allocated view * @m_since_latest * - * Like @ref meshesAsArray(), 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 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. * @see @ref fieldSize(SceneField) const */ - void meshesInto(const Containers::StridedArrayView1D& destination) const; + void meshesMaterialsInto(const Containers::StridedArrayView1D& meshDestination, const Containers::StridedArrayView1D& meshMaterialDestination) const; /** - * @brief A subrange of mesh IDs as 32-bit integers into a pre-allocated view + * @brief A subrange of mesh and material IDs as 32-bit integers into a pre-allocated view * @m_since_latest * - * Compared to @ref meshesInto(const Containers::StridedArrayView1D&) const + * Compared to @ref meshesMaterialsInto(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. - * @see @ref fieldSize(SceneField) const - */ - std::size_t meshesInto(std::size_t offset, const Containers::StridedArrayView1D& destination) const; - - /** - * @brief Mesh material IDs as 32-bit integers - * @m_since_latest - * - * Convenience alternative to @ref field(SceneField) const with - * @ref SceneField::MeshMaterial 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. - * @see @ref meshMaterialsInto(), @ref hasField() - */ - Containers::Array meshMaterialsAsArray() const; - - /** - * @brief Mesh material IDs as 32-bit integers into a pre-allocated view - * @m_since_latest - * - * Like @ref meshMaterialsAsArray(), but puts the result into - * @p destination instead of allocating a new array. Expects that - * @p destination is sized to contain exactly all data. - * @see @ref fieldSize(SceneField) const - */ - void meshMaterialsInto(const Containers::StridedArrayView1D& destination) const; - - /** - * @brief A subrange of mesh material IDs as 32-bit integers into a pre-allocated view - * @m_since_latest - * - * Compared to @ref meshMaterialsInto(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 */ - std::size_t meshMaterialsInto(std::size_t offset, const Containers::StridedArrayView1D& destination) const; + std::size_t meshesMaterialsInto(std::size_t offset, const Containers::StridedArrayView1D& meshDestination, const Containers::StridedArrayView1D& meshMaterialsDestination) const; /** * @brief Light IDs as 32-bit integers @@ -1692,7 +1659,7 @@ class MAGNUM_TRADE_EXPORT SceneData { 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 indexFieldAsArrayInternal(const UnsignedInt fieldId) const; + MAGNUM_TRADE_LOCAL void meshesMaterialsIntoInternal(UnsignedInt fieldId, std::size_t offset, const Containers::StridedArrayView1D& meshDestination, const Containers::StridedArrayView1D& meshMaterialDestination) const; DataFlags _dataFlags; SceneObjectType _objectType; diff --git a/src/Magnum/Trade/Test/SceneDataTest.cpp b/src/Magnum/Trade/Test/SceneDataTest.cpp index fce55fd68..3f3f16d47 100644 --- a/src/Magnum/Trade/Test/SceneDataTest.cpp +++ b/src/Magnum/Trade/Test/SceneDataTest.cpp @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -124,12 +125,9 @@ struct SceneDataTest: TestSuite::Tester { void transformations3DIntoArray(); void transformations3DIntoArrayTRS(); void transformations3DIntoArrayInvalidSizeOrOffset(); - template void meshesAsArray(); - void meshesIntoArray(); - void meshesIntoArrayInvalidSizeOrOffset(); - template void meshMaterialsAsArray(); - void meshMaterialsIntoArray(); - void meshMaterialsIntoArrayInvalidSizeOrOffset(); + template void meshesMaterialsAsArray(); + void meshesMaterialsIntoArray(); + void meshesMaterialsIntoArrayInvalidSizeOrOffset(); template void lightsAsArray(); void lightsIntoArray(); void lightsIntoArrayInvalidSizeOrOffset(); @@ -295,22 +293,14 @@ SceneDataTest::SceneDataTest() { Containers::arraySize(IntoArrayOffsetData)); addTests({&SceneDataTest::transformations3DIntoArrayInvalidSizeOrOffset, - &SceneDataTest::meshesAsArray, - &SceneDataTest::meshesAsArray, - &SceneDataTest::meshesAsArray}); + &SceneDataTest::meshesMaterialsAsArray, + &SceneDataTest::meshesMaterialsAsArray, + &SceneDataTest::meshesMaterialsAsArray}); - addInstancedTests({&SceneDataTest::meshesIntoArray}, + addInstancedTests({&SceneDataTest::meshesMaterialsIntoArray}, Containers::arraySize(IntoArrayOffsetData)); - addTests({&SceneDataTest::meshesIntoArrayInvalidSizeOrOffset, - &SceneDataTest::meshMaterialsAsArray, - &SceneDataTest::meshMaterialsAsArray, - &SceneDataTest::meshMaterialsAsArray}); - - addInstancedTests({&SceneDataTest::meshMaterialsIntoArray}, - Containers::arraySize(IntoArrayOffsetData)); - - addTests({&SceneDataTest::meshMaterialsIntoArrayInvalidSizeOrOffset, + addTests({&SceneDataTest::meshesMaterialsIntoArrayInvalidSizeOrOffset, &SceneDataTest::lightsAsArray, &SceneDataTest::lightsAsArray, &SceneDataTest::lightsAsArray}); @@ -2787,32 +2777,60 @@ void SceneDataTest::transformations3DIntoArrayInvalidSizeOrOffset() { "Trade::SceneData::transformations3DInto(): offset 4 out of bounds for a field of size 3\n"); } -template void SceneDataTest::meshesAsArray() { - setTestCaseTemplateName(NameTraits::name()); +template void SceneDataTest::meshesMaterialsAsArray() { + setTestCaseTemplateName({NameTraits::name(), NameTraits::name()}); struct Field { UnsignedByte object; T mesh; - } fields[3]{ - {0, T(15)}, - {1, T(37)}, - {15, T(44)} + U meshMaterial; + } fields[]{ + {0, T(15), U(3)}, + {1, T(37), U(-1)}, + {15, T(44), U(25)} }; Containers::StridedArrayView1D view = fields; - SceneData scene{SceneObjectType::UnsignedByte, 50, {}, fields, { - /* To verify it isn't just picking the first ever field */ - SceneFieldData{SceneField::Parent, SceneObjectType::UnsignedByte, nullptr, SceneFieldType::Int, nullptr}, - SceneFieldData{SceneField::Mesh, view.slice(&Field::object), view.slice(&Field::mesh)} - }}; + SceneFieldData meshes{SceneField::Mesh, + view.slice(&Field::object), + view.slice(&Field::mesh)}; + SceneFieldData meshMaterials{SceneField::MeshMaterial, + view.slice(&Field::object), + view.slice(&Field::meshMaterial)}; - CORRADE_COMPARE_AS(scene.meshesAsArray(), - Containers::arrayView({15, 37, 44}), - TestSuite::Compare::Container); + /* Both meshes and materials */ + { + SceneData scene{SceneObjectType::UnsignedByte, 50, {}, fields, { + /* To verify it isn't just picking the first ever field */ + SceneFieldData{SceneField::Parent, SceneObjectType::UnsignedByte, nullptr, SceneFieldType::Int, nullptr}, + meshes, + meshMaterials + }}; + + CORRADE_COMPARE_AS(scene.meshesMaterialsAsArray(), + (Containers::arrayView>({ + {15, 3}, + {37, -1}, + {44, 25} + })), TestSuite::Compare::Container); + + /* Only meshes */ + } { + SceneData scene{SceneObjectType::UnsignedByte, 50, {}, fields, { + meshes + }}; + + CORRADE_COMPARE_AS(scene.meshesMaterialsAsArray(), + (Containers::arrayView>({ + {15, -1}, + {37, -1}, + {44, -1} + })), TestSuite::Compare::Container); + } } -void SceneDataTest::meshesIntoArray() { +void SceneDataTest::meshesMaterialsIntoArray() { auto&& data = IntoArrayOffsetData[testCaseInstanceId()]; setTestCaseDescription(data.name); @@ -2823,10 +2841,11 @@ void SceneDataTest::meshesIntoArray() { struct Field { UnsignedInt object; UnsignedInt mesh; + Int meshMaterial; } fields[]{ - {1, 15}, - {0, 37}, - {4, 44} + {1, 15, 3}, + {0, 37, -1}, + {4, 44, 22} }; Containers::StridedArrayView1D view = fields; @@ -2837,148 +2856,112 @@ void SceneDataTest::meshesIntoArray() { SceneFieldData{SceneField::Mesh, view.slice(&Field::object), view.slice(&Field::mesh)}, + SceneFieldData{SceneField::MeshMaterial, + view.slice(&Field::object), + view.slice(&Field::meshMaterial)}, }}; /* The offset-less overload should give back all data */ { - UnsignedInt out[3]; - scene.meshesInto(out); - CORRADE_COMPARE_AS(Containers::stridedArrayView(out), + 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), + TestSuite::Compare::Container); - /* The offset variant only a subset */ + /* Variant with just meshes */ } { - Containers::Array out{data.size}; - CORRADE_COMPARE(scene.meshesInto(data.offset, out), data.expectedSize); - CORRADE_COMPARE_AS(out.prefix(data.expectedSize), - view.slice(&Field::mesh) - .slice(data.offset, data.offset + data.expectedSize), + UnsignedInt meshesOut[3]; + scene.meshesMaterialsInto(meshesOut, nullptr); + CORRADE_COMPARE_AS(Containers::stridedArrayView(meshesOut), + view.slice(&Field::mesh), TestSuite::Compare::Container); - } -} - -void SceneDataTest::meshesIntoArrayInvalidSizeOrOffset() { - #ifdef CORRADE_NO_ASSERT - CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); - #endif - - struct Field { - UnsignedInt object; - UnsignedInt mesh; - } fields[3]{}; - Containers::StridedArrayView1D view = fields; - - SceneData scene{SceneObjectType::UnsignedInt, 5, {}, fields, { - SceneFieldData{SceneField::Mesh, view.slice(&Field::object), view.slice(&Field::mesh)} - }}; - - std::ostringstream out; - Error redirectError{&out}; - UnsignedInt destination[2]; - scene.meshesInto(destination); - scene.meshesInto(4, destination); - CORRADE_COMPARE(out.str(), - "Trade::SceneData::meshesInto(): expected a view with 3 elements but got 2\n" - "Trade::SceneData::meshesInto(): offset 4 out of bounds for a field of size 3\n"); -} - -template void SceneDataTest::meshMaterialsAsArray() { - setTestCaseTemplateName(NameTraits::name()); - - struct Field { - UnsignedByte object; - T meshMaterial; - } fields[]{ - {0, T(15)}, - {1, T(-1)}, - {15, T(44)} - }; - - Containers::StridedArrayView1D view = fields; - - SceneData scene{SceneObjectType::UnsignedByte, 50, {}, fields, { - /* To verify it isn't just picking the first ever field */ - SceneFieldData{SceneField::Parent, SceneObjectType::UnsignedByte, nullptr, SceneFieldType::Int, nullptr}, - SceneFieldData{SceneField::MeshMaterial, view.slice(&Field::object), view.slice(&Field::meshMaterial)} - }}; - - CORRADE_COMPARE_AS(scene.meshMaterialsAsArray(), - Containers::arrayView({15, -1, 44}), - TestSuite::Compare::Container); -} - -void SceneDataTest::meshMaterialsIntoArray() { - auto&& data = IntoArrayOffsetData[testCaseInstanceId()]; - setTestCaseDescription(data.name); - - /* Both AsArray() and Into() share a common helper. The AsArray() test - above verified handling of various data types and this checks the - offset/size parameters of the Into() variant. */ - - struct Field { - UnsignedInt object; - Int meshMaterial; - } fields[]{ - {1, 15}, - {0, -1}, - {4, 44} - }; + /* Variant with just materials */ + } { + Int meshMaterialsOut[3]; + scene.meshesMaterialsInto(nullptr, meshMaterialsOut); + CORRADE_COMPARE_AS(Containers::stridedArrayView(meshMaterialsOut), + view.slice(&Field::meshMaterial), + TestSuite::Compare::Container); - Containers::StridedArrayView1D view = fields; + /* Variant with neither is stupid, but should work too */ + } { + scene.meshesMaterialsInto(nullptr, nullptr); - 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::MeshMaterial, - view.slice(&Field::object), - view.slice(&Field::meshMaterial)}, - }}; + /* 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) + .slice(data.offset, data.offset + data.expectedSize), + TestSuite::Compare::Container); - /* The offset-less overload should give back all data */ - { - Int out[3]; - scene.meshMaterialsInto(out); - CORRADE_COMPARE_AS(Containers::stridedArrayView(out), - view.slice(&Field::meshMaterial), + /* 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), + view.slice(&Field::mesh) + .slice(data.offset, data.offset + data.expectedSize), TestSuite::Compare::Container); - /* The offset variant only a subset */ + /* Variant with just materials */ } { - Containers::Array out{data.size}; - CORRADE_COMPARE(scene.meshMaterialsInto(data.offset, out), data.expectedSize); - CORRADE_COMPARE_AS(out.prefix(data.expectedSize), + Containers::Array meshMaterialsOut{data.size}; + CORRADE_COMPARE(scene.meshesMaterialsInto(data.offset, nullptr, meshMaterialsOut), data.expectedSize); + CORRADE_COMPARE_AS(meshMaterialsOut.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); } } -void SceneDataTest::meshMaterialsIntoArrayInvalidSizeOrOffset() { +void SceneDataTest::meshesMaterialsIntoArrayInvalidSizeOrOffset() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif struct Field { UnsignedInt object; - Int meshMaterial; + UnsignedInt mesh; } fields[3]{}; Containers::StridedArrayView1D view = fields; SceneData scene{SceneObjectType::UnsignedInt, 5, {}, fields, { - SceneFieldData{SceneField::MeshMaterial, view.slice(&Field::object), view.slice(&Field::meshMaterial)} + SceneFieldData{SceneField::Mesh, view.slice(&Field::object), view.slice(&Field::mesh)} }}; std::ostringstream out; Error redirectError{&out}; - Int destination[2]; - scene.meshMaterialsInto(destination); - scene.meshMaterialsInto(4, destination); + 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); CORRADE_COMPARE(out.str(), - "Trade::SceneData::meshMaterialsInto(): expected a view with 3 elements but got 2\n" - "Trade::SceneData::meshMaterialsInto(): offset 4 out of bounds for a field of size 3\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(): mesh and mesh material destination views have different size, 3 vs 2\n"); } template void SceneDataTest::lightsAsArray() { @@ -3448,12 +3431,9 @@ void SceneDataTest::fieldNotFound() { scene.transformations3DAsArray(); scene.transformations3DInto(nullptr); scene.transformations3DInto(0, nullptr); - scene.meshesAsArray(); - scene.meshesInto(nullptr); - scene.meshesInto(0, nullptr); - scene.meshMaterialsAsArray(); - scene.meshMaterialsInto(nullptr); - scene.meshMaterialsInto(0, nullptr); + scene.meshesMaterialsAsArray(); + scene.meshesMaterialsInto(nullptr, nullptr); + scene.meshesMaterialsInto(0, nullptr, nullptr); scene.lightsAsArray(); scene.lightsInto(nullptr); scene.lightsInto(0, nullptr); @@ -3499,12 +3479,9 @@ void SceneDataTest::fieldNotFound() { "Trade::SceneData::transformations3DInto(): no transformation-related field found\n" "Trade::SceneData::transformations3DInto(): no transformation-related field found\n" "Trade::SceneData::transformations3DInto(): no transformation-related field found\n" - "Trade::SceneData::meshesInto(): field not found\n" - "Trade::SceneData::meshesInto(): field not found\n" - "Trade::SceneData::meshesInto(): field not found\n" - "Trade::SceneData::meshMaterialsInto(): field not found\n" - "Trade::SceneData::meshMaterialsInto(): field not found\n" - "Trade::SceneData::meshMaterialsInto(): field not found\n" + "Trade::SceneData::meshesMaterialsInto(): field Trade::SceneField::Mesh not found\n" + "Trade::SceneData::meshesMaterialsInto(): field Trade::SceneField::Mesh not found\n" + "Trade::SceneData::meshesMaterialsInto(): field Trade::SceneField::Mesh not found\n" "Trade::SceneData::lightsInto(): field not found\n" "Trade::SceneData::lightsInto(): field not found\n" "Trade::SceneData::lightsInto(): field not found\n"